feat: 给榜单增加字段
This commit is contained in:
parent
895f759a54
commit
d5a645eaea
@ -231,6 +231,8 @@ func convertRankingResponse(resp *pbRanking.GetRankingResponse) map[string]inter
|
|||||||
"owner_avatar": item.OwnerAvatar,
|
"owner_avatar": item.OwnerAvatar,
|
||||||
"like_count": item.LikeCount,
|
"like_count": item.LikeCount,
|
||||||
"is_original": item.IsOriginal,
|
"is_original": item.IsOriginal,
|
||||||
|
"is_liked": item.IsLiked,
|
||||||
|
"exhibition_id": item.ExhibitionId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -174,6 +174,8 @@ func ConvertInspirationFlowData(pbData *pbGallery.InspirationFlowData) *GetInspi
|
|||||||
OwnerAvatar: item.OwnerAvatar,
|
OwnerAvatar: item.OwnerAvatar,
|
||||||
Span: item.Span,
|
Span: item.Span,
|
||||||
MaterialType: item.MaterialType,
|
MaterialType: item.MaterialType,
|
||||||
|
IsLiked: item.IsLiked,
|
||||||
|
ExhibitionID: item.ExhibitionId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -106,6 +106,8 @@ type InspirationFlowItemDTO struct {
|
|||||||
OwnerAvatar string `json:"owner_avatar"` // 展出者头像
|
OwnerAvatar string `json:"owner_avatar"` // 展出者头像
|
||||||
Span int32 `json:"span"` // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
|
Span int32 `json:"span"` // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
|
||||||
MaterialType string `json:"material_type"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
MaterialType string `json:"material_type"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
||||||
|
IsLiked bool `json:"is_liked"` // 当前用户是否已点赞
|
||||||
|
ExhibitionID int64 `json:"exhibition_id"` // 当前展出记录ID(未展出时为0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInspirationFlowResponseDTO 获取灵感瀑布藏品列表响应
|
// GetInspirationFlowResponseDTO 获取灵感瀑布藏品列表响应
|
||||||
|
|||||||
@ -1539,6 +1539,8 @@ type InspirationFlowItem struct {
|
|||||||
OwnerAvatar string `protobuf:"bytes,8,opt,name=owner_avatar,json=ownerAvatar,proto3" json:"owner_avatar,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
|
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(新鲜上架)
|
MaterialType string `protobuf:"bytes,7,opt,name=material_type,json=materialType,proto3" json:"material_type,omitempty"` // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
||||||
|
IsLiked bool `protobuf:"varint,9,opt,name=is_liked,json=isLiked,proto3" json:"is_liked,omitempty"` // 当前用户是否已点赞
|
||||||
|
ExhibitionId int64 `protobuf:"varint,10,opt,name=exhibition_id,json=exhibitionId,proto3" json:"exhibition_id,omitempty"` // 当前展出记录ID(未展出时为0)
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -1629,6 +1631,20 @@ func (x *InspirationFlowItem) GetMaterialType() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *InspirationFlowItem) GetIsLiked() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsLiked
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *InspirationFlowItem) GetExhibitionId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExhibitionId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// 获取他人展出的作品列表请求(暂不实现)
|
// 获取他人展出的作品列表请求(暂不实现)
|
||||||
type GetUserExhibitedAssetsRequest struct {
|
type GetUserExhibitedAssetsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -1863,7 +1879,7 @@ const file_gallery_proto_rawDesc = "" +
|
|||||||
"\x06cursor\x18\x02 \x01(\tR\x06cursor\x12\x19\n" +
|
"\x06cursor\x18\x02 \x01(\tR\x06cursor\x12\x19\n" +
|
||||||
"\bhas_more\x18\x03 \x01(\bR\ahasMore\x12\x1d\n" +
|
"\bhas_more\x18\x03 \x01(\bR\ahasMore\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"session_id\x18\x04 \x01(\tR\tsessionId\"\x83\x02\n" +
|
"session_id\x18\x04 \x01(\tR\tsessionId\"\xc3\x02\n" +
|
||||||
"\x13InspirationFlowItem\x12\x19\n" +
|
"\x13InspirationFlowItem\x12\x19\n" +
|
||||||
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x12\n" +
|
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x12\n" +
|
||||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" +
|
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" +
|
||||||
@ -1873,7 +1889,10 @@ const file_gallery_proto_rawDesc = "" +
|
|||||||
"\x0eowner_nickname\x18\x05 \x01(\tR\rownerNickname\x12!\n" +
|
"\x0eowner_nickname\x18\x05 \x01(\tR\rownerNickname\x12!\n" +
|
||||||
"\fowner_avatar\x18\b \x01(\tR\vownerAvatar\x12\x12\n" +
|
"\fowner_avatar\x18\b \x01(\tR\vownerAvatar\x12\x12\n" +
|
||||||
"\x04span\x18\x06 \x01(\x05R\x04span\x12#\n" +
|
"\x04span\x18\x06 \x01(\x05R\x04span\x12#\n" +
|
||||||
"\rmaterial_type\x18\a \x01(\tR\fmaterialType\"i\n" +
|
"\rmaterial_type\x18\a \x01(\tR\fmaterialType\x12\x19\n" +
|
||||||
|
"\bis_liked\x18\t \x01(\bR\aisLiked\x12#\n" +
|
||||||
|
"\rexhibition_id\x18\n" +
|
||||||
|
" \x01(\x03R\fexhibitionId\"i\n" +
|
||||||
"\x1dGetUserExhibitedAssetsRequest\x12\x17\n" +
|
"\x1dGetUserExhibitedAssetsRequest\x12\x17\n" +
|
||||||
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x12\n" +
|
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x12\n" +
|
||||||
"\x04page\x18\x02 \x01(\x05R\x04page\x12\x1b\n" +
|
"\x04page\x18\x02 \x01(\x05R\x04page\x12\x1b\n" +
|
||||||
|
|||||||
@ -112,6 +112,8 @@ type RankingItem struct {
|
|||||||
OwnerAvatar string `protobuf:"bytes,9,opt,name=owner_avatar,json=ownerAvatar,proto3" json:"owner_avatar,omitempty"` // 持有者头像
|
OwnerAvatar string `protobuf:"bytes,9,opt,name=owner_avatar,json=ownerAvatar,proto3" json:"owner_avatar,omitempty"` // 持有者头像
|
||||||
LikeCount int32 `protobuf:"varint,7,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 点赞数
|
LikeCount int32 `protobuf:"varint,7,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 点赞数
|
||||||
IsOriginal bool `protobuf:"varint,8,opt,name=is_original,json=isOriginal,proto3" json:"is_original,omitempty"` // 是否自制藏品
|
IsOriginal bool `protobuf:"varint,8,opt,name=is_original,json=isOriginal,proto3" json:"is_original,omitempty"` // 是否自制藏品
|
||||||
|
IsLiked bool `protobuf:"varint,10,opt,name=is_liked,json=isLiked,proto3" json:"is_liked,omitempty"` // 当前用户是否已点赞(仅当藏品在展示中时为true)
|
||||||
|
ExhibitionId int64 `protobuf:"varint,11,opt,name=exhibition_id,json=exhibitionId,proto3" json:"exhibition_id,omitempty"` // 当前展出记录ID(未展出时为0)
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -209,6 +211,20 @@ func (x *RankingItem) GetIsOriginal() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RankingItem) GetIsLiked() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsLiked
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RankingItem) GetExhibitionId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExhibitionId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// 我的排名信息
|
// 我的排名信息
|
||||||
type MyRanking struct {
|
type MyRanking struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -413,7 +429,7 @@ const file_ranking_proto_rawDesc = "" +
|
|||||||
"\astar_id\x18\x02 \x01(\x03R\x06starId\x12\x12\n" +
|
"\astar_id\x18\x02 \x01(\x03R\x06starId\x12\x12\n" +
|
||||||
"\x04page\x18\x03 \x01(\x05R\x04page\x12\x1b\n" +
|
"\x04page\x18\x03 \x01(\x05R\x04page\x12\x1b\n" +
|
||||||
"\tpage_size\x18\x04 \x01(\x05R\bpageSize\x12\x17\n" +
|
"\tpage_size\x18\x04 \x01(\x05R\bpageSize\x12\x17\n" +
|
||||||
"\auser_id\x18\x05 \x01(\x03R\x06userId\"\x9f\x02\n" +
|
"\auser_id\x18\x05 \x01(\x03R\x06userId\"\xdf\x02\n" +
|
||||||
"\vRankingItem\x12\x12\n" +
|
"\vRankingItem\x12\x12\n" +
|
||||||
"\x04rank\x18\x01 \x01(\x05R\x04rank\x12\x19\n" +
|
"\x04rank\x18\x01 \x01(\x05R\x04rank\x12\x19\n" +
|
||||||
"\basset_id\x18\x02 \x01(\x03R\aassetId\x12\x1d\n" +
|
"\basset_id\x18\x02 \x01(\x03R\aassetId\x12\x1d\n" +
|
||||||
@ -426,7 +442,10 @@ const file_ranking_proto_rawDesc = "" +
|
|||||||
"\n" +
|
"\n" +
|
||||||
"like_count\x18\a \x01(\x05R\tlikeCount\x12\x1f\n" +
|
"like_count\x18\a \x01(\x05R\tlikeCount\x12\x1f\n" +
|
||||||
"\vis_original\x18\b \x01(\bR\n" +
|
"\vis_original\x18\b \x01(\bR\n" +
|
||||||
"isOriginal\"\x99\x02\n" +
|
"isOriginal\x12\x19\n" +
|
||||||
|
"\bis_liked\x18\n" +
|
||||||
|
" \x01(\bR\aisLiked\x12#\n" +
|
||||||
|
"\rexhibition_id\x18\v \x01(\x03R\fexhibitionId\"\x99\x02\n" +
|
||||||
"\tMyRanking\x12\x12\n" +
|
"\tMyRanking\x12\x12\n" +
|
||||||
"\x04rank\x18\x01 \x01(\x05R\x04rank\x12\x19\n" +
|
"\x04rank\x18\x01 \x01(\x05R\x04rank\x12\x19\n" +
|
||||||
"\basset_id\x18\x02 \x01(\x03R\aassetId\x12\x1d\n" +
|
"\basset_id\x18\x02 \x01(\x03R\aassetId\x12\x1d\n" +
|
||||||
|
|||||||
@ -247,6 +247,8 @@ message InspirationFlowItem {
|
|||||||
string owner_avatar = 8; // 展出者头像
|
string owner_avatar = 8; // 展出者头像
|
||||||
int32 span = 6; // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
|
int32 span = 6; // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4
|
||||||
string material_type = 7; // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
string material_type = 7; // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
||||||
|
bool is_liked = 9; // 当前用户是否已点赞
|
||||||
|
int64 exhibition_id = 10; // 当前展出记录ID(未展出时为0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 他人作品相关消息(暂不实现)====================
|
// ==================== 他人作品相关消息(暂不实现)====================
|
||||||
|
|||||||
@ -29,6 +29,8 @@ message RankingItem {
|
|||||||
string owner_avatar = 9; // 持有者头像
|
string owner_avatar = 9; // 持有者头像
|
||||||
int32 like_count = 7; // 点赞数
|
int32 like_count = 7; // 点赞数
|
||||||
bool is_original = 8; // 是否自制藏品
|
bool is_original = 8; // 是否自制藏品
|
||||||
|
bool is_liked = 10; // 当前用户是否已点赞(仅当藏品在展示中时为true)
|
||||||
|
int64 exhibition_id = 11; // 当前展出记录ID(未展出时为0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 我的排名信息
|
// 我的排名信息
|
||||||
|
|||||||
@ -143,7 +143,7 @@ func main() {
|
|||||||
assetService := service.NewAssetService(assetRepo, mintOrderRepo, assetLikeRepo, userClient, database.GetDB(), registryRepo)
|
assetService := service.NewAssetService(assetRepo, mintOrderRepo, assetLikeRepo, userClient, database.GetDB(), registryRepo)
|
||||||
mintService := service.NewMintService(assetRepo, mintOrderRepo, userClient, database.GetDB(), config.GlobalAssetConfig, registryRepo, mintCostRepo, userMintCountRepo, assetLevelSvc)
|
mintService := service.NewMintService(assetRepo, mintOrderRepo, userClient, database.GetDB(), config.GlobalAssetConfig, registryRepo, mintCostRepo, userMintCountRepo, assetLevelSvc)
|
||||||
assetLikeService := service.NewAssetLikeService(assetRepo, assetLikeRepo, database.GetDB(), assetLevelSvc)
|
assetLikeService := service.NewAssetLikeService(assetRepo, assetLikeRepo, database.GetDB(), assetLevelSvc)
|
||||||
rankingService := service.NewRankingService(rankingRepo, userClient)
|
rankingService := service.NewRankingService(rankingRepo, assetLikeRepo, userClient)
|
||||||
materialService := service.NewMaterialService(materialRepo, relationRepo)
|
materialService := service.NewMaterialService(materialRepo, relationRepo)
|
||||||
logger.Logger.Info("Service layer initialized")
|
logger.Logger.Info("Service layer initialized")
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,9 @@ type AssetLikeRepository interface {
|
|||||||
// Exists 检查点赞记录是否存在(按 asset_id, user_id, exhibition_id)
|
// Exists 检查点赞记录是否存在(按 asset_id, user_id, exhibition_id)
|
||||||
Exists(assetID, userID, starID, exhibitionID int64) (bool, error)
|
Exists(assetID, userID, starID, exhibitionID int64) (bool, error)
|
||||||
|
|
||||||
|
// ExistsByAsset 检查用户是否对资产有点赞记录(不管 exhibition_id)
|
||||||
|
ExistsByAsset(assetID, userID, starID int64) (bool, error)
|
||||||
|
|
||||||
// GetByAsset 获取资产的点赞记录列表(分页)
|
// GetByAsset 获取资产的点赞记录列表(分页)
|
||||||
GetByAsset(assetID int64, limit, offset int) ([]*models.AssetLike, error)
|
GetByAsset(assetID int64, limit, offset int) ([]*models.AssetLike, error)
|
||||||
|
|
||||||
@ -139,6 +142,33 @@ func (r *assetLikeRepository) Exists(assetID, userID, starID, exhibitionID int64
|
|||||||
return count > 0, nil
|
return count > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExistsByAsset 检查用户是否对资产有点赞记录(不管 exhibition_id)
|
||||||
|
// 用于排行榜等场景,判断用户是否对某个藏品点赞过
|
||||||
|
func (r *assetLikeRepository) ExistsByAsset(assetID, userID, starID int64) (bool, error) {
|
||||||
|
if assetID <= 0 {
|
||||||
|
return false, errors.New("asset_id must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if userID <= 0 {
|
||||||
|
return false, errors.New("user_id must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if starID <= 0 {
|
||||||
|
return false, errors.New("star_id must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&models.AssetLike{}).
|
||||||
|
Where("asset_id = ? AND user_id = ? AND star_id = ?", assetID, userID, starID).
|
||||||
|
Count(&count).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetByAsset 获取资产的点赞记录列表(分页)
|
// GetByAsset 获取资产的点赞记录列表(分页)
|
||||||
func (r *assetLikeRepository) GetByAsset(assetID int64, limit, offset int) ([]*models.AssetLike, error) {
|
func (r *assetLikeRepository) GetByAsset(assetID int64, limit, offset int) ([]*models.AssetLike, error) {
|
||||||
if assetID <= 0 {
|
if assetID <= 0 {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ type RankingItem struct {
|
|||||||
OwnerAvatar *string
|
OwnerAvatar *string
|
||||||
LikeCount int32
|
LikeCount int32
|
||||||
IsOriginal bool
|
IsOriginal bool
|
||||||
|
ExhibitionID int64 // 当前展出记录ID(未展出时为0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RankingRepository 排行榜Repository接口
|
// RankingRepository 排行榜Repository接口
|
||||||
@ -115,16 +116,21 @@ func (r *rankingRepository) getHotRankingByDimension(starID int64, dimension str
|
|||||||
case "displaying":
|
case "displaying":
|
||||||
// 展示中:关联 Exhibition 表,筛选未过期的、未删除的,且是当前star的展品
|
// 展示中:关联 Exhibition 表,筛选未过期的、未删除的,且是当前star的展品
|
||||||
// occupier_star_id 表示展品所属的明星
|
// occupier_star_id 表示展品所属的明星
|
||||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id AND exhibitions.deleted_at IS NULL").
|
db = db.Select("assets.id as asset_id, assets.name as asset_name, assets.cover_url, assets.owner_uid, MAX(assets.like_count) as like_count, MAX(assets.is_original::int) as is_original, MAX(fp.nickname) as owner_nickname, MAX(fp.avatar_url) as owner_avatar, MAX(exhibitions.id) as exhibition_id").
|
||||||
|
Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id AND exhibitions.deleted_at IS NULL").
|
||||||
Where("exhibitions.expire_at > ?", now).
|
Where("exhibitions.expire_at > ?", now).
|
||||||
Where("exhibitions.occupier_star_id = ?", starID)
|
Where("exhibitions.occupier_star_id = ?", starID).
|
||||||
|
Group("assets.id, exhibitions.id")
|
||||||
case "month":
|
case "month":
|
||||||
// 本月:本月内展览过的藏品(包括已下架的,只要 expire_at 在本月内即可)
|
// 本月:本月内展览过的藏品(包括已下架的,只要 expire_at 在本月内即可)
|
||||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
db = db.Select("assets.id as asset_id, assets.name as asset_name, assets.cover_url, assets.owner_uid, MAX(assets.like_count) as like_count, MAX(assets.is_original::int) as is_original, MAX(fp.nickname) as owner_nickname, MAX(fp.avatar_url) as owner_avatar, MAX(exhibitions.id) as exhibition_id").
|
||||||
|
Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||||
Where("exhibitions.expire_at >= ?", startOfMonth).
|
Where("exhibitions.expire_at >= ?", startOfMonth).
|
||||||
Where("exhibitions.occupier_star_id = ?", starID)
|
Where("exhibitions.occupier_star_id = ?", starID).
|
||||||
|
Group("assets.id, exhibitions.id")
|
||||||
case "total":
|
case "total":
|
||||||
// 全部:直接使用 assets 表的 like_count,无需额外条件
|
// 全部:直接使用 assets 表的 like_count,无需额外条件
|
||||||
|
db = db.Select("assets.id as asset_id, assets.name as asset_name, assets.cover_url, assets.owner_uid, MAX(assets.like_count) as like_count, MAX(assets.is_original::int) as is_original, MAX(fp.nickname) as owner_nickname, MAX(fp.avatar_url) as owner_avatar, 0 as exhibition_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计总数(先查询 ID 列表再 Count,避免 DISTINCT 干扰)
|
// 统计总数(先查询 ID 列表再 Count,避免 DISTINCT 干扰)
|
||||||
@ -153,11 +159,20 @@ func (r *rankingRepository) getHotRankingByDimension(starID int64, dimension str
|
|||||||
|
|
||||||
// 查询列表(使用 GROUP BY 去重,按点赞数排序)
|
// 查询列表(使用 GROUP BY 去重,按点赞数排序)
|
||||||
var results []*RankingItem
|
var results []*RankingItem
|
||||||
err := db.Group("assets.id").
|
var err error
|
||||||
|
if dimension == "total" {
|
||||||
|
err = db.Group("assets.id").
|
||||||
Order("MAX(assets.like_count) DESC, assets.id ASC").
|
Order("MAX(assets.like_count) DESC, assets.id ASC").
|
||||||
Limit(limit).
|
Limit(limit).
|
||||||
Offset(offset).
|
Offset(offset).
|
||||||
Scan(&results).Error
|
Scan(&results).Error
|
||||||
|
} else {
|
||||||
|
err = db.Group("assets.id, exhibitions.id").
|
||||||
|
Order("MAX(assets.like_count) DESC, assets.id ASC").
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset).
|
||||||
|
Scan(&results).Error
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
|||||||
@ -21,13 +21,15 @@ type RankingService interface {
|
|||||||
// rankingService 排行榜Service实现
|
// rankingService 排行榜Service实现
|
||||||
type rankingService struct {
|
type rankingService struct {
|
||||||
rankingRepo repository.RankingRepository
|
rankingRepo repository.RankingRepository
|
||||||
|
assetLikeRepo repository.AssetLikeRepository
|
||||||
userClient client.UserServiceClient
|
userClient client.UserServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRankingService 创建排行榜Service实例
|
// NewRankingService 创建排行榜Service实例
|
||||||
func NewRankingService(rankingRepo repository.RankingRepository, userClient client.UserServiceClient) RankingService {
|
func NewRankingService(rankingRepo repository.RankingRepository, assetLikeRepo repository.AssetLikeRepository, userClient client.UserServiceClient) RankingService {
|
||||||
return &rankingService{
|
return &rankingService{
|
||||||
rankingRepo: rankingRepo,
|
rankingRepo: rankingRepo,
|
||||||
|
assetLikeRepo: assetLikeRepo,
|
||||||
userClient: userClient,
|
userClient: userClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,6 +83,15 @@ func (s *rankingService) GetHotRanking(ctx context.Context, req *ranking.GetRank
|
|||||||
// 转换结果
|
// 转换结果
|
||||||
rankingItems := make([]*ranking.RankingItem, len(items))
|
rankingItems := make([]*ranking.RankingItem, len(items))
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
|
// 判断当前用户是否已点赞(只有登录用户才检查,且仅当藏品在展示中时)
|
||||||
|
isLiked := false
|
||||||
|
if req.UserId > 0 && item.ExhibitionID > 0 {
|
||||||
|
exists, err := s.assetLikeRepo.ExistsByAsset(item.AssetID, req.UserId, starID)
|
||||||
|
if err == nil {
|
||||||
|
isLiked = exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rankingItems[i] = &ranking.RankingItem{
|
rankingItems[i] = &ranking.RankingItem{
|
||||||
Rank: int32(i + 1 + (int(req.Page)-1)*int(req.PageSize)),
|
Rank: int32(i + 1 + (int(req.Page)-1)*int(req.PageSize)),
|
||||||
AssetId: item.AssetID,
|
AssetId: item.AssetID,
|
||||||
@ -91,6 +102,8 @@ func (s *rankingService) GetHotRanking(ctx context.Context, req *ranking.GetRank
|
|||||||
OwnerAvatar: getStringPointer(item.OwnerAvatar),
|
OwnerAvatar: getStringPointer(item.OwnerAvatar),
|
||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
IsOriginal: item.IsOriginal,
|
IsOriginal: item.IsOriginal,
|
||||||
|
IsLiked: isLiked,
|
||||||
|
ExhibitionId: item.ExhibitionID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +208,15 @@ func (s *rankingService) GetOriginalRanking(ctx context.Context, req *ranking.Ge
|
|||||||
// 转换结果
|
// 转换结果
|
||||||
rankingItems := make([]*ranking.RankingItem, len(items))
|
rankingItems := make([]*ranking.RankingItem, len(items))
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
|
// 判断当前用户是否已点赞(只有登录用户才检查,且仅当藏品在展示中时)
|
||||||
|
isLiked := false
|
||||||
|
if req.UserId > 0 && item.ExhibitionID > 0 {
|
||||||
|
exists, err := s.assetLikeRepo.ExistsByAsset(item.AssetID, req.UserId, starID)
|
||||||
|
if err == nil {
|
||||||
|
isLiked = exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rankingItems[i] = &ranking.RankingItem{
|
rankingItems[i] = &ranking.RankingItem{
|
||||||
Rank: int32(i + 1 + (int(req.Page)-1)*int(req.PageSize)),
|
Rank: int32(i + 1 + (int(req.Page)-1)*int(req.PageSize)),
|
||||||
AssetId: item.AssetID,
|
AssetId: item.AssetID,
|
||||||
@ -205,6 +227,8 @@ func (s *rankingService) GetOriginalRanking(ctx context.Context, req *ranking.Ge
|
|||||||
OwnerAvatar: getStringPointer(item.OwnerAvatar),
|
OwnerAvatar: getStringPointer(item.OwnerAvatar),
|
||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
IsOriginal: item.IsOriginal,
|
IsOriginal: item.IsOriginal,
|
||||||
|
IsLiked: isLiked,
|
||||||
|
ExhibitionId: item.ExhibitionID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,11 +72,12 @@ type GalleryRepository interface {
|
|||||||
|
|
||||||
// GetRandomExhibitions 获取随机展品列表
|
// GetRandomExhibitions 获取随机展品列表
|
||||||
// starID: 明星ID
|
// starID: 明星ID
|
||||||
|
// viewerID: 当前查看用户ID(用于判断点赞状态)
|
||||||
// materialType: 素材类型过滤(空字符串表示不过滤)
|
// materialType: 素材类型过滤(空字符串表示不过滤)
|
||||||
// excludeIDs: 排除的展品ID列表(用于去重)
|
// excludeIDs: 排除的展品ID列表(用于去重)
|
||||||
// limit: 返回数量
|
// limit: 返回数量
|
||||||
// offset: 偏移量(随机生成)
|
// offset: 偏移量(随机生成)
|
||||||
GetRandomExhibitions(starID int64, materialType string, excludeIDs []int64, limit, offset int) ([]*InspirationFlowItem, error)
|
GetRandomExhibitions(starID, viewerID int64, materialType string, excludeIDs []int64, limit, offset int) ([]*InspirationFlowItem, error)
|
||||||
|
|
||||||
// GetSlotOwnerUserID 获取展位所有者的用户ID
|
// GetSlotOwnerUserID 获取展位所有者的用户ID
|
||||||
GetSlotOwnerUserID(slotID int64) (int64, error)
|
GetSlotOwnerUserID(slotID int64) (int64, error)
|
||||||
@ -95,6 +96,7 @@ type InspirationFlowItem struct {
|
|||||||
Span int32 // 卡片大小: N→1, R→2, SR→3, SSR→4, UR→5
|
Span int32 // 卡片大小: N→1, R→2, SR→3, SSR→4, UR→5
|
||||||
MaterialType string // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
MaterialType string // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架)
|
||||||
CreatedAt int64 // 创建时间(用于判断是否为潜力之星)
|
CreatedAt int64 // 创建时间(用于判断是否为潜力之星)
|
||||||
|
IsLiked bool // 当前用户是否已点赞
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExhibitedAssetInfo 我展出的作品信息
|
// ExhibitedAssetInfo 我展出的作品信息
|
||||||
@ -539,7 +541,7 @@ func (r *galleryRepository) CountValidExhibitions(starID int64, materialType str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRandomExhibitions 获取随机展品列表
|
// GetRandomExhibitions 获取随机展品列表
|
||||||
func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType string, excludeIDs []int64, limit, offset int) ([]*InspirationFlowItem, error) {
|
func (r *galleryRepository) GetRandomExhibitions(starID, viewerID int64, materialType string, excludeIDs []int64, limit, offset int) ([]*InspirationFlowItem, error) {
|
||||||
var items []*InspirationFlowItem
|
var items []*InspirationFlowItem
|
||||||
now := time.Now().UnixMilli()
|
now := time.Now().UnixMilli()
|
||||||
|
|
||||||
@ -561,10 +563,11 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri
|
|||||||
var err error
|
var err error
|
||||||
if materialType == "" || materialType == "all" || materialType == "random" {
|
if materialType == "" || materialType == "all" || materialType == "random" {
|
||||||
err = baseQuery.
|
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, fp.avatar_url as owner_avatar, 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, CASE WHEN al.id IS NOT NULL THEN true ELSE false END as is_liked`).
|
||||||
Joins("JOIN assets a ON a.id = exhibitions.asset_id").
|
Joins("JOIN assets a ON a.id = exhibitions.asset_id").
|
||||||
Joins("LEFT JOIN asset_level_records alr ON alr.asset_id = a.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").
|
Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id").
|
||||||
|
Joins("LEFT JOIN asset_likes al ON al.asset_id = a.id AND al.user_id = ? AND al.star_id = ?", viewerID, starID).
|
||||||
Where("a.status = 1 AND a.is_active = true").
|
Where("a.status = 1 AND a.is_active = true").
|
||||||
Order("RANDOM()").
|
Order("RANDOM()").
|
||||||
Limit(limit).
|
Limit(limit).
|
||||||
@ -573,9 +576,10 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri
|
|||||||
} else {
|
} else {
|
||||||
// baseQuery 已经包含了 assets JOIN,不需要重复添加
|
// baseQuery 已经包含了 assets JOIN,不需要重复添加
|
||||||
err = baseQuery.
|
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, fp.avatar_url as owner_avatar, 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, CASE WHEN al.id IS NOT NULL THEN true ELSE false END as is_liked`).
|
||||||
Joins("LEFT JOIN asset_level_records alr ON alr.asset_id = a.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").
|
Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id").
|
||||||
|
Joins("LEFT JOIN asset_likes al ON al.asset_id = a.id AND al.user_id = ? AND al.star_id = ?", viewerID, starID).
|
||||||
Where("a.status = 1 AND a.is_active = true").
|
Where("a.status = 1 AND a.is_active = true").
|
||||||
Order("RANDOM()").
|
Order("RANDOM()").
|
||||||
Limit(limit).
|
Limit(limit).
|
||||||
|
|||||||
@ -349,7 +349,7 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := s.repo.GetRandomExhibitions(starID, materialType, excludeIDs, int(limit), offset)
|
items, err := s.repo.GetRandomExhibitions(starID, userID, materialType, excludeIDs, int(limit), offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Logger.Warn("GetInspirationFlow failed",
|
logger.Logger.Warn("GetInspirationFlow failed",
|
||||||
zap.Int64("star_id", starID),
|
zap.Int64("star_id", starID),
|
||||||
@ -371,6 +371,8 @@ func (s *galleryService) GetInspirationFlow(userID, starID int64, cursor, direct
|
|||||||
OwnerAvatar: item.OwnerAvatar,
|
OwnerAvatar: item.OwnerAvatar,
|
||||||
Span: item.Span,
|
Span: item.Span,
|
||||||
MaterialType: item.MaterialType,
|
MaterialType: item.MaterialType,
|
||||||
|
IsLiked: item.IsLiked,
|
||||||
|
ExhibitionId: item.ExhibitionID,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add items to cache
|
// Add items to cache
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
import { getInspirationFlowApi } from '@/utils/api.js'
|
import { getInspirationFlowApi } from '@/utils/api.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -175,9 +176,13 @@ onMounted(() => {
|
|||||||
creationList.value = [...creationList.value];
|
creationList.value = [...creationList.value];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loadUsers();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onShow(()=>{
|
||||||
|
loadUsers();
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
isComponentMounted = false;
|
isComponentMounted = false;
|
||||||
uni.$off("assetLiked");
|
uni.$off("assetLiked");
|
||||||
|
|||||||
@ -55,6 +55,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
import { getHotRankingApi } from '@/utils/api.js'
|
import { getHotRankingApi } from '@/utils/api.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -129,10 +130,14 @@ const loadData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadData()
|
|
||||||
uni.$on('assetLiked', onAssetLiked)
|
uni.$on('assetLiked', onAssetLiked)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onShow(()=>{
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
uni.$off('assetLiked', onAssetLiked)
|
uni.$off('assetLiked', onAssetLiked)
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user