Files
cmshoppe/docs/tasks/T-643.md
T

142 lines
7.4 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.
---
id: T-643
title: 商品套图持久化当前生成轮次
phase: 7
deps: [T-642]
status: TODO
created: 2026-07-16
---
## 问题 / 背景
⑥「商品套图」当前把 `SuiteTaskState.current_job_ids` 作为“本轮结果”的唯一依据。该字段只存在于 GUI 内存中:
- 软件重启、关闭任务后重新输入商品 ID,无法恢复上次正在使用的一轮生成结果;
- `image_studio_jobs` 只有单张生成 job,没有持久化的轮次和槽位字段;
- 「历史生成」当前只能把当前项目全部 job 混在同一个结果网格中,无法可靠区分整轮生成、单张失败重试和更早历史;
- 如果直接隐藏「历史生成」,已有项目重新打开后可能显示“还没有生成结果”,但数据库和本地图片实际仍然存在。
产品目标是:每个店铺商品项目在主界面默认只展示一轮“当前结果”,同时保留以前的 job、图片、错误和计费记录,供后续历史弹窗查看。不能通过创建时间间隔、文件夹名称或图片数量猜测轮次。
## 方案
### 1. 为生成 job 增加持久化轮次与槽位
修改 SQLite schema、迁移和 `app/image_studio.py` 数据模型:
- `image_studio_projects` 增加可空 `current_generation_round_key`,表示主界面当前使用的生成轮次;
- `image_studio_jobs` 增加 `generation_round_key`,同一次整轮生成创建的 job 使用同一个不可复用 UUID;
- `image_studio_jobs` 增加 `generation_slot_index`,按本轮生成规格顺序从 0 开始稳定编号;
- 为 `(project_id, generation_round_key, generation_slot_index, id)` 增加适合查询当前槽位和历史轮次的索引;
- 数据类、建库和增量迁移同步兼容旧数据库。
不把 GUI `run_token` 直接作为长期业务主键。轮次 key 可以在本轮开始时由 GUI 创建并传给 worker,但持久化语义是“生成轮次”,不承担迟到信号隔离职责。
### 2. 整轮生成创建新轮次
修改 `app/gui/tabs/product_suite.py`、`app/gui/workers.py` 和生成服务调用:
- 正常点击「生成套图」时创建新的 `generation_round_key`;
- worker 创建每条 job 时同时写入该轮次 key 和稳定槽位;
- 活动运行仍使用现有 `generation_run_token` 做线程/迟到信号隔离,不改变 T-639/T-640 的终态看门狗;
- 当前会话内继续用 `current_job_ids` 实时展示本轮 job,避免等待整轮完成后才显示;
- 本轮至少有一个成功结果时,把项目 `current_generation_round_key` 原子更新为新轮次;
- 本轮全部失败、全部取消或未创建有效结果时,保留原来的当前轮,失败轮仍进入历史记录;
- 部分成功时允许新轮成为当前轮,主界面保留成功和失败槽位,用户可继续按 T-640 单张重试。
切换当前轮不得删除、覆盖或改写旧轮次 job、计费、错误和输出资产。
### 3. 单张重试留在原轮次和原槽位
T-640 的单张失败重试继续新建 job,但必须:
- 继承原 job 的 `generation_round_key`;
- 继承原 job 的 `generation_slot_index`;
- 当前轮每个槽位默认取该轮次、该槽位最新创建的 job;
- 重试成功、再次失败或取消都只替换该槽位在主界面的当前版本,其他槽位和轮次不变;
- 原失败 job 和每次重试 job 全部保留,供历史详情和问题审计使用。
成功图片的主动“重新生成”若后续开放,也必须明确是替换当前槽位还是新增变体;本任务不扩大该入口。
### 4. 已有项目和旧版 job 兼容
旧数据库中的 job 没有可靠的轮次边界,不得按时间差或图片数量猜测:
- 迁移后旧 job 使用明确的 `legacy` 兼容轮次语义,或保持空值并由查询层统一映射为“旧版历史记录”;
- 若项目只有旧版 job、没有新格式当前轮,主界面可以临时展示“旧版历史记录”,避免用户误以为图片丢失;
- 用户完成下一次新格式整轮生成后,项目切换到新轮次,旧 job 只在历史记录中展示;
- 文件缺失时保留 job/asset 记录并显示不可用状态,不自动删除历史。
### 5. 当前结果查询与恢复
在 `app/image_studio.py` 提供结构化 API:
- 设置或读取项目当前轮次;
- 按项目列出生成轮次摘要;
- 按轮次和槽位读取当前有效 job;
- 按轮次读取全部尝试记录,供 T-644 历史弹窗使用。
商品项目绑定、草稿恢复、任务切换和软件重启后:
- 从项目持久化的当前轮次恢复 `current_job_ids`;
- 按槽位顺序展示,不使用 `updated_at` 排序打乱图片位置;
- 当前轮不存在、job 被软删除或图片文件缺失时给出稳定空态/缺失态,不抛出裸英文错误;
- `generation_job_ids` 仍只表示当前活动请求,不从历史轮次恢复。
### 6. 数据一致性和诊断
- 设置当前轮次、读取轮次槽位和单张重试替换必须校验 project/job 归属;
- 不允许把其他账号、商品项目或草稿项目的轮次设为当前轮;
- 当前轮 key、job 数量、成功/失败数量可写诊断日志,不记录提示词全文、图片 URL、密钥或 Cookie;
- schema 变化同步更新 `docs/04-architecture.md`、`docs/api.md` 和 `docs/routes.md`。
## 验收要点
- [ ] 商品完成一轮生成后,关闭软件并重新打开相同店铺和商品 ID,主结果区恢复同一轮、同一顺序的图片。
- [ ] 再生成一整轮后,主界面只展示新当前轮,旧轮 job、图片、错误和计费记录仍保留。
- [ ] 新轮全部失败或全部取消时,原当前轮继续显示;失败轮可以被历史查询读取。
- [ ] 新轮部分成功时切换为当前轮,成功和失败槽位均可见,失败槽位可继续单张重试。
- [ ] 单张失败重试沿用原轮次和槽位,其他当前图片不消失、不换序。
- [ ] 程序重启后重试结果仍占据原槽位,原失败 job 仍存在。
- [ ] 旧数据库可以自动迁移;旧 job 不被猜测性拆分、删除或覆盖。
- [ ] 当前轮和历史轮查询不会跨店铺、跨商品或跨项目串数据。
- [ ] 不影响生成停止、迟到信号隔离、恢复未完成 cmhub 任务和本地图片保存。
## 测试
- `tests/test_db.py`:
- 新建数据库字段与索引;
- 旧数据库增量迁移;
- 重复初始化幂等。
- `tests/test_image_studio.py`:
- 当前轮设置/读取和项目归属校验;
- 轮次摘要、槽位当前 job、全部尝试查询;
- 旧版 job 兼容。
- `tests/test_workers.py`:
- 整轮 job 写入相同轮次和稳定槽位;
- 单张重试继承轮次和槽位。
- `tests/test_product_suite_gui.py`:
- 项目重新绑定和重启恢复当前轮;
- 新轮成功、部分成功、全失败/取消时的当前轮切换;
- 单张重试跨重启保持槽位。
- 运行:
```bash
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
```
## 边界(不改什么)
- 不删除旧轮次 job、生成图片、错误、扣点或余额记录。
- 不通过扫描目录、文件名、时间差或图片数量猜测轮次。
- 不在本任务实现历史弹窗、历史图片删除或“恢复历史轮为当前轮”。
- 不修改 cmhub API、模型别名、提示词组装、并发、超时、轮询和下载协议。
- 不修改 CDP、蝦皮主图拉取、①导入采集、②AI生成、③更新蝦皮、④账号管理或⑤设置。
## 执行记录
- 待执行。