feat(street): 公开详情改为runway同形嵌套;审核页合并为图片流

This commit is contained in:
toom1996
2026-09-21 23:24:00 +08:00
parent 49bd7b74c6
commit 85ad3a3f68
5 changed files with 119 additions and 108 deletions

View File

@ -134,24 +134,22 @@ func (s *streetSnapService) Detail(ctx context.Context, id string) (*dto.PublicS
return nil, internalErr(err.Error())
}
flat, groups := s.groupSnapImages(imgs)
return &dto.PublicStreetSnapDetail{
UID: hashid.EncodeWithType(snap.ID, hashid.TypeSnap),
Title: snap.Title,
Cover: s.img.Compose(snap.Cover),
Images: flat,
Groups: groups,
Images: s.buildSnapImages(imgs),
}, nil
}
// groupSnapImages 把街拍图片按主副图分组:主图各成一组,副图归入其(存活)主图组。
// 父引用指向「副图」时沿链向上归并到该副图所在组的主图;父行缺失(被软删)则把该图按主图渲染(不丢图)。
// 返回的 flat(扁平)与 groups 同源同序:groups 中每张主图对应的 Details 顺序,等于 flat 中该主图之后的副图出现顺序。
func (s *streetSnapService) groupSnapImages(imgs []model.StreetSnapImage) ([]dto.PublicArticleImage, []dto.PublicStreetSnapGroup) {
// buildSnapImages 把街拍图片组装成「主图 + 其副图」的嵌套结构(与走秀详情同形):
// Images 只含主图(is_detail=0),每张主图的副图挂在其 Detail 子数组下。
// 父引用指向「副图」时沿链向上归并到该副图所在组的主图;父行缺失 / 成环则把该图按主图渲染(不丢图)。
// LookIndex 是合成的 1-based 主图序号(街拍不落库组序号)。
func (s *streetSnapService) buildSnapImages(imgs []model.StreetSnapImage) []dto.PublicArticleImage {
if len(imgs) == 0 {
return nil, nil
return nil
}
flat := s.toPublicSnapImages(imgs)
byID := make(map[uint32]*model.StreetSnapImage, len(imgs))
for i := range imgs {
@ -178,26 +176,35 @@ func (s *streetSnapService) groupSnapImages(imgs []model.StreetSnapImage) ([]dto
}
}
groups := make([]dto.PublicStreetSnapGroup, 0, len(imgs))
// 第一遍:按 sort_order 建立主图(根),并记录「主图行 id → 输出下标」,副图排在主图之前也能正确归位。
out := make([]dto.PublicArticleImage, 0, len(imgs))
index := make(map[uint32]int, len(imgs))
for i, im := range imgs {
root := resolveRoot(im.ID)
isDetail := root != im.ID
ref := flat[i]
if !isDetail {
groups = append(groups, dto.PublicStreetSnapGroup{Image: ref})
index[im.ID] = len(groups) - 1
for _, im := range imgs {
if resolveRoot(im.ID) != im.ID {
continue
}
if gi, ok := index[root]; ok {
groups[gi].Details = append(groups[gi].Details, ref)
} else {
// 兜底(理论不发生):主图未先出现,自成一组
groups = append(groups, dto.PublicStreetSnapGroup{Image: ref})
index[im.ID] = len(groups) - 1
}
pub := s.toPublicSnapImage(im)
pub.LookIndex = uint32(len(out) + 1) // 合成的 1-based 主图序号
index[im.ID] = len(out)
out = append(out, pub)
}
return flat, groups
// 第二遍:把副图挂到所属主图的 Detail 下(LookIndex 取所属主图的值)。
for _, im := range imgs {
root := resolveRoot(im.ID)
if root == im.ID {
continue
}
gi, ok := index[root]
if !ok {
continue // 理论不发生(孤儿已按主图渲染)
}
pub := s.toPublicSnapImage(im)
pub.IsDetail = 1
pub.LookIndex = out[gi].LookIndex
out[gi].Detail = append(out[gi].Detail, pub)
}
return out
}
// IDs 返回全部街拍的编码 uid(供 SSG 枚举详情页路径使用,对外暴露的即编码串)。
@ -229,16 +236,21 @@ func (s *streetSnapService) toPublicSnapImages(imgs []model.StreetSnapImage) []d
}
out := make([]dto.PublicArticleImage, 0, len(imgs))
for _, im := range imgs {
out = append(out, dto.PublicArticleImage{
ID: hashid.EncodeWithType(im.ID, hashid.TypeSnapImage),
Image: s.img.Compose(im.Image),
Thumb: s.img.ComposeThumb(im.Image),
Name: im.Name,
})
out = append(out, s.toPublicSnapImage(im))
}
return out
}
// toPublicSnapImage 把单张图片实体映射为对外结构(url + 名称 + 图片自身的编码 id)。
func (s *streetSnapService) toPublicSnapImage(im model.StreetSnapImage) dto.PublicArticleImage {
return dto.PublicArticleImage{
ID: hashid.EncodeWithType(im.ID, hashid.TypeSnapImage),
Image: s.img.Compose(im.Image),
Thumb: s.img.ComposeThumb(im.Image),
Name: im.Name,
}
}
// toThumbSnapImages 与 toPublicSnapImages 同构,但图片地址走「列表缩略图」样式(ComposeThumb),
// 仅供列表卡片 / 图条等小尺寸场景使用;详情页与大图灯箱仍用 toPublicSnapImages(展示样式)。
func (s *streetSnapService) toThumbSnapImages(imgs []model.StreetSnapImage) []dto.PublicArticleImage {