Files

114 lines
7.1 KiB
Markdown
Raw Permalink 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-640
title: 商品套图单张失败重试保持当前轮结果
phase: 7
deps: [T-639]
status: DONE
created: 2026-07-16
---
## 问题 / 背景
⑥「商品套图」一轮生成中有单张图片失败时,用户点击失败图片卡片的「重试」,当前实现会把这次单张操作当成一轮全新的套图生成:
- `retry_job()` 重新组装单条 spec 后直接调用完整的 `start_generation()`;
- `start_generation()` 会清空 `current_job_ids`、重置本轮进度并退出历史视图;
- worker 会新建一条 job,随后 GUI 只把这个新 job 识别为当前轮;
- 原本同轮已经成功的图片会从「本轮」结果区消失,用户看到的是只有一张图片的“第二轮生成”。
新建 job 保留失败历史、计费和诊断记录是正确的,但「失败重试」在产品语义上应当是修复当前轮中的失败槽位,不应表现为重新开始一整轮。成功图片的「重新生成」与失败图片的「重试」也应保持不同语义。
## 方案
### 1. 分离活动生成 job 与当前结果 job
修改 `app/gui/tabs/product_suite.py` 的 `SuiteTaskState`:
- `current_job_ids` 继续表示「本轮结果区当前有效的图片槽位」;
- 新增仅用于当前运行生命周期的 `generation_job_ids`,表示本次实际提交、被进度/停止/终态看门狗跟踪的 job;
- 正常整轮生成时同时重建两组 ID;
- 单张失败重试时只清空 `generation_job_ids`,保留其他 `current_job_ids`;
- T-639 的终态快照、停止、线程结束和看门狗统一只读取 `generation_job_ids`,不得把当前轮其他历史成功 job 重复计入本次重试汇总。
这些字段只保存在 GUI 内存,不修改 SQLite schema。
### 2. 单张重试替换失败槽位
为 `start_generation()` 增加明确的单张重试上下文:
- `retry_job()` 传入原失败 job ID,而不是把单条 spec 当作普通整轮生成;
- worker 仍创建新的 job,不覆盖或删除原失败 job;
- worker 首次返回新 job ID 后,在 `current_job_ids` 中把原失败 ID 原位替换为新 job ID;
- 当前轮其他成功 job ID 保持不变;
- 如果重试再次失败或被停止,新的失败/停止 job 继续占据该槽位,用户可以再次重试;
- 「历史生成」继续列出原失败 job 与每次新建的重试 job,保留完整审计记录。
当前结果区按 `current_job_ids` 的槽位顺序展示,避免新建重试 job 因数据库更新时间排序而跳到第一张。
### 3. 区分重试与整轮生成反馈
单张失败重试期间:
- 进度只统计本次重试的 1 张,不重新统计当前轮全部图片;
- 不清空当前轮结果区,不切换成只有一张图片的新一轮;
- 完成弹窗使用「图片重试完成」或「图片重试已停止」等中文文案;
- 汇总显示本次重试成功、失败、停止数量,不使用「本轮共 1 张」误导用户这是第二轮套图;
- 状态栏使用「图片重试成功 / 图片重试失败 / 图片重试已停止」;
- 原整轮「商品套图生成完成」与停止反馈保持不变。
从「历史生成」发起重试时保持历史视图,避免自动跳到一个无法表达原历史轮次的临时“本轮”;新 job 会按历史排序显示。
### 4. 操作边界
- 只有 `failed/expired/cancelled` 卡片显示的「重试」使用当前轮修复语义;
- 成功图片右键「重新生成」继续视为主动新增变体,不在本任务中改成失败槽位替换;
- 重试会重新调用 cmhub,可能再次扣点;不伪装为恢复原请求;
- 不删除旧失败 job、旧错误、计费记录或已成功图片;
- 不修改 cmhub 请求、并发上限、提示词、图片下载、套图数量和项目存储结构。
## 验收要点
- [ ] 一轮 N 张中有 1 张失败,点击该卡片「重试」后,当前结果区仍保留其余 N-1 张成功图片。
- [ ] 新 job ID 到达后,原失败卡片在原位置被新重试卡片替换,当前结果区仍为 N 个槽位。
- [ ] 重试成功后当前结果区显示 N 张成功图片,不表现为只有 1 张图片的第二轮。
- [ ] 重试再次失败或停止时,该槽位显示最新状态,并可再次重试。
- [ ] 原失败 job 和新重试 job 都可在「历史生成」中查看。
- [ ] 本次重试的进度、停止、线程结束和终态看门狗只统计新 job,不重复统计当前轮其他成功 job。
- [ ] 从历史视图点击重试时保持历史视图,新 job 正常进入历史列表。
- [ ] 正常整轮生成、历史生成、手动停止、T-639 终态兜底和旧 `run_token` 隔离不回归。
- [ ] 用户可见文案全部为中文,不暴露接口路径、图片 URL、提示词全文或密钥。
## 测试
- `tests/test_product_suite_gui.py`:
- 当前轮失败卡片重试后保留其他成功 job;
- 新 job 原位替换失败 job,结果顺序稳定;
- 活动 job 快照只包含重试 job;
- 重试完成使用独立中文汇总;
- 历史视图发起重试时不自动退出历史;
- 正常整轮生成仍重建当前结果。
- 运行:
```bash
py -3.10 -m unittest 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
```
## 边界(不改什么)
- 不修改 SQLite schema,不新增 `retry_of_job_id` 或持久化轮次表。
- 不删除或覆盖原失败 job,不修改历史计费记录。
- 不修改 cmhub API、提交/轮询/下载协议和失败重试次数。
- 不修改套图提示词模板、分类数量、比例、原图管理或导出逻辑。
- 不修改 CDP、蝦皮主图拉取、①导入采集、②AI生成、③更新蝦皮、④账号管理或⑤设置。
## 执行记录
- 2026-07-16:`SuiteTaskState` 新增内存态 `generation_job_ids`、`generation_mode` 与 `generation_retry_job_id`。`generation_job_ids` 专门供当前活动请求的进度、停止、线程结束和终态看门狗使用;`current_job_ids` 只表示当前结果区的有效槽位,解决单张重试时旧成功 job 被错误纳入本次汇总的问题。未修改 SQLite schema。
- 2026-07-16:失败、过期或停止卡片的「重试」改为显式单张重试模式。worker 仍新建 job 保留历史和计费记录;新 job ID 返回后会原位替换当前结果区中的旧失败 job,其他成功图片保持不变。当前结果改按 `current_job_ids` 顺序展示;历史视图继续展示原失败与每次重试记录,从历史发起重试时不会强制退出历史视图。
- 2026-07-16:重试运行状态、停止确认、进度文案和完成弹窗改为「图片重试」语义;重试成功、失败、停止和未完整结束分别给中文反馈。正常整轮生成、停止、T-639 `run_token` 隔离与终态兜底保持原行为。
- 2026-07-16:补齐当前轮原位替换、历史记录保留、活动快照只统计新 job、重试再次失败仍可继续重试、历史视图保持和真实 QThread 完整退出测试。相关 42 项测试通过;在不包含用户未提交默认提示词改名的临时隔离 worktree 中,全量 535 项 unittest 通过,Ruff、`compileall` 与 `git diff --check` 均通过。