Files

75 lines
6.6 KiB
Markdown
Raw Permalink Normal View History

---
id: T-689
title: 商品套图已完成历史任务不阻断原图删除
phase: 7
deps: [T-631, T-658b]
status: DONE
created: 2026-07-22
---
## 问题 / 背景
商品套图第一轮全部生成成功后,用户需要移除一张商品原图、调整参考素材并开始第二轮生成。当前 `remove_original_assets_if_unused()` 只要发现任意 `image_studio_jobs.source_asset_id` 指向该原图,就一律阻断删除,不区分任务是否已经结束。因此第一轮已成功且不可重试的历史 job 仍会错误提示“选中的图片正在被生成任务或终选引用,不能移除”。
此外,T-658b 已把参考图保存为 `reference_asset_ids` JSON 快照,但现有引用计数只统计 `source_asset_id`、`output_asset_id` 和终选表,未检查参考图快照。若直接放宽所有 job 引用,会造成仍在执行或可恢复的任务读取到已删除参考图,违反任务快照和 fail-fast 规则。
## 方案
### 1. 按可执行性而非历史存在性判定阻断
- 为“从商品原图列表删除”提供专用的引用判定 helper;不直接放宽通用 `asset_reference_counts()` 或非原图的删除语义。
- 对每个待删原图同时检查:
- 当前 `image_studio_selections` 终选直接引用;
- job 的 `source_asset_id`;
- job 的 `reference_asset_ids` 有序 JSON 快照。
- 以下情形必须阻断整批删除:
- job 状态为 `pending`、`submitted`、`running`;
- job 为 `failed`、`expired`、`cancelled` 且 `recovery_action` 为 `resume` 或 `regenerate`,仍可继续查询或重试;
- 原图被当前终选直接引用;
- 参考图快照格式无效,无法可靠判定引用关系。
- `succeeded` 且 `recovery_action=none` 的 job 仅属于不可重试的历史记录,不得阻断删除。其他终态且 `recovery_action=none` 同样按已放弃历史处理。
- 批量删除保持全有或全无:任一图片有阻断原因时,全部不删,并向 GUI 返回中文、可行动的原因,例如“所选原图仍被可继续处理的生成任务引用,请先完成、停止或放弃该任务”。
### 2. 删除后的历史兼容
- 允许删除已完成历史 job 的原图资产行。既有 SQLite 外键会将 job 的 `source_asset_id` 和生成图的 `parent_asset_id` 置为 `NULL`;历史 job 的提示词、状态、扣点、生成结果和生成轮次仍保留,历史生成列表和已生成图片不得丢失。
- 对已完成 job 的 `reference_asset_ids` 保持原快照文本,不尝试重写、猜测替代图片或改变历史 prompt;该 job 不可重试,因此不再依赖当前原图资产。
- 原图本地文件继续保留,不删除蝦皮线上图片;用户重新拉取蝦皮原图时,线上仍存在的图片可重新进入原图列表。
### 3. GUI 反馈与第二轮生成
- 第一轮所有任务已成功结束后,删除未被终选直接引用的原图应正常通过,删除成功后重排 `source_order`,用户可立即以新顺序开始第二轮生成。
- 若第一轮存在运行中、远端已提交未完成、失败可重试或可恢复任务,维持删除阻断;提示应说明是“可继续处理的生成任务”而非笼统的“正在生成”。
- 已完成的生成结果、历史生成弹窗、终选和导出继续按原轮次读取;下一轮 job 只使用删除后的当前原图列表,并保持 T-658b 的输入快照冻结规则。
### 4. 服务与兼容边界
- 继续复用 `remove_original_assets_if_unused()` 作为 GUI 的唯一原图删除入口,在内部改用专用阻断判定;单张、勾选批量、键盘、右键和悬停删除必须一致。
- 保持项目归属、资产类型、下载中、拉取中、生成中和事务重排校验;不允许通过 GUI 直接删除数据库行。
- 不新增 SQLite schema,不删除 job、生成结果、终选记录或本地文件,不修改 cmhub 提交、轮询、下载、计费、Shopee CDP 和①至⑤模块。
## 验收要点
- 第一轮全部 `succeeded/recovery_action=none` 后,删除作为主图或参考图使用过、但未被终选直接引用的商品原图成功;剩余 `source_order` 连续,第二轮使用新顺序。
- `pending/submitted/running` job 的源图和参考图删除均被阻断。
- `failed/expired/cancelled` 且可 `resume/regenerate` 的源图和参考图删除均被阻断。
- 已完成历史 job 不再误拦截;历史轮次、生成结果、提示词、扣点和已选生成图片仍可查看,历史 job 的源图显示缺失时不影响查看生成结果。
- 任意被终选直接引用的原图仍被阻断;混合选择“可删 + 不可删”时不发生部分删除。
- 无效 `reference_asset_ids` 快照走保守阻断并显示中文错误,不得静默放行。
- 自动验证:
- `py -3.10 -m unittest tests.test_image_studio tests.test_product_suite tests.test_product_suite_gui`
- `py -3.10 -m unittest discover -s tests`
- `py -3.10 -m ruff check app tests main.py`
- `py -3.10 -m compileall app main.py`
- `git diff --check`
## 执行记录
- 2026-07-22 完成。
- `remove_original_assets_if_unused()` 改用商品原图专用引用判定,不再由通用 `asset_reference_counts()` 的“所有历史 job 一律阻断”语义决定;通用资产删除和拉取停止清理逻辑保持不变。
- 删除前会校验终选直接引用,以及所有 job 的主图和 `reference_asset_ids` 快照。`pending/submitted/running`、或仍可 `resume/regenerate` 的失败/停止任务继续阻断;`succeeded + recovery_action=none` 和其它不可再执行的历史 job 不再阻断第二轮生成前的原图调整。
- 已完成历史原图删除后,SQLite 外键将 job 的 `source_asset_id` 和生成图 `parent_asset_id` 置空;历史 job 的状态、提示词、扣点、轮次、生成结果和参考图 JSON 快照保持可查。本地源文件与蝦皮线上图片均不删除。
- 参考图快照解析失败或 job 状态异常时采取保守阻断;批量删除继续保持全有或全无,并给出“可继续处理的生成任务”或“终选”中文原因。
- 已同步 `docs/04-architecture.md`、`docs/routes.md` 与 `docs/api.md`。新增服务层测试覆盖成功历史的主图/参考图可删、六种运行或可恢复状态的主图/参考图阻断、参考图快照无效阻断,以及历史外键置空后生成结果保持。
- 验证通过:`py -3.10 -m unittest tests.test_image_studio tests.test_product_suite tests.test_product_suite_gui`(105项)、`py -3.10 -m unittest discover -s tests`(672项)、`py -3.10 -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check`。离屏 PySide6 输出字体目录/插件告警,但无测试失败。