diff --git a/backend/gateway/dto/gallery_converter.go b/backend/gateway/dto/gallery_converter.go index f5c5c8b..ebc5037 100644 --- a/backend/gateway/dto/gallery_converter.go +++ b/backend/gateway/dto/gallery_converter.go @@ -171,6 +171,7 @@ func ConvertInspirationFlowData(pbData *pbGallery.InspirationFlowData) *GetInspi CoverURL: item.CoverUrl, LikeCount: item.LikeCount, OwnerNickname: item.OwnerNickname, + OwnerAvatar: item.OwnerAvatar, Span: item.Span, MaterialType: item.MaterialType, }) diff --git a/backend/gateway/dto/gallery_dto.go b/backend/gateway/dto/gallery_dto.go index fc1b270..b36614c 100644 --- a/backend/gateway/dto/gallery_dto.go +++ b/backend/gateway/dto/gallery_dto.go @@ -103,6 +103,7 @@ type InspirationFlowItemDTO struct { CoverURL string `json:"cover_url"` // 封面图URL LikeCount int32 `json:"like_count"` // 点赞数 OwnerNickname string `json:"owner_nickname"` // 展出者昵称 + OwnerAvatar string `json:"owner_avatar"` // 展出者头像 Span int32 `json:"span"` // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4 MaterialType string `json:"material_type"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架) } diff --git a/backend/pkg/database/redis.go b/backend/pkg/database/redis.go index 52a0ab4..28c75e1 100644 --- a/backend/pkg/database/redis.go +++ b/backend/pkg/database/redis.go @@ -162,6 +162,7 @@ type InspirationFlowCacheEntry struct { CoverURL string `json:"cover_url"` LikeCount int32 `json:"like_count"` OwnerNickname string `json:"owner_nickname"` + OwnerAvatar string `json:"owner_avatar"` Span int32 `json:"span"` MaterialType string `json:"material_type"` } diff --git a/backend/pkg/proto/gallery/gallery.pb.go b/backend/pkg/proto/gallery/gallery.pb.go index afdaa88..f945f9e 100644 --- a/backend/pkg/proto/gallery/gallery.pb.go +++ b/backend/pkg/proto/gallery/gallery.pb.go @@ -1536,6 +1536,7 @@ type InspirationFlowItem struct { CoverUrl string `protobuf:"bytes,3,opt,name=cover_url,json=coverUrl,proto3" json:"cover_url,omitempty"` // 封面图URL LikeCount int32 `protobuf:"varint,4,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 点赞数 OwnerNickname string `protobuf:"bytes,5,opt,name=owner_nickname,json=ownerNickname,proto3" json:"owner_nickname,omitempty"` // 展出者昵称 + OwnerAvatar string `protobuf:"bytes,8,opt,name=owner_avatar,json=ownerAvatar,proto3" json:"owner_avatar,omitempty"` // 展出者头像 Span int32 `protobuf:"varint,6,opt,name=span,proto3" json:"span,omitempty"` // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4 MaterialType string `protobuf:"bytes,7,opt,name=material_type,json=materialType,proto3" json:"material_type,omitempty"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架) unknownFields protoimpl.UnknownFields @@ -1607,6 +1608,13 @@ func (x *InspirationFlowItem) GetOwnerNickname() string { return "" } +func (x *InspirationFlowItem) GetOwnerAvatar() string { + if x != nil { + return x.OwnerAvatar + } + return "" +} + func (x *InspirationFlowItem) GetSpan() int32 { if x != nil { return x.Span @@ -1855,14 +1863,15 @@ const file_gallery_proto_rawDesc = "" + "\x06cursor\x18\x02 \x01(\tR\x06cursor\x12\x19\n" + "\bhas_more\x18\x03 \x01(\bR\ahasMore\x12\x1d\n" + "\n" + - "session_id\x18\x04 \x01(\tR\tsessionId\"\xe0\x01\n" + + "session_id\x18\x04 \x01(\tR\tsessionId\"\x83\x02\n" + "\x13InspirationFlowItem\x12\x19\n" + "\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" + "\tcover_url\x18\x03 \x01(\tR\bcoverUrl\x12\x1d\n" + "\n" + "like_count\x18\x04 \x01(\x05R\tlikeCount\x12%\n" + - "\x0eowner_nickname\x18\x05 \x01(\tR\rownerNickname\x12\x12\n" + + "\x0eowner_nickname\x18\x05 \x01(\tR\rownerNickname\x12!\n" + + "\fowner_avatar\x18\b \x01(\tR\vownerAvatar\x12\x12\n" + "\x04span\x18\x06 \x01(\x05R\x04span\x12#\n" + "\rmaterial_type\x18\a \x01(\tR\fmaterialType\"i\n" + "\x1dGetUserExhibitedAssetsRequest\x12\x17\n" + diff --git a/backend/proto/gallery.proto b/backend/proto/gallery.proto index a27f81b..958b052 100644 --- a/backend/proto/gallery.proto +++ b/backend/proto/gallery.proto @@ -244,6 +244,7 @@ message InspirationFlowItem { string cover_url = 3; // 封面图URL int32 like_count = 4; // 点赞数 string owner_nickname = 5; // 展出者昵称 + string owner_avatar = 8; // 展出者头像 int32 span = 6; // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4 string material_type = 7; // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架) } diff --git a/backend/services/galleryService/repository/gallery_repository.go b/backend/services/galleryService/repository/gallery_repository.go index 7a23422..d38a309 100644 --- a/backend/services/galleryService/repository/gallery_repository.go +++ b/backend/services/galleryService/repository/gallery_repository.go @@ -90,6 +90,7 @@ type InspirationFlowItem struct { LikeCount int32 Level string // 藏品等级: N, R, SR, SSR, UR OwnerNickname string + OwnerAvatar string Span int32 // 卡片大小: N→1, R→2, SR→3, SSR→4, UR→5 MaterialType string // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架) CreatedAt int64 // 创建时间(用于判断是否为潜力之星) @@ -546,7 +547,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri var err error if materialType == "" || materialType == "all" || materialType == "random" { err = baseQuery. - Select(`exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, COALESCE(alr.current_level, 'N') as level, fp.nickname as owner_nickname, a.material_type, a.created_at`). + Select(`exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, COALESCE(alr.current_level, 'N') as level, fp.nickname as owner_nickname, fp.avatar_url as owner_avatar, a.material_type, a.created_at`). Joins("JOIN assets a ON a.id = exhibitions.asset_id"). Joins("LEFT JOIN asset_level_records alr ON alr.asset_id = a.id"). Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id"). @@ -558,7 +559,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri } else { // baseQuery 已经包含了 assets JOIN,不需要重复添加 err = baseQuery. - Select(`exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, COALESCE(alr.current_level, 'N') as level, fp.nickname as owner_nickname, a.material_type, a.created_at`). + Select(`exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, COALESCE(alr.current_level, 'N') as level, fp.nickname as owner_nickname, fp.avatar_url as owner_avatar, a.material_type, a.created_at`). Joins("LEFT JOIN asset_level_records alr ON alr.asset_id = a.id"). Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id"). Where("a.status = 1 AND a.is_active = true"). diff --git a/backend/services/galleryService/service/gallery_service.go b/backend/services/galleryService/service/gallery_service.go index 5f39939..7b368ef 100644 --- a/backend/services/galleryService/service/gallery_service.go +++ b/backend/services/galleryService/service/gallery_service.go @@ -326,6 +326,20 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct // 向右滚动:从仓库随机查询新数据(排除已展示) if direction == "right" { + // 解析游标中的 offset + offset := 0 + if cursor != "" { + decoded, err := base64.StdEncoding.DecodeString(cursor) + if err == nil { + var cursorData map[string]interface{} + if json.Unmarshal(decoded, &cursorData) == nil { + if o, ok := cursorData["offset"].(float64); ok { + offset = int(o) + } + } + } + } + // Get excludeIDs from cache var excludeIDs []int64 if sessionID != "" { @@ -335,7 +349,7 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct } } - items, err := s.repo.GetRandomExhibitions(starID, materialType, excludeIDs, int(limit), 0) + items, err := s.repo.GetRandomExhibitions(starID, materialType, excludeIDs, int(limit), offset) if err != nil { logger.Logger.Warn("GetInspirationFlow failed", zap.Int64("star_id", starID), @@ -354,6 +368,7 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct CoverUrl: item.CoverURL, LikeCount: item.LikeCount, OwnerNickname: item.OwnerNickname, + OwnerAvatar: item.OwnerAvatar, Span: item.Span, MaterialType: item.MaterialType, }) @@ -366,6 +381,7 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct CoverURL: item.CoverURL, LikeCount: item.LikeCount, OwnerNickname: item.OwnerNickname, + OwnerAvatar: item.OwnerAvatar, Span: item.Span, MaterialType: item.MaterialType, } @@ -373,12 +389,13 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct } } - // 生成新游标 - newCursor := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"limit":%d}`, decodedLimit))) + // 生成新游标(包含 offset 以便下次分页) + newOffset := offset + len(items) + newCursor := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"limit":%d,"offset":%d}`, decodedLimit, newOffset))) // 检查是否还有更多数据 total, err := s.repo.CountValidExhibitions(starID, materialType) - hasMore := int64(len(items)) < total + hasMore := int64(len(items)) < total && newOffset < int(total) return &pb.InspirationFlowData{ Items: pbItems, @@ -433,6 +450,7 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct CoverUrl: item.CoverURL, LikeCount: item.LikeCount, OwnerNickname: item.OwnerNickname, + OwnerAvatar: item.OwnerAvatar, Span: item.Span, MaterialType: item.MaterialType, }) diff --git a/frontend/index.html b/frontend/index.html index 20d05ca..103cd12 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -20,4 +20,14 @@