package controller import ( "context" "net/http" "time" "dubbo.apache.org/dubbo-go/v3/client" "github.com/gin-gonic/gin" "github.com/topfans/backend/gateway/pkg/response" pbCastlove "github.com/topfans/backend/pkg/proto/castlove" "github.com/topfans/backend/pkg/logger" "go.uber.org/zap" ) // CastloveConfigController 铸爱工艺配置 控制器 // 端点:GET /api/v1/castlove/config // 流程:JWT 鉴权 → 透传 Dubbo RPC → 透传 DB → sanitize → 5s 进程内缓存 → JSON 响应 type CastloveConfigController struct { castloveConfigClient pbCastlove.CastloveConfigService } // NewCastloveConfigController 创建铸爱工艺配置 Controller 实例 // dubboClient:指向 assetService(端口 20003)的 Dubbo 客户端 func NewCastloveConfigController(dubboClient *client.Client) (*CastloveConfigController, error) { cli, err := pbCastlove.NewCastloveConfigService(dubboClient) if err != nil { return nil, err } return &CastloveConfigController{ castloveConfigClient: cli, }, nil } // GetCastloveConfig 获取铸爱工艺配置(全量分类 + 卡片) // @Summary 获取铸爱工艺配置 // @Description 强制鉴权;返回全量分类(星卡/吧唧/海报 等)及其下卡片(含 route_path / route_params) // @Tags castlove // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} response.Response // @Router /api/v1/castlove/config [get] func (ctrl *CastloveConfigController) GetCastloveConfig(c *gin.Context) { logger.Logger.Info("GetCastloveConfig request") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := ctrl.castloveConfigClient.GetCastloveConfig(ctx, &pbCastlove.GetCastloveConfigRequest{}) if err != nil { logger.Logger.Error("GetCastloveConfig RPC failed", zap.Error(err)) response.Error(c, http.StatusInternalServerError, "配置加载失败") return } if resp.Base == nil || resp.Base.GetCode() != 200 { // proto Base 为 nil 视为异常,但仍尝试把 categories 透传出去,避免空响应 logger.Logger.Warn("GetCastloveConfig: unexpected base", zap.Any("base", resp.Base), ) } // proto 数组转 []map 以便 gin 序列化为 JSON 数组而非 proto 字段名 categories := make([]map[string]interface{}, 0, len(resp.Categories)) for _, cat := range resp.Categories { crafts := make([]map[string]interface{}, 0, len(cat.Crafts)) for _, cr := range cat.Crafts { crafts = append(crafts, map[string]interface{}{ "id": cr.Id, "name": cr.Name, "image_url": cr.ImageUrl, "route_path": nullableString(cr.RoutePath), // "" → nil,与 DB NULL 行为一致 "route_params": nullableJSONString(cr.RouteParams), "sort_order": cr.SortOrder, }) } categories = append(categories, map[string]interface{}{ "id": cat.Id, "name": cat.Name, "type_key": nullableString(cat.TypeKey), "sort_order": cat.SortOrder, "crafts": crafts, }) } response.Success(c, map[string]interface{}{ "categories": categories, "version": resp.Version, }) } // nullableString 空串转 nil(JSON 序列化时为 null,与 DB NULL 行为一致) func nullableString(s string) interface{} { if s == "" { return nil } return s } // nullableJSONString 空串转 nil(route_params 同上) func nullableJSONString(s string) interface{} { if s == "" { return nil } return s }