topfans/backend/gateway/controller/starbook_controller.go
zerosaturation e079a6c2e2 feat(asset): restore starbook home More button by Asset.LikeCount ordering
starbook 首页每个 grade/group 按 Asset.LikeCount DESC, Asset.ID ASC 取前三张,超三显示 has_more;
GetAssetsByType 不截断返回全部匹配项。包含:
- proto/asset.proto 声明 GetAssetsByType RPC + 消息,重新生成 .pb.go/.triple.go
- gateway starbook controller 改用标准 AssetService 客户端
- assetService: 三个 build*GroupForAssets 增加 previewLimit 参数
- 新增 sortAndLimitAssetItems 共享助手
- 新增 asset_service_group_test.go DB-free 单测
- 新增 TestStarbookHomePreviewAndMoreUseAssetLikeRanking 端到端回归
- frontend pages/starbook/items.vue 适配嵌套 groups/grades/items 响应,单 grade 竖向展示

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 19:20:16 +08:00

152 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
"context"
"net/http"
"strconv"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"github.com/gin-gonic/gin"
"github.com/topfans/backend/gateway/pkg/response"
"github.com/topfans/backend/pkg/logger"
pbAsset "github.com/topfans/backend/pkg/proto/asset"
"go.uber.org/zap"
)
// StarbookController 星册控制器
//
// ★ 批次 4.2 起,星册路由由 gateway → assetService 接管(原 starbookService 已删除)。
// GetMyAssets(home) 与 GetAssetsByType(items) 均由 asset.proto 生成的标准 AssetService client 提供。
type StarbookController struct {
assetClient pbAsset.AssetService
}
// NewStarbookController 创建星册控制器
func NewStarbookController(assetClient *client.Client) (*StarbookController, error) {
assetService, err := pbAsset.NewAssetService(assetClient)
if err != nil {
return nil, err
}
return &StarbookController{assetClient: assetService}, nil
}
// GetStarbookHome 获取星册首页
// @Summary 获取星册首页
// @Description 获取当前用户的星册首页数据(按类型和分组展示)
// @Tags starbook
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} response.Response
// @Router /api/v1/starbook/home [get]
func (ctrl *StarbookController) GetStarbookHome(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
// 设置 Dubbo attachments(供下游 provider 通过 ctx.Value(constant.AttachmentKey) 解析)
ctx := context.WithValue(c.Request.Context(), constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
logger.Logger.Debug("Calling GetStarbookHome",
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
)
// 调用 assetService.GetMyAssets(分组后的全部藏品)
// 响应形态 {base, data: AssetListData(groups, total, page, ...)} — 与旧 starbook HomeItems 一致
resp, err := ctrl.assetClient.GetMyAssets(ctx, &pbAsset.GetMyAssetsRequest{})
if err != nil {
logger.Logger.Error("GetStarbookHome RPC failed",
zap.Error(err),
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
)
response.Error(c, http.StatusInternalServerError, "获取星册首页失败: "+err.Error())
return
}
response.Success(c, resp)
}
// GetStarbookItems 获取星册藏品列表
// @Summary 获取星册藏品列表
// @Description 按 type/category/grade 分页获取星册藏品
// @Tags starbook
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param type query string true "资产类型: regular/collection/activity"
// @Param category query string false "子分类regular 时固定为 castlove"
// @Param grade query int false "等级,仅 regular 类型有效"
// @Param page query int false "页码默认1"
// @Param page_size query int false "每页数量默认20"
// @Success 200 {object} response.Response
// @Router /api/v1/starbook/items [get]
func (ctrl *StarbookController) GetStarbookItems(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
assetType := c.Query("type")
if assetType == "" {
response.Error(c, http.StatusBadRequest, "type 参数不能为空")
return
}
category := c.DefaultQuery("category", "castlove")
grade, _ := strconv.Atoi(c.DefaultQuery("grade", "0"))
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
ctx := context.WithValue(c.Request.Context(), constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
req := &pbAsset.GetAssetsByTypeRequest{
Type: assetType,
Category: category,
Grade: int32(grade),
Page: int32(page),
PageSize: int32(pageSize),
}
logger.Logger.Debug("Calling GetStarbookItems",
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
zap.String("type", assetType),
zap.Int32("page", int32(page)),
)
resp, err := ctrl.assetClient.GetAssetsByType(ctx, req)
if err != nil {
logger.Logger.Error("GetStarbookItems RPC failed",
zap.Error(err),
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
)
response.Error(c, http.StatusInternalServerError, "获取星册藏品列表失败: "+err.Error())
return
}
response.Success(c, resp)
}