This commit is contained in:
toom1996
2026-09-25 11:31:52 +08:00
parent 4f4c11d508
commit 42f6316125
51 changed files with 4489 additions and 998 deletions

View File

@ -28,8 +28,8 @@ type RecordCard struct {
// RecordImageRef 记录图片引用(统一结构)。
//
// IsDetail / ParentImageID 只有街拍模块会填(走秀的主/细节图走 LookIndex 那套,语义不同,不复用);
// DetailCount 让模板不用自己数副图。
// 街拍 / 走秀共用同一套主副图模型:is_detail=0 为主图(parent_image_id=0),
// =1 为副图(parent_image_id 指向同记录内主图的行 id)。DetailCount 让模板不用自己数副图。
type RecordImageRef struct {
ID uint32 `json:"id"`
Image string `json:"image"`
@ -70,8 +70,9 @@ type RecordDetailView struct {
Images []RecordImageRef `json:"images"`
// Fields 是审核前可微调的字段列表(按模块声明顺序渲染),替代模板里的 IsStreet 分支。
Fields []EditField `json:"fields"`
// Groups 街拍详情的「主图 → 其副图」分组视图(按 sort_order 顺序)。走秀模块留空。
// 与 Images 同源同序:Images 保持扁平(既有渲染 / runway 仍用),Groups 供按组折叠。
// Groups 详情的「主图 → 其副图」分组视图(按 sort_order 顺序)。
// 街拍与走秀均按 parent_image_id 分组(见各模块 RecordDetail),结构同构。
// 与 Images 同源同序:Images 保持扁平(既有渲染),Groups 供按组折叠。
Groups []ImageGroup `json:"groups,omitempty"`
}
@ -286,20 +287,28 @@ func (s *reviewService) DeleteImage(ctx context.Context, kind string, recordID,
}
func (s *reviewService) AttachImages(ctx context.Context, kind string, recordID, mainImgID uint32, imgIDs []uint32) error {
if kind != dto.IngestKindStreet {
return ErrInvalidImageGroup
}
if len(imgIDs) == 0 {
return ErrInvalidImageGroup
}
return translateGroupErr(s.repo.AttachStreetSnapImages(ctx, recordID, mainImgID, imgIDs))
switch kind {
case dto.IngestKindStreet:
return translateGroupErr(s.repo.AttachStreetSnapImages(ctx, recordID, mainImgID, imgIDs))
case dto.IngestKindRunway:
return translateGroupErr(s.repo.AttachRunwayImages(ctx, recordID, mainImgID, imgIDs))
default:
return ErrInvalidImageGroup
}
}
func (s *reviewService) DetachImage(ctx context.Context, kind string, recordID, imgID uint32) error {
if kind != dto.IngestKindStreet {
switch kind {
case dto.IngestKindStreet:
return translateGroupErr(s.repo.DetachStreetSnapImage(ctx, recordID, imgID))
case dto.IngestKindRunway:
return translateGroupErr(s.repo.DetachRunwayImage(ctx, recordID, imgID))
default:
return ErrInvalidImageGroup
}
return translateGroupErr(s.repo.DetachStreetSnapImage(ctx, recordID, imgID))
}
// translateGroupErr 把仓储层的哨兵错误翻成给用户看的中文文案;其余错误原样透传。
@ -367,12 +376,58 @@ func (m *runwayModule) RecordDetail(ctx context.Context, id uint32) (*RecordDeta
{Name: "season_code", Label: "季节码", Value: d.SeasonCode, Type: "text"},
},
}
// 按 parent_image_id 成组(与街拍同构):is_detail=0 为主图;is_detail=1 且 parent_image_id>0
// 的是其副图;is_detail=1 且 parent_image_id=0 的孤儿细节图按主图渲染(自成一组,不丢图)。
// 这样走秀详情页(消费 Groups)与街拍同构,编辑页(消费 Images)也能按主图→细节图顺序渲染。
mains := make([]model.BrandRunwayImage, 0, len(imgs))
mainSeen := make(map[uint32]bool, len(imgs))
for _, im := range imgs {
v.Images = append(v.Images, RecordImageRef{ID: im.ID, Image: m.img.Compose(im.Image), Name: im.Name, SortOrder: im.SortOrder})
isMain := im.IsDetail == 0 || im.ParentImageID == 0
if isMain && !mainSeen[im.ID] {
mainSeen[im.ID] = true
mains = append(mains, im)
}
}
detailsByParent := make(map[uint32][]model.BrandRunwayImage, len(imgs))
for _, im := range imgs {
if im.IsDetail == 1 && im.ParentImageID != 0 && !mainSeen[im.ID] {
detailsByParent[im.ParentImageID] = append(detailsByParent[im.ParentImageID], im)
}
}
detailCount := make(map[uint32]int, len(detailsByParent))
for p, ds := range detailsByParent {
detailCount[p] = len(ds)
}
for _, main := range mains {
ref := RecordImageRef{
ID: main.ID,
Image: m.img.Compose(main.Image),
Name: main.Name,
SortOrder: main.SortOrder,
IsDetail: 0,
ParentImageID: 0,
DetailCount: detailCount[main.ID],
}
v.Images = append(v.Images, ref)
grp := ImageGroup{Image: ref}
for _, d := range detailsByParent[main.ID] {
dref := RecordImageRef{
ID: d.ID,
Image: m.img.Compose(d.Image),
Name: d.Name,
SortOrder: d.SortOrder,
IsDetail: 1,
ParentImageID: d.ParentImageID,
}
grp.Details = append(grp.Details, dref)
v.Images = append(v.Images, dref)
}
v.Groups = append(v.Groups, grp)
}
return v, nil
}
func (m *runwayModule) UpdateRecord(ctx context.Context, id uint32, fields map[string]any) error {
return m.repo.UpdateRecord(ctx, id, fields)
}