Files
zt-closet-backend/.codebuddy/plans/删除衣物时清理COS图片_39034235.md
T
2026-09-11 01:41:52 +08:00

75 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: 删除衣物时清理COS图片
overview: 在衣物删除流程中,同步删除 COS 上对应的处理后图片对象,并对上传处理中的并发竞态做防护,避免删除衣物后 COS 图片残留。
todos:
- id: add-cos-delete-util
content: 在 pkg/upload/upload.go 新增 FileDeleteCos 删除工具函数
status: completed
- id: delete-cos-on-clothes-delete
content: 修改 DeleteByUUID,事务成功后删除 processed_image 对应 COS 对象
status: completed
dependencies:
- add-cos-delete-util
- id: guard-async-upload-race
content: 修改 processImageAsync,上传后检测衣物已删除则清理 COS 对象
status: completed
dependencies:
- add-cos-delete-util
---
## 产品概述
修复用户删除衣物后,腾讯云 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
```