fix(publish): 后台详情与品牌列表改读基表,修复未发布内容在后台不可达
This commit is contained in:
@ -24,6 +24,8 @@ type MediaCleanupEnqueuer interface {
|
||||
type ArticleService interface {
|
||||
List(ctx context.Context, q dto.ArticleQuery) ([]dto.PublicArticle, int64, error)
|
||||
Detail(ctx context.Context, id string, locale string) (*dto.PublicArticleDetail, error)
|
||||
// DetailAdmin 后台详情:读基表,能渲染 pending / rejected(供后台详情页复用)。
|
||||
DetailAdmin(ctx context.Context, id string, locale string) (*dto.PublicArticleDetail, error)
|
||||
// AdminList 后台管理列表:返回含季节 / 年份 / 系列等管理字段的走秀项。
|
||||
AdminList(ctx context.Context, q dto.ArticleQuery) ([]dto.AdminRunway, int64, error)
|
||||
// GetForEdit 取原始走秀记录与图片,供后台编辑表单预填。
|
||||
@ -151,22 +153,46 @@ func excludeCover(imgs []dto.PublicArticleImage, cover string) []dto.PublicArtic
|
||||
return kept
|
||||
}
|
||||
|
||||
// Detail 查询文章详情,含完整图片集。locale 决定标题/描述/品牌名的展示语言;
|
||||
// 图片统一走展示样式(VIP 与免费同质量)。
|
||||
// Detail 公开详情:读公开视图,pending / rejected 视同不存在(ErrArticleNotFound)。
|
||||
// locale 决定标题/描述/品牌名的展示语言;图片统一走展示样式(VIP 与免费同质量)。
|
||||
func (s *articleService) Detail(ctx context.Context, id string, locale string) (*dto.PublicArticleDetail, error) {
|
||||
row, err := s.articles.FindByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrNotFound) {
|
||||
return nil, ErrArticleNotFound
|
||||
}
|
||||
return nil, NewError(http.StatusInternalServerError, err.Error())
|
||||
return nil, s.detailErr(err)
|
||||
}
|
||||
|
||||
imgs, err := s.articles.ListImages(ctx, id)
|
||||
if err != nil {
|
||||
return nil, internalErr(err.Error())
|
||||
}
|
||||
return s.buildDetail(row, imgs, locale), nil
|
||||
}
|
||||
|
||||
// DetailAdmin 后台详情:读基表,能渲染 pending / rejected。
|
||||
//
|
||||
// 后台列表(AdminList)故意把待审行链到详情页,且详情页是通往编辑页的唯一入口;
|
||||
// 若后台详情也走公开视图,待审项会 404、审核无从下手。
|
||||
func (s *articleService) DetailAdmin(ctx context.Context, id string, locale string) (*dto.PublicArticleDetail, error) {
|
||||
row, err := s.articles.FindByIDBase(ctx, id)
|
||||
if err != nil {
|
||||
return nil, s.detailErr(err)
|
||||
}
|
||||
imgs, err := s.articles.ListImagesBase(ctx, id)
|
||||
if err != nil {
|
||||
return nil, internalErr(err.Error())
|
||||
}
|
||||
return s.buildDetail(row, imgs, locale), nil
|
||||
}
|
||||
|
||||
// detailErr 统一详情取数失败的错误映射(公开 Detail 与 DetailAdmin 共用)。
|
||||
func (s *articleService) detailErr(err error) error {
|
||||
if errors.Is(err, repository.ErrNotFound) {
|
||||
return ErrArticleNotFound
|
||||
}
|
||||
return NewError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
// buildDetail 把查询行与图片集映射为详情 DTO(公开/后台共用,差异只在数据来源)。
|
||||
func (s *articleService) buildDetail(row *model.RunwayRow, imgs []model.BrandRunwayImage, locale string) *dto.PublicArticleDetail {
|
||||
// 主图(is_detail=0)进 Images,默认展示;细节图(is_detail=1)按 look_index
|
||||
// 直接挂到所属主图的 Detail 子数组下 —— 「look → 细节」的层级与前端 UI 一致,
|
||||
// 前端读 img.detail 即可,无需再单独维护一份「按 look_index 分组」的映射表。
|
||||
@ -198,7 +224,7 @@ func (s *articleService) Detail(ctx context.Context, id string, locale string) (
|
||||
// 品牌名强制优先英文("en"),不随 locale 切换,保持英文观感。
|
||||
BrandName: pickLocale(row.BrandNameEn, row.BrandNameCn, "en"),
|
||||
Images: pubMains,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ── 后台编辑支持 ──────────────────────────────────────────────────────────────
|
||||
|
||||
@ -16,6 +16,9 @@ import (
|
||||
// BrandService 品牌业务接口。
|
||||
type BrandService interface {
|
||||
List(ctx context.Context, q dto.BrandQuery) ([]dto.PublicBrand, int64, error)
|
||||
// AdminList 后台品牌列表:字母索引不要求走秀已发布,
|
||||
// 使只有待审 / 驳回走秀的品牌仍出现在后台(公开 List 读视图,会挡掉它们)。
|
||||
AdminList(ctx context.Context, q dto.BrandQuery) ([]dto.PublicBrand, int64, error)
|
||||
// Hot 返回热门品牌(按走秀图片数排名前 limit 个)并附带各品牌档案数。
|
||||
// 供 SSG 构建期(RunwayLooks 侧栏 / 品牌弹窗 HOT 标签)一次性取前 N 个热门品牌;
|
||||
// 复用 List 的 featured 收窄逻辑(FeaturedIDs("images", limit)),与首页精选口径一致。
|
||||
@ -70,6 +73,18 @@ func (s *brandService) List(ctx context.Context, q dto.BrandQuery) ([]dto.Public
|
||||
return s.toPublicBrands(items, q.Locale), total, nil
|
||||
}
|
||||
|
||||
// AdminList 后台品牌列表:与 List 同形,但走 ListAdmin —— 字母索引不过滤走秀发布态,
|
||||
// 只有待审 / 驳回走秀的品牌仍可见。后台不做 featured 收窄,故 restrictIDs 传 nil。
|
||||
func (s *brandService) AdminList(ctx context.Context, q dto.BrandQuery) ([]dto.PublicBrand, int64, error) {
|
||||
q.Normalize()
|
||||
|
||||
items, total, err := s.brands.ListAdmin(ctx, q, nil)
|
||||
if err != nil {
|
||||
return nil, 0, internalErr(err.Error())
|
||||
}
|
||||
return s.toPublicBrands(items, q.Locale), total, nil
|
||||
}
|
||||
|
||||
// Hot 返回热门品牌:按走秀图片总数排名取前 limit 个,结果按热度顺序(而非字母序)排列。
|
||||
//
|
||||
// 供 SSG 构建期(走秀页侧栏品牌 filter / 品牌弹窗 HOT 标签)一次性取用——
|
||||
|
||||
@ -20,6 +20,8 @@ type StreetSnapService interface {
|
||||
// AdminList 后台街拍列表:读基表,包含 pending / rejected(否则审核员看不到待审内容)。
|
||||
AdminList(ctx context.Context, q dto.StreetSnapQuery) ([]dto.PublicStreetSnap, int64, error)
|
||||
Detail(ctx context.Context, id string) (*dto.PublicStreetSnapDetail, error)
|
||||
// DetailAdmin 后台详情:读基表,能渲染 pending / rejected(供后台详情页复用)。
|
||||
DetailAdmin(ctx context.Context, id string) (*dto.PublicStreetSnapDetail, error)
|
||||
// Popular 返回热度前 N 个街拍(按图片数降序),供 SSG 首页热门街拍展示。
|
||||
Popular(ctx context.Context, limit int) ([]dto.PublicStreetSnap, error)
|
||||
// GetForEdit 取原始街拍记录与图片,供后台编辑表单预填。
|
||||
@ -144,27 +146,52 @@ func excludeCoverSnap(imgs []dto.PublicArticleImage, cover string) []dto.PublicA
|
||||
return kept
|
||||
}
|
||||
|
||||
// Detail 查询街拍详情,含完整图片集。图片统一走展示样式(VIP 与免费同质量)。
|
||||
// Detail 公开详情:读公开视图,pending / rejected 视同不存在(ErrStreetSnapNotFound)。
|
||||
// 图片统一走展示样式(VIP 与免费同质量)。
|
||||
func (s *streetSnapService) Detail(ctx context.Context, id string) (*dto.PublicStreetSnapDetail, error) {
|
||||
snap, err := s.repo.FindByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrNotFound) {
|
||||
return nil, ErrStreetSnapNotFound
|
||||
}
|
||||
return nil, internalErr(err.Error())
|
||||
return nil, s.detailErr(err)
|
||||
}
|
||||
|
||||
imgs, err := s.repo.ListImages(ctx, id)
|
||||
if err != nil {
|
||||
return nil, internalErr(err.Error())
|
||||
}
|
||||
return s.buildSnapDetail(snap, imgs), nil
|
||||
}
|
||||
|
||||
// DetailAdmin 后台详情:读基表,能渲染 pending / rejected。
|
||||
//
|
||||
// 后台列表(AdminList)故意把待审行链到详情页,且详情页是通往编辑页的唯一入口;
|
||||
// 若后台详情也走公开视图,待审项会 404、审核无从下手。
|
||||
func (s *streetSnapService) DetailAdmin(ctx context.Context, id string) (*dto.PublicStreetSnapDetail, error) {
|
||||
snap, err := s.repo.FindByIDBase(ctx, id)
|
||||
if err != nil {
|
||||
return nil, s.detailErr(err)
|
||||
}
|
||||
imgs, err := s.repo.ListImagesBase(ctx, id)
|
||||
if err != nil {
|
||||
return nil, internalErr(err.Error())
|
||||
}
|
||||
return s.buildSnapDetail(snap, imgs), nil
|
||||
}
|
||||
|
||||
// detailErr 统一详情取数失败的错误映射(公开 Detail 与 DetailAdmin 共用)。
|
||||
func (s *streetSnapService) detailErr(err error) error {
|
||||
if errors.Is(err, repository.ErrNotFound) {
|
||||
return ErrStreetSnapNotFound
|
||||
}
|
||||
return internalErr(err.Error())
|
||||
}
|
||||
|
||||
// buildSnapDetail 把街拍行与图片集映射为详情 DTO(公开/后台共用,差异只在数据来源)。
|
||||
func (s *streetSnapService) buildSnapDetail(snap *model.StreetSnap, imgs []model.StreetSnapImage) *dto.PublicStreetSnapDetail {
|
||||
return &dto.PublicStreetSnapDetail{
|
||||
UID: hashid.EncodeWithType(snap.ID, hashid.TypeSnap),
|
||||
Title: snap.Title,
|
||||
Cover: s.img.Compose(snap.Cover),
|
||||
Images: s.buildSnapImages(imgs),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// buildSnapImages 把街拍图片组装成「主图 + 其副图」的嵌套结构(与走秀详情同形):
|
||||
|
||||
Reference in New Issue
Block a user