4.7 KiB
4.7 KiB
name, overview, todos
| name | overview | todos | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 删除衣物时清理COS图片 | 在衣物删除流程中,同步删除 COS 上对应的处理后图片对象,并对上传处理中的并发竞态做防护,避免删除衣物后 COS 图片残留。 |
|
产品概述
修复用户删除衣物后,腾讯云 COS 中对应抠图图片对象未被删除的问题。当前删除衣物只软删除了数据库记录,衣物抠图后上传到 COS 的图片仍残留在云端,产生无效存储占用。
核心功能
- 删除衣物时,若该衣物已有处理后的图片(
processed_image非空),同步删除 COS 上对应的图片对象。 - COS 删除失败不影响衣物删除结果:用户删除请求始终成功返回,删除失败仅记录日志,便于后续排查与补偿。
- 处理「上传处理中删除衣物」的竞态:用户在图片异步抠图/上传期间删除衣物时,异步流程在检测到衣物已被删除后,主动清理刚上传的 COS 对象,避免残留。
技术栈
- 语言与框架:Go + Gin + GORM(沿用现有项目技术栈)
- 对象存储:腾讯云 COS SDK
github.com/tencentyun/cos-go-sdk-v5 v0.7.75(已存在) - 复用现有:
global.CosClient、pkg/upload工具层、slog日志、OpenTelemetry trace span
实现方案
总体思路
在现有文件上传工具层新增一个 COS 删除工具函数,供删除流程与异步处理流程复用;在衣物删除事务提交成功后调用删除,删除失败仅记录日志不影响主流程;同时在异步图片处理成功回填前增加「衣物是否已被删除」的存在性检查,处理上传/删除并发竞态。
关键设计决策
- 删除失败不影响主流程:COS 删除是尽力而为的清理操作。数据库软删除与额度回退仍以现有事务为准,COS 删除放在事务提交之后、并忽略错误返回,保证用户删除体验不因外部存储异常而失败。
- 复用对象键语义:
clothes.ProcessedImage字段存储的值即 COS 对象键(如{用户UUID}/{用户ID}/{随机名}.png),上传时FileUploadCos(ctx, cosKey)正是以该值作为对象名上传,因此删除时直接用c.ProcessedImage作为 key 调用Object.Delete。 - 短超时防阻塞:删除工具函数内部使用 10 秒超时 context,避免 COS 网络异常长时间阻塞删除接口。
- 竞态防护位置:在
processImageAsync上传 COS 成功后、回填processed_image之前,通过ClothesGetByUUID检查衣物是否仍存在(软删除后普通查询会过滤已删行,isNotFound为 true),若已删除则删除刚上传的对象并结束,避免回填丢失造成残留。
性能与可靠性
- COS Delete 为单次网络调用,时间复杂度 O(1);删除接口新增一次短超时调用,影响可控。
- 上传/删除竞态窗口:检测存在性与删除 COS 之间仍可能存在极小窗口,但该窗口下后果仅为偶发残留单个对象,风险可控;核心场景(已处理完成再删除)被完整覆盖。
- 不改动现有额度回退逻辑、错误码与软删除行为,保持向后兼容。
实现要点
pkg/upload/upload.go新增FileDeleteCos,风格与FileUploadCos一致:nil client 保护、空 key 直接返回、trace span、10s 超时。internal/service/clothes.go的DeleteByUUID:在事务闭包外声明变量承接c.ProcessedImage,事务成功后非空则调用删除,失败用slog.WarnContext记录clothes_uuid与cos_key,不记录密钥。internal/service/clothes.go的processImageAsync:上传 COS 成功后、分类与回填前检查衣物是否存在;若已删除,删除刚上传对象并return(额度已由删除事务回退,无需再处理)。
目录结构
project-root/
├── pkg/upload/upload.go # [MODIFY] 新增 FileDeleteCos 删除 COS 对象工具函数
└── internal/service/clothes.go # [MODIFY] DeleteByUUID 删除后清理 COS;processImageAsync 增加删除竞态防护
关键代码结构
// FileDeleteCos 删除 COS 对象。key 为对象键(即 processed_image 存储值)。
// client 未初始化返回 error;key 为空返回 nil。
func FileDeleteCos(ctx context.Context, key string) error