refactor(publish): 审核侧切到正式表,通过即改状态不再晋升
This commit is contained in:
@ -13,8 +13,8 @@ import (
|
||||
"fashionapi/internal/repository"
|
||||
)
|
||||
|
||||
// DraftCard 审核列表统一卡片(所有模块共用),供后台列表页渲染。
|
||||
type DraftCard struct {
|
||||
// RecordCard 审核列表统一卡片(所有模块共用),供后台列表页渲染。
|
||||
type RecordCard struct {
|
||||
Kind string `json:"kind"` // runway | street | 未来其它模块
|
||||
ID uint32 `json:"id"`
|
||||
Title string `json:"title"` // 展示标题
|
||||
@ -26,11 +26,11 @@ type DraftCard struct {
|
||||
UpdatedAt uint32 `json:"updated_at"`
|
||||
}
|
||||
|
||||
// DraftImageRef 草稿图片引用(统一结构)。
|
||||
// RecordImageRef 记录图片引用(统一结构)。
|
||||
//
|
||||
// IsDetail / ParentImageID 只有街拍模块会填(走秀的主/细节图走 LookIndex 那套,语义不同,不复用);
|
||||
// DetailCount 让模板不用自己数副图。
|
||||
type DraftImageRef struct {
|
||||
type RecordImageRef struct {
|
||||
ID uint32 `json:"id"`
|
||||
Image string `json:"image"`
|
||||
Name string `json:"name"`
|
||||
@ -49,39 +49,39 @@ type EditField struct {
|
||||
Type string `json:"type"` // text | textarea | number
|
||||
}
|
||||
|
||||
// DraftDetailView 审核详情统一视图(所有模块共用)。
|
||||
type DraftDetailView struct {
|
||||
Kind string `json:"kind"`
|
||||
ID uint32 `json:"id"`
|
||||
BrandID uint32 `json:"brand_id"` // 仅 runway 等有品牌的模块有,其余为 0
|
||||
Title string `json:"title"` // 展示标题(runway=title_en,street=title)
|
||||
TitleEn string `json:"title_en"`
|
||||
TitleCn string `json:"title_cn"`
|
||||
DescriptionEn string `json:"description_en"`
|
||||
DescriptionCn string `json:"description_cn"`
|
||||
Year uint16 `json:"year"`
|
||||
Season string `json:"season"`
|
||||
CollectionType string `json:"collection_type"`
|
||||
SeasonCode string `json:"season_code"`
|
||||
City string `json:"city"`
|
||||
Cover string `json:"cover"`
|
||||
ImageCount uint16 `json:"image_count"`
|
||||
Status string `json:"status"`
|
||||
Images []DraftImageRef `json:"images"`
|
||||
// RecordDetailView 审核详情统一视图(所有模块共用)。
|
||||
type RecordDetailView struct {
|
||||
Kind string `json:"kind"`
|
||||
ID uint32 `json:"id"`
|
||||
BrandID uint32 `json:"brand_id"` // 仅 runway 等有品牌的模块有,其余为 0
|
||||
Title string `json:"title"` // 展示标题(runway=title_en,street=title)
|
||||
TitleEn string `json:"title_en"`
|
||||
TitleCn string `json:"title_cn"`
|
||||
DescriptionEn string `json:"description_en"`
|
||||
DescriptionCn string `json:"description_cn"`
|
||||
Year uint16 `json:"year"`
|
||||
Season string `json:"season"`
|
||||
CollectionType string `json:"collection_type"`
|
||||
SeasonCode string `json:"season_code"`
|
||||
City string `json:"city"`
|
||||
Cover string `json:"cover"`
|
||||
ImageCount uint16 `json:"image_count"`
|
||||
Status string `json:"status"`
|
||||
Images []RecordImageRef `json:"images"`
|
||||
// Fields 是审核前可微调的字段列表(按模块声明顺序渲染),替代模板里的 IsStreet 分支。
|
||||
Fields []EditField `json:"fields"`
|
||||
// Groups 街拍详情的「主图 → 其副图」分组视图(按 sort_order 顺序)。走秀模块留空。
|
||||
// 与 Images 同源同序:Images 保持扁平(既有渲染 / runway 仍用),Groups 供按组折叠。
|
||||
Groups []DraftImageGroup `json:"groups,omitempty"`
|
||||
Groups []ImageGroup `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
// DraftImageGroup 一张主图及其副图,供审核页按组折叠渲染。
|
||||
type DraftImageGroup struct {
|
||||
Image DraftImageRef `json:"image"` // 主图
|
||||
Details []DraftImageRef `json:"details"` // 该主图名下的副图(可为空)
|
||||
// ImageGroup 一张主图及其副图,供审核页按组折叠渲染。
|
||||
type ImageGroup struct {
|
||||
Image RecordImageRef `json:"image"` // 主图
|
||||
Details []RecordImageRef `json:"details"` // 该主图名下的副图(可为空)
|
||||
}
|
||||
|
||||
// ErrInvalidImageGroup 主副图分组操作不合法:主图不存在 / 不属于本草稿 / 自身不是主图 / 空集合。
|
||||
// ErrInvalidImageGroup 主副图分组操作不合法:主图不存在 / 不属于该记录 / 自身不是主图 / 空集合。
|
||||
// 这里是给用户看的文案,handler 直接渲染 err.Error()。
|
||||
var ErrInvalidImageGroup = errors.New("主图无效:它必须属于本草稿、未被删除,且自身是主图")
|
||||
|
||||
@ -92,40 +92,41 @@ type KindTab struct {
|
||||
Pending int64 `json:"pending"`
|
||||
}
|
||||
|
||||
// ReviewModule 一种待审草稿模块(runway / street / 未来任意爬虫数据类型)的统一契约。
|
||||
// ReviewModule 一种待审记录模块(runway / street / 未来任意爬虫数据类型)的统一契约。
|
||||
//
|
||||
// 新增一种爬虫数据类型需要审核时,只需:
|
||||
// 1. 建草稿表 + 迁移;
|
||||
// 2. 在仓储层实现 List/Detail/Images/Update/SetStatus/SaveFromDraft/CountPending;
|
||||
// 1. 建正式表 + 迁移(记录自带 status 列);
|
||||
// 2. 在仓储层实现 List/Detail/Images/Update/SetStatus/CountByStatus;
|
||||
// 3. 在此文件实现本接口并注册到 NewReviewService。
|
||||
//
|
||||
// 列表、详情、通过、驳回、待审计数、类型标签都会自动覆盖,无需改动 handler / 模板分支。
|
||||
type ReviewModule interface {
|
||||
Kind() string
|
||||
Label() string
|
||||
ListDrafts(ctx context.Context, status string, page, size int) ([]DraftCard, int64, error)
|
||||
DraftDetail(ctx context.Context, id uint32) (*DraftDetailView, error)
|
||||
UpdateDraft(ctx context.Context, id uint32, fields map[string]any) error
|
||||
// Approve 晋升到正式表并把草稿置 approved;返回正式表主键。
|
||||
ListRecords(ctx context.Context, status string, page, size int) ([]RecordCard, int64, error)
|
||||
RecordDetail(ctx context.Context, id uint32) (*RecordDetailView, error)
|
||||
UpdateRecord(ctx context.Context, id uint32, fields map[string]any) error
|
||||
// Approve 把正式记录置为 published;返回记录主键。
|
||||
// 单表发布模型:通过只是改状态,不再晋升重建(图片在入库时已落到正式表)。
|
||||
Approve(ctx context.Context, id uint32, reviewer string) (uint32, error)
|
||||
Reject(ctx context.Context, id uint32, reviewer, reason string) error
|
||||
CountPending(ctx context.Context) (int64, error)
|
||||
}
|
||||
|
||||
// ReviewService 审核草稿的业务逻辑:列出 / 查看 / 微调 / 通过(晋升)/ 驳回。
|
||||
// ReviewService 审核的业务逻辑:列出 / 查看 / 微调 / 通过(置 published)/ 驳回。
|
||||
// 全部按 kind 委派到对应 ReviewModule;kind 为空表示合并所有模块。
|
||||
type ReviewService interface {
|
||||
ListDrafts(ctx context.Context, kind, status string, page, size int) ([]DraftCard, int64, error)
|
||||
DraftDetail(ctx context.Context, kind string, id uint32) (*DraftDetailView, error)
|
||||
UpdateDraft(ctx context.Context, kind string, id uint32, fields map[string]any) error
|
||||
ListRecords(ctx context.Context, kind, status string, page, size int) ([]RecordCard, int64, error)
|
||||
RecordDetail(ctx context.Context, kind string, id uint32) (*RecordDetailView, error)
|
||||
UpdateRecord(ctx context.Context, kind string, id uint32, fields map[string]any) error
|
||||
Approve(ctx context.Context, kind string, id uint32, reviewer string) (uint32, error)
|
||||
Reject(ctx context.Context, kind string, id uint32, reviewer, reason string) error
|
||||
// DeleteDraftImage 软删审核草稿里的单张图片(审核阶段剔除坏图,不删S4文件)。
|
||||
DeleteDraftImage(ctx context.Context, kind string, draftID, imgID uint32) error
|
||||
// AttachDraftImages 把若干张草稿图并入指定主图(街拍专用;走秀模块返回 ErrInvalidImageGroup)。
|
||||
AttachDraftImages(ctx context.Context, kind string, draftID, mainImgID uint32, imgIDs []uint32) error
|
||||
// DetachDraftImage 把副图拆出恢复为主图(街拍专用)。
|
||||
DetachDraftImage(ctx context.Context, kind string, draftID, imgID uint32) error
|
||||
// DeleteImage 软删审核记录里的单张图片(审核阶段剔除坏图,不删S4文件)。
|
||||
DeleteImage(ctx context.Context, kind string, recordID, imgID uint32) error
|
||||
// AttachImages 把若干张图并入指定主图(街拍专用;走秀模块返回 ErrInvalidImageGroup)。
|
||||
AttachImages(ctx context.Context, kind string, recordID, mainImgID uint32, imgIDs []uint32) error
|
||||
// DetachImage 把副图拆出恢复为主图(街拍专用)。
|
||||
DetachImage(ctx context.Context, kind string, recordID, imgID uint32) error
|
||||
// KindTabs 返回类型标签(含「全部」与每个模块的待审数),驱动大审核列表概览。
|
||||
KindTabs(ctx context.Context) []KindTab
|
||||
}
|
||||
@ -165,22 +166,22 @@ func (s *reviewService) module(kind string) (ReviewModule, bool) {
|
||||
return m, ok
|
||||
}
|
||||
|
||||
func (s *reviewService) ListDrafts(ctx context.Context, kind, status string, page, size int) ([]DraftCard, int64, error) {
|
||||
func (s *reviewService) ListRecords(ctx context.Context, kind, status string, page, size int) ([]RecordCard, int64, error) {
|
||||
if kind == "all" {
|
||||
kind = ""
|
||||
}
|
||||
if kind != "" {
|
||||
m, ok := s.module(kind)
|
||||
if !ok {
|
||||
return []DraftCard{}, 0, nil
|
||||
return []RecordCard{}, 0, nil
|
||||
}
|
||||
return m.ListDrafts(ctx, status, page, size)
|
||||
return m.ListRecords(ctx, status, page, size)
|
||||
}
|
||||
// 合并所有模块:各取较多行后在内存按创建时间倒序,再分页(后台量小,足够准确)。
|
||||
var all []DraftCard
|
||||
var all []RecordCard
|
||||
var total int64
|
||||
for _, k := range s.order {
|
||||
cards, t, err := s.modules[k].ListDrafts(ctx, status, 1, 1000)
|
||||
cards, t, err := s.modules[k].ListRecords(ctx, status, 1, 1000)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@ -208,20 +209,20 @@ func (s *reviewService) ListDrafts(ctx context.Context, kind, status string, pag
|
||||
return all[start:end], total, nil
|
||||
}
|
||||
|
||||
func (s *reviewService) DraftDetail(ctx context.Context, kind string, id uint32) (*DraftDetailView, error) {
|
||||
func (s *reviewService) RecordDetail(ctx context.Context, kind string, id uint32) (*RecordDetailView, error) {
|
||||
m, ok := s.module(kind)
|
||||
if !ok {
|
||||
return nil, repository.ErrNotFound
|
||||
}
|
||||
return m.DraftDetail(ctx, id)
|
||||
return m.RecordDetail(ctx, id)
|
||||
}
|
||||
|
||||
func (s *reviewService) UpdateDraft(ctx context.Context, kind string, id uint32, fields map[string]any) error {
|
||||
func (s *reviewService) UpdateRecord(ctx context.Context, kind string, id uint32, fields map[string]any) error {
|
||||
m, ok := s.module(kind)
|
||||
if !ok {
|
||||
return repository.ErrNotFound
|
||||
}
|
||||
return m.UpdateDraft(ctx, id, fields)
|
||||
return m.UpdateRecord(ctx, id, fields)
|
||||
}
|
||||
|
||||
func (s *reviewService) Approve(ctx context.Context, kind string, id uint32, reviewer string) (uint32, error) {
|
||||
@ -256,22 +257,22 @@ func (s *reviewService) KindTabs(ctx context.Context) []KindTab {
|
||||
return tabs
|
||||
}
|
||||
|
||||
// DeleteDraftImage 软删审核草稿里的单张图片(审核阶段剔除坏图)。
|
||||
// 按 kind 委派到对应草稿图片表的软删,并同步草稿 image_count;
|
||||
// DeleteImage 软删审核记录里的单张图片(审核阶段剔除坏图)。
|
||||
// 按 kind 委派到对应图片表的软删,并同步记录 image_count;
|
||||
// 若该 key 在全库已无其他引用(引用计数归零),则一并清理S4孤儿文件。
|
||||
func (s *reviewService) DeleteDraftImage(ctx context.Context, kind string, draftID, imgID uint32) error {
|
||||
func (s *reviewService) DeleteImage(ctx context.Context, kind string, recordID, imgID uint32) error {
|
||||
if _, ok := s.module(kind); !ok {
|
||||
return repository.ErrNotFound
|
||||
}
|
||||
key, err := s.repo.GetDraftImageKey(ctx, kind, draftID, imgID)
|
||||
key, err := s.repo.GetImageKey(ctx, kind, recordID, imgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch kind {
|
||||
case dto.IngestKindRunway:
|
||||
err = s.repo.SoftDeleteRunwayDraftImage(ctx, draftID, imgID)
|
||||
err = s.repo.SoftDeleteRunwayImage(ctx, recordID, imgID)
|
||||
case dto.IngestKindStreet:
|
||||
err = s.repo.SoftDeleteStreetDraftImage(ctx, draftID, imgID)
|
||||
err = s.repo.SoftDeleteStreetImage(ctx, recordID, imgID)
|
||||
default:
|
||||
return repository.ErrNotFound
|
||||
}
|
||||
@ -284,21 +285,21 @@ func (s *reviewService) DeleteDraftImage(ctx context.Context, kind string, draft
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *reviewService) AttachDraftImages(ctx context.Context, kind string, draftID, mainImgID uint32, imgIDs []uint32) error {
|
||||
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.AttachStreetDraftImages(ctx, draftID, mainImgID, imgIDs))
|
||||
return translateGroupErr(s.repo.AttachStreetSnapImages(ctx, recordID, mainImgID, imgIDs))
|
||||
}
|
||||
|
||||
func (s *reviewService) DetachDraftImage(ctx context.Context, kind string, draftID, imgID uint32) error {
|
||||
func (s *reviewService) DetachImage(ctx context.Context, kind string, recordID, imgID uint32) error {
|
||||
if kind != dto.IngestKindStreet {
|
||||
return ErrInvalidImageGroup
|
||||
}
|
||||
return translateGroupErr(s.repo.DetachStreetDraftImage(ctx, draftID, imgID))
|
||||
return translateGroupErr(s.repo.DetachStreetSnapImage(ctx, recordID, imgID))
|
||||
}
|
||||
|
||||
// translateGroupErr 把仓储层的哨兵错误翻成给用户看的中文文案;其余错误原样透传。
|
||||
@ -322,24 +323,24 @@ type runwayModule struct {
|
||||
func (m *runwayModule) Kind() string { return dto.IngestKindRunway }
|
||||
func (m *runwayModule) Label() string { return "走秀" }
|
||||
|
||||
func (m *runwayModule) ListDrafts(ctx context.Context, status string, page, size int) ([]DraftCard, int64, error) {
|
||||
rows, total, err := m.repo.ListDrafts(ctx, status, page, size)
|
||||
func (m *runwayModule) ListRecords(ctx context.Context, status string, page, size int) ([]RecordCard, int64, error) {
|
||||
rows, total, err := m.repo.ListRecords(ctx, status, page, size)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return m.toRunwayCards(rows), total, nil
|
||||
}
|
||||
|
||||
func (m *runwayModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetailView, error) {
|
||||
d, err := m.repo.DraftDetail(ctx, id)
|
||||
func (m *runwayModule) RecordDetail(ctx context.Context, id uint32) (*RecordDetailView, error) {
|
||||
d, err := m.repo.RecordDetail(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgs, err := m.repo.DraftImages(ctx, id)
|
||||
imgs, err := m.repo.RecordImages(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := &DraftDetailView{
|
||||
v := &RecordDetailView{
|
||||
Kind: dto.IngestKindRunway,
|
||||
ID: d.ID,
|
||||
BrandID: d.BrandID,
|
||||
@ -367,32 +368,28 @@ func (m *runwayModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetail
|
||||
},
|
||||
}
|
||||
for _, im := range imgs {
|
||||
v.Images = append(v.Images, DraftImageRef{ID: im.ID, Image: m.img.Compose(im.Image), Name: im.Name, SortOrder: im.SortOrder})
|
||||
v.Images = append(v.Images, RecordImageRef{ID: im.ID, Image: m.img.Compose(im.Image), Name: im.Name, SortOrder: im.SortOrder})
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (m *runwayModule) UpdateDraft(ctx context.Context, id uint32, fields map[string]any) error {
|
||||
return m.repo.UpdateDraft(ctx, id, fields)
|
||||
func (m *runwayModule) UpdateRecord(ctx context.Context, id uint32, fields map[string]any) error {
|
||||
return m.repo.UpdateRecord(ctx, id, fields)
|
||||
}
|
||||
|
||||
func (m *runwayModule) Approve(ctx context.Context, id uint32, reviewer string) (uint32, error) {
|
||||
officialID, err := m.repo.SaveRunwayFromDraft(ctx, id)
|
||||
if err != nil {
|
||||
if err := m.repo.SetRecordStatus(ctx, id, model.StatusPublished, reviewer, ""); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := m.repo.SetDraftStatus(ctx, id, model.DraftStatusApproved, reviewer, ""); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return officialID, nil
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (m *runwayModule) Reject(ctx context.Context, id uint32, reviewer, reason string) error {
|
||||
return m.repo.SetDraftStatus(ctx, id, model.DraftStatusRejected, reviewer, reason)
|
||||
return m.repo.SetRecordStatus(ctx, id, model.StatusRejected, reviewer, reason)
|
||||
}
|
||||
|
||||
func (m *runwayModule) CountPending(ctx context.Context) (int64, error) {
|
||||
return m.repo.CountDraftPending(ctx, model.DraftStatusPending, &model.BrandRunwayDraft{})
|
||||
return m.repo.CountByStatus(ctx, model.StatusPending, &model.BrandRunway{})
|
||||
}
|
||||
|
||||
// ── street 模块 ────────────────────────────────────────────────────────────────
|
||||
@ -405,24 +402,24 @@ type streetModule struct {
|
||||
func (m *streetModule) Kind() string { return dto.IngestKindStreet }
|
||||
func (m *streetModule) Label() string { return "街拍" }
|
||||
|
||||
func (m *streetModule) ListDrafts(ctx context.Context, status string, page, size int) ([]DraftCard, int64, error) {
|
||||
rows, total, err := m.repo.ListStreetDrafts(ctx, status, page, size)
|
||||
func (m *streetModule) ListRecords(ctx context.Context, status string, page, size int) ([]RecordCard, int64, error) {
|
||||
rows, total, err := m.repo.ListStreetRecords(ctx, status, page, size)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return m.toStreetCards(rows), total, nil
|
||||
}
|
||||
|
||||
func (m *streetModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetailView, error) {
|
||||
d, err := m.repo.StreetDraftDetail(ctx, id)
|
||||
func (m *streetModule) RecordDetail(ctx context.Context, id uint32) (*RecordDetailView, error) {
|
||||
d, err := m.repo.StreetRecordDetail(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgs, err := m.repo.StreetDraftImages(ctx, id)
|
||||
imgs, err := m.repo.StreetRecordImages(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := &DraftDetailView{
|
||||
v := &RecordDetailView{
|
||||
Kind: dto.IngestKindStreet,
|
||||
ID: d.ID,
|
||||
Title: d.Title,
|
||||
@ -440,7 +437,7 @@ func (m *streetModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetail
|
||||
|
||||
// 按 sort_order 顺序成组:主图各成一组;副图归入其(存活)主图组,父引用失效则自成一主图组。
|
||||
// 父引用指向「副图」时沿链向上归并到该副图所在组的主图(保证与公开 API 同源同序)。
|
||||
byID := make(map[uint32]model.StreetSnapDraftImage, len(imgs))
|
||||
byID := make(map[uint32]model.StreetSnapImage, len(imgs))
|
||||
for _, im := range imgs {
|
||||
byID[im.ID] = im
|
||||
}
|
||||
@ -472,7 +469,7 @@ func (m *streetModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetail
|
||||
}
|
||||
}
|
||||
|
||||
groups := make([]DraftImageGroup, 0, len(imgs))
|
||||
groups := make([]ImageGroup, 0, len(imgs))
|
||||
groupIndex := make(map[uint32]int, len(imgs))
|
||||
for _, im := range imgs {
|
||||
root := resolveRoot(im.ID)
|
||||
@ -483,7 +480,7 @@ func (m *streetModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetail
|
||||
isDetailVal = 1
|
||||
parent = root
|
||||
}
|
||||
ref := DraftImageRef{
|
||||
ref := RecordImageRef{
|
||||
ID: im.ID,
|
||||
Image: m.img.Compose(im.Image),
|
||||
Name: im.Name,
|
||||
@ -494,14 +491,14 @@ func (m *streetModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetail
|
||||
}
|
||||
v.Images = append(v.Images, ref)
|
||||
if !isDetail {
|
||||
groups = append(groups, DraftImageGroup{Image: ref})
|
||||
groups = append(groups, ImageGroup{Image: ref})
|
||||
groupIndex[im.ID] = len(groups) - 1
|
||||
continue
|
||||
}
|
||||
if gi, ok := groupIndex[root]; ok {
|
||||
groups[gi].Details = append(groups[gi].Details, ref)
|
||||
} else { // 兜底(理论不发生):主图未先出现,自成一组
|
||||
groups = append(groups, DraftImageGroup{Image: ref})
|
||||
groups = append(groups, ImageGroup{Image: ref})
|
||||
groupIndex[im.ID] = len(groups) - 1
|
||||
}
|
||||
}
|
||||
@ -509,33 +506,29 @@ func (m *streetModule) DraftDetail(ctx context.Context, id uint32) (*DraftDetail
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (m *streetModule) UpdateDraft(ctx context.Context, id uint32, fields map[string]any) error {
|
||||
return m.repo.UpdateStreetDraft(ctx, id, fields)
|
||||
func (m *streetModule) UpdateRecord(ctx context.Context, id uint32, fields map[string]any) error {
|
||||
return m.repo.UpdateStreetRecord(ctx, id, fields)
|
||||
}
|
||||
|
||||
func (m *streetModule) Approve(ctx context.Context, id uint32, reviewer string) (uint32, error) {
|
||||
officialID, err := m.repo.SaveStreetSnapFromDraft(ctx, id)
|
||||
if err != nil {
|
||||
if err := m.repo.SetStreetRecordStatus(ctx, id, model.StatusPublished, reviewer, ""); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := m.repo.SetStreetDraftStatus(ctx, id, model.DraftStatusApproved, reviewer, ""); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return officialID, nil
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (m *streetModule) Reject(ctx context.Context, id uint32, reviewer, reason string) error {
|
||||
return m.repo.SetStreetDraftStatus(ctx, id, model.DraftStatusRejected, reviewer, reason)
|
||||
return m.repo.SetStreetRecordStatus(ctx, id, model.StatusRejected, reviewer, reason)
|
||||
}
|
||||
|
||||
func (m *streetModule) CountPending(ctx context.Context) (int64, error) {
|
||||
return m.repo.CountDraftPending(ctx, model.DraftStatusPending, &model.StreetSnapDraft{})
|
||||
return m.repo.CountByStatus(ctx, model.StatusPending, &model.StreetSnap{})
|
||||
}
|
||||
|
||||
// ── 卡片转换(保留原逻辑)──────────────────────────────────────────────────────
|
||||
|
||||
func (m *runwayModule) toRunwayCards(rows []model.BrandRunwayDraft) []DraftCard {
|
||||
cards := make([]DraftCard, 0, len(rows))
|
||||
func (m *runwayModule) toRunwayCards(rows []model.BrandRunway) []RecordCard {
|
||||
cards := make([]RecordCard, 0, len(rows))
|
||||
for _, d := range rows {
|
||||
sub := d.SeasonCode
|
||||
if d.CollectionType != "" {
|
||||
@ -544,7 +537,7 @@ func (m *runwayModule) toRunwayCards(rows []model.BrandRunwayDraft) []DraftCard
|
||||
if sub == "" {
|
||||
sub = "—"
|
||||
}
|
||||
cards = append(cards, DraftCard{
|
||||
cards = append(cards, RecordCard{
|
||||
Kind: dto.IngestKindRunway,
|
||||
ID: d.ID,
|
||||
Title: d.TitleEn,
|
||||
@ -559,14 +552,14 @@ func (m *runwayModule) toRunwayCards(rows []model.BrandRunwayDraft) []DraftCard
|
||||
return cards
|
||||
}
|
||||
|
||||
func (m *streetModule) toStreetCards(rows []model.StreetSnapDraft) []DraftCard {
|
||||
cards := make([]DraftCard, 0, len(rows))
|
||||
func (m *streetModule) toStreetCards(rows []model.StreetSnap) []RecordCard {
|
||||
cards := make([]RecordCard, 0, len(rows))
|
||||
for _, d := range rows {
|
||||
sub := d.City
|
||||
if sub == "" {
|
||||
sub = "—"
|
||||
}
|
||||
cards = append(cards, DraftCard{
|
||||
cards = append(cards, RecordCard{
|
||||
Kind: dto.IngestKindStreet,
|
||||
ID: d.ID,
|
||||
Title: d.Title,
|
||||
|
||||
Reference in New Issue
Block a user