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

82 lines
7.5 KiB
Markdown
Raw Normal View History

---
id: T-650
title: 商品套图AI帮写的付费前价格确认
phase: 7
deps: [T-647]
status: DONE
created: 2026-07-17
---
## 问题 / 背景
⑥「商品套图」的「AI帮写」会将当前商品前 1 至 8 张可用本地原图一次提交给 cmhub 图片理解接口,读取等待最长可达 120 秒,并可能产生点数消耗。当前点击按钮后会立即创建 `ProductSuiteAiWriteWorker`;用户只能在接口返回后看到实际扣点和余额,没有机会在发起付费请求前确认本次理解范围与预估成本。
cmhub 的模型目录可能提供图片理解别名的 `prices[].points_cost`,但这类信息目前只供⑤设置页刷新下拉时使用,⑥没有可复用的短期价格缓存。不能根据图片张数自行把单次价格乘以图片数,也不能把最终接口返回的实际扣点提前当作确定事实。
## 方案
### 1. 在发起图片理解前取得可用价格信息
- 为 cmhub 模型目录增加进程内短期缓存,缓存键至少包含规整后的网关地址和 `vision_alias`,不得包含 API Key;缓存只保存公开模型元数据,不写入 `config.json`、SQLite、运行日志或导出文件;
- ⑤设置页刷新模型列表与⑥AI帮写共用该缓存。缓存缺失或超过合理短期时限时,⑥使用独立后台 worker 调用既有 `ai.fetch_cmhub_models()` 读取模型目录;查询本身不调用图片理解接口、不提交图片、不扣点,GUI 不得卡顿;
- 只接受 `operation_type=vision`、`requires_image=true`、`pricing_status=priced` 且别名等于当前配置 `vision_alias` 的模型价格;
- 仅当模型目录能明确给出本次单个图片理解请求唯一、无条件歧义的 `points_cost` 时,显示「预计扣点:X 点」;多档条件价格、未定价、字段缺失或目录读取失败时,不得猜测、相加或按图片数乘算;
- 价格不可用时仍允许用户在明确提示「暂时无法取得预计扣点,实际以网关返回为准」后自行决定是否继续,绝不自动提交请求;不因价格目录暂时不可用而静默绕过确认。
### 2. 增加显式中文确认框
在 `ProductSuiteTab.start_ai_write()` 完成现有本地图片可用性校验和前 8 张截取后、创建 worker 前增加确认流程:
- 标题为「开始AI帮写」;正文明确本次会理解当前商品前 `N` 张可用商品原图(最多 8 张),并生成「商品卖点与要求」;
- 价格可用时显示「预计扣点:X 点,实际以网关返回为准」;价格不可用时显示对应的无法确认说明;不显示 API URL、模型别名、图片路径、base64、Key 或上游原始错误;
- 明确说明开始后可取消本地等待,但已提交到网关的图片理解请求仍可能产生扣点;
- 使用「开始AI帮写」和「取消」两个中文按钮,默认和 Esc/关闭窗口均为取消;不提供“以后不再提示”选项;
- 用户取消、关闭确认框或价格读取阶段取消时,不创建 `ProductSuiteAiWriteWorker`、不调用 `analyze_product_images()`、不修改卖点、不会产生图片理解请求;
- 用户确认后重新检查当前任务仍有效、仍未在运行且选中的前 `N` 张本地图片仍可用,再复用现有 worker、冲突覆盖确认、取消、计时、实际计费回显和自动保存逻辑。
### 3. 并发、状态和最终计费边界
- 同一套图任务在“读取价格”或“等待确认”期间必须防止重复点击创建多个查询 worker、多个确认框或多个图片理解请求;切换任务、关闭任务和程序退出时正确取消/释放未完成的价格读取 worker,避免 `QThread: Destroyed while thread is still running`;
- 价格查询失败只显示脱敏中文原因;用户可取消,或在价格未知提示下再次显式选择开始;不得把失败写成 AI帮写失败、不得覆盖已有卖点;
- 成功完成后仍以 cmhub 响应 metadata 中的 `points_cost` 和 `points_balance` 作为唯一实际计费结果;预估值只用于确认,不写数据库、不参与余额扣减或成功/失败判定;
- 既有 AI帮写取消只在安全边界停止本地等待的语义保持不变,不承诺撤销已发往 cmhub 的请求或退回点数。
### 4. 测试与文档
- `tests/test_product_suite_gui.py`:覆盖已知价格时确认框显示前 8 张和预估点数;点击取消不会创建图片理解 worker;确认后只创建一次 worker 且图片顺序不变;价格未知时文案准确、仍须二次显式确认;重复点击、切换任务和关闭窗口不产生重复请求;
- `tests/test_ai.py` / 新增纯逻辑测试:模型目录价格筛选只接受有效图片理解别名;唯一价格、多个条件价格、未定价、缺字段和缓存过期的处理正确;不按图片数乘算;
- 同步更新 `docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`,说明AI帮写预估价格、确认和实际计费的边界;
- 运行:
```bash
py -3.10 -m unittest tests.test_ai tests.test_product_suite_gui tests.test_workers
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
```
## 验收要点
- [ ] 点击「AI帮写」不再立即提交图片理解请求,而是先展示本次前 `N` 张原图范围和付费确认。
- [ ] 已知唯一单次价格时显示「预计扣点:X 点」,且不按 `N` 张图片进行倍乘;最终仍以接口返回的实际扣点为准。
- [ ] 价格未知、目录不可用或价格有歧义时不伪造具体点数,用户仍可在清晰说明下选择继续或取消。
- [ ] 取消、Esc 或关闭确认框不会创建图片理解 worker、修改卖点或提交 cmhub 请求。
- [ ] 确认后沿用一次请求提交前 8 张同商品原图、现有冲突覆盖确认、取消、超时和实际计费展示。
- [ ] 价格查询与确认不阻塞 GUI,不重复发起模型目录读取或 AI帮写请求,关闭窗口不遗留线程。
- [ ] 用户可见文案均为中文,且不暴露图片路径、接口 URL、模型别名、Key、base64 或上游原始错误。
- [ ] 不影响②AI生成、⑥正式套图生图、历史、导出、Shopee CDP、采集、更新蝦皮、账号管理或设置保存。
## 边界(不改什么)
- 不修改 cmhub 的图片理解、生图、标题生成接口、模型别名配置、超时、重试、并发或计费协议。
- 不新增余额预扣、余额硬阻断、自动充值、自动重试或“以后不再确认”开关。
- 不把模型价格、预估点数或 API Key 持久化到 SQLite、配置文件、日志、Excel 或导出文件。
- 不修改商品原图的前 8 张顺序、原图勾选无关语义、AI帮写输出格式、卖点自动保存或覆盖冲突确认。
## 执行记录
- 2026-07-17:根据AI帮写耗时和付费前确认需求创建任务。待实现。
- 2026-07-17:开始实现共享模型目录缓存和AI帮写付费前确认。
- 2026-07-17:已实现 `cmhub_models` 进程内模型目录缓存、设置页与商品套图共用缓存、后台目录读取 worker,以及默认取消的「开始AI帮写」付费确认。仅唯一无条件的图片理解价格显示预计扣点,实际扣点继续以网关响应 metadata 为准;关闭任务和应用会取消目录读取 worker。验证:`py -3.10 -m unittest discover -s tests`(574 项通过)、`py -3.10 -m ruff check app/cmhub_models.py app/gui/__init__.py app/gui/workers.py app/gui/tabs/product_suite.py tests/test_cmhub_models.py tests/test_workers.py tests/test_product_suite_gui.py`、`py -3.10 -m compileall app main.py`、`git diff --check` 通过。