166 lines
11 KiB
Markdown
166 lines
11 KiB
Markdown
---
|
||||
|
|
id: T-636
|
|||
|
|
title: 商品套图支持无商品ID临时草稿与正式绑定
|
|||
|
|
status: TODO
|
|||
|
|
phase: 7
|
|||
|
|
deps: [T-622, T-635]
|
|||
|
|
created: 2026-07-16
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# T-636 商品套图支持无商品ID临时草稿与正式绑定
|
|||
|
|
|
|||
|
|
## 问题 / 背景
|
|||
|
|
|
|||
|
|
⑥「商品套图」当前要求先选择账号并输入纯数字商品 ID,才能创建 `image_studio_projects` 项目并导入本地图片。用户点击「添加图片」选完文件后,`_start_import()` 才尝试绑定项目;商品 ID 为空时会中止导入。实际运营中经常先取得商品素材、后确认蝦皮商品 ID,这个限制会阻断本地选图和生图准备。
|
|||
|
|
|
|||
|
|
需求是:未输入正式商品 ID 时也能选择、拖入或粘贴图片;程序使用内部随机英文标识保存为临时草稿。输入正式数字商品 ID 后,草稿应原地绑定,已经添加的图片、提示词、生成记录和选择结果全部保留。临时草稿不能拉取蝦皮主图。
|
|||
|
|
|
|||
|
|
不能只把随机字符串直接当作普通 `item_id` 使用:当前项目唯一键是 `账号 + 商品ID`,图片目录也包含 `item_id`。若直接把临时 ID 改成正式 ID,后续文件会写入新目录,旧图片仍留在临时目录;当前 GUI 切换商品 ID 还会直接清空项目绑定,造成用户看不到刚添加的图片。
|
|||
|
|
|
|||
|
|
## 目标
|
|||
|
|
|
|||
|
|
1. 账号可用但商品 ID 为空时,用户可以正常添加本地图片并建立可持久化的临时草稿项目。
|
|||
|
|
2. 内部临时 ID 与正式纯数字商品 ID 明确区分,但内部值不得显示给用户、传给模型或写入用户日志。
|
|||
|
|
3. 草稿绑定正式商品 ID 时保持同一个 `project_id`,不丢失图片、设置、任务、生成结果和选择记录。
|
|||
|
|
4. 临时草稿允许本地图片管理、AI 帮写和套图生成,但禁止拉取蝦皮主图。
|
|||
|
|
5. 临时草稿关闭或程序重启后仍可恢复,不产生无法访问的孤立项目。
|
|||
|
|
|
|||
|
|
## 实现方案
|
|||
|
|
|
|||
|
|
### 1. 显式草稿状态和稳定存储键
|
|||
|
|
|
|||
|
|
- 为 `image_studio_projects` 增加独立绑定状态,例如 `binding_state`:
|
|||
|
|
- `draft`:未绑定正式商品;
|
|||
|
|
- `bound`:已绑定正式数字商品 ID。
|
|||
|
|
- 增加稳定的 `storage_key`。项目图片目录改为优先使用 `storage_key`,仅对迁移前或异常旧数据回退到 `item_id`。
|
|||
|
|
- 数据库迁移必须幂等:
|
|||
|
|
- 既有项目统一回填为 `binding_state = 'bound'`;
|
|||
|
|
- 既有项目的 `storage_key` 回填为原 `item_id`,保证现有图片目录完全不变;
|
|||
|
|
- 不修改、搬动或重新编码既有图片文件。
|
|||
|
|
- 临时项目内部 `item_id` 使用保留前缀加 UUID,例如 `draft_<uuid4 hex>`;同时依靠 `binding_state` 判断草稿,不允许业务代码仅用“是否为数字”猜测项目状态。
|
|||
|
|
- `item_id` 继续保持非空及现有 `UNIQUE(account_alias, item_id)` 约束;正式 ID 仍沿用当前纯数字校验,不在本任务中增加未经验证的长度规则。
|
|||
|
|
- 在 `app/image_studio.py` 集中提供草稿创建、草稿判断和正式绑定服务,GUI 不直接拼接随机 ID 或执行 SQL。
|
|||
|
|
|
|||
|
|
### 2. 无商品 ID 添加本地图片
|
|||
|
|
|
|||
|
|
- 仍要求选择一个有效账号;本任务只放宽商品 ID,不创建无账号项目。
|
|||
|
|
- 点击「添加图片」时,即使商品 ID 为空也应先打开文件选择窗口:
|
|||
|
|
- 用户取消选择时不创建草稿;
|
|||
|
|
- 用户选中有效文件后才创建草稿并导入;
|
|||
|
|
- 当前任务已经绑定草稿时继续复用同一个草稿,不重复创建项目。
|
|||
|
|
- 拖入文件和粘贴剪贴板图片使用相同规则,在收到有效图片载荷后按需创建草稿。
|
|||
|
|
- 如果首次导入的文件全部无效或导入全部失败,不保留没有资产、没有任务的空草稿;回滚新建项目或按现有安全语义软删除。
|
|||
|
|
- 保留 T-635 的 16 张上限、重复图片判断、后台导入、真实数量、勾选、排序、预览和删除行为。
|
|||
|
|
- GUI 状态必须分离:商品 ID 输入框保存用户输入的正式 ID;内部 `draft_<uuid>` 只存在于项目服务和数据库,不得回填到输入框。
|
|||
|
|
|
|||
|
|
### 3. 商品 ID 提示
|
|||
|
|
|
|||
|
|
- 在「添加图片」按钮右侧增加状态 label。
|
|||
|
|
- 商品 ID 为空、当前项目为草稿或输入格式不正确时显示 `请输入正确的商品ID`。
|
|||
|
|
- 使用警示语义色,不使用失败红;这是允许继续添加和生成的待完善状态,不是运行失败。
|
|||
|
|
- 正式绑定成功后隐藏该提示,避免长期占用工具栏空间;tooltip 可显示当前已绑定的正式商品 ID。
|
|||
|
|
- 在 `1180x760` 和项目支持的最小窗口尺寸下,提示文字不得挤压账号、商品 ID 和「拉取蝦皮主图」控件;必要时设置合理的最小宽度和收缩优先级。
|
|||
|
|
|
|||
|
|
### 4. 草稿绑定正式商品
|
|||
|
|
|
|||
|
|
- 当前任务已有草稿时,用户输入合法数字 ID 并结束编辑,弹出中文确认:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
将当前临时草稿绑定到商品 <商品ID> 吗?
|
|||
|
|
已添加图片和生成记录会继续保留。
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 确认后调用统一服务原地绑定:
|
|||
|
|
- `project_id`、`storage_key`、资产、job、selection、提示词和套图设置保持不变;
|
|||
|
|
- 只更新项目正式 `item_id`、`binding_state` 和更新时间;
|
|||
|
|
- 因 `storage_key` 不变,不移动目录,也不批量改写资产 `local_path`。
|
|||
|
|
- 绑定必须在事务中先检查同账号是否已经存在目标正式项目,包括可恢复的软删除项目。
|
|||
|
|
- 目标正式项目已经存在时不得覆盖、自动合并或猜测用户意图;保持当前草稿不变,并提示该商品项目已存在。第一版不实现跨项目图片和生成历史合并。
|
|||
|
|
- 用户取消确认、输入非数字 ID 或绑定失败时,草稿和所有本地数据保持原样。
|
|||
|
|
- 当前正式项目切换到另一个正式商品 ID 的既有行为不借本任务改变;只有 `draft -> bound` 走原地绑定语义。
|
|||
|
|
|
|||
|
|
### 5. 拉取与生成边界
|
|||
|
|
|
|||
|
|
- 临时草稿可以执行:添加/删除/排序/预览本地图片、编辑提示词、AI 帮写、生成套图、查看历史结果和打开结果文件夹。
|
|||
|
|
- 草稿执行 AI 帮写或生图时,提示词上下文使用 `未绑定商品` 或省略商品 ID;绝不能把内部 `draft_<uuid>` 发送给 cmhub。
|
|||
|
|
- 点击「拉取蝦皮主图」时必须同时满足项目已经 `bound` 且商品 ID 为纯数字。
|
|||
|
|
- 草稿状态点击拉取时,在创建 worker、启动 Chrome 或调用 CDP 之前中止,并弹出:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
当前为临时项目,无法拉取蝦皮主图。
|
|||
|
|
请先输入正式商品ID后再试。
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 拉取按钮可保持可点击以解释原因,但 tooltip 必须预先说明 `需要先绑定正式商品ID`;不得产生 Chrome、网络、数据库拉取副作用。
|
|||
|
|
- 本任务不修改③「更新蝦皮」的高风险边界,也不允许临时 ID 进入更新任务。
|
|||
|
|
|
|||
|
|
### 6. 草稿生命周期与恢复
|
|||
|
|
|
|||
|
|
- 草稿项目持久化在 SQLite,不使用仅存在于内存的临时对象或系统临时目录。
|
|||
|
|
- 程序启动时恢复仍为 `draft`、未软删除且至少包含一项资产或生成任务的草稿,每个草稿恢复为独立套图任务标签;商品 ID 输入框保持空白,标签使用中文草稿名称,不显示内部 ID。
|
|||
|
|
- 多个草稿按最近更新时间排序,不能把不同草稿自动合并到同一任务。
|
|||
|
|
- 关闭未绑定草稿标签时提供三个明确选择:`保留草稿`、`删除草稿`、`取消`:
|
|||
|
|
- 保留草稿:只关闭当前标签,下次启动恢复;
|
|||
|
|
- 删除草稿:沿用项目软删除,不物理删除图片目录;
|
|||
|
|
- 取消:不关闭标签。
|
|||
|
|
- 空草稿不恢复;本任务不得在每次点击、取消文件选择或普通界面刷新时产生空项目。
|
|||
|
|
|
|||
|
|
### 7. 日志与错误边界
|
|||
|
|
|
|||
|
|
- 用户日志只记录 `临时草稿`、账号显示名、正式商品 ID 和操作结果,不输出内部临时 ID、绝对目录、数据库 SQL 或接口路径。
|
|||
|
|
- 数据库迁移、草稿创建、正式绑定或恢复失败时使用中文错误外壳;原草稿数据不能因失败被清空。
|
|||
|
|
- 正式绑定服务需要可重复调用:已经绑定到同一正式 ID 时返回当前项目,不重复更新;绑定到不同 ID 时按冲突规则拒绝。
|
|||
|
|
|
|||
|
|
## 验收标准
|
|||
|
|
|
|||
|
|
- [ ] 已选择账号、商品 ID 为空时,点击「添加图片」可以选择文件;取消选择不创建项目。
|
|||
|
|
- [ ] 选中有效图片后自动创建草稿并成功显示缩略图,商品 ID 输入框仍为空,内部临时 ID 不可见。
|
|||
|
|
- [ ] 空列表拖入或粘贴图片同样可以创建/复用草稿;16 张上限和后台导入状态正常。
|
|||
|
|
- [ ] 首次导入全部失败时不留下空草稿。
|
|||
|
|
- [ ] 「添加图片」右侧在空值、草稿和非法输入状态显示 `请输入正确的商品ID`,正式绑定后隐藏。
|
|||
|
|
- [ ] 草稿可以完成 AI 帮写和套图生成,cmhub 请求及用户日志中不包含 `draft_` 临时 ID。
|
|||
|
|
- [ ] 草稿点击「拉取蝦皮主图」显示指定中文提示,不创建 worker、不启动 Chrome、不执行 CDP。
|
|||
|
|
- [ ] 草稿绑定不存在冲突的正式数字 ID 后,`project_id`、`storage_key`、原图、生成记录、选择结果、提示词及设置全部保持不变。
|
|||
|
|
- [ ] 绑定后继续添加或生成的文件仍写入同一稳定项目目录;旧图片路径有效,打开结果文件夹可以访问完整结果。
|
|||
|
|
- [ ] 同账号目标正式项目已存在时绑定被安全拒绝,正式项目和草稿都不被覆盖或合并。
|
|||
|
|
- [ ] 取消绑定、非法 ID、数据库失败时草稿内容保持原样。
|
|||
|
|
- [ ] 保留的非空草稿在程序重启后恢复为独立任务;关闭时的保留、软删除和取消语义正确。
|
|||
|
|
- [ ] 既有正式项目迁移后目录、图片、项目查询、拉取和生成行为不变。
|
|||
|
|
- [ ] ③「更新蝦皮」、cmhub 协议和 CDP 选择器不受影响。
|
|||
|
|
|
|||
|
|
## 测试要求
|
|||
|
|
|
|||
|
|
- `tests/test_image_studio.py`:覆盖 schema 幂等迁移、既有项目回填、草稿 ID 唯一性、显式状态、稳定存储键、原地绑定、重复调用和正式项目冲突。
|
|||
|
|
- `tests/test_image_studio_images.py`:覆盖草稿导入目录、绑定前后目录不变、既有项目路径兼容和首次导入全失败清理。
|
|||
|
|
- `tests/test_product_suite_gui.py`:覆盖无 ID 文件选择/取消、拖放、粘贴、状态 label、草稿恢复、关闭三选项、绑定确认和绑定失败保持状态。
|
|||
|
|
- worker/GUI 测试覆盖草稿拉取提前中止,无 Chrome/CDP/worker 副作用;覆盖草稿生图 prompt 不包含内部 ID。
|
|||
|
|
- 保留 T-622、T-631、T-635 的任务切换、图片管理、生成和布局回归。
|
|||
|
|
|
|||
|
|
## 文档同步
|
|||
|
|
|
|||
|
|
- 实现时同步更新 `docs/04-architecture.md` 中 `image_studio_projects` schema、项目唯一性、目录键和草稿/正式绑定边界。
|
|||
|
|
- 若新增配置字段或启动恢复行为,同步更新对应配置说明;不得修改冻结的 `docs/06-tasks.md`。
|
|||
|
|
|
|||
|
|
## 验证
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
py -3.10 -m unittest tests.test_image_studio
|
|||
|
|
py -3.10 -m unittest tests.test_image_studio_images
|
|||
|
|
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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 非目标
|
|||
|
|
|
|||
|
|
- 不允许没有账号的匿名草稿。
|
|||
|
|
- 不合并两个项目的图片、job、selection 或生成历史。
|
|||
|
|
- 不在线验证用户输入的数字商品 ID 是否真实存在;真实存在性继续由拉取流程确认。
|
|||
|
|
- 不修改 cmhub API、图片生成模型、计费、并发、超时或下载协议。
|
|||
|
|
- 不修改③「更新蝦皮」的提交、确认、上传、拖拽和结果判定逻辑。
|
|||
|
|
- 不物理删除用户图片目录。
|
|||
|
|
|
|||
|
|
## 执行记录
|