fix(publish): 最终审查补修(编辑页删图重算计数、迁移编号后置、图片 id 稳定断言)

D1 编辑页删图:DeleteRunwayImage/DeleteSnapImage 在单事务内取父 id→软删→按存活行重算 image_count,口径与审核侧 SoftDelete*Image 完全一致(走秀只计主图、街拍计全部),并补集成测试(含重复删同一张)

D2 不可逆删表迁移 02→05(git mv),使文件名顺序 01→01b→03→04→05 与必需执行顺序一致;同步 README/文件头/规格/计划全部引用

D3 新增「通过审核后图片行 id 集合完全一致」的集成断言,锁住单表发布头号收益

D4 修正 01 迁移文件头「只做三件事」自相矛盾表述;D5 删除受版本控制的陈旧产物 cmd/dbtool/db_dump.json 并加 gitignore 规则;D6 清理遗留空目录 scripts/tmp_verify/
This commit is contained in:
toom1996
2026-09-23 16:27:14 +08:00
parent be4db0bf96
commit f04b5707cb
10 changed files with 411 additions and 37116 deletions

View File

@ -35,7 +35,7 @@ type StreetSnapRepository interface {
GetForEdit(ctx context.Context, id uint32) (*model.StreetSnap, []model.StreetSnapImage, error)
// UpdateSnap 编辑后更新字段(白名单,杜绝任意列注入)。
UpdateSnap(ctx context.Context, id uint32, fields map[string]any) error
// DeleteSnapImage 软删单张街拍图片。
// DeleteSnapImage 软删单张街拍图片,并按实际存活图片行数重算 image_count。
DeleteSnapImage(ctx context.Context, imageID uint32) error
// GetSnapImageKey 取单张街拍图片的S4 key(删除前清理用);不存在返回空串。
GetSnapImageKey(ctx context.Context, imageID uint32) (string, error)
@ -261,11 +261,35 @@ func (r *streetSnapRepository) UpdateSnap(ctx context.Context, id uint32, fields
Updates(clean).Error
}
// DeleteSnapImage 软删单张街拍图片(后台正式编辑页删图),并同步重算 image_count。
//
// 与审核侧 SoftDeleteStreetImage 口径一致:删完按**实际存活图片行数**重算
// (`is_deleted = 0`,主图 + 副图全部计入)。
//
// 为什么必须重算而不是「递减 1」:理由同 articleRepository.DeleteRunwayImage —— 单表发布模型
// 删掉了晋升重建,编辑页删图造成的计数漂移不再被抹平,会永久显示在公开列表卡片的「N 张」上。
//
// 入参只有 imageID(拿不到父记录 id),故在一个事务里三步走:取 snap_id → 软删 → 重算。
// 图片行不存在时与旧行为一致:静默无操作(不报错、不重算)。
func (r *streetSnapRepository) DeleteSnapImage(ctx context.Context, imageID uint32) error {
return r.db.WithContext(ctx).
Model(&model.StreetSnapImage{}).
Where("id = ? AND is_deleted = 0", imageID).
Updates(map[string]any{"is_deleted": 1, "updated_at": uint32(time.Now().Unix())}).Error
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var img model.StreetSnapImage
err := tx.Select("snap_id").Where("id = ?", imageID).First(&img).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
if err != nil {
return err
}
if err := tx.Model(&model.StreetSnapImage{}).
Where("id = ? AND is_deleted = 0", imageID).
Updates(map[string]any{"is_deleted": 1, "updated_at": uint32(time.Now().Unix())}).Error; err != nil {
return err
}
return tx.Exec(`UPDATE street_snaps SET image_count =
(SELECT COUNT(*) FROM street_snap_images WHERE snap_id = ? AND is_deleted = 0)
WHERE id = ?`, img.SnapID, img.SnapID).Error
})
}
func (r *streetSnapRepository) GetSnapImageKey(ctx context.Context, imageID uint32) (string, error) {