145 lines
8.5 KiB
Markdown
145 lines
8.5 KiB
Markdown
---
|
||
id: T-643
|
||
title: 商品套图持久化当前生成轮次
|
||
phase: 7
|
||
deps: [T-642]
|
||
status: DONE
|
||
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`。
|
||
|
||
## 验收要点
|
||
|
||
- [x] 商品完成一轮生成后,关闭软件并重新打开相同店铺和商品 ID,主结果区恢复同一轮、同一顺序的图片。
|
||
- [x] 再生成一整轮后,主界面只展示新当前轮,旧轮 job、图片、错误和计费记录仍保留。
|
||
- [x] 新轮全部失败或全部取消时,原当前轮继续显示;失败轮可以被历史查询读取。
|
||
- [x] 新轮部分成功时切换为当前轮,成功和失败槽位均可见,失败槽位可继续单张重试。
|
||
- [x] 单张失败重试沿用原轮次和槽位,其他当前图片不消失、不换序。
|
||
- [x] 程序重启后重试结果仍占据原槽位,原失败 job 仍存在。
|
||
- [x] 旧数据库可以自动迁移;旧 job 不被猜测性拆分、删除或覆盖。
|
||
- [x] 当前轮和历史轮查询不会跨店铺、跨商品或跨项目串数据。
|
||
- [x] 不影响生成停止、迟到信号隔离、恢复未完成 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生成、③更新蝦皮、④账号管理或⑤设置。
|
||
|
||
## 执行记录
|
||
|
||
- 2026-07-16:为 `image_studio_projects` 增加 `current_generation_round_key`,为 `image_studio_jobs` 增加 `generation_round_key`、`generation_slot_index` 及组合索引;旧数据库原位补列,既有 job 保持 NULL 作为旧版历史记录,不猜测轮次。
|
||
- 2026-07-16:新增轮次当前值、原子成功提升、轮次摘要、当前槽位和完整尝试查询 API;常规生成写入 UUID 轮次和稳定槽位,单张重试继承原轮次和槽位。⑥重新绑定或重启后从持久化当前轮恢复结果;全失败/全取消恢复原当前轮,部分成功切换新轮。
|
||
- 2026-07-16:生成轮次的 GUI 测试改为在 GUI 事件循环中等待 `QThread` 实际退出,避免在主线程直接阻塞 `thread.quit()` 的排队事件;连续运行 5 次均通过。
|
||
- 验证:`py -3.10 -m unittest tests.test_image_studio tests.test_workers tests.test_product_suite_gui`(75 项通过);`py -3.10 -m unittest discover -s tests`(554 项通过);`py -3.10 -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check` 均通过。
|