This commit is contained in:
toom1996
2026-09-30 11:58:32 +08:00
parent 065d57457e
commit 1b06bd5bda
24 changed files with 2588 additions and 1659 deletions

View File

@ -46,6 +46,10 @@ type ArticleRepository interface {
GetRunwayImageRunwayID(ctx context.Context, imageID uint32) (uint32, error)
// ReresolveRunwayDedup 删图后重算本画廊去重,消除陈旧 dup_of(见方法注释)。
ReresolveRunwayDedup(ctx context.Context, runwayID uint32) error
// ReresolveRunwayDupOfSources 删图后清理「以被删图为源」的跨文章重复标记(见方法注释)。
ReresolveRunwayDupOfSources(ctx context.Context, deletedImageIDs []uint32) error
// GetRunwayLiveImageIDs 取某走秀全部存活图片 id(级联下架前用于跨文章去重清理)。
GetRunwayLiveImageIDs(ctx context.Context, runwayID uint32) ([]uint32, error)
// SoftDeleteRunwayImages 软删某走秀的全部图片(级联下架用),返回被删图片的 key 列表。
SoftDeleteRunwayImages(ctx context.Context, runwayID uint32) ([]string, error)
// SetRunwayCover 更新走秀封面(同时回到图片列表首张)。
@ -480,6 +484,60 @@ func (r *articleRepository) ReresolveRunwayDedup(ctx context.Context, runwayID u
})
}
// ReresolveRunwayDupOfSources 删除图片后,清理「以被删图为源」的跨文章重复标记。
// 给定刚被软删的走秀图片 id 列表,找出所有仍存活(其它文章)且 dup_of 指向它们的图片,
// 在全局存活图里重新近重复判定:找到新源则改指新源,否则清掉重复标记。
// 为什么需要:删 B 文章/图时,A 文章中 dup_of 指向 B 图的行不会被 ReresolveRunwayDedup
// (仅本集内重算)清理,导致 A 仍误显「重复」且「查看来源」指向已删图打不开。
func (r *articleRepository) ReresolveRunwayDupOfSources(ctx context.Context, deletedImageIDs []uint32) error {
if len(deletedImageIDs) == 0 {
return nil
}
strIDs := make([]string, 0, len(deletedImageIDs))
for _, id := range deletedImageIDs {
strIDs = append(strIDs, strconv.FormatUint(uint64(id), 10))
}
var affected []model.BrandRunwayImage
if err := r.db.WithContext(ctx).
Where("is_deleted = 0 AND dup_of IN ?", strIDs).
Find(&affected).Error; err != nil {
return err
}
if len(affected) == 0 {
return nil
}
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
for _, im := range affected {
if !im.Phash.Valid || im.Phash.String == "" {
if err := tx.Model(&model.BrandRunwayImage{}).
Where("id = ? AND is_deleted = 0", im.ID).
Updates(map[string]any{"is_duplicate": 0, "dup_of": ""}).Error; err != nil {
return err
}
continue
}
src, err := nearDupExceptRunway(tx, im.Phash.String, phash.DefaultThreshold, im.ID, im.RunwayID, im.SortOrder)
if err != nil {
return err
}
if src != 0 && src != im.ID {
if err := tx.Model(&model.BrandRunwayImage{}).
Where("id = ? AND is_deleted = 0", im.ID).
Updates(map[string]any{"is_duplicate": 1, "dup_of": strconv.FormatUint(uint64(src), 10)}).Error; err != nil {
return err
}
} else {
if err := tx.Model(&model.BrandRunwayImage{}).
Where("id = ? AND is_deleted = 0", im.ID).
Updates(map[string]any{"is_duplicate": 0, "dup_of": ""}).Error; err != nil {
return err
}
}
}
return nil
})
}
// GetRunwayDupSources 批量反查重复源(见接口注释)。一次 IN 查询 + JOIN 走秀与品牌,
// 避免逐图 N+1;源图可能已被删(查不到则跳过,不报错)。
func (r *articleRepository) GetRunwayDupSources(ctx context.Context, imageIDs []uint32) (map[uint32]dto.DupSource, error) {
@ -554,6 +612,18 @@ func (r *articleRepository) SoftDeleteRunwayImages(ctx context.Context, runwayID
return keys, nil
}
// GetRunwayLiveImageIDs 取某走秀全部存活图片的 id(级联下架前取出,供跨文章去重清理)。
func (r *articleRepository) GetRunwayLiveImageIDs(ctx context.Context, runwayID uint32) ([]uint32, error) {
var ids []uint32
if err := r.db.WithContext(ctx).
Model(&model.BrandRunwayImage{}).
Where("runway_id = ? AND is_deleted = 0", runwayID).
Pluck("id", &ids).Error; err != nil {
return nil, err
}
return ids, nil
}
func (r *articleRepository) SetRunwayCover(ctx context.Context, runwayID uint32, image string) error {
return r.db.WithContext(ctx).
Model(&model.BrandRunway{}).