package service import ( "context" "fashionapi/internal/dto" "fashionapi/internal/pkg/hashid" "fashionapi/internal/pkg/imgurl" "fashionapi/internal/repository" ) // IndexService 首页(/index/)各区块数据的聚合服务。 // // 首页由多个独立区块(runway 等)组成,每个区块的数据各自组合、互不干扰。 // 该服务专管「首页区块数据」这一领域,避免把首页数据拼装逻辑散落到 BrandService 等其它服务里。 type IndexService interface { // Runway 首页 runway 区块数据:包含 hotBrand(热门品牌,固定 20 条)等多个模块。 // 服务端写死各模块数量,不接收任何筛选参数;locale 决定标题展示语言。 Runway(ctx context.Context, locale string) (IndexRunwayData, error) } // IndexRunwayData 首页 runway 区块数据,按模块拆分;hotBrand 为其中之一。 // // 后续新增首页 runway 模块时,在此结构体追加字段即可,前端按需读取,互不影响。 type IndexRunwayData struct { HotBrand []dto.PopularBrand `json:"hotBrand"` } type indexService struct { brands repository.BrandRepository img *imgurl.Composer } // NewIndexService 创建首页数据服务。 // img 负责把库里存储的七牛 key 拼装为可访问 URL(首页统一普通图)。 func NewIndexService(brands repository.BrandRepository, img *imgurl.Composer) IndexService { return &indexService{brands: brands, img: img} } // Runway 首页 runway 区块数据。 // // 复用仓储层 PopularWithCover 的「按图片数排名取热门品牌 + 代表走秀封面/标题」逻辑, // 保持与品牌「代表品牌」一致的"热门"定义;品牌名强制英文观感,标题按 locale 选列。 // hotBrand 数量写死 20,接口不接收任何参数。 func (s *indexService) Runway(ctx context.Context, locale string) (IndexRunwayData, error) { const hotBrandLimit = 20 rows, err := s.brands.PopularWithCover(ctx, hotBrandLimit) if err != nil { return IndexRunwayData{}, internalErr(err.Error()) } brands := make([]dto.PopularBrand, 0, len(rows)) for _, b := range rows { brands = append(brands, dto.PopularBrand{ UID: hashid.Encode(b.ID), ArticleUID: hashid.Encode(b.ArticleID), // 品牌名强制优先英文("en"),不随 locale 切换,保持英文观感。 Brand: pickLocale(b.BrandEn, b.BrandCn, "en"), Cover: s.img.Compose(b.Cover), // 首页热门卡片为大图展示,用高清样式(high) Title: pickLocale(b.TitleEn, b.TitleCn, locale), }) } return IndexRunwayData{HotBrand: brands}, nil }