Files
cmshoppe/progress.md
T

1354 lines
191 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.
# 执行进度记录
> 本文件是只追加的历史流水,记录任务执行过程、验证命令、阻塞点和关键决策。
> 当前目录、当前命令、下一个可领取任务等可覆盖快照,写入 [`docs/current-state.md`](docs/current-state.md)。
> 历史条目里的“下一步”只代表当时判断,不作为当前任务领取依据;当前任务必须按 [`docs/06-tasks.md`](docs/06-tasks.md) 的顺序与 [`docs/current-state.md`](docs/current-state.md) 的快照执行。
## 职责边界
- `docs/06-tasks.md`:任务看板,维护任务状态、依赖和验收要点。
- `progress.md`:历史流水,只追加记录每轮执行发生了什么。
- `docs/current-state.md`:当前快照,可覆盖更新仓库现实、可运行命令和下一步。
## 记录格式
```markdown
## 【YYYY-MM-DD】T-【编号】 【任务名】
- 状态:【DONE / BLOCKED / PARTIAL】
- 变更:【修改了哪些文件或模块】
- 验证:【运行的真实命令和结果】
- 阻塞:【如有,写明原因和需要谁决策】
- 决策:【如有,记录本轮确定的关键取舍】
- 下一步:【建议下一个任务 ID 或待确认事项】
```
## 执行记录
## 【2026-06-25】Phase -1 单账号流程验证(建文档前)
- 状态:DONE
- 变更:新增 `cdp.py`、`demo.py`、`set_title.py`、`set_cover.py`、`get_title.py`、`cookies.py`、`inspect_images.py`、`grab.py`;本轮建立 `docs/` 全套 harness coding 文档与 `progress.md`、`AGENTS.md`、`CLAUDE.md`。
- 验证:
- CDP 连接 Chrome(开发期经 WSL→Windows portproxy `192.168.0.224:9333`→`127.0.0.1:9222`)成功。
- `cookies.py`:读到 shopee.tw 标签页 50 个 Cookie(含 csrftoken)。
- `get_title.py`:读到测试商品(ITEM_ID 51100639510)标题。
- `set_title.py`:改标题后 `value`==`modelvalue`==新值。
- `set_cover.py`:`setFileInputFiles` 上传成功(张数 5→6),拖拽落点 `first.left-0.30*w` 后新图到第一位。
- `demo.py`:在 Windows 端跑通改标题 + 换封面(未提交)。
- 文档:`python -m py_compile *.py` 通过;`demo.py` 增加 `UPDATE=1` 提交开关并语法校验通过。
- 决策:
- 浏览器自动化用自研 `cdp.py`(websocket-client + requests),不引入 playwright/selenium。
- 必须清代理环境变量 + `suppress_origin=True` + `--remote-allow-origins=*`,否则连不上 CDP。
- SPA 就绪用“标题框+图片 itembox+上传框”三元素存在判断,不用 load 事件。
- 封面由位置决定,用模拟鼠标拖拽到第一位;「更新」默认不点,`UPDATE=1` 才提交。
- 下一阶段做多账号管理 + GUI,GUI 暂定 Tkinter(待维护者确认)。
- 下一步:从 `docs/06-tasks.md` 领取 T-001(抽 `editor.py`)或 T-002(`accounts.json` + `config.py`)。
## 【2026-06-25】决策 · 多账号隔离方案
- 状态:DONE(仅文档决策,未涉及代码)
- 变更:更新 `docs/04-architecture.md`(新增 3.0 多账号隔离方案决策表)、`docs/02-requirements.md`(功能加“生成快捷方式 P1”、决策表加“多账号隔离方式”)、`docs/03-tech-stack.md`(决策记录)、`docs/api.md`(chrome 模块加 `create_shortcut`)、`docs/06-tasks.md`(新增 T-105 生成快捷方式)。
- 决策:
- 多账号隔离采用**方案 B:每账号独立 user-data-dir**,否决方案 A(Chrome profile)。理由:`--remote-debugging-port` 绑在 user-data-dir/进程上而非 profile,方案 A 无法每账号独立 CDP 端点、无法并行、串号风险高。
- 启动主路径用程序 `subprocess` 直接带参数启动;快捷方式为可选便利,用 PowerShell `WScript.Shell.CreateShortcut` 生成,参数须写在快捷方式「目标」字段而非「起始位置」。
- 下一步:实现 `chrome.py`(T-102 启动器 + T-105 快捷方式),或先做 T-001/T-002 地基。
## 【2026-06-25】决策 · 双 Tab + 配置文件 + SQLite + Excel 批量 + 登录检测 + 导出
- 状态:DONE(仅文档决策,未涉及代码)
- 变更:系统性更新 `docs/02-requirements.md`(第二版功能/决策表/风险)、`docs/03-tech-stack.md`(config.json+SQLite+openpyxl)、`docs/04-architecture.md`(双 Tab 结构、4.1 config.json、4.2 SQLite schema、4.3 Excel 模板、5.x 批量与登录检测流程、第六节加登录检测事实)、`docs/api.md`(拆 appconfig/db/excel 模块、editor 加登录检测与 run_task)、`docs/06-tasks.md`(重排 Phase 0-4)、`docs/routes.md`(重写为双 Tab 线框)、`docs/00-ai-start-here.md`、`docs/current-state.md`。
- 决策:
- 存储拆两层:应用设置 `config.json` + 业务数据 SQLite `cmshopee.db`,取代早期 `accounts.json`。
- GUI 双 Tab:Tab1 Excel 批量(导入/列表/执行/导出),Tab2 账号管理(密码加密仅参考、登录检测)。
- 账号↔任务绑定以 Excel“别名”列为权威(弃文件名);未匹配略过,结束弹窗汇总。
- Excel 模板列:别名/商品id/新标题/新封面图片路径/旧标题(留档)/旧封面图片路径(留档)/更新状态(输出结果)。
- 是否提交更新由 Tab1 批次级开关控制,默认关;多账号串行、单条失败继续。
- 密码不自动登录、仅参考、加密存;人工登录 + 程序检测登录态。
- 引入 openpyxl;DB/config/用户数据目录均 gitignore。
- 下一步:从 Phase 0 起步(T-001 editor.py / T-002 appconfig / T-003 db.py,任选其一)。
## 【2026-06-25】修正 · 旧数据为程序输出 + 回写时机 + 文件锁
- 状态:DONE(仅文档修正)
- 变更:`docs/04-architecture.md`(4.2 schema 注释、4.3 列性质、3.x 关键事实、5.1 流程)、`docs/api.md`(`run_task` 抓旧值、`update_task_result` 加旧数据参数、`export_results`→`write_back` 回写原文件 + `export_copy` 退路)、`docs/06-tasks.md`(T-303/304/305)、`docs/02-requirements.md`(功能行 + 决策表)。
- 决策修正:
- 「旧标题/旧封面」由“运营输入/留档”改为 **程序改动前抓取的快照(输出列)**,用于审计/回滚。
- SQLite **逐条实时写回**(每条任务处理完立即写状态+旧数据),不是批末统一写。
- Excel **回写到原文件**:该 Excel 全部任务完成后,批量把旧数据+更新状态写回原 .xlsx;原文件被锁则提示关闭重试或 `export_copy` 另存副本。SQLite 为事实来源。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-25】扩展 · 5 Tab 流水线 + AI 生成 + 采集回写 + 首次引导
- 状态:DONE(仅文档)
- 变更:重写 `docs/routes.md`(5 Tab + AI生成界面)、`docs/04-architecture.md`(流水线/ai 模块/采集/本地图片目录/扩展 tasks schema)、`docs/api.md`(新增 `ai` 模块、editor 加采集 read_cover/download_cover/collect、db 拆 set_collected/generated/confirmed/applied)、`docs/06-tasks.md`(Phase 0-5 按 5 Tab 重排);增量更新 `docs/02-requirements.md`、`docs/03-tech-stack.md`(AI 依赖待定)、`docs/00-ai-start-here.md`、`docs/current-state.md`。
- 决策:
- GUI 定为 **5 Tab,工作流优先顺序**:① 导入采集 ② AI生成 ③ 更新shopee ④ 账号管理 ⑤ 设置。
- 流水线五阶段:imported→collected→generated→confirmed→applied,stage 字段贯穿。
- 「采集」只读抓旧标题+下载旧封面到本地并回写 Excel;「旧数据」为程序输出(已确认)。
- 新增 AI:提示词+旧标题→新标题、提示词+旧封面→新封面(image-to-image);**服务商/模型待定**;产出必须人工确认才进更新。
- Excel 增 `账号名` 输入列;输出列扩为 旧标题/旧封面/新标题/新封面/更新状态。
- 本地 `images/<slug>/<item_id>_old|_new` 存图,路径记 DB;`images/` 加 gitignore。
- 首次未配账号/未登录:① ③ 执行按钮禁用并提示去④。
- 待确认:AI 服务商/模型/计费/合规;密码与 AI Key 加密密钥来源。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-25】增补 · Tab① 导入汇总栏
- 状态:DONE(文档 + 效果图)
- 变更:`docs/routes.md`(Tab① 加导入汇总栏 + 说明)、`docs/02-requirements.md`(功能加“导入校验汇总”)、`docs/api.md`(`import_tasks` 返回 stats、新增 `match_summary`)、`docs/06-tasks.md`(新增 T-202b)、`docs/ui/tab1-import-collect.svg`(加汇总栏)。
- 决策:导入多个 Excel 后即时展示 文件数/解析行数/有效/无效/匹配(按账号细分)/未匹配,作为跑采集前的校验关口;未匹配/无效可点击在列表筛出。与执行后的“结束弹窗汇总”互补。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-25】简化 · 去掉人工确认 + 去掉提交开关 + Tab② 左右布局
- 状态:DONE(文档;SVG 同步)
- 变更:`docs/04-architecture.md`(流水线去 confirmed、schema 删 confirmed 列、config 删 commit_update_default、5.x/6.x 流程、第七/十节)、`docs/api.md`(删 set_confirmed、apply_task 去 commit 参数恒提交、ai 说明)、`docs/routes.md`(Tab② 左右布局无确认、Tab③ 去开关/改“已生成”、流程/组件)、`docs/06-tasks.md`(T-302/303/401/402、里程碑)、`docs/02-requirements.md`、`docs/03-tech-stack.md`、`docs/00-ai-start-here.md`、`docs/current-state.md`;SVG `tab2/tab3/overview`。
- 决策(用户拍板):
- **去掉人工确认**:流水线 imported→collected→generated→applied(删 confirmed 阶段)。
- **去掉提交开关**:③ 执行即点「更新」总是提交线上。
- Tab② 改左右布局:左 ~1/4 提示词(标题/封面),右 筛选(批次/店铺)+任务列表(店铺/商品id/旧标题/新标题),双击弹窗看新旧封面(纯查看);底部「开始生成」。
- 新标题不可编辑,直接用 AI 输出。
- 已知并接受风险:AI 标题/封面经 ③ 直接上线无核对,本地留档+回写 Excel 为唯一追溯。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-25】AI 生成 · 并发参数 + 单按钮两段式
- 状态:DONE(文档 + SVG)
- 变更:`docs/04-architecture.md`(config.json ai 段加 title_concurrency/image_concurrency/retry/resolution/jpg_quality;6.2 两段式并发流程)、`docs/api.md`(`gen_title`/`gen_cover` 加重试/分辨率/质量、新增 `generate_batch` 并发编排、`ai_config` 字段)、`docs/routes.md`(Tab② 单按钮+停止+进度、参数移⑤、⑤ 设置补参数)、`docs/06-tasks.md`(T-301/303/501)、`docs/03-tech-stack.md`(concurrent.futures 并发、Pillow 存盘);SVG `tab2`(单按钮+停止+进度、参数移⑤)、`tab5`(AI 段加 5 参数)。
- 决策(用户):
- 配置(⑤)新增:标题并发数、图片并发数、失败重试次数、分辨率、jpg 质量。
- Tab② 单个「开始生成」+「停止」;点击后**先按 title_concurrency 并发生成标题,接着按 image_concurrency 并发生成图片**;进度显示 标题/封面/失败。
- 每条/每张完成即写库;停止取消未开始项,可继续。并发用 `concurrent.futures.ThreadPoolExecutor`。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-25】AI 生成 · 提示词管理
- 状态:DONE(文档 + SVG)
- 变更:`docs/04-architecture.md`(存储加 title_prompt.txt + prompts/cover/、6.2 提示词管理与变量、目录结构)、`docs/api.md`(新增 `prompts` 模块:标题存取、封面模板 CRUD、`render_prompt`)、`docs/routes.md`(Tab② 左侧提示词管理说明)、`docs/02-requirements.md`(提示词管理功能行)、`docs/06-tasks.md`(T-302p)、SVG `tab2`(标题加保存、封面加模板下拉+图标工具栏+插入标题/预览)。
- 决策(用户):
- 标题提示词:单文件 `title_prompt.txt` +「保存」;启动回显。
- 封面提示词:多模板,存 `prompts/cover/<名>.txt`;下拉 + 图标工具栏(新建/保存/另存为/重命名/删除)+「插入标题」(=插 `{新标题}`)+「预览」。
- 变量 `{旧标题}/{新标题}/{商品id}/{店铺}`,生成前 `render_prompt` 替换;预览=替换后查看。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-25】更新shopee · 加批次/店铺/状态筛选
- 状态:DONE(文档 + SVG)
- 变更:`docs/routes.md`(Tab③ 加筛选栏 + 「开始更新」作用于当前筛选 + 状态=失败重试说明)、`docs/02-requirements.md`(③ 功能行)、`docs/06-tasks.md`(T-401)、SVG `tab3`(顶部加批次/店铺/状态筛选行,表与按钮下移避让)。
- 决策(用户):③ 与 ①② 一致加筛选;店铺筛选利于逐店铺更新,状态筛选用于失败重试;「开始更新」仅作用于当前筛选结果(兼作范围控制/安全)。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-26】AI 模型配置 · 模型清单 + 角色分配 + 分辨率/超时
- 状态:DONE(文档 + SVG)
- 变更:`docs/04-architecture.md`(config.json ai 段改为 default_text/image_model + resolution + resolution_timeouts;新增 5.1b `config/ai_models.json` schema 含 category/api_type/connect_timeout;存储/目录/gitignore;6.2 模型选择与超时派生)、`docs/api.md`(appconfig 加 response_timeout、模型 CRUD/测试连接、get_model;ai 模块超时来源)、`docs/02-requirements.md`(⑤ 功能)、`docs/03-tech-stack.md`(AI 多模型行)、`docs/06-tasks.md`(T-501 模型管理 + T-501b 角色与参数);SVG `tab5` 重画为模型管理(master-detail)+角色下拉+分辨率/超时+路径。
- 决策(用户 + 分析):
- 模型清单 `config/ai_models.json`:每模型加 `category`(text/image)、唯一 name、api_type、`connect_timeout_seconds`(默认30,每模型)。
- ⑤ 设置:模型下拉 + 新增/删除/测试连接 + 详情(服务商名/类别/api_type/模型ID/网址/密钥打码);至少各一个 text+image。
- 角色:标题大模型/图片大模型下拉(按 category 过滤)→ config.json default_text/image_model。
- 分辨率下拉 512/1k/2k/4k;**返回超时由 resolution_timeouts 映射自动**(180/240/360/600),不单独填。
- 安全:密钥加密、打码、不入日志;`config/` 必须 gitignore;提醒轮换已暴露的 Key。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-26】整理 · 测试脚本迁入 prototypes/
- 状态:DONE
- 变更:`git mv` 把 `demo.py`/`set_title.py`/`set_cover.py`/`get_title.py`/`cookies.py`/`inspect_images.py`/`grab.py`/`1.py` 移入 `prototypes/`;`cdp.py` 留根目录(正式模块);新增 `prototypes/README.md`;批量把文档里的脚本引用改为 `prototypes/` 路径(含 04/00/03/05/06/api/AGENTS、current-state);修掉 `config.json` 里残留的 `api_key_enc` 描述(密钥已移至 ai_models.json)。
- 决策:这些是已验证原型/探查脚本,逻辑待 `editor.py`(T-001) 移植并实测后清理;现保留为唯一已验证参照。`1.py` 作为 `ai.py` 调用方式参考。
- 注意:`prototypes/demo.py` 依赖根目录 `cdp.py`,需从项目根运行或设 PYTHONPATH。
- 下一步:Phase 0 起步(T-001/T-002/T-003)。
## 【2026-06-26】修正 · V1 提交策略 + 入口引用 + 版本边界
- 状态:DONE(仅文档)
- 变更:更新 `AGENTS.md`、`CLAUDE.md`、`docs/00-ai-start-here.md`、`docs/01-vision.md`、`docs/02-requirements.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/05-coding-rules.md`、`docs/06-tasks.md`、`docs/routes.md`、`docs/current-state.md`、`docs/README.md`、`docs/api.md` 与 UI 线框说明。
- 决策:
- ③ 更新 shopee 的提交策略统一为:点击「开始更新」后弹窗确认当前筛选范围和任务数量,用户确认后才批量更新并逐条点「更新」提交线上;取消则不执行、不改库。
- V1 不设逐条人工审核阶段,也无常驻提交开关;批量确认弹窗是提交线上前的确认边界。
- V0 = 已验证单账号原型;V1 = 当前 5 Tab + Excel + AI + SQLite 流水线;V2 = 多账号并行 / dry-run / 完整运行日志等后续能力。
- CDP 已验证事实统一引用 `docs/04-architecture.md` 第七节。
- 下一步:继续处理剩余全栈落地缺口,如 SQLite/Excel schema、GUI 线程模型、AI 接入任务依赖。
## 【2026-06-26】决策 · GUI 框架改为 PySide6
- 状态:DONE(仅文档)
- 变更:更新 `docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/routes.md`、`docs/06-tasks.md`、`docs/api.md`、`docs/00-ai-start-here.md`、`docs/02-requirements.md`、`docs/05-coding-rules.md`、`docs/current-state.md`、`docs/README.md`。
- 决策:
- V1 GUI 框架固定为 PySide6(Qt for Python),不再使用 Tkinter。
- 理由:当前环境已安装 PySide6,Tkinter 不可导入;V1 需要表格、图片预览、后台任务进度、停止按钮、确认弹窗,Qt 的 `QThread` + signal/slot 更适合。
- GUI 线程模型:主线程只操作 Qt widget;采集/生成/更新/回写使用 `QObject` worker + `QThread`;worker 通过 signal 回传 `progress/log/row_updated/failed/finished/cancelled`;worker 不直接操作 QWidget,不跨线程共享 SQLite connection。
- 新增 `workers.py` 作为可选模块承载 PySide6 worker/QThread 编排。
- 下一步:继续处理 SQLite/Excel schema 与 AI 接入任务依赖。
## 【2026-06-26】补强 · 数据模型 / AI 顺序 / 测试 / SQLite 并发 / 明文敏感信息
- 状态:DONE(仅文档)
- 变更:更新 `docs/04-architecture.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/03-tech-stack.md`、`docs/05-coding-rules.md`、`docs/00-ai-start-here.md`、`docs/02-requirements.md`、`docs/current-state.md`、`docs/routes.md`、`AGENTS.md`、`CLAUDE.md` 与 UI 线框。
- 决策:
- 数据模型补强:新增 `batches` 表;`tasks` 增加 `source_file_abs/source_sheet/source_row/row_key`、`stage/status` 分离、时间戳、attempts、`last_error`,用于可靠回写、重试与断点续跑。
- SQLite 并发:每个 worker 独立 connection;统一 `foreign_keys=ON`、`journal_mode=WAL`、`busy_timeout=5000`、`synchronous=NORMAL`;短事务单条提交。
- AI 顺序:先做 T-005 AI 模型清单后端,再做 T-301 真实 AI 接入;T-501 只做设置页 UI。
- 测试策略:引入 `tests/` + stdlib `unittest` 基座,优先覆盖 appconfig/db/excel/prompts 纯逻辑;CDP/Shopee/真实 AI 作为集成或手动验证。
- 不做加密:密码与 AI Key 本地明文保存,文件必须 gitignore;UI 打码,日志/导出脱敏,不自动登录。
- 下一步:继续按任务看板从 Phase 0 开始实现,建议优先 T-003/T-006 或 T-001。
## 【2026-06-26】修正 · 任务领取规则与 tests 命令时机
- 状态:DONE(仅文档)
- 变更:更新 `docs/current-state.md`、`docs/00-ai-start-here.md`、`docs/05-coding-rules.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`AGENTS.md`。
- 决策:
- 任务领取规则统一为:每轮只取 `docs/06-tasks.md` 中第一个 `TODO` 且依赖均 `DONE` 的任务;`current-state.md` 只能展示按该规则计算出的当前任务,不能写“任选”。
- 当前下一个可领取任务修正为 T-001;T-002/T-003 虽无依赖,但排在 T-001 后,不能跳过。
- `tests/` 与 `python -m unittest discover -s tests` 由 T-006 建立;T-006 前缺少 `tests/` 不算验证失败,T-006 后纯逻辑改动必须补测并运行该命令。
- 下一步:按任务看板领取 T-001。
## 【2026-06-26】修正 · 敏感文件清单与 Excel 容错策略
- 状态:DONE(仅文档)
- 变更:更新 `AGENTS.md`、`CLAUDE.md`、`docs/05-coding-rules.md`、`docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`、`docs/02-requirements.md`、`docs/04-architecture.md`。
- 决策:
- gitignore/禁止提交清单统一为:`config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/`。
- `current-state.md` 的待建数据路径补齐 `config/ai_models.json` 与 `images/`。
- Excel 导入容错定稿:必需列缺失(别名/商品id)时拒绝整个文件;单行缺值或商品id格式错误等脏数据逐行跳过,计入 invalid,不阻塞同文件其他有效行。
- 下一步:按任务看板领取 T-001。
## 【2026-06-26】决策 · 正式代码迁入 app 包
- 状态:DONE(仅文档)
- 变更:更新 `AGENTS.md`、`docs/00-ai-start-here.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/05-coding-rules.md`、`docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`、`prototypes/README.md`。
- 决策:
- 正式代码统一放入 `app/` 包,根目录只保留 `main.py`、配置/数据目录、文档和原型目录。
- `cdp.py` 由根目录迁入 `app/cdp.py`;包内模块优先使用相对导入,根入口使用 `from app.gui import main`。
- 新增 T-000 作为首个任务:创建 `app/`、迁移 CDP、补 `app/__main__.py` 和 `main.py`,并修正 `prototypes/demo.py` 导入。
- 正式启动入口为 `python main.py`,开发/包入口为 `python -m app`。
- 下一步:按任务看板领取 T-000。
## 【2026-06-26】T-000 正式代码包结构
- 状态:DONE
- 变更:创建 `app/` 包;将根目录 `cdp.py` 迁入 `app/cdp.py`;新增 `app/__init__.py`、`app/__main__.py`、`app/gui.py` 占位入口、根目录 `main.py`;修改 `prototypes/demo.py` 改为从项目根导入 `app.cdp`;同步 `docs/06-tasks.md` 与 `docs/current-state.md`。
- 验证:`py -3 -m compileall app main.py` 通过;`python main.py` 输出 GUI 未实现提示并退出;`py -3 main.py` 输出同样提示并退出;`py -3 -m app` 输出同样提示并退出;`py -3 -c "import prototypes.demo"` 通过。
- 决策:`app/gui.py` 只保留最小入口占位,不提前实现 PySide6 主窗口,完整 GUI 留给 T-104;`app/cdp.py` 对 `requests` / `websocket-client` 改为按需导入,使包导入不依赖外部库,真正连接 CDP 时仍要求安装依赖。
- 注意:本机 `python` 指向 Python 3.7.9 isolated,不符合项目 Python 3.10+ 要求,且 `python -m app` 不搜索当前目录;本轮用 `py -3`(Python 3.14.4)完成包入口验证。
- 下一步:按任务看板领取 T-001。
## 【2026-06-26】T-001 editor 模块化
- 状态:DONE
- 变更:新增 `app/editor.py`,封装 `is_logged_in/open_product/read_title/read_cover_src/download_cover/collect/change_title/replace_cover/click_update/apply_task`;`app/cdp.py` 增加 `host` 参数,支持后续按账号端口连接;更新 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 验证:
- `py -3 -m compileall app main.py` 通过。
- `python main.py` 输出 GUI 未实现提示并退出。
- `py -3 -c "import app.editor as e; ..."` 导入通过,`apply_task` 可调用。
- 测试商品 `51100639510`:`open_product` + `read_title` 读到标题;`change_title` 写回原标题后 `value == modelvalue == 原标题`。
- 测试商品 `51100639510`:`read_cover_src` 读到第一张封面 CDN 链接。
- 测试商品 `51100639510`:使用 `1_TY030.jpg` 执行 `replace_cover`,上传成功并拖到第 0 位,返回 `ok=True`;全程未点击「更新」。
- 决策:满 9 张封面的删除确认框仍未实测,`replace_cover` 当前返回 `FULL_IMAGE_SLOTS`,实际删第一张流程留给 T-502。
- 注意:本轮真实页面验证使用本机 `python`(3.7.9,已装 requests/websocket-client)连接 CDP;项目版本要求仍为 Python 3.10+,后续开发优先用 `py -3` 并补齐依赖。
- 下一步:按任务看板领取 T-002。
<!-- 新一轮从这里向下追加记录。 -->
## 【2026-06-27】T-002 appconfig 应用配置
- 状态:DONE
- 变更:新增 `app/appconfig.py`,实现 `config.json` 默认配置、缺失时写默认、读取/保存/合并更新、路径/端口/CDP 等待时间/AI 参数读取、按分辨率派生返回超时;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 配置边界:`config.json` 只保存应用路径、端口、AI 角色选择与生成参数;`api_key` / `*_key` / `token` / `password` 等敏感字段写入时抛 `ConfigError`,AI Key 仍留给 T-005 的 `config/ai_models.json`。
- 验证:`py -3 -m compileall app main.py` 通过;临时 `config.json` 的 `load_config` / `update_config` / `response_timeout` 验证通过;尝试写入 `api_key` 返回 `ConfigError: config.json 不允许保存敏感字段: api_key`。
- 注意:`python -m unittest discover -s tests` 仍等 T-006 建立测试基座后再运行。
- 下一步:按任务看板领取 T-003。
## 【2026-06-27】T-003 SQLite 持久化地基
- 状态:DONE
- 变更:新增 `app/db.py`,按架构 5.2 建立 `batches`、`accounts`、`tasks` 三张表和索引;实现 `connect/init_db`、批次、账号、任务导入、任务筛选,以及 `mark_running/mark_failed/mark_skipped/set_collected/set_generated/set_applied`;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 连接规则:`connect()` 设置 `foreign_keys=ON`、`journal_mode=WAL`、`busy_timeout=5000`、`synchronous=NORMAL`、`row_factory=sqlite3.Row`;每个函数默认短事务写入并提交,worker 后续可按线程自建 connection。
- 阶段规则:失败和跳过只更新 `status/last_error/attempts`,`stage` 保持最后成功阶段;成功采集/生成/应用分别推进到 `collected/generated/applied`。
- 验证:`py -3 -m compileall app main.py` 通过;临时 SQLite 文件中重复 `init_db` 通过;PRAGMA 输出 `(1, 'wal', 5000)`;批次/账号/任务写入、失败状态、采集/生成/应用阶段推进验证通过。
- 注意:`python -m unittest discover -s tests` 仍等 T-006 建立测试基座后再运行。
- 下一步:按任务看板领取 T-004。
## 【2026-06-27】T-004 本地数据 gitignore
- 状态:DONE
- 变更:校准 `.gitignore`,明确排除 `config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/`;保留本地测试图片与临时启动脚本忽略项;同步 `docs/06-tasks.md`、`docs/current-state.md`。
- 决策:将原来的 `config/` 整目录忽略收窄为 `config/ai_models.json`,与文档安全红线保持一致,避免后续非敏感模板或说明文件被误隐藏。
- 验证:`git check-ignore -v -- config.json config/ai_models.json cmshopee.db chrome_user_data_dir/ images/` 五个路径均命中仓库 `.gitignore`;命令同时提示全局 ignore 文件权限不可读,但不影响仓库规则生效。
- 下一步:按任务看板领取 T-005。
## 【2026-06-27】T-005 AI 模型清单后端
- 状态:DONE
- 变更:扩展 `app/appconfig.py`,实现 `config/ai_models.json` 默认清单、读写、增删改、category 过滤、展示打码、`get_model` 明文返回、最小请求测试连接;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 安全边界:`list_ai_models()` 默认只返回打码 `api_key` 和 `api_key_set`;只有 `get_model()` 返回明文 key 给实际调用层;所有 key 只写入 gitignored 的 `config/ai_models.json`。
- 约束:模型 `name` 唯一;`category` 只允许 `text/image`;`api_type` 只允许 `chat/images_edits/auto`;保存后必须至少有一个启用的 text 和一个启用的 image 模型。
- 验证:`py -3 -m compileall app main.py` 通过;临时 `ai_models.json` 默认写入、分类过滤、key 打码、`get_model` 明文 key、重命名/禁用、缺少 url/model/key 的测试连接错误返回均通过;重复 name 与删除最后 image 模型均返回 `ConfigError`。
- 注意:本轮未使用真实 API Key 发外部网络请求;真实「测试连接」需用户在本地 `config/ai_models.json` 填入有效 url/model/api_key 后由 UI 或函数触发。
- 下一步:按任务看板领取 T-006。
## 【2026-06-27】T-006 单元测试基座
- 状态:DONE
- 变更:新增 `tests/` 基座,包含 `_helpers.py`、`test_appconfig.py`、`test_db.py`、`test_module_contracts.py`;覆盖 `config.json` 默认/更新/敏感字段拦截、`config/ai_models.json` CRUD/过滤/打码/约束、SQLite 初始化/PRAGMA/账号批次任务生命周期/失败状态;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 补强:`config.json` 敏感字段拦截从 exact `token/password` 扩展到 `*_token`、`*_password`,测试覆盖 `provider_token`。
- 说明:`app.excel` 与 `app.prompts` 尚未实现,`test_module_contracts.py` 对这两个模块做契约占位测试;当前表现为 2 个 skip,后续模块文件出现后会检查公开函数是否齐全。
- 验证:`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(9 tests,skipped=2);`python -m unittest discover -s tests` 通过(9 tests,skipped=2);测试临时目录位于 `tests/` 下并已清理。
- 下一步:按任务看板领取 T-101。
## 【2026-06-27】T-101 账号 slug 与 user-data-dir
- 状态:DONE
- 变更:新增 `app/config.py`,实现 `make_slug(alias)` 与 `ensure_user_data_dir(slug, root=None, config=None)`;`app/db.py` 改为复用同一 slug 生成规则;新增 `tests/test_config.py` 覆盖 slug 稳定性、非 ASCII 别名、非法 slug 拒绝、目录创建与绝对路径;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 规则:slug = 可读 ASCII 前缀 + 8 位 SHA1 后缀;非 ASCII 别名使用 `account_<hash>`;`ensure_user_data_dir` 只接受 `[a-z0-9_]`,防止路径穿越。
- 验证:`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(12 tests,skipped=2);`python -m unittest discover -s tests` 通过(12 tests,skipped=2);测试临时目录已清理。
- 下一步:按任务看板领取 T-102。
## 【2026-06-27】T-102 Chrome 启动器
- 状态:DONE
- 变更:新增 `app/chrome.py`,实现 `build_launch_args`、`launch_chrome`、`is_running`、`wait_debug_ready`;启动参数包含 `--remote-debugging-port`、`--remote-allow-origins=*`、`--user-data-dir`;新增 `tests/test_chrome.py` 覆盖参数拼装、Popen 调用模拟、本地 `/json/version` 端口探测;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:端口探测使用 stdlib `urllib` 访问 `127.0.0.1:<port>/json/version`,显式禁用环境代理,避免 localhost/CDP 被代理干扰。
- 范围:本轮不实现 `.lnk` 快捷方式,留给 T-106。
- 验证:`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(17 tests,skipped=2);`python -m unittest discover -s tests` 通过(17 tests,skipped=2)。
- 下一步:按任务看板领取 T-103。
## 【2026-06-27】T-103 登录保活与检测
- 状态:DONE
- 变更:增强 `app/editor.py` 登录检测,新增 `login_status(account, timeout=8)` 返回 `logged_in/reason/url/host/cookie_names`,`is_logged_in()` 保持 bool 包装;无 Shopee tab 时打开卖家中心用于检测/人工登录;新增 `tests/test_editor_login.py` 覆盖登录页、会话 Cookie、缺 Cookie、无 Shopee tab 创建检测页;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 规则:不自动登录、不填密码;登录态依赖 T-101/T-102 的稳定 user-data-dir 保留;URL 命中登录页返回 `LOGIN_PAGE`,缺 `SPC_ST/SPC_U` 返回 `NO_SESSION_COOKIE`。
- 验证:`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(22 tests,skipped=2);`python -m unittest discover -s tests` 通过(22 tests,skipped=2)。
- 下一步:按任务看板领取 T-104。
## 【2026-06-27】T-104 PySide6 五 Tab 主窗口骨架
- 状态:DONE
- 变更:将 `app/gui.py` 从占位入口升级为 PySide6 `MainWindow(QMainWindow)`,内含固定顺序 `QTabWidget` 五 Tab:①导入采集、②AI生成、③更新shopee、④账号管理、⑤设置;实现基础状态栏与 Tab 切换状态提示;新增 `tests/test_gui.py` 覆盖 Tab 顺序与状态栏;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:Tab 内容保持空壳容器,不提前实现后续业务 UI;缺 PySide6 时 `main()` 返回 1 并输出明确提示,便于依赖诊断。
- 验证:`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(23 tests,skipped=3,其中 GUI 因 py -3 缺 PySide6 跳过);`python -m unittest discover -s tests` 通过(24 tests,skipped=2,实际 offscreen 创建 MainWindow);默认 `python` 的 PySide6 版本为 6.5.3。
- 下一步:按任务看板领取 T-104b。
## 【2026-06-27】审查 · T-001~T-103(含 T-000/T-004/T-005/T-006)
- 状态:DONE(审查通过)
- 结论:按文档完成、忠实于已验证 CDP 事实、文档↔代码一致、22 测试通过;未臆造未验证内容(满 9 张删除留 T-502)。
- 待办:`app/editor.py` 接 GUI(T-104+)前,建议用 prototypes 同款环境做一次真机冒烟(本轮测试均为 mock CDP,未连真实 Shopee)。
- 完整报告:[`docs/reviews/2026-06-27-T001-T103-acceptance.md`](docs/reviews/2026-06-27-T001-T103-acceptance.md)
## 【2026-06-27】T-104b PySide6 worker 基座
- 状态:DONE
- 变更:新增 `app/workers.py`,实现 `BaseWorker(QObject)`、`progress/log/row_updated/failed/finished/cancelled` 通用 signals、协作式取消标记 `cancel()/should_cancel()`、统一 `run()` 执行入口与 `run_worker()` QThread 包装;新增 `tests/test_workers.py` 覆盖 signal 回传、取消、异常转失败、普通对象拒绝;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`、`docs/04-architecture.md`。
- 细节:`workers.py` 不导入 `QtWidgets`,业务 worker 子类只能通过 signal 回传 UI 所需数据;`run_worker()` 默认启动线程,也支持 `start=False` 便于测试或调用方先连接额外信号;缺 PySide6 时抛明确 `WorkerError`,测试自动 skip。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(28 tests,skipped=2);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(24 tests,skipped=4,其中 GUI/worker 因 py -3 缺 PySide6 跳过)。
- 下一步:按任务看板领取 T-105。
## 【2026-06-27】T-105 Tab④ 账号管理
- 状态:DONE
- 变更:新增 `app/accounts.py` 账号管理服务层,封装账号新增/编辑/删除、slug 与 user-data-dir 创建、端口默认分配、启动登录、登录检测与 `last_login_at` 更新;`app/db.py` 支持账号别名更新并把唯一约束错误转为 `DbError`;`app/gui.py` 将 ④ Tab 替换为账号表格、账号编辑弹窗、启动登录与检测登录按钮,检测登录通过 `BaseWorker` 后台执行;新增/更新 `tests/test_accounts.py`、`tests/test_gui.py`;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`、`docs/04-architecture.md`。
- 细节:密码只在 DB 本地明文保存与弹窗密码框中打码显示,账号表格不展示密码;账号服务层校验调试端口不重复;「启动登录」只启动对应 user-data-dir 的 Chrome,人工登录,不读取/填写/提交密码;删除账号只删 DB 记录,不删除本地 user-data-dir。
- 追加收尾:运行最新 GUI 后发现顶部 5 个 Tab 间距偏紧,容易误点;已在 `MainWindow` 应用全局 `TAB_STYLE`,包含 Tab 最小宽度、padding、间距、hover 与当前态高亮,并补测试防止后续误删。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过;`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过;本轮未连接真实 Shopee/CDP 做登录检测实跑,登录检测路径由 mock 覆盖。
- 下一步:按任务看板领取 T-106(可选)。
## 【2026-06-27】T-106 账号桌面快捷方式
- 状态:DONE
- 变更:`app/chrome.py` 新增 `create_shortcut()`,复用 `build_launch_args()` 生成 Chrome 目标与参数,并通过 PowerShell `WScript.Shell.CreateShortcut` 写 `.lnk`;`app/accounts.py` 新增 `create_shortcut()` 服务包装;`app/gui.py` 在 ④ 账号管理增加「快捷方式」按钮,选中账号后生成默认桌面快捷方式并提示路径;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`、`docs/routes.md`。
- 细节:快捷方式参数包含 `--remote-debugging-port`、`--remote-allow-origins=*`、`--user-data-dir=<该账号目录>`;不包含账号密码/API Key;测试 mock PowerShell 调用,不在测试环境真实写桌面。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过;`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过。
- 下一步:按任务看板领取 T-201。
## 【2026-06-27】审查 · T-104~T-106(含 T-104b)
- 状态:DONE(审查通过)
- 结论:按文档完成;5 Tab 顺序/降级、worker 6 信号与线程纪律、账号④增删改/打码/不自动登录、`.lnk` 含账号参数均对上;33 测试通过(skipped=4 为 excel/prompts 契约测试)。
- 待办:账号启动登录、快捷方式、登录检测本轮均为 mock;接 Phase 2(T-201/T-203 真连 CDP)前,建议 `editor.py` 真机冒烟 + 账号④走一遍(新增→启动→人工登录→检测→生成快捷方式)一起验。
- 完整报告:[`docs/reviews/2026-06-27-T104-T106-acceptance.md`](docs/reviews/2026-06-27-T104-T106-acceptance.md)
## 【2026-06-27】T-201 Excel 导入入库
- 状态:DONE
- 变更:新增 `app/excel.py`,实现多 Excel 文件导入、输入列解析、必需列校验、脏行跳过、`batches/tasks` 入库、导入统计与 `match_summary()`;新增 `tests/test_excel.py` 覆盖有效/无效行、缺必需列整文件拒绝、DB 写入和别名匹配统计;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:输入列只解析 `账号名`(可选)、`别名`、`商品id`;有效行记录 `source_file/source_file_abs/source_sheet/source_row/row_key`;缺 `别名` 或 `商品id` 的非空工作表会拒绝整个文件并写入 `file_errors`;单行缺别名、缺商品 id 或商品 id 非数字会跳过并计入 `invalid/row_errors`。`write_back()` 与 `export_copy()` 本轮只保留可调用占位,实际回写留给 T-204/T-403。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(45 tests,skipped=1,默认 python 环境已安装 openpyxl 并执行真实 Excel 解析测试);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过,`app.excel` 契约测试通过)。
- 注意:本轮不涉及 Shopee/CDP 页面改动,无需测试商品实跑。目录中未跟踪的本地 Excel 文件未处理、未纳入本次变更。
- 下一步:按任务看板领取 T-202(Tab① 任务列表 + 导入按钮 + 别名匹配标记)。
## 【2026-06-27】T-202 Tab① 任务列表 + 导入按钮 + 别名匹配标记
- 状态:DONE
- 变更:`app/gui.py` 新增 `CollectTab` 与 `TaskTableModel(QAbstractTableModel)`;① 导入采集 Tab 接入「导入 Excel...」按钮、`QTableView` 任务列表和刷新按钮;导入时调用 `excel.import_tasks(file_paths, path=db_path)` 并展示当前批次任务;任务表列为账号、别名、商品ID、阶段,别名未匹配账号时阶段显示“略过”。更新 `tests/test_gui.py` 覆盖 Tab① 接入、任务展示、未匹配略过和导入按钮刷新;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:账号列优先显示匹配到的 `accounts.account_name`,未匹配时保留 Excel 输入账号名;T-202 只做 UI 标记,不把未匹配任务立即写库为 skipped,实际跳过与原因入库留给 T-203 采集执行阶段。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(8 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(47 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过)。
- 注意:本轮只改 GUI/DB 展示与 Excel 导入入口,不涉及 Shopee/CDP 页面操作,无需测试商品实跑。
- 下一步:按任务看板领取 T-202b(Tab① 导入汇总栏)。
## 【2026-06-27】补充 · 标准 Excel 模板入库规则
- 状态:DONE(文档/忽略规则补充)
- 变更:将根目录 `shopee待处理任务模板.xlsx` 整理为可提交的标准空模板(单工作表 `待处理任务`,含输入列与后续输出列);更新 `.gitignore`,默认忽略运营填写后的 `*.xlsx/*.xlsm` 业务文件与 Excel 临时锁文件,但放行该标准模板;同步 `AGENTS.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/current-state.md`、`docs/05-coding-rules.md`。
- 决策:标准模板推荐表头固定为 `账号名 | 别名 | 商品id`;`别名` 必须与 ④ 账号管理中的别名一致;运营实际填写时复制模板另存,填写后的副本不进版本库。
- 验证:只读检查 `shopee待处理任务模板.xlsx` 可被当前 `app.excel` 识别核心列(`账号名/别名/商品id`),`file_errors=[]`、`row_errors=[]`;本轮未导入数据库。
## 【2026-06-27】T-202b Tab① 导入汇总栏
- 状态:DONE
- 变更:`app/gui.py` 的 `CollectTab` 增加导入汇总栏,展示文件数、解析行数、有效/无效、匹配、未匹配;增加按账号的匹配明细;`TaskTableModel` 支持 `all/unmatched` 过滤,「未匹配(n)」按钮可筛出别名未匹配账号的任务,「全部」恢复完整列表。更新 `tests/test_gui.py` 覆盖汇总文本、匹配明细和未匹配筛选;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:汇总栏的无效行数量来自本次 `excel.import_tasks()` 返回的 `stats`;未匹配数量基于当前批次任务与 ④ 账号管理中的 `accounts.alias` 实时计算。未匹配在本阶段仍只做 UI 标记,不写库为 skipped,实际跳过留给 T-203 采集执行阶段。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(9 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(48 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过)。
- 注意:本轮只改 GUI 汇总与过滤,不涉及 Shopee/CDP 页面操作,无需测试商品实跑。
- 下一步:按任务看板领取 T-203(采集旧标题+旧封面,只读下载并立即写库)。
## 【2026-06-27】T-203 采集旧标题+旧封面
- 状态:DONE
- 变更:`app/gui.py` 新增 `CollectWorker`,Tab① 增加「采集旧标题/旧封面」与「停止」按钮;worker 逐条处理 `stage=imported` 任务,别名未匹配写 `db.mark_skipped(..., "别名未匹配账号")`,已登录则调用 `editor.collect()` 下载旧封面到 `image_dir/<slug>/<item_id>_old.jpg` 并 `db.set_collected()`;单条失败 `mark_failed(..., "collect", error)` 后继续。T-203 当时覆盖了未登录逐条兼容分支;最终 Phase 2 语义已在 T-205 调整为匹配账号未登录由采集前预检阻断,不进入逐条采集。更新 `tests/test_gui.py` 覆盖成功采集、preflight=False 兼容分支、未匹配略过;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:采集通过 `BaseWorker/QThread` 后台执行,使用 signal 回传进度、行更新、失败和完成;GUI 线程只刷新表格与状态栏。停止使用协作式 cancel,正在执行的单条跑到安全边界后结束。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(10 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(49 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过)。
- 注意:本轮未连接真实 Shopee/CDP 实跑采集,`accounts.detect_login()` 与 `editor.collect()` 路径由 mock 覆盖;接 T-204 前建议用测试商品和已登录账号做一次 Tab① 真机采集冒烟。
- 下一步:按任务看板领取 T-204(回写旧字段到原 Excel,含文件锁处理)。
## 【2026-06-27】修正 · 登录检测入口 URL
- 状态:DONE
- 变更:`app/editor.py` 将无 Shopee tab 时的登录检测入口从 `https://<region_host>/portal/` 改为 `https://<region_host>/`,默认即 `https://seller.shopee.tw/`;更新 `tests/test_editor_login.py`、`docs/api.md`、`docs/04-architecture.md`、`docs/current-state.md`。
- 决策:商品编辑页 URL 仍保持 `/portal/product/<item_id>?pageEntry=product_list&ignore-html-cache=1`,本轮只改登录检测/人工登录入口。
- 验证:`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(5 tests);`python -m unittest discover -s tests -p "test_accounts.py"` 通过(8 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(49 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4)。
## 【2026-06-27】T-204 回写旧字段到原 Excel
- 状态:DONE
- 变更:`app/excel.py` 实现 `write_back()` 与 `export_copy()`:按 `tasks.source_file_abs/source_sheet/source_row` 定位原 Excel 行,回写 `旧标题` 与 `旧封面图片路径`;输出列缺失时自动追加;`.xlsm` 写入时保留 VBA;原文件被占用或无权限时抛中文 `ExcelError`,SQLite 采集结果不回滚。`app/gui.py` 在 Tab① 增加「回写旧数据到 Excel」按钮与 `WriteBackWorker`,后台调用 `excel.write_back()`,锁文件时弹窗提示关闭后重试或另存副本。更新 `tests/test_excel.py`、`tests/test_gui.py`、`docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`。
- 细节:`write_back(batch_id, excel_path=None, path=None)` 默认按批次涉及的源文件分组回写;只写已到 `collected/generated/applied` 阶段或已有旧字段值的任务;无可写行时返回 `rows=0`。`export_copy(batch_id, out_dir_or_path, path=None)` 不修改原文件,目录输出时生成 `*_cmshopee回写.xlsx`,同名自动加序号。
- 验证:`python -m unittest discover -s tests -p "test_excel.py"` 通过(7 tests);`python -m unittest discover -s tests -p "test_gui.py"` 通过(11 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(53 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过)。
- 注意:本轮只做 Excel 回写与 GUI 后台接入,不涉及 Shopee/CDP 页面操作,无需测试商品实跑。`export_copy()` 已有后端能力,Tab① 当前只在锁文件提示中说明可另存副本,尚未提供另存按钮。
- 下一步:按任务看板领取 T-205(首次未配账号/未登录的引导保护)。
## 【2026-06-27】文档补充 · 采集自动回写与 Chrome 未启动引导
- 状态:DONE(文档调整)
- 背景:运行最新代码后发现两个体验缺口:① 采集旧标题/旧封面后仍需手动点「回写旧数据到 Excel」;② 未在④账号管理启动对应账号 Chrome 时无法采集。
- 变更:`docs/06-tasks.md` 新增 T-204b(采集完成后自动回写旧字段到 Excel),并把 T-205 扩展为“未配账号 / Chrome 未启动 / 未登录”的引导保护;同步 `docs/current-state.md` 下一个可领取任务为 T-204b,并补充当前已发现待修体验问题;同步 `docs/routes.md`、`docs/api.md`、`docs/04-architecture.md`、`docs/02-requirements.md`。
- 决策:T-204b 先解决采集闭环自动回写,保留手动回写作为锁文件失败后的重试入口;T-205 解决 Chrome 未启动/未登录的禁用、提示和跳转④,不做无提示批量启动所有账号 Chrome。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-06-27】T-204b 采集完成自动回写旧字段
- 状态:DONE
- 变更:`app/gui.py` 将 Tab① 手动回写启动逻辑抽为 `_start_write_back()`,`CollectTab._on_collect_finished()` 在本轮成功采集数量大于 0 时自动启动 `WriteBackWorker` 回写当前批次旧标题/旧封面路径;自动回写成功时状态栏显示“Excel 自动回写完成”;自动回写遇到原 Excel 被占用时弹窗提示关闭文件后点击「回写旧数据到 Excel」手动重试,SQLite 采集结果不回滚。更新 `tests/test_gui.py` 覆盖自动触发、无成功采集不触发、锁文件提示手动重试;同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/api.md`、`docs/04-architecture.md`。
- 细节:手动「回写旧数据到 Excel」按钮保留,主要作为自动回写失败后的重试入口;若回写 worker 已在运行,会提示“Excel 回写正在进行”,避免重复启动。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(14 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(56 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过)。
- 下一步:按任务看板领取 T-205(首次未配账号 / Chrome 未启动 / 未登录的引导保护)。
## 【2026-06-27】T-205 首次未配账号 / Chrome 未启动 / 未登录引导保护
- 状态:DONE
- 变更:`app/gui.py` 的 `CollectWorker` 增加采集前账号就绪预检:无账号、当前批次匹配账号 CDP 端口未响应、账号未登录时返回 `blocked=True`,不进入逐条采集、不写 skipped/failed;`CollectTab` 收到 blocked 后弹窗汇总原因,并跳转/引导用户去④账号管理配置账号、启动对应 Chrome、人工登录。`MainWindow` 为①传入打开④的回调;GUI 测试增加无账号、Chrome 未启动、未登录、弹窗引导覆盖。
- 细节:预检只检查当前批次中别名能匹配到账号的任务;别名未匹配的任务仍按既有逻辑在采集 worker 中略过。设计上不无提示批量启动所有账号 Chrome,避免开错账号或启动过多浏览器进程。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(18 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(60 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(34 tests,skipped=4,py -3 环境缺 openpyxl/PySide6,相关测试按设计跳过)。
- 下一步:按任务看板领取 T-301(确定 AI 服务商/模型并接入 `app/ai.py`)。
## 【2026-06-27】审查 · T-201~T-205(含 T-204b)
- 状态:DONE(审查通过)
- 验证:本机(WSL)临时装 openpyxl 后 `test_excel` 7/7 实跑通过、`compileall` 通过;GUI/worker 测试因本机无 PySide6 按测试清单+源码核对(以 Codex Windows 运行为准)。
- 结论:T-201 导入(行定位/缺列拒文件/脏行跳过/row_key 唯一)、T-202/202b 列表与汇总栏、T-203 采集 worker、T-204 回写+锁处理+export_copy、T-204b 自动回写、T-205 账号就绪预检拦截 均按当前文档完成。
- T-205 口径:当前文档已对齐为「预检拦截 + 弹窗说明 + 自动跳④」;实现方案接受,作为最终方案记录。匹配账号未登录由 T-205 预检 `blocked=True` 拦截,不进入逐条采集、不写 skipped/failed;别名未匹配仍按 T-203 逐条逻辑 `mark_skipped`。
- 待办:采集/`editor.collect` 真连 CDP 仍未冒烟;进 Phase 3 前建议对 Tab① 做一次真机采集冒烟(导入→采集→出图→回写)。
- 完整报告:[`docs/reviews/2026-06-27-T201-T205-acceptance.md`](docs/reviews/2026-06-27-T201-T205-acceptance.md)
## 【2026-06-27】修复 · T-201~T-205 审查三点
- 状态:DONE
- 变更:修正评审报告中的 GUI 用例数量为 18;确认 T-205「预检拦截 + 弹窗说明 + 自动跳④」为最终方案,不再作为待决事项;同步 `docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`,明确匹配账号未登录由 T-205 预检 `blocked=True` 阻断,不进入逐条采集、不写 skipped/failed,别名未匹配仍按 T-203 `mark_skipped`。
- 测试补强:`tests/test_gui.py` 为未登录预检分支增加断言,确认任务保持 `stage=imported/status=pending/last_error=None`,防止后续误写 skipped。
## 【2026-06-27】冒烟 · Tab① 真实采集回写
- 状态:DONE
- 前置:账号 `papa` 使用专属 Chrome user-data-dir 启动,CDP 端口 `9224` 就绪,`accounts.detect_login()` 返回已登录。
- 验证:因原业务 Excel 正被 Excel 锁定,改用数据库现有 5 条任务生成 gitignore 的临时 Excel;执行 `excel.import_tasks()` 导入 5 行,`CollectWorker` 真连 Shopee/CDP 采集 5 条旧标题/旧封面,下载 5 张旧封面到 `images/`,随后 `excel.write_back()` 成功回写临时 Excel。
- 结果:`collect_summary {'ok': True, 'total': 5, 'done': 5, 'collected': 5, 'skipped': 0, 'failed': 0}`;旧标题长度非空,旧封面路径均存在。
## 【2026-06-27】T-301 AI 生成接口
- 状态:DONE
- 变更:新增 `app/ai.py`,实现 `AIError`、`gen_title()`、`gen_cover()`;按 `config.json` 的默认文本/图片模型名读取 `config/ai_models.json` 明文模型定义,调用时不记录 Key;支持 retry、模型启用/字段校验、错误脱敏、OpenAI-compatible chat JSON、images_edits multipart、图片 URL/data URL/base64/b64_json 解析、按 `resolution` resize 并用 `jpg_quality` 保存 JPEG。
- 文档:`docs/06-tasks.md` 将 T-301 标为 DONE;同步 `docs/api.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/current-state.md`,下一个可领取任务更新为 T-302。
- 测试:新增 `tests/test_ai.py`,覆盖文本生成重试与 extra_body、缺字段错误不泄露 Key、图片生成保存 JPEG。
- 验证:`python -m unittest discover -s tests -p "test_ai.py"` 通过(3 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(63 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests -p "test_ai.py"` 通过(3 tests,skipped=1,py -3 环境缺 Pillow);`py -3 -m unittest discover -s tests` 通过(37 tests,skipped=5,py -3 环境缺 openpyxl/PySide6/Pillow,相关测试按设计跳过)。
- 注意:T-301 只做通用 AI 接口与 mock 单测;真实 AI 调用还需要在 `config/ai_models.json` 填入可用 url/model/api_key 后做一次小样本实测。生成批次编排和 GUI 接入留给 T-303/T-302。
## 【2026-06-27】T-302 Tab② AI生成布局与任务列表
- 状态:DONE
- 变更:`app/gui.py` 新增 `GenerateTab` 与 `GenerateTaskTableModel`;MainWindow 的②从占位页切换为实际 AI 生成页。左侧使用 `QSplitter` 约 1/4 宽度放标题提示词、封面提示词两个多行输入;右侧提供批次、店铺、状态筛选和任务列表。
- 细节:任务列表列为店铺、商品ID、旧标题、新标题、状态;店铺优先使用匹配账号名;状态将 `stage=collected` 显示为“待生成”、`stage=generated` 显示为“已生成”,并优先显示 failed/skipped/running。筛选只读 SQLite,不调用 AI、不写库。
- 测试:`tests/test_gui.py` 增加 MainWindow 挂载②、提示词多行输入、任务列表展示,以及按批次/店铺/状态筛选的覆盖。
- 文档:`docs/06-tasks.md` 将 T-302 标为 DONE;同步 `docs/api.md`、`docs/current-state.md`,下一个可领取任务更新为 T-302p。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(20 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(65 tests,skipped=1);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(37 tests,skipped=5,py -3 环境缺 openpyxl/PySide6/Pillow,相关测试按设计跳过)。
## 【2026-06-27】T-302p Tab② 提示词管理
- 状态:DONE
- 变更:新增 `app/prompts.py`,实现标题提示词 `title_prompt.txt` 读写、封面模板 `prompts/cover/*.txt` 列表/读取/保存/重命名/删除,以及 `{旧标题}`、`{新标题}`、`{商品id}`、`{店铺}` 变量替换。模板名做空值、路径分隔符、`..` 与 Windows 非法字符校验;列表读取不自动创建文件。
- GUI:`GenerateTab` 启动时回显标题提示词;封面提示词增加模板下拉、新建、保存、另存为、重命名、删除、插入 `{新标题}`、预览。预览使用选中任务渲染变量并弹窗展示;删除模板先二次确认。T-302p 仍不调用 AI、不写 SQLite,生成执行留给 T-303。
- 测试:新增 `tests/test_prompts.py`;`tests/test_gui.py` 覆盖标题回显/保存、封面模板另存/重命名/删除、插入标题与变量预览。
- 文档:`docs/06-tasks.md` 将 T-302p 标为 DONE;同步 `docs/api.md`、`docs/routes.md`、`docs/current-state.md`,下一个可领取任务更新为 T-303。
- 验证:`python -m unittest discover -s tests -p "test_prompts.py"` 通过(3 tests);`python -m unittest discover -s tests -p "test_gui.py"` 通过(21 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(69 tests);`py -3 -m compileall app main.py tests` 通过;`py -3 -m unittest discover -s tests` 通过(40 tests,skipped=4,py -3 环境缺 openpyxl/PySide6/Pillow,相关测试按设计跳过)。
## 【2026-06-27】T-303 Tab② 开始生成 + 停止 + 进度
- 状态:DONE
- 变更:`app/ai.py` 新增 `generate_batch()` 批量编排,按 `title_concurrency` 先并发生成标题,再按 `image_concurrency` 并发生成封面;封面成功后立即 `db.set_generated()`,标题/封面失败 `db.mark_failed(..., "generate", error)`,失败不阻塞其余任务;`should_stop()` 为真时取消未开始的 Future,被取消任务不记失败。
- GUI:`GenerateTab` 底部接入「开始生成」「停止」和进度标签;开始生成只处理当前筛选结果里 `stage=collected` 的任务;新增 `GenerateWorker` 通过 signal 回传进度、行刷新、完成/取消状态;任务表双击弹窗查看旧封面与新封面对照。
- 测试:`tests/test_ai.py` 覆盖 `generate_batch` 正常落库、单条失败不阻塞、停止前取消且任务保持 collected;`tests/test_gui.py` 覆盖生成控件、双击预览弹窗、`GenerateWorker` 调用 `ai.generate_batch()` 并发出 progress/row_updated。
- 文档:`docs/06-tasks.md` 将 T-303 标为 DONE;同步 `docs/api.md`、`docs/routes.md`、`docs/current-state.md`,下一个可领取任务更新为 T-401。
- 验证:`python -m unittest discover -s tests -p "test_ai.py"` 通过(6 tests);`python -m unittest discover -s tests -p "test_gui.py"` 通过(22 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(73 tests)。
- 注意:本轮仍为 mock AI 单测,未调用真实 AI 服务;真实生成需在 `config/ai_models.json` 配好可用 text/image 模型后做小样本实测。
## 【2026-06-27】T-205b 采集后关闭自动新建商品页 tab
- 状态:DONE
- 背景:真机采集时发现程序为不同商品新建编辑页 tab,采集标题和封面后只断开 CDP 连接,不会关闭浏览器页面,批量采集后 Chrome 会堆积多个商品页。
- 方案:区分“复用用户已有 tab”和“本轮程序自动新建 tab”。`CDP.close()` 仍只断开 WebSocket;新增 `close_tab(target_id)` 调 Chrome `/json/close/<targetId>` 关闭浏览器 target;`open_product()` 给 CDP 对象标记 `target_id/created_by_app/cdp_host`;`collect()` finally 中只关闭 `created_by_app=True` 的商品页,用户原本打开的 tab 不关闭。③ 更新流程暂不自动关页,失败时保留现场便于排查。
- 变更:更新 `app/cdp.py`、`app/editor.py`;同步 `docs/06-tasks.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/current-state.md`。
- 测试:`tests/test_editor_login.py` 增加 `open_product()` 新/旧 tab 标记、`collect()` 关闭自动新建 tab、不关闭复用 tab 的覆盖。
- 验证:`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(9 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(77 tests)。
## 【2026-06-27】审查 · T-301~T-303(含 T-302p + T-205b 采集关页)
- 状态:DONE(审查通过)
- 验证:本机(WSL)临时装 openpyxl+Pillow 后 `test_ai` 6/6、`test_prompts` 3/3 实跑通过,全套 53 tests OK(skipped=2 仅 PySide6 的 gui/workers);GUI 测试按源码+清单核对,以 Codex Windows 运行为准。
- 结论:T-301 ai.py(脱敏/重试/多 API 形态/两段并发/失败隔离)、T-302 Tab② 布局、T-302p prompts.py(防穿越校验+模板 CRUD+render_prompt 接入)、T-303 开始生成(先并发标题再并发封面、逐条 set_generated、停止取消未开始项、双击新旧封面对照)均按文档完成。
- 小问题(不阻验收):标题提示词不走 render_prompt(与设计自洽,建议 UI 注明“标题提示词不支持变量”);urllib 单一 timeout 取 max(connect,read);_looks_base64 启发式。
- 待办:gen_title/gen_cover 对真实 AI 仍 mock,未真连;进 Phase 4 前建议端到端真机冒烟(账号④登录→①真机采集出图→②真实模型各生成 1 标题+1 封面→看 images/<slug>/<id>_new.jpg 落盘与分辨率/质量)。
- 完整报告:[`docs/reviews/2026-06-27-T301-T303-acceptance.md`](docs/reviews/2026-06-27-T301-T303-acceptance.md)
## 【2026-06-27】T-401 Tab③ 更新列表 + 开始更新确认
- 状态:DONE
- 变更:`app/gui.py` 新增 `ApplyTaskTableModel` 与 `ApplyTab`,MainWindow 的③从空壳切换为更新页。③ 顶部提供批次、店铺、状态筛选,默认显示 `stage=generated/status=success|pending` 的已生成待更新任务;状态筛选支持失败、已更新、略过和全部。
- 细节:③任务表列为店铺、商品ID、新标题、新封面、阶段、结果;店铺优先显示匹配账号名。候选范围只包含已具备新标题/新封面、可进入更新阶段的任务;导入/采集/生成失败但没有新字段的任务不会进入③。
- 确认边界:点击「开始更新」只作用于当前筛选结果;弹窗展示批次、店铺、状态、任务数和提交线上风险。用户取消时不执行、不改库;用户确认后当前 T-401 只记录“已确认范围”,实际 `apply_task()` 串行执行留给 T-402。无常驻提交开关。
- 测试:`tests/test_gui.py` 覆盖 MainWindow 挂载③、③筛选列表、失败/已更新状态展示、开始更新确认弹窗,以及确认/取消均不调用 `editor.apply_task()`、不改 DB。
- 文档:`docs/06-tasks.md` 将 T-401 标为 DONE;同步 `docs/api.md`、`docs/routes.md`、`docs/current-state.md`,下一个可领取任务更新为 T-402。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(24 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(79 tests)。
## 【2026-06-27】T-402 Tab③ 确认后串行更新
- 状态:DONE
- 变更:`app/gui.py` 新增 `ApplyWorker`,③点击「开始更新」并确认后才创建后台 worker;worker 只处理当前筛选结果里 `stage=generated` 且已有新标题或新封面的可执行任务,串行调用 `editor.apply_task()`。
- 执行策略:更新前先做账号就绪预检;无账号、对应账号 Chrome 未启动或未登录时整体阻断并弹窗引导去④账号管理,不调用 `apply_task()`、不写失败状态。预检通过后逐条 `mark_running(..., "apply")`、执行更新、`set_applied()` 写库;成功推进 `stage=applied/status=success/committed=1`,失败保留原 stage 并写 `status=failed/last_error`,单条失败继续。
- GUI:③运行中禁用筛选/刷新/开始按钮,启用「停止」;停止使用 worker 协作式 `cancel()`,已开始单条跑到安全边界后结束。未匹配账号任务逐条 `mark_skipped()`,原因 `别名未匹配账号`。结果回写 Excel 和结束弹窗汇总留给 T-403。
- 测试:`tests/test_gui.py` 调整 T-401 确认测试为“取消不启动”和“确认启动 ApplyWorker”;新增 `ApplyWorker` 成功/失败/略过串行写库、Chrome 未启动预检阻断、③阻断弹窗引导④的覆盖。
- 文档:`docs/06-tasks.md` 将 T-402 标为 DONE;同步 `docs/api.md`、`docs/routes.md`、`docs/current-state.md`,下一个可领取任务更新为 T-403。
- 验证:`python -m unittest discover -s tests -p test_gui.py` 通过(28 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(83 tests)。
## 【2026-06-27】T-403 Tab③ 结果回写与结束汇总
- 状态:DONE
- 变更:`app/excel.py` 新增 `write_back_results()`,按 `source_file_abs/source_sheet/source_row` 把新标题、新封面图片路径、更新状态写回原 Excel;更新状态输出为“成功 / 失败:原因 / 略过:原因 / 待更新”。`write_back()` 仍保持旧标题/旧封面回写给①使用。
- GUI:`ApplyTab` 在 `ApplyWorker` 完成后自动启动 `WriteBackWorker(mode="results")` 回写结果;成功后弹窗汇总成功/失败/略过数量与 Excel 回写文件/行数;文件被占用时提示关闭原 Excel 后点击「回写结果到 Excel」手动重试,SQLite 更新结果不回滚。③的「回写结果到 Excel」按钮作为手动重试入口。
- Worker:`WriteBackWorker` 增加 `mode`,默认 `old` 兼容①旧字段回写,`results` 调用 `excel.write_back_results()`;支持单批次或批次列表。
- 测试:`tests/test_excel.py` 覆盖结果字段与三种更新状态写回;`tests/test_gui.py` 覆盖③自动结果回写、结束汇总弹窗、文件锁手动重试提示、`WriteBackWorker(mode="results")` 调用;模块契约加入 `write_back_results`。
- 文档:`docs/06-tasks.md` 将 T-403 标为 DONE;同步 `docs/api.md`、`docs/routes.md`、`docs/current-state.md`,下一个可领取任务更新为 T-501。
- 验证:`python -m unittest discover -s tests -p test_excel.py` 通过(8 tests);`python -m unittest discover -s tests -p test_gui.py` 通过(32 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(88 tests)。
## 【2026-06-27】审查 · T-401~T-403(Phase 4 更新 Shopee)
- 状态:DONE(代码层面审查通过)
- 验证:本机(WSL)装 openpyxl+Pillow 后 `test_excel` 8/8 实跑通过(含 write_back_results),全套 54 tests OK(skipped=2 仅 PySide6);UpdateTab/ApplyWorker 9 个 GUI 用例按源码+清单核对,以 Codex Windows 运行为准。
- 结论:T-401 三筛选+「开始更新」仅作用当前筛选+确认弹窗(批次/店铺/状态/任务数/线上风险,取消不执行);T-402 确认门控(未确认不调 apply)+串行 apply+单条失败继续+预检引导+逐条 set_applied;T-403 自动 write_back_results 回写新标题/新封面/更新状态(锁处理)+结束弹窗汇总。均按文档完成。失败任务保留 stage=generated 可重试,闭环正确。
- 小问题:③ 不自动关 tab,批量后堆积编辑页(有意决策,V2 可优化);_is_update_task(显示)⊇ _is_actionable_task(执行),建议加注释防误改。
- ⚠️ 关键边界:T-402 是唯一不可逆、真提交线上的步骤,但 apply_task→change_title/replace_cover/click_update 全程 mock,真机冒烟为 0。强烈建议真批量前做单条端到端冒烟(④登录→①采集→②生成→③只选1条确认提交→看线上改动+Excel 回写=成功),作为 Phase 4 正式验收门槛。
- 完整报告:[`docs/reviews/2026-06-27-T401-T403-acceptance.md`](docs/reviews/2026-06-27-T401-T403-acceptance.md)
## 【2026-06-27】复核 · Phase 4 冒烟策略延后
- 状态:DONE(文档决策更新)
- 背景:复核 Claude Code 的 Phase 4 验收报告后,确认报告总体正确,但当前不希望马上真实更新 Shopee 商品图片或标题。
- 决策:真实 Shopee 单条更新冒烟登记为 T-404,但依赖新增的 T-501c “Shopee 更新安全开关”完成后再执行。T-404 默认先只测标题更新,封面更新作为可选子项;未通过前不得扩大真实批量更新。
- 细化:取消语义明确为协作式取消,只阻止后续未开始任务,不中断当前正在执行的商品;Tab① 旧标题/旧封面采集已有真机冒烟,未真机闭环的是真实 AI 生成与 Tab③ 在线提交更新。
- 变更:更新 `docs/06-tasks.md`、`docs/current-state.md`、`docs/reviews/2026-06-27-T401-T403-acceptance.md`。
- 验证:仅文档更新,未运行测试。
## 【2026-06-27】T-501 Tab⑤ AI 模型管理 UI
- 状态:DONE
- 变更:`app/gui.py` 新增 `SettingsTab`,将⑤设置从空壳替换为 AI 模型 master-detail 管理界面。左侧为模型下拉、新增、删除;右侧为启用、服务商名、类别、api_type、模型ID、网址、密钥、连接超时、保存、测试连接。
- 安全:密钥字段使用 `QLineEdit.Password` 打码;模型清单仍写入 gitignore 的 `config/ai_models.json`;删除按钮在当前类别只剩一个模型时禁用,后端继续保证至少有 text/image 各一个启用模型。
- Worker:新增 `AIModelTestWorker`,通过后台线程调用 `appconfig.test_ai_model()`,避免在 GUI 主线程发网络请求。
- 测试:`tests/test_gui.py` 覆盖 MainWindow 挂载⑤、模型加载与密钥打码、新增/保存/删除、测试连接 worker、`AIModelTestWorker.execute()`。
- 文档:`docs/06-tasks.md` 将 T-501 标为 DONE;同步 `docs/routes.md`、`docs/api.md`、`docs/current-state.md`,下一个可领取任务更新为 T-501b。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(36 tests);`python -m unittest discover -s tests` 通过(92 tests)。
## 【2026-06-29】T-501b Tab⑤ 角色与生成参数
- 状态:DONE
- 变更:`app/gui.py` 扩展 `SettingsTab`,⑤设置页新增角色与生成参数、路径与端口配置区。标题大模型下拉只列 text 类模型,图片大模型下拉只列 image 类模型;支持标题/图片并发、失败重试、分辨率、jpg 质量、Chrome 路径、账号数据根目录、图片目录、DB 路径、默认调试端口、端口范围、CDP 就绪超时。
- 配置:点击「保存设置」写入 `config.json`;保留 `resolution_timeouts`,分辨率切换时只读展示对应返回超时;保存前校验端口范围和默认端口。
- 细节:`SettingsTab` 和 `MainWindow` 支持注入 `config_path` 便于测试;保存时不会把测试用的 `config_path`/`ai_models_path` 写入 `config.json`。
- 测试:`tests/test_gui.py` 覆盖角色下拉类别过滤、分辨率返回超时显示、生成参数/路径/端口保存到临时 `config.json`、非法端口范围拦截。
- 文档:`docs/06-tasks.md` 将 T-501b 标为 DONE;同步 `docs/routes.md`、`docs/api.md`、`docs/current-state.md`,下一个可领取任务更新为 T-501c。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(38 tests);`python -m unittest discover -s tests` 通过(94 tests)。
## 【2026-06-29】T-501c Tab⑤ Shopee 更新安全开关
- 状态:DONE
- 配置:`app/appconfig.py` 的 `config.json` 默认值新增 `shopee_update` 段:测试商品 ID、允许真实提交、允许更新封面、单次最大更新条数、成功后关闭本次新开编辑页。默认关闭真实提交和封面更新,单次最大更新 1 条。
- GUI:`SettingsTab` 新增 Shopee 更新安全表单并持久化;`ApplyTab.start_update()` 在弹确认前读取安全设置,拦截未开启真实提交、超过最大条数、包含非测试商品 ID、或包含新封面但未允许封面更新的任务。确认弹窗会展示当前安全设置。
- 执行:`ApplyWorker` 将 `close_success_tab` 传给 `editor.apply_task()`;`editor.apply_task()` 在成功提交、且商品页为本轮程序自动新建时,可按设置关闭该 tab。失败任务和复用的用户已有 tab 保留现场。
- 测试:`tests/test_gui.py` 覆盖安全设置默认值、保存到 `config.json`、⑤保存后③立即读取共享安全配置、③安全拦截、最大条数/测试商品 ID 校验、确认后 worker 接收 `close_success_tab`;`tests/test_editor_login.py` 覆盖成功提交后关闭本轮新开 tab、失败时不关闭。
- 文档:`docs/06-tasks.md` 将 T-501c 标为 DONE;同步 `docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/current-state.md`,下一个可领取任务更新为 T-404。
- 验证:`python -m unittest discover -s tests -p test_gui.py` 通过(42 tests);`python -m unittest discover -s tests -p test_editor_login.py` 通过(11 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(100 tests)。未执行真实 Shopee 提交,T-404 仍需在测试商品上手动冒烟。
## 【2026-06-29】T-502 满 9 张封面删除前备份策略
- 状态:文档决策更新,代码未实现。
- 决策:满 9 张封面需要删除第一张再上传时,不允许盲删线上图片;删除前必须确认该任务已有本地旧封面备份,即 `old_cover_path` 非空且文件存在。
- 失败语义:备份缺失时直接阻断该条封面更新,返回明确错误,要求先回到①采集旧封面或修复本地备份;不进入删除确认框、不删除线上第一张图。
- 实测边界:删除确认框选择器仍需在真实 Shopee 测试商品页面探查;真实保存线上变更仍需经过 ⑤ 安全开关和 ③ 二次确认。
- 变更:同步 `docs/02-requirements.md`、`docs/04-architecture.md`、`docs/05-coding-rules.md`、`docs/06-tasks.md`、`docs/api.md`、`docs/routes.md`、`docs/current-state.md`。
- 验证:仅文档更新,未运行测试。
## 【2026-06-29】T-502 满 9 张封面删除代码路径
- 状态:BLOCKED(代码路径和 mock 单测已完成;真实 Shopee 9 图商品删除确认框仍需实测)。
- 变更:`app/editor.py` 的 `replace_cover()` 支持 `old_cover_path`,满 9 张时先校验本地旧封面备份存在;缺失时返回 `OLD_COVER_BACKUP_MISSING`,不点击删除。备份存在时点击第一张图片的删除按钮,在可见 dialog/modal/popover 中点击删除/确认类按钮,确认图片数减少后再上传新图并拖到第一位。
- 执行链路:`apply_task()` 会把任务的 `old_cover_path` 传给 `replace_cover()`,确保③真实封面更新继承①采集备份保护。
- 测试:`tests/test_editor_login.py` 新增满 9 张备份缺失阻断、删除确认后上传并拖首位、删除按钮缺失报错、`apply_task()` 传递旧封面备份路径的覆盖。
- 文档:`docs/06-tasks.md` 将 T-502 标为 BLOCKED;同步 `docs/04-architecture.md`、`docs/api.md`、`docs/current-state.md`。
- 验证:`python -m unittest discover -s tests -p test_editor_login.py` 通过(15 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(104 tests)。未执行真实 Shopee 删除/提交。
## 【2026-06-29】T-502 真实 9 图商品不提交实测
- 状态:DONE
- 商品:`29671243750`,URL `https://seller.shopee.tw/portal/product/29671243750?pageEntry=product_list&ignore-html-cache=1`,账号别名 `papa`,CDP 端口 `9224`。
- 前置:本地旧封面备份存在:`images/papa_f722f20f/29671243750_old.jpg`;商品编辑页初始图片数为 9。
- 删除确认框实测:新开测试 tab,执行 `_delete_first_cover()`,结果 `ok=True`,图片数 9 → 8,测试 tab 已关闭,未点击「更新」。
- 满 9 张完整换封面实测:新开测试 tab,执行 `replace_cover(..., image=1_TY030.jpg, old_cover_path=备份路径)`,结果 `ok=True`,删除后上传新图,图片数回到 9,新图 `index=0` 且为第一位,测试 tab 已关闭。
- 安全边界:全程只操作新开的测试 tab,未点击「更新」保存线上;线上真实保存仍需走 ⑤ 安全开关和 ③ 二次确认。
- 文档:`docs/06-tasks.md` 将 T-502 标为 DONE;同步 `docs/04-architecture.md`、`docs/api.md`、`docs/current-state.md`。
## 【2026-06-29】T-503 敏感信息本地明文保存提示与日志脱敏
- 状态:DONE
- 变更:`app/gui.py` 在④账号管理保存/变更非空密码前弹出“本地明文保存提示”,在⑤设置保存/变更非空 API Key 前弹出同类提示;账号表格仍不展示密码,API Key 字段继续使用 `QLineEdit.Password` 打码。
- 脱敏:`app/appconfig.py` 新增 `mask_secret()`、`sanitize_for_log()`、`redact_secrets()`;`AIModelTestWorker.execute()` 返回 GUI 前对测试连接结果做结构化脱敏;`test_ai_model()` 的异常文本会用已知 API Key 做替换。
- 测试:`tests/test_appconfig.py` 覆盖结构化日志脱敏;`tests/test_gui.py` 覆盖保存 API Key 提示、账号密码保存提示、表格/状态不泄露明文、AI 测试 worker payload 脱敏。
- 文档:`docs/06-tasks.md` 将 T-503 标为 DONE;同步 `docs/02-requirements.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/05-coding-rules.md`、`docs/api.md`、`docs/routes.md`、`docs/current-state.md`。
- 验证:`python -m unittest discover -s tests -p test_appconfig.py` 通过(5 tests);`python -m unittest discover -s tests -p test_gui.py` 通过(44 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(107 tests)。
## 【2026-06-29】T-504 多账号并行 / dry-run / 运行日志
- 状态:DONE
- 配置:`app/appconfig.py` 的 `shopee_update` 新增 `dry_run`、`parallel_accounts`、`max_parallel_accounts`;⑤ 设置页可配置并持久化,默认 dry-run 关闭、多账号并行关闭。
- 数据:`app/db.py` 新增 `run_logs` / `run_log_events` schema 与创建、结束、逐条事件、查询函数;日志 payload 统一走脱敏工具。
- 执行:③ `ApplyWorker` 支持 dry-run 预览、默认串行真实更新、按账号并行真实更新;dry-run 不打开 Shopee、不调用 `editor.apply_task()`、不改任务状态、不回写 Excel,只写运行日志和汇总;真实更新前检查账号就绪和本轮账号调试端口冲突,不同账号可并行,同一账号内仍串行。
- UI:③ 增加运行日志视图,确认弹窗展示 dry-run 与多账号并行状态;dry-run 完成只弹预览汇总,不触发结果回写。端口冲突阻断时弹窗列出冲突端口并引导去④。
- 测试:`tests/test_db.py` 覆盖运行日志持久化;`tests/test_gui.py` 覆盖 dry-run 不变更任务、真实更新串行、按账号并行、端口冲突阻断、⑤设置读写与③共享配置。
- 文档:`docs/06-tasks.md` 将 T-504 标为 DONE;同步 `docs/00-ai-start-here.md`、`docs/02-requirements.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/05-coding-rules.md`、`docs/api.md`、`docs/routes.md`、`docs/current-state.md` 和 UI 草图。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(112 tests);未执行真实 Shopee 提交。
## 【2026-06-29】AI 模型 category 启动报错排查记录
- 问题:启动最新代码时报 “AI 模型 category 必须是 text 或 image”。
- 原因:本地 gitignored 的 `config/ai_models.json` 来自旧数据或手工维护,模型缺少 `category`,而当前 schema 要求每个模型必须是 `text` 或 `image`。
- 处理:已在本机只补齐模型 `category` 和空的 `enabled=true`,保留原有 url/model/API Key;验证 `appconfig.list_ai_models()` 可正常加载,且 `config/ai_models.json` 仍被 gitignore。
- 文档:新增 `docs/troubleshooting.md`,并在 `docs/README.md`、`docs/03-tech-stack.md`、`docs/current-state.md` 增加索引和说明。排查命令只输出 `name/category/api_type/enabled/has_api_key`,不输出 API Key。
## 【2026-06-29】设置页 AI 模型布局微调
- 状态:DONE
- 变更:`app/gui.py` 将⑤设置页的 AI 模型下拉/新增/删除从左侧分栏移到“模型详情”上方,设置页改为单列滚动布局;模型详情、角色与生成参数、路径与端口、Shopee 更新安全表单尽量两项一行,URL/API Key/Chrome 路径等长字段独占整行。
- 文档:同步 `docs/routes.md`、`docs/api.md`、`docs/current-state.md` 中的设置页布局描述,并更新 `docs/ui/tab5-settings.svg` UI 草图。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(48 tests)。
## 【2026-06-29】T-206 删除指定批次(设计登记)
- 状态:TODO(设计登记;硬删除语义仍待用户最终确认,排在 T-404 之后实现)
- 问题:①导入采集仅有批次筛选下拉,没有任何删除入口;`db` 有 `delete_account` 但无 `delete_batch`。导错文件 / 测试导入 / 处理完的历史批次都无法清理,批次下拉只增不减,长期必然劣化使用体验。
- 数据模型:`tasks.batch_id REFERENCES batches(id)`;每条 task 关联磁盘封面图片 `old_cover_path`(采集下载的旧封面)、`new_cover_path`(AI 生成的新封面),均为本工具产出,硬删后会成为孤儿文件。
- 方案(硬删除):
- `app/db.py` 新增 `delete_batch(batch_id)`:单事务内先 `DELETE FROM tasks WHERE batch_id=?` 再 `DELETE FROM batches WHERE id=?`;返回 `{"tasks": 删除任务数, "committed": 其中已上线数, "images": 关联封面绝对路径列表}`。
- `app/gui.py` ①导入采集页批次筛选旁加「删除批次」按钮:仅选中具体批次时可用,运行中随其他控件一起禁用。
- 二次确认弹窗(不可逆动作,符合安全红线):展示批次标签 + 任务数 + 已上线(`committed=1`)任务数,明确提示「删除本地记录不会回滚 Shopee 线上已提交的修改」;提供「同时删除已下载/生成的封面图片」勾选项,默认不勾。
- 确认后调用 `db.delete_batch`,按勾选 `os.remove` 清理 images 列表中的孤儿图片(容错,缺失/占用不报错只记日志),刷新①与③的批次下拉和任务列表。
- 安全:删除前显式弹窗确认;提示线上修改不可回滚;图片清理默认不开启需用户主动勾选。
- 测试计划:`tests/test_db.py` 覆盖 `delete_batch` 删任务+批次、返回计数/图片路径、外键不残留;`tests/test_gui.py` 覆盖未选具体批次时按钮禁用、确认后调用删除并刷新、取消不删除。
## 【2026-06-29】T-303b ②/③ 商品ID筛选
- 状态:DONE
- 文档:先同步 `docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`,明确 ② AI生成与 ③ 更新shopee 的商品ID输入框按包含匹配 `item_id`,清空表示全部;当前筛选结果约束「开始生成」和「开始更新」范围;③确认弹窗显示商品ID筛选条件。
- 实现:`app/gui.py` 在 `GenerateTab` 和 `ApplyTab` 增加商品ID输入框,刷新时与批次/店铺/状态共同过滤;运行中禁用该输入框,避免生成/更新过程中改变作用范围。
- 测试:`tests/test_gui.py` 覆盖 ② 商品ID筛选、③ 商品ID筛选,以及③确认弹窗显示商品ID。
- 任务顺序:T-206 删除批次仍为设计登记,依赖 T-404,避免抢在真实 Shopee 单条冒烟前执行。
- 验证:`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(48 tests);`python -m unittest discover -s tests` 通过(112 tests)。
## 【2026-06-29】文档补充 · 采集失败诊断日志方案
- 背景:用户本次导入 5 个商品 ID 采集旧标题/旧封面时有 1 条失败;当前实现会把该任务写成 `status=failed` 并保存 `last_error`,但不会写 ① 采集的持久运行日志,也没有本地 traceback,因此无法判断卡在打开商品页、页面就绪、读标题、读封面、下载图片、写库还是 Excel 回写。
- 方案:采用两层日志。SQLite `run_logs/run_log_events` 保存业务可读事件(run_type、task_id、alias、item_id、step、简短错误);本地 gitignore 的 `logs/` 保存脱敏 traceback、异常类型、步骤、耗时和必要上下文。
- 任务:新增 T-207「① 采集诊断日志(run_logs + 本地 log)」作为下一步可领取任务;新增 T-505「全流程诊断日志扩展」覆盖 ② AI生成、Excel 导入/回写、③ 更新shopee、④ Chrome/登录检测、⑤ AI 模型测试连接。
- 安全:日志不得记录 Cookie、密码、API Key、token;结构化 payload 过 `sanitize_for_log()`,自由文本异常按需 `redact_secrets()`。
- 文档:同步 `docs/04-architecture.md`、`docs/06-tasks.md`、`docs/api.md`、`docs/routes.md`、`docs/05-coding-rules.md`、`docs/current-state.md`。
- 验证:仅文档更新,未运行测试。
## 【2026-06-29】T-207 ①采集诊断日志(run_logs + 本地 log)
- 状态:DONE
- 文档:`docs/06-tasks.md` 将 T-207 标为 DONE;`docs/04-architecture.md` 更新为 T-207 已接入 ①采集、T-505 待扩展;`docs/current-state.md` 同步当前快照、目录清单、下一步和已知限制。
- 实现:新增 `app/diagnostics.py`,写入 gitignore 的 `logs/cmshopee.log`,支持大小滚动、异常类型、traceback、step、task_id/alias/item_id、耗时和 payload;结构化 payload 与自由文本统一脱敏。
- 采集链路:`app/editor.py` 为采集流程增加 `on_step` 回调,覆盖 `open_product`、`wait_ready`、`read_title`、`read_cover`、`download_cover`;`CollectWorker` 创建 `run_type=collect` 的 `run_logs`,逐任务写 `run_log_events`,失败时保留最后 step、简短错误并继续后续任务。
- UI:①导入采集页新增最近一次采集运行日志视图;采集完成自动回写 Excel 时记录 `excel_write_back` 开始/成功/失败事件。
- 安全:DB 运行日志、状态栏错误、本地诊断日志均不记录 Cookie、密码、API Key、token;自由文本中的 `token/password/cookie/authorization/api_key` 形式会写入前脱敏。
- 测试:`tests/test_gui.py` 覆盖采集成功/略过写 run log、采集失败写 DB 事件与本地诊断 log、token 脱敏、①日志视图加载最近一次 collect run log。
- 验证:`python -m py_compile app\diagnostics.py app\editor.py app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(50 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(114 tests);`git diff --check` 通过(仅 LF/CRLF 提示)。
- 下一步:按 `docs/current-state.md`,建议领取 T-404(真实 Shopee 单条更新冒烟)。
## 【2026-06-29】修复⑤设置测试连接 base URL 返回 HTTP 404
- 问题:本地文本模型 URL 配置为 OpenAI-compatible base URL(如 `https://api.vectorengine.ai/v1`)时,⑤「测试连接」原样 POST 到 `/v1`,服务端返回 HTTP 404;实际 AI 生成请求也存在同类风险。
- 修复:`app/appconfig.py` 新增 `model_request_url()`,当 `url` 是 `/v1`、`/v1beta`、`/api/v1`、`/api/v1beta` 这类 base URL 时,按 `api_type` 自动补 `/chat/completions` 或 `/images/edits`;完整 endpoint 和自定义路径保持不变。
- 接入:`appconfig.test_ai_model()` 与 `app/ai.py` 的真实 AI HTTP 调用统一使用解析后的 endpoint。
- 文档:更新 `docs/api.md`、`docs/routes.md`、`docs/troubleshooting.md`、`docs/current-state.md`,说明⑤网址可填完整 endpoint 或 OpenAI-compatible base URL,并记录 HTTP 404 排查方法。
- 测试:`tests/test_appconfig.py` 覆盖 base URL/full endpoint/custom path 解析和测试连接请求 URL;`tests/test_ai.py` 覆盖标题生成使用 base URL 自动补 endpoint。
- 验证:`python -m unittest discover -s tests -p test_appconfig.py` 通过(7 tests);`python -m unittest discover -s tests -p test_ai.py` 通过(7 tests);`python -m compileall app main.py tests` 通过;`python -m unittest discover -s tests` 通过(117 tests);`git diff --check` 通过(仅 LF/CRLF 提示)。
## 【2026-06-29】② AI生成图片失败诊断日志补丁
- 背景:用户实测 ② AI生成已能按商品 ID 和提示词生成标题,但生成图片失败时页面与本地都找不到可定位原因的日志。
- 代码:`app/ai.py` 为标题/封面生成增加 `on_step` 与 `on_event/on_error` 回调,覆盖模型加载、请求构建、请求发送、响应解析、图片保存、写库等步骤;失败错误进入任务 `last_error` 前先脱敏。
- UI/日志:`GenerateWorker` 创建 `run_type=generate` 的 `run_logs/run_log_events`,② 页面新增「AI生成运行日志」;图片生成异常同时写本地 `logs/cmshopee.log` 脱敏 traceback。
- 测试:新增覆盖 GenerateWorker 图片生成失败时 DB 运行日志、本地诊断日志和 token 脱敏,以及 ② 页面加载最近一次 generate 日志。
## 【2026-06-29】T-404 冒烟发现 · Shopee 更新二次确认框
- 现象:用户用测试商品走完整流程,③ 更新shopee 已替换标题和封面,并自动点击页面主「更新」按钮;随后 Shopee 弹出站点侧确认框 `確定您要更新商品嗎?`,底部按钮为 `立即優化` 和主按钮 `更新`,当前代码没有点击最终确认,因此未真正提交。
- 结论:T-404 不能标 DONE;`click_update()` 的提交成功定义需要扩展为“页面主更新按钮已点击,且 Shopee 站点侧确认框未出现或已点击确认框内主按钮「更新」”。
- 方案:基于用户提供 HTML,检测可见 `.eds-modal__box`,标题匹配 `確定您要更新商品嗎?`/简繁变体;只在该 modal footer 内点击 `button.eds-button--primary` 且文案为 `更新` 的按钮;明确不点击 `立即優化`。弹窗出现但未确认成功时,该任务返回失败、保留现场、不得写 `committed=1`。
- 文档:同步 `docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/06-tasks.md`、`docs/current-state.md`。本轮只更新文档,未改代码。
## 【2026-06-29】T-404 补丁 · 点击 Shopee 更新确认框主按钮
- 代码:`app/editor.py` 新增 `JS_FIND_UPDATE_CONFIRM` / `JS_CLICK_UPDATE_CONFIRM`,`click_update(cdp, confirm_timeout=3)` 点击页面主「更新」后等待 Shopee 站点侧确认框;若可见 `.eds-modal__box` 标题匹配 `確定/确定 + 更新商品`,只点击同一 modal 内文案精确为 `更新` 的按钮,优先 `eds-button--primary`,不点击 `立即優化`。
- 失败语义:确认框出现但未点到主按钮时返回 `clicked=False` 和 `NO_UPDATE_CONFIRM_BUTTON` / `UPDATE_CONFIRM_NOT_CLICKED`,`apply_task()` 不写 committed 成功,失败商品保留现场。
- 测试:`tests/test_editor_login.py` 增加 Shopee 更新确认框成功点击、确认框未确认失败、无确认框保持原成功语义的 mock 覆盖。
- 文档:同步 `docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`。T-404 仍为 TODO,下一步需要用户用测试商品复跑真实提交验收。
## 【2026-06-29】设计登记 · ②/③ 选中记录重置
- 背景:用户真实联调时,② AI生成和③ 更新shopee列表没有重置选中记录功能;商品一旦生成/上传/提交成功,就不方便用同一商品重复测试图片上传和 Shopee 提交流程。
- 决策:新增 T-404a「②/③ 选中记录重置(联调重试辅助)」,排在 T-404 真实单条冒烟前。先做单选记录,不做批量重置,避免误操作放大线上风险。
- ② 方案:增加「重置生成结果」。确认后只改本地 DB,清空 `new_title/new_cover_path/last_error`,退回 `stage=collected/status=success`,默认不删除本地新封面文件;若记录已提交过线上,确认框必须提示本地重置不回滚 Shopee。
- ③ 方案:增加「重置更新状态」。确认后保留 `new_title/new_cover_path`,本地退回 `stage=generated/status=pending`,用于重复测试上传/提交;若 `committed=1`,必须醒目提示线上已提交过、重复更新会再次提交,并保留 committed 历史事实/运行日志。
- 日志与边界:两类重置都写 `run_type=reset` 运行日志;运行中禁用;不触碰 Shopee,不自动回写 Excel,后续由用户按需手动回写结果。
- 文档:同步 `docs/06-tasks.md`、`docs/routes.md`、`docs/api.md`、`docs/04-architecture.md`、`docs/current-state.md`。本轮只更新文档,未改代码。
## 【2026-06-30】T-404a 完成 · ②/③ 选中记录重置
- 代码:`app/db.py` 新增 `get_task()`、`reset_generated()`、`reset_apply_status()`;`app/gui.py` 在② AI生成增加「重置生成结果」,在③ 更新shopee增加「重置更新状态」。两者仅作用当前选中单条,运行中禁用,确认后只改本地 SQLite,不触碰 Shopee,不自动回写 Excel。
- 行为:② 清空 `new_title/new_cover_path/last_error` 并退回 `stage=collected/status=success`,默认不删除本地新封面文件;③ 保留 `new_title/new_cover_path`,退回 `stage=generated/status=pending`。已 `committed=1` 的记录会在确认框提示线上已提交过、本地重置不回滚 Shopee、重复更新会再次提交。
- 日志:两类重置都创建 `run_type=reset` 的 `run_logs/run_log_events`,记录 action、task_id、alias、item_id。
- 测试:新增 `tests/test_db.py` reset 生命周期覆盖;新增 `tests/test_gui.py` 覆盖②重置生成结果、③重置更新状态、确认框提示和 reset 运行日志。已运行 `python -m py_compile app/db.py app/gui.py tests/test_db.py tests/test_gui.py`、`python -m unittest discover -s tests -p "test_db.py"`、`python -m unittest discover -s tests -p "test_gui.py"`。
- 文档:`docs/06-tasks.md` 将 T-404a 标为 DONE;同步 `docs/current-state.md`、`docs/api.md`、`docs/04-architecture.md`。下一个任务仍是 T-404 真实 Shopee 单条更新冒烟,必须用测试商品复跑站点侧确认框主按钮「更新」路径后才能标 DONE。
## 【2026-06-30】T-404 补丁 · 兼容 Shopee `.eds-modal__content` 更新确认框
- 现象:用户实跑最新代码后仍停在 Shopee 站点侧确认框,真实 DOM 根节点为 `.eds-modal__content`,标题 `確定您要更新商品嗎?`,footer 按钮为 `立即優化` 和主按钮 `更新`。
- 原因:确认框检测候选 root 过宽,可能把 `.eds-modal__title` 等子节点当成最后一个候选,导致点击逻辑在没有 footer 按钮的 root 中查找「更新」失败。
- 代码:`app/editor.py` 的 `JS_FIND_UPDATE_CONFIRM` / `JS_CLICK_UPDATE_CONFIRM` 改为优先查找 `.eds-modal__content,.eds-modal__box,[role=dialog]`,并要求候选 root 标题匹配更新确认框且包含可点击「更新」按钮;按钮查找优先限定在 `.eds-modal__footer` 内,仍只点击文案精确为「更新」的主按钮,不点击「立即優化」。
- 测试:`tests/test_editor_login.py` 增加 `.eds-modal__content` 结构覆盖,防止回退到只支持 `.eds-modal__box`。T-404 仍需用户用测试商品复跑真实提交确认后才能标 DONE。
## 【2026-06-30】T-404 补丁 · 新封面上传状态诊断
- 现象:用户实跑最新代码时,新封面上传后页面一直转圈,程序等待一段时间后提示更新失败。
- 现状确认:旧 `replace_cover()` 已有上传成功检测,但只判断“图片列表出现新 `susercontent` CDN 地址”;若页面一直 loading/blob 预览或 Shopee 返回页面错误,最终只给 `UPLOAD_TIMEOUT`,无法定位是仍在上传、格式/大小错误还是裁剪弹窗。
- 代码:`app/editor.py` 新增 `JS_UPLOAD_STATE` / `_upload_state()`,上传等待期间采集图片管理器状态:当前 src、blob 数、CDN 数、loading/progress 数、错误文案、裁剪弹窗和 toast;默认上传等待从 90 秒调到 180 秒。
- 失败语义:`replace_cover()` 现在区分 `UPLOAD_STILL_PROCESSING`(仍在转圈或 blob 预览)、`UPLOAD_PAGE_ERROR`(页面/Toast 出现失败错误)、`UPLOAD_CROP_REQUIRED`(出现裁剪弹窗)、`UPLOAD_TIMEOUT`(没有任何新图/CDN/忙碌状态);失败结果包含 `upload_state`、`count_after`、`blob_seen`、`file_size`。`apply_task()` 会把封面失败转换成更可读的中文错误。
- 测试:`tests/test_editor_login.py` 新增上传仍在处理中、页面上传错误、apply_task 封面失败中文错误的覆盖。T-404 仍需用户用测试商品复跑真实上传和提交路径。
## 【2026-06-30】T-404 补丁 · 删除旧封面后等待图片管理器稳定
- 现象:用户实跑最新代码后,新封面上传后仍停在转圈并最终提示更新失败;怀疑满 9 张时删除旧图后立刻上传,Shopee 图片管理组件尚未完成重排。
- 分析:判断方向成立。旧流程在 `_delete_first_cover()` 里只等待图片数量减少,随后立刻重新取上传 input 并 `DOM.setFileInputFiles`;但数量减少不等于 Vue 组件已重渲染完成、上传入口已恢复、loading/blob 已消失。
- 代码:`JS_UPLOAD_STATE` 增加上传 input 存在/禁用状态;`replace_cover()` 上传前先等图片管理器稳定,初始图片稳定后再操作,满 9 张删除确认后等待删除后的数量、无 busy/blob、上传 input 恢复并短暂稳定,再重新获取 input 上传;触发文件选择后等待 2 秒再轮询新图 CDN。新增 `IMAGE_MANAGER_BUSY`、`IMAGE_COUNT_NOT_READY`、`UPLOAD_INPUT_NOT_READY` 等失败语义。
- 测试:`tests/test_editor_login.py` 增加“删除后 busy 两轮,必须等稳定后才 setFileInputFiles”的覆盖;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(23 tests),`python -m compileall app main.py` 通过,`python -m unittest discover -s tests` 通过(130 tests),`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
- 边界:本轮仍未直接操作真实 Shopee 测试商品,T-404 仍为 TODO,需要用户复跑单条真实上传与提交路径后才能标 DONE。
## 【2026-06-30】T-404 补丁 · 记录更新成功后跳回商品列表页
- 现象:用户复跑最新代码后,Shopee 站点侧确认框里的主按钮「更新」已成功点击,提交成功后当前 tab 跳回 `https://seller.shopee.tw/portal/product/list/all?operationSortBy=modified_time`,但浏览器 tab 没有关闭。
- 分析:代码已有可选关闭逻辑:只有 ⑤ 开启 `close_success_tab` 且商品页是 `open_product()` 本轮自动新建 tab,成功提交后才调用 `close_tab()`;默认不关闭,复用用户已有 tab 也不关闭。旧代码没有把“跳回商品列表页”作为提交后观测结果记录。
- 代码:`click_update()` 新增提交后状态探测,点击确认框主按钮后等待并记录 `post_update.url`、`redirected_to_list`、toast 结果;若出现失败 toast,则本条视为未提交失败;如果没有可观测跳转/成功 toast,仍沿用点击确认成功的兼容语义。
- 测试:`tests/test_editor_login.py` 增加确认后跳回商品列表页的 mock 覆盖;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(24 tests),`python -m compileall app main.py` 通过,`python -m unittest discover -s tests` 通过(131 tests),`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
- 边界:是否关闭 tab 仍由 ⑤“成功后关闭本次新开编辑页”和 `created_by_app` 决定;跳到商品列表页本身不会强制关闭用户原本打开的 tab。
## 【2026-06-30】T-404 回归分析 · 满 9 张删除前误等上传入口
- 现象:用户复跑最新代码后,商品详情页加载完成,但没有删除原商品图片第一张图,也没有后续上传和更新确认。
- 原因:上次为解决“删除后立刻上传导致转圈”新增了 `_wait_image_manager_stable()`,但在 `replace_cover()` 开头、删除旧图之前就要求上传 input 存在且未禁用。满 9 张时 Shopee 可能因已满格隐藏或禁用上传入口,所以前置等待返回 `UPLOAD_INPUT_NOT_READY`,流程在删除前中断。
- 正确顺序:满 9 张时,删除前只等待现有图片列表稳定(无 loading/blob、src 稳定),不检查上传 input;完成第一张删除并确认图片数量减少后,再等待上传 input 恢复可用并短暂稳定,然后上传新图。
- 测试缺口:mock 中满 9 张场景默认 `upload_input_ready=True`,没有覆盖真实页面“满格时上传入口不可用”的状态,导致回归未被单测发现。
- 修复:`_wait_image_manager_stable()` 增加 `require_upload_input` 参数;`replace_cover()` 初始稳定等待传 `False`,满 9 张先删除第一张;删除后和未满 9 张上传前再传 `True` 等上传入口恢复。
- 测试:`tests/test_editor_login.py` 新增 `test_replace_cover_full_slots_deletes_before_upload_input_is_ready`,模拟满 9 张时上传入口初始不可用、删除后恢复;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(25 tests),`python -m compileall app main.py` 通过,`python -m unittest discover -s tests` 通过(132 tests),`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
- 下一步:用户用测试商品复跑真实 T-404 封面上传和更新确认链路。
## 【2026-06-30】T-404 回归分析 · 物流校验被误判为封面上传失败
- 现象:用户复跑最新代码后,程序能删除第一张图片,但一直没有上传新图片,最后报 `新封面上传失败:此物流選項不支援較長備貨商品`。
- 原因:`JS_UPLOAD_STATE` 在封面上传阶段扫描了全局 toast/message/notice,并把包含 `不支援` 的文本都归入 `error_toasts`。`此物流選項不支援較長備貨商品` 是物流/备货页面级校验,不是图片上传错误;当前代码在删除后等待上传入口恢复时看到该全局错误,提前返回 `UPLOAD_PAGE_ERROR`,因此没有继续执行 `DOM.setFileInputFiles`。
- 正确边界:封面上传阶段只应把图片管理器内错误,或图片/文件/上传相关的全局 toast,当作封面上传失败。物流、备货、价格、库存等页面级校验应保留给点击「更新」提交阶段处理,不能阻断上传新图。
- 修复:`JS_UPLOAD_STATE` 新增图片/文件/上传相关 `uploadRe` 过滤;全局页面错误仍记录在 `page_error_toasts`,但只有匹配图片上传语义的 toast 才进入 `error_toasts` 并阻断封面上传。`JS_POST_UPDATE_STATE` 同步把 `不支援/不支持/無法/无法` 识别为提交阶段错误,避免真正点击「更新」后误判成功。
- 测试:`tests/test_editor_login.py` 新增 `test_replace_cover_ignores_unrelated_logistics_page_error_toast` 和 `test_click_update_reports_logistics_validation_after_confirm`;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(27 tests),`python -m compileall app main.py` 通过,`python -m unittest discover -s tests` 通过(134 tests),`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
- 下一步:用户复跑真实 T-404 链路。
## 【2026-06-30】T-404 回归分析 · 成功跳转列表页后 GUI 误报 POST_UPDATE_ERROR
- 现象:用户复跑最新代码后,浏览器上标题和封面都已成功更新,点击 Shopee 确认框主按钮「更新」后跳转到 `https://seller.shopee.tw/portal/product/list/all?operationSortBy=modified_time`,但 GUI 仍提示 `任务 1 商品 28431952912 更新失败:POST_UPDATE_ERROR`。
- 原因:`click_update()` 的提交后观测 `_wait_post_update()` 当前先检查 `error_toasts`,再检查 `redirected_to_list`。若页面上存在残留或短暂的物流/备货 error toast,即使 Shopee 已接受提交并跳转到商品列表页,也会先返回 `POST_UPDATE_ERROR`,导致 GUI 把真实成功误判为失败。
- 正确判断优先级:跳转到 `/portal/product/list/` 是提交成功的强信号,必须优先于残留/短暂 error toast;只有在未跳转商品列表、无成功 toast,且错误 toast 持续存在到观测窗口结束时,才应判 `POST_UPDATE_ERROR`。
- 解决方案:调整 `_wait_post_update()` 顺序与策略:每轮先判断 `redirected_to_list` 或成功 toast;错误 toast 只记录为诊断,延迟到未见成功信号且持续存在时再判失败。GUI 错误显示应尽量带出 `post_update.error_toasts` 的具体文案,避免只显示 `POST_UPDATE_ERROR`。
- 修复:`_wait_post_update()` 改为先判断 `redirected_to_list` 或成功 toast;错误 toast 只记录为 `last_error_state`,观测窗口结束仍没有成功信号时才返回 `POST_UPDATE_ERROR`。
- 测试:`tests/test_editor_login.py` 新增 `test_click_update_treats_list_redirect_as_success_even_with_stale_error_toast`,保留 `test_click_update_reports_logistics_validation_after_confirm` 覆盖未跳转时错误仍失败;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(28 tests),`python -m compileall app main.py` 通过,`python -m unittest discover -s tests` 通过(135 tests),`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
- 下一步:用户复跑真实 T-404 链路,确认 GUI 与浏览器成功状态一致。
## 【2026-06-30】文档补充 · ③ 更新前账号 Chrome 就绪预检
- 背景:讨论 ③「开始更新」后如果本轮需要的账号 Chrome 未打开,是否应由程序自动打开。产品判断为:真实更新是线上高风险动作,不应静默启动多个账号浏览器并继续提交。
- 决策:③ 点击「开始更新」并通过安全开关/确认后,真实提交前必须检查当前筛选结果涉及的账号 Chrome/CDP/登录态;若任一账号 Chrome 未启动、CDP 端口不可访问、未登录或端口冲突,弹窗列出账号和原因,整体中止本轮更新,不调用 `editor.apply_task()`,不写失败状态,不自动打开 Chrome。
- ④ 边界:④「启动登录」只打开对应账号浏览器供人工登录;④「检测登录」与①/③预检只验证当前环境是否可用,不自动登录、不填密码。
- 文档:同步 `docs/02-requirements.md`、`docs/routes.md`、`docs/api.md`、`docs/04-architecture.md`、`docs/06-tasks.md`、`docs/current-state.md`。本轮只更新文档,未改代码。
## 【2026-06-30】代码补齐 · ③ 账号未就绪时明确中止更新
- 代码:`app/gui.py` 的 ③ 更新账号未就绪弹窗新增明确文案:本轮更新已中止,不会自动打开账号 Chrome,也不会提交任何商品;① 采集账号未就绪文案单独保持为“本轮采集已中止”,避免把更新语义带到采集模块。
- 测试:`tests/test_gui.py` 覆盖 ③ 账号 Chrome 未启动时 `ApplyWorker` 不调用 `accounts.launch_for_login()`、不调用 `editor.apply_task()`、不写任务失败状态;覆盖弹窗包含“本轮更新已中止 / 不会自动打开账号 Chrome / 不会提交任何商品”。同步修正①采集阻断文案断言。
- 验证:`python -m py_compile app/gui.py tests/test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(54 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(135 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
## 【2026-06-30】文档补充 · 正式使用批量更新与设置页布局
- 背景:单条真实更新已人工测试通过,正式使用需要支持当前筛选结果无论多少条都能更新到 Shopee,同时降低误筛选风险,并整理⑤设置页视觉密度。
- 决策:保留预览能力但不再对用户显示 `dry-run` 技术词,③ 增加「预览本轮更新」按钮;「开始更新」按每批最大更新条数自动分批,确认弹窗显示总数/每批/预计批次;点击停止为协作式停止,当前商品跑到安全边界后不再开始新商品或下一批。
- 设置页:⑤ 内容区居中并加大左右留白到当前约 250% 的视觉尺度;模型详情、角色与生成参数、路径与端口、Shopee 更新安全均改为响应式三列布局,短字段一格,URL/API Key/路径等长字段跨列;测试商品 ID 后续移入高级/调试区或弱化展示。
- 文档:同步 `docs/02-requirements.md`、`docs/routes.md`、`docs/api.md`、`docs/04-architecture.md`、`docs/06-tasks.md`、`docs/current-state.md`。本轮只更新文档,未改代码。
## 【2026-06-30】代码补齐 · T-506 正式使用批量更新体验
- 代码:③ 更新shopee新增「预览本轮更新」按钮,预览不再依赖⑤里的 dry-run 开关;正式「开始更新」固定走真实更新安全开关和确认弹窗,弹窗展示任务数、每批最大更新条数和预计批次。
- 分批:`ApplyWorker` 新增 `batch_size`,真实更新按当前筛选全部可更新记录切批执行;每批写运行日志,summary/progress/run_log options 记录 `batch_size` 和 `batch_count`;点击停止为协作式停止,不再开始下一条或下一批。
- 设置页:⑤ 隐藏用户可见 dry-run 开关,保存设置时将 `dry_run` 固定为 `False`;模型详情、角色与生成参数、路径与端口、Shopee 更新安全改为三列布局,内容区居中并加大左右留白。
- 测试:更新 `tests/test_gui.py` 覆盖预览按钮、不再按总数阻断、Worker 分批日志、设置页 dry-run 隐藏与保存为 False。
- 文档:`docs/00-ai-start-here.md`、`docs/04-architecture.md`、`docs/routes.md` 同步新语义;`docs/06-tasks.md` 将 T-506 标记 DONE,`docs/current-state.md` 更新当前实现快照。
- 验证:`python -m py_compile app/gui.py tests/test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(54 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(135 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
## 【2026-06-30】设置页微调 · 左右留白缩短为约 40%
- 背景:用户反馈 ⑤ 设置模块两边空白需要缩短为当前约 40%。
- 代码:`app/gui.py` 中 SettingsTab 外层 `layout.setContentsMargins(45, 18, 45, 18)` 调整为 `18, 18, 18, 18`,中间内容区 `panel_layout.setContentsMargins(32, 18, 32, 18)` 调整为 `13, 18, 13, 18`;保留居中内容区和三列表单,当前 `panel.setMaximumWidth(1800)` 用于减少宽屏下的左右空白。
- 文档:同步 `docs/02-requirements.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`,将 250% 留白描述改为约 40% / 适度留白。
## 【2026-06-30】设置页微调 · 保存成功提示框
- 背景:⑤ 设置点击「保存设置」成功后原来只写状态栏,用户不容易感知保存结果。
- 文档:先同步 `docs/02-requirements.md`、`docs/routes.md`、`docs/api.md`、`docs/current-state.md`,明确保存成功后保留状态栏提示并弹轻量提示框。
- 代码:`SettingsTab.save_app_settings()` 在保存、替换配置、回填表单并设置状态栏后,调用 `QMessageBox.information(self, "保存设置", "设置已保存")`。
- 测试:更新 `tests/test_gui.py` 的设置保存用例,mock 成功提示框并断言标题/文案,避免模态框阻塞测试。
## 【2026-06-30】设置页微调 · 最大并行账号数移到多账号并行后
- 文档:先同步 `docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`,明确 Shopee 更新安全三列布局中「最大并行账号数」必须位于「多账号并行更新」后面同一行。
- 代码:`SettingsTab` 的 Shopee 更新安全字段顺序调整为第二行「允许更新封面 / 多账号并行更新 / 最大并行账号数」,「成功后关闭本次新开编辑页」移动到下一行;同时保留 `self.shopee_update_form_layout` 供测试定位。
- 测试:`tests/test_gui.py` 新增布局位置断言,确认 `max_parallel_accounts_spin` 与 `parallel_accounts_checkbox` 同行且在其后。
## 【2026-06-30】设置页微调 · 并行账号数与并行开关合并同一组件
- 背景:用户确认「最大并行账号数」不是只放到同一网格行,而是必须紧跟「多账号并行更新」后面,不能显示到下面。
- 文档:先同步 `docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`,明确「多账号并行更新」与「最大并行账号数」合并为同一个横向组件,最大并行账号数紧跟其后且不换行。
- 代码:`SettingsTab` 新增 `parallel_accounts_group`,内部用 `QHBoxLayout` 顺序放置 `parallel_accounts_checkbox`、`max_parallel_accounts_label`、`max_parallel_accounts_spin`;Shopee 更新安全表单支持字段跨 2 个组件位,并把该组合组件放在「允许更新封面」后面。
- 测试:更新 `tests/test_gui.py`,断言组合组件与「允许更新封面」同一行,跨 2 个组件位,且内部 widget 顺序为多账号并行开关、最大并行账号数标签、输入框。
## 【2026-06-30】文档补充 · T-507 移除普通流程测试商品 ID 限制
- 背景:正式批量更新时,③「开始更新」当前仍因任务包含非测试商品 ID 阻断,只允许更新 ⑤ 配置的测试商品 ID。这适合开发测试阶段,但不适合正式批量更新真实商品。
- 决策:新增 T-507。普通正式更新不再以 `test_item_id` 阻断当前筛选结果;当前筛选结果可包含多个真实商品 ID。`test_item_id` 仅保留为历史/调试兼容字段,后续如需要调试模式再单独启用。
- 保留安全闸:允许真实提交线上商品、允许更新封面、每批最大更新条数、开始更新二次确认、账号 Chrome/CDP/登录态预检、多账号并行上限、运行日志和 Excel 回写。
- 文档:同步 `docs/02-requirements.md`、`docs/routes.md`、`docs/api.md`、`docs/04-architecture.md`、`docs/06-tasks.md`、`docs/current-state.md`。本轮只更新文档,代码尚未改;下一步代码更新前仍会因非测试商品 ID 阻断。
## 【2026-06-30】代码更新 · T-507 移除普通流程测试商品 ID 限制
- 代码:`ApplyTab._update_safety_error()` 移除 `test_item_id` 非空与商品 ID 比对阻断;普通正式更新只继续检查「允许真实提交线上商品」和「允许更新封面」。
- 代码:③「开始更新」确认弹窗不再显示测试商品 ID;当前筛选结果可包含多个真实商品 ID,仍按每批最大更新条数分批执行。
- 代码:⑤ 设置页隐藏普通用户可见的测试商品 ID 输入入口,但保留 `test_item_id` 字段加载/保存兼容旧配置和后续调试模式。
- 测试:更新 `tests/test_gui.py`,覆盖确认弹窗不含测试商品 ID、非测试商品 ID/空测试商品 ID 不再阻断、设置页测试商品 ID 入口隐藏且配置兼容。
- 验证:`python -m py_compile app/gui.py tests/test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(54 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(135 tests)。
## 【2026-06-30】T-404 补丁 · 上传前点击上传块模拟人工路径
- 现象:商品 26887160467 更新封面时,代码删除第一张图片后直接 `DOM.setFileInputFiles`,Shopee 图片组件长期转圈;同一张约 220KB 图片在浏览器手动上传成功。
- 分析:文件大小和格式不是主因;差异在于人工流程会先点击「新增图片」上传块,Shopee 前端可能在点击时初始化上传队列,而旧代码直接给隐藏 input 注入文件,可能导致队列未完整初始化。
- 文档:先同步 `docs/04-architecture.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`,将封面上传策略改为“点击上传块 → 短暂等待 → 重新获取 input → `DOM.setFileInputFiles` → 派发 input/change”。
- 代码:`app/editor.py` 新增 `JS_CLICK_UPLOAD_TILE` 和 `_click_upload_tile()`;`replace_cover()` 在上传前先点击 `.shopee-image-manager__upload`,等待 1 秒后重新获取 `input[type=file]`,再注入本地图片;失败时返回 `UPLOAD_TILE_NOT_READY` 并带 `upload_click` 诊断。
- 测试:`tests/test_editor_login.py` 扩展 `FakeCoverCDP`,新增上传块点击成功和上传块不可点击的回归测试,确认执行 `DOM.setFileInputFiles` 时上传块已先被点击。
- 验证:`python -m py_compile app/editor.py tests/test_editor_login.py` 通过;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(30 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(137 tests)。T-404 仍需真实商品复跑确认。
## 【2026-06-30】T-404 补丁 · 返回商品列表后延迟关闭 tab
- 背景:真实流程为点击页面主「更新」后出现 Shopee 确认弹窗,点击弹窗主按钮「更新」后跳回我的商品列表页;如果此时立即关闭本轮新开的 tab,可能不给页面跳转与状态稳定留时间。
- 文档:先同步 `docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/06-tasks.md`、`docs/current-state.md`,明确 `close_success_tab=true` 且成功提交、tab 为本轮自动新开时,确认后跳回商品列表页场景关闭前等待 2 秒。
- 代码:`app/editor.py` 的 `_close_applied_product()` 在真正调用 `close_tab(target_id, host=...)` 前执行 `time.sleep(2)`;失败任务、复用的用户已有 tab、未开启 `close_success_tab` 的任务不等待关闭。
- 测试:更新 `tests/test_editor_login.py`,成功提交并关闭自动新开 tab 的路径断言 `time.sleep(2)`;失败提交路径断言不关闭且不等待。
- 验证:`python -m py_compile app/editor.py tests/test_editor_login.py` 通过;`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(30 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(137 tests)。T-404 仍需真实商品复跑确认。
## 【2026-06-30】T-404/T-502 补丁 · 8张图也先删第一张 + 图片路径按批次细分
- 第1点文档:同步 `docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/05-coding-rules.md`、`docs/06-tasks.md`、`docs/current-state.md`,明确更新封面统一按“替换第一张”执行,不再只在满 9 张时删除;`有1張重複的圖片` / `重複` / `重复` / `duplicate` 作为封面上传错误立即失败。
- 第1点代码:`app/editor.py` 的 `replace_cover()` 改为只要有新封面就先校验 `old_cover_path` 本地备份,再删除当前第一张、等待稳定、点击上传块并上传新图;上传循环优先处理错误 toast,重复图返回 `UPLOAD_DUPLICATE_IMAGE`。`tests/test_editor_login.py` 覆盖 8 张图也删除第一张、重复图片 toast 和旧备份缺失保护。
- 第2点文档:同步本地图片目录规则,新采集/新生成图片改为 `images/<batch_id>/<slug>/<task_id>_<item_id>_old/new.jpg`;DB 已存旧路径继续作为权威路径,不迁移、不推断。
- 第2点代码:新增 `app/image_paths.py` 统一图片路径 helper;`CollectWorker` 的旧封面目标路径、`ai.generate_batch()` 的新封面目标路径、`editor.collect()` 兜底旧封面路径均接入该规则。历史 `old_cover_path/new_cover_path` 仍按 DB 记录读取。
- 测试:新增 `tests/test_image_paths.py`;更新 `tests/test_ai.py`、`tests/test_gui.py`、`tests/test_editor_login.py`。验证通过:`python -m py_compile app/image_paths.py app/ai.py app/gui.py app/editor.py tests/test_image_paths.py tests/test_ai.py tests/test_gui.py tests/test_editor_login.py`;`python -m unittest discover -s tests -p "test_image_paths.py"`(2 tests);`python -m unittest discover -s tests -p "test_ai.py"`(7 tests);`python -m unittest discover -s tests -p "test_gui.py"`(54 tests);`python -m unittest discover -s tests -p "test_editor_login.py"`(32 tests);`python -m compileall app main.py`;`python -m unittest discover -s tests`(141 tests);`git diff --check` 无空白错误,仅 LF/CRLF 提示。
- 下一步:用户复跑 T-404 真实商品链路,重点确认 8 张图商品先删第一张、重复图片提示不再拖到超时、新图片上传成功后可提交。
## 【2026-07-01】T-404 完成 · 5 个真实商品更新 Shopee 验收
- 验收事实:用户手动导入包含 5 个商品 ID 的 Excel,完成 AI 标题和图片生成,并通过 Tab③「开始更新」流程更新到 Shopee。
- 覆盖链路:Excel 导入 → AI 生成标题/图片 → Tab③ 安全确认与账号预检 → 打开真实商品详情页 → 更新标题/封面 → Shopee 站点侧确认框 → 提交到线上 → 本地更新结果链路。
- 结论:该实测已超过 T-404 原本“真实 Shopee 单条更新冒烟验收”的范围,T-404 标记为 DONE。
- 后续:可以开始 T-206「Tab① 删除指定批次」;实现时仍需保留已上线记录提示,默认不删除本地图片,只有用户勾选时才清理关联封面文件。
## 【2026-07-01】T-206 完成 · Tab① 指定批次软删除
- 产品决策:删除指定批次改为软删除,不物理删除 `batches/tasks`,也不删除原 Excel 或本地图片;删除后该批次默认不能在软件页面查看、筛选、采集、生成、更新或回写。
- 数据库:`batches` 增加 `deleted_at/deleted_reason`,`init_db()` 自动补旧库字段;`delete_batch()` 只写删除标记并返回任务数、已提交线上数、关联图片路径;`list_batches/list_tasks/get_task` 默认排除已软删除批次,`include_deleted=True` 仅供诊断/测试。
- GUI:① 导入采集页新增批次下拉和「删除批次」按钮;只能删除具体批次,运行中禁用;确认框显示任务数、已提交线上数,并提示不会回滚 Shopee。确认后刷新①自身和②/③批次筛选与任务列表。
- 测试:新增 DB 软删除默认过滤测试;新增 GUI 软删除回归测试,覆盖删除后①/②/③下拉和任务列表都不再包含该批次,且默认 `get_task` 也不能绕过软删除。
- 验证:`python -m py_compile app\db.py app\gui.py tests\test_db.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_db.py"` 通过(6 tests);`python -m unittest discover -s tests -p "test_gui.py" -k soft_deletes` 通过(1 test);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(143 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示)。
- 下一步:T-505 全流程诊断日志扩展。
## 【2026-07-01】T-508/T-509 完成 · 更新区生产化与新标题微调
- 产品化:③「开始更新」改为主操作视觉;「重置更新状态」从底部批处理区移到任务表右键菜单,保持运行中禁用,避免与批量线上提交放在同一操作组。
- 安全拦截:③ 更新安全开关阻断弹窗现在写明具体未开启设置,并提供「前往设置」跳到⑤,便于正式使用时快速修正配置。
- 新标题微调:②「新标题」列允许已生成、未提交线上、非运行中任务双击本地编辑;写回 `tasks.new_title`,保留 `stage=generated`,清空 `last_error` 并回到可更新状态,不触碰 Shopee/CDP/Excel/封面。
- 测试:新增 DB 与 GUI 回归测试,覆盖本地标题编辑、③ 右键重置入口、开始更新主按钮标识、安全拦截前往设置。
- 验证:`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(145 tests);`git diff --check` 无空白错误,仅 LF/CRLF 提示。
## 【2026-07-01】T-510 完成 · 「预览本轮更新」改名为「检查本轮更新」
- 文档:先同步 `docs/00-ai-start-here.md`、`docs/02-requirements.md`、`docs/04-architecture.md`、`docs/06-tasks.md`、`docs/api.md`、`docs/routes.md`、`docs/current-state.md`,新增 T-510,明确用户可见按钮/弹窗/日志统一使用「检查本轮更新」。
- 代码:`app/gui.py` 将③按钮、风险提示、确认框标题、取消/开始/完成状态、完成弹窗和 dry-run 运行日志文案从「预览本轮更新」改为「检查本轮更新」;内部 `preview_update_button`、`preview_update()` 与 `dry_run` 字段保留,不改变执行语义。
- 测试:更新 `tests/test_gui.py` 的按钮文案、状态栏和 dry-run 日志断言。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(57 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(145 tests);`git diff --check` 无空白错误,仅 LF/CRLF 提示。
## 【2026-07-01】T-505 完成 · 全流程诊断日志扩展
- 范围:在 T-207 采集诊断和②AI生成诊断基础上,补齐 Excel 导入/回写、③更新shopee、④Chrome 启动/登录检测、⑤AI模型测试连接的业务运行日志与本地脱敏诊断日志。
- 代码:`app/gui.py` 新增安全 run log helper;`CollectTab.import_excel()` 写 `run_type=import`,记录选中文件、缺列、脏行和入库统计;`WriteBackWorker` 写 `run_type=write_back`,记录写入文件、行数和文件锁/保存异常;`ApplyWorker` 的 `run_type=apply` 改为步骤级事件,覆盖预检、Chrome/CDP 检查、登录检测、打开商品页、改标题、换封面、点更新、写库;④「启动登录」写 `run_type=chrome_launch`,登录检测 worker 写 `run_type=login_check`;⑤AI模型测试连接写 `run_type=ai_model_test`。
- CDP:`app/editor.py` 只给 `apply_task()` 增加可选 `on_step` 回调,用于上报 `open_product/change_title/replace_cover/click_update`,不改选择器、上传方式、确认框点击或提交逻辑。
- 安全:DB 业务日志和本地 `logs/cmshopee.log` 均通过现有脱敏工具处理,不记录 Cookie、密码、API Key、token;异常路径本地日志保留脱敏 traceback。
- 测试:新增/更新 `tests/test_gui.py` 覆盖 import/write_back/apply/chrome_launch/login_check/ai_model_test 运行日志和本地日志脱敏;`python -m py_compile app\gui.py app\editor.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(63 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(151 tests);`git diff --check` 无空白错误,仅 LF/CRLF 提示。
## 【2026-07-01】T-511 完成 · 语义色板与任务状态列上色
- 范围:按 `docs/ui-color-design.md` 落地第一步状态列上色,只改 GUI 展示层,不改 CDP、Shopee 提交流程、DB schema 或业务状态流转。
- 代码:`app/gui.py` 顶部新增统一语义色板常量(success/danger/info/pending/muted/warning/danger-bg),`TaskTableModel`、`GenerateTaskTableModel`、`ApplyTaskTableModel` 在状态相关列响应 `Qt.ForegroundRole`,按内部 `stage/status` 返回 `QColor`;完成态用 success、失败用 danger、略过/取消用 muted、待处理用 pending、进行中用 info,不按中文显示文案硬匹配,不给普通行刷底色。
- 测试:`tests/test_gui.py` 新增状态列前景色断言,覆盖①待采集/未匹配、②待生成/已生成/失败、③待更新/失败/已更新。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(63 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(151 tests)。测试输出仍有 PySide6 字体目录提示,不影响结果。
- 下一步:T-512「③高风险按钮上色 + ①导入校验数字标红」。
## 【2026-07-01】T-512 完成 · 高风险按钮与导入校验数字标红
- 范围:按 `docs/ui-color-design.md` 落地第二步颜色优化,只改 GUI 样式层,不改 Tab 名、不改 CDP、Shopee 提交流程、DB schema 或业务状态流转。
- 代码:③「开始更新」按钮使用 warning `#bc4c00` 文字和描边,保留「停止」中性;①导入汇总使用 rich text 将无效行数 >0、未匹配数 >0 标为 danger `#cf222e`;未匹配按钮 >0 时同样使用 danger 描边/文字并保持可点击筛出,=0 时清空样式并禁用;无效行仅标红提示,不新增筛表入口。
- 测试:`tests/test_gui.py` 新增开始更新按钮 warning 样式、未匹配按钮 danger 样式、无效行 rich text danger 标记断言。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(63 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(151 tests)。测试输出仍有 PySide6 字体目录提示,不影响结果。
- 下一步:T-513「登录点 / ③Tab危险标识 / 破坏性按钮上色」。
## 【2026-07-02】T-513 完成 · 登录点、③Tab风险标识与破坏性按钮上色
- 文档先行:补充 `docs/ui-color-design.md` 的 T-513 落地口径,明确④登录状态列显示 `● 状态` 并用 `QTableWidgetItem.setForeground(QColor(...))` 上色;③更新shopee Tab 用 `QTabWidget.setTabIcon(2, ...)` 的小 warning 圆点,不改 Tab 文案/文字色;删除类按钮只用 danger 文字/描边,二次确认仍是主防线。
- 代码:`app/gui.py` 新增 `_login_status_display()`、`_login_status_color()`、`_warning_dot_icon()`;④账号管理登录状态列按状态着色(已登录 success、检测中 info、未登录/检测失败 danger、未知/已启动 muted);③更新shopee Tab 设置 warning 小圆点图标;①「删除批次」和④「删除账号」复用 danger 描边样式。
- 测试:`tests/test_gui.py` 补充主窗口 ③Tab icon、账号登录状态文本与颜色、删除账号/删除批次按钮 danger 样式断言。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(63 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(151 tests)。测试输出仍有 PySide6 字体目录提示,不影响结果。
- 下一步:T-514「①②③ 首次空状态引导卡片」。
## 【2026-07-02】T-514 完成 · ①②③首次空状态引导卡片
- 文档先行:扩展 `docs/06-tasks.md` 的 T-514 口径,明确无账号时①②③均显示「前往账号管理」引导,有账号但无任务时分别提示①导入 Excel、②先完成①采集、③先完成②生成;卡片只做 UI 引导,不替代执行前账号/Chrome/登录态预检。
- 代码:`app/gui.py` 新增共享 `_build_empty_state_card()` / `_set_empty_state()`;①导入采集、②AI生成、③更新shopee在刷新任务时按账号和任务状态显示/隐藏空状态卡片;②新增 `open_accounts_callback` 并由 `MainWindow` 传入跳④回调;原有导入、采集、生成、更新和安全拦截流程不变。
- 测试:`tests/test_gui.py` 新增①②③空状态断言,覆盖无账号时按钮跳④、有账号但无任务时显示对应下一步提示且隐藏跳④按钮。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(66 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(154 tests)。测试输出仍有 PySide6 字体目录提示,不影响结果。
- 下一步:T-515「批次阶段进度总览」。
## 【2026-07-02】T-515 完成 · 批次阶段进度总览
- 文档先行:`docs/06-tasks.md` 将 T-515 标记为 DOING 后补充计数口径,明确①②③同显当前批次/全部批次总数、导入、已采集、已生成、已更新、失败、略过,失败/略过按 `status` 优先统计,不新增表、不改执行流程。
- 代码:`app/gui.py` 新增共享批次进度聚合和总览条;①导入采集、②AI生成、③更新shopee在摘要区下方展示同一组计数。③任务列表仍只显示可更新相关记录,但总览使用当前批次全部未软删除任务统计。
- 测试:`tests/test_gui.py` 新增①②③批次进度总览覆盖,构造导入/已采集/已生成/已更新/失败/略过各 1 条,并确认软删除批次不出现在筛选和计数中。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py`、`python -m unittest discover -s tests -p "test_gui.py"`(67 tests)、`python -m compileall app main.py`、`python -m unittest discover -s tests`(155 tests)均通过;Qt 仍有本机字体目录提示,不影响测试结果。
- 下一步:T-516「①筛选对齐②③」。
## 【2026-07-02】T-516 完成 · ①筛选对齐②③
- 文档先行:`docs/06-tasks.md` 将 T-516 标记为 DOING 后补充验收口径,明确①新增店铺、商品ID、状态筛选;当前筛选结果用于①表格和采集作用范围,导入汇总栏/批次进度/未匹配数量仍按当前批次全量统计。
- 代码:`app/gui.py` 的①导入采集新增 `collectShopFilter`、`collectItemFilter`、`collectStatusFilter`,筛选栏顺序对齐②③;刷新时用全量批次任务更新汇总和进度,用筛选后任务更新表格;「采集旧标题/旧封面」改为只处理当前表格可见任务;采集/回写运行中禁用新增筛选控件。
- 测试:`tests/test_gui.py` 新增①筛选覆盖,验证店铺、商品ID、状态筛选、空结果提示、未匹配筛出和采集 worker 接收当前筛选任务。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py`、`python -m unittest discover -s tests -p "test_gui.py"`(68 tests)、`python -m compileall app main.py`、`python -m unittest discover -s tests`(156 tests)均通过;Qt 仍有本机字体目录提示,不影响测试结果。
- 下一步:T-517「⑤设置分区 + 清理兼容字段」。
## 【2026-07-02】T-517 完成 · ⑤设置分区与兼容字段清理
- 文档先行:`docs/06-tasks.md` 将 T-517 标记为 DOING 后明确范围:只改⑤设置页 UI 分区、保存/加载展示和 GUI 测试,不改配置 schema,不改 Shopee/CDP 更新流程。
- 代码:`app/gui.py` 将「Shopee 更新安全 / 执行模式」前置为独立分区,将「基础设施(路径与端口)」后置;移除 `test_item_id_edit`、`dry_run_checkbox` 这类用户入口,加载时只记录 `test_item_id` 内部兼容值,保存时保留该值并固定 `dry_run=false`。
- 测试:`tests/test_gui.py` 更新设置页断言,覆盖分区顺序、旧入口不存在、保存设置仍同步 ApplyTab 安全配置且兼容字段不丢。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(68 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(156 tests);`git diff --check` 无空白错误,仅 LF/CRLF 提示。
## 【2026-07-02】T-518 文档启动 · ②左栏提示词区组件密度优化
- 背景:Claude Code review T-511~T-517 未发现问题;下一步从 `docs/06-tasks.md` Backlog 和 `docs/ux-review.md` P2 提升②左栏组件密度优化为正式任务。
- 文档:`docs/06-tasks.md` 新增 T-518 并标记 DOING,明确只收敛②AI生成左栏封面模板低频操作「另存为/重命名/删除」,第一版不移入⑤,不改 prompts 数据结构、AI生成、DB、Excel、Shopee/CDP 流程;同步 `docs/current-state.md` 与 `docs/ux-review.md`。
- 验证:文档-only 更新,未运行单元测试。
- 下一步:按 T-518 文档更新 `app/gui.py` 和 `tests/test_gui.py`。
## 【2026-07-02】T-518 完成 · ②左栏提示词区组件密度优化
- 代码:`app/gui.py` 将②AI生成左栏封面模板低频操作「另存为 / 重命名 / 删除」从平铺按钮收敛进「模板操作」菜单;「新建」「保存」继续作为可见按钮,菜单 action 仍调用原有模板保存/重命名/删除方法,删除二次确认和错误处理不变。
- 范围控制:不迁移到⑤设置,不改 prompts 数据结构,不改 AI 生成 HTTP、DB schema、Excel 或 Shopee/CDP 流程。
- 测试:`tests/test_gui.py` 覆盖「模板操作」菜单入口、菜单 action 触发原有行为,以及旧低频按钮不再平铺。
- 验证:`python -m py_compile app\gui.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(68 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(156 tests)。Qt 仍有本机字体目录提示,不影响测试结果。
## 【2026-07-02】T-519 文档启动 · ②AI生成长任务进度条与用户日志
- 背景:用户反馈批量生成几十/上百个标题或图片超时重试时耗时较长,需要在②AI生成里更清楚地看到剩余量、是否卡住、慢在哪一步。
- 文档:`docs/06-tasks.md` 新增 T-519 并标记 DOING,明确②底部增加标题/图片两条独立进度条,现有「AI生成运行日志」增强为用户可读自动滚动日志;日志展示本轮开始、标题/图片开始与成功、商品ID/店铺、图片超时/重试、失败原因、停止请求和完成汇总,同时禁止显示 API Key、密码、Cookie、token、完整请求体、base64 图片或超长 prompt。
- 范围:只改②生成 UI、`GenerateWorker`/AI 生成事件文案和测试;不改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。
- 验证:文档-only 更新,未运行单元测试。
- 下一步:按 T-519 文档更新 `app/gui.py`、`app/ai.py` 和相关测试。
## 【2026-07-02】T-519 完成 · ②AI生成长任务进度条与用户日志
- 代码:`app/gui.py` 在②AI生成底部增加标题/图片两条独立 `QProgressBar`,分别显示 `标题 x/y`、`图片 x/y` 和失败数;保留原进度文本用于状态汇总;现有「AI生成运行日志」追加后自动滚到底部,加载最近一次日志时按时间顺序显示。
- 日志:`GenerateWorker` 将生成事件转为用户可读短句,显示本轮开始、标题/图片开始与成功、商品ID/店铺、调用失败重试、失败、停止请求和完成汇总;日志继续通过脱敏工具处理并写入 `run_logs/run_log_events`。
- AI事件:`app/ai.py` 的 `_call_with_retry()` 支持 `on_retry` 回调,标题/图片请求失败但仍会重试时上报 `retry` 事件;不改 AI HTTP 协议、DB schema、Excel 或 Shopee/CDP 流程。
- 测试:`tests/test_gui.py` 覆盖双进度条初始状态、用户可读日志、重试日志脱敏和旧日志加载;`tests/test_ai.py` 覆盖标题生成重试事件上报。
- 验证:`python -m py_compile app\ai.py app\gui.py tests\test_ai.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_ai.py"` 通过(7 tests);`python -m unittest discover -s tests -p "test_gui.py"` 通过(68 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(156 tests)。Qt 仍有本机字体目录提示,不影响测试结果。
## 【2026-07-02】T-520 文档启动 · ②AI生成封面可选生成开关
- 背景:②AI生成当前默认标题和图片都生成,图片模型成本明显高于标题;用户只想改标题时也会产生图片生成成本。⑤「允许更新封面」只控制③线上提交阶段,不能替代②生成阶段的成本选择。
- 决策:新增 T-520。②AI生成页增加「生成封面图片(成本较高)」复选框,默认关闭并持久化到 `config.json` 的 `ai.generate_cover`;关闭时只生成标题,标题成功即 `db.set_generated(task_id, new_title, NULL)`,③只更新标题;开启时保持标题后图片两段流程。
- 文档:同步 `docs/02-requirements.md`、`docs/04-architecture.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`。本轮只更新文档,未改代码。
- 下一步:按 T-520 文档更新 `app/appconfig.py`、`app/ai.py`、`app/gui.py` 和相关单测。
## 【2026-07-02】T-520 完成 · ②AI生成封面可选生成开关
- 配置:`app/appconfig.py` 新增 `ai.generate_cover=false` 默认值;②AI生成页复选框状态会持久化到 `config.json`,设置页保存其他 AI 参数时保留该隐藏成本开关。
- 代码:`app/ai.py` 的 `generate_batch()` 按 `generate_cover` 分支执行;关闭时只生成标题,标题成功即 `db.set_generated(task_id, new_title, None)` 并进入 `generated`,不调用 `gen_cover()`、不渲染封面提示词、不创建新封面文件;开启时保留标题后图片两段流程。
- GUI:`app/gui.py` 在②底部增加「生成封面图片(成本较高)」复选框;开始生成前保存当前开关;标题-only 模式进度显示 `图片0/0`,运行日志写明“本轮仅生成标题,不生成图片”,完成统计使用 `generated_done`,避免只看 `cover_done`。
- 测试:`tests/test_appconfig.py` 覆盖默认关闭;`tests/test_ai.py` 覆盖跳过封面时不调用 `gen_cover` 且任务进入 generated;`tests/test_gui.py` 覆盖开关持久化、Worker 标题-only 日志/运行日志统计,以及旧图片路径显式开启开关。
- 验证:`python -m py_compile app\appconfig.py app\ai.py app\gui.py tests\test_appconfig.py tests\test_ai.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_appconfig.py"` 通过(7 tests);`python -m unittest discover -s tests -p "test_ai.py"` 通过(8 tests);`python -m unittest discover -s tests -p "test_gui.py"` 通过(70 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(159 tests);`git diff --check` 无空白错误,仅 LF/CRLF 提示。Qt 仍有本机字体目录提示,不影响测试结果。
## 【2026-07-02】T-521 完成 · 依赖清单(锁版本 requirements)
- 代码/配置:新增根目录 `requirements.txt`,锁定当前直接运行依赖:PySide6==6.5.3、openpyxl==3.1.3、requests==2.31.0、websocket-client==1.6.1、Pillow==9.5.0;不新增运行时依赖,不改业务代码。
- 文档:`docs/03-tech-stack.md` 的安装命令改为 `python -m pip install -r requirements.txt`,并补充依赖纪律;`docs/README.md` 增加运行环境安装命令;`docs/06-tasks.md` 标记 T-521 DONE;`docs/current-state.md` 同步当前快照和下一任务 T-522。
- 验证:`python -c "import PySide6, openpyxl, requests, websocket, PIL; ..."` 确认当前版本;`python -m pip install --no-index -r requirements.txt` 确认当前环境已满足锁定依赖且不触网;`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(159 tests)。`python -m pip check` 在全局环境发现 albumentations/paddleocr/flask-babel 等非本项目包的既有依赖冲突,非 T-521 新增依赖导致;本项目锁定的 5 个直接依赖可正常 import。
## 【2026-07-02】T-522 完成 · CI 自动跑语法 + 单元 / GUI 测试
- 配置:新增 `.github/workflows/tests.yml`,在 push / pull_request 上使用 `windows-latest` + Python 3.11,安装 `requirements.txt`,设置 `QT_QPA_PLATFORM=offscreen`,运行 `python -m compileall app main.py` 与 `python -m unittest discover -s tests`。
- 文档:同步 `docs/03-tech-stack.md`、`docs/README.md`、`docs/current-state.md`;`docs/06-tasks.md` 将 T-522 标为 DONE,下一个可领取任务更新为 T-523。
- 验证:`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(159 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示)。本地未执行 GitHub Actions 远端 runner,workflow 已按同等命令配置。
## 【2026-07-02】T-523 完成 · 拆分 app/gui.py 为 app/gui/ 包
- 代码:删除旧 `app/gui.py`,新增 `app/gui/` 包;`__init__.py` 保持 `from app import gui`、`from app.gui import MainWindow/各 Tab/各 Worker` 等旧导入兼容;`main_window.py` 放 `MainWindow`;`models.py` 放 3 个 TableModel;`widgets.py` 放 Qt 导入、语义色板、空状态卡、批次总览和日志 helper;`workers.py` 放 `GenerateWorker`/`ApplyWorker`/`CollectWorker`/`WriteBackWorker`/`AccountLoginCheckWorker`/`AIModelTestWorker`;`tabs/` 按 ①~⑤ 拆分各 Tab。
- 边界:纯结构重构;未改 Shopee/CDP、AI、DB、Excel 行为;PySide6 缺失时仍由 `main()` 返回 1 并提示,旧公开导入路径继续可用。
- 文档:同步 `docs/04-architecture.md`、`docs/api.md`、`docs/current-state.md`;`docs/06-tasks.md` 将 T-523 标为 DONE,下一个可领取任务更新为 T-524。
- 验证:`python -m compileall app main.py` 通过;`python -m unittest discover -s tests -p "test_gui.py"` 通过(70 tests);`python -m unittest discover -s tests` 通过(159 tests)。
## 【2026-07-02】文档 · T-523a ②/③ 本轮日志显示重置
- 需求:②AI生成和③更新shopee启动新一轮运行时,不应继续显示上一轮日志,避免用户把旧失败、旧进度误认为本轮状态。
- 方案:新增 T-523a;点击②「开始生成」、③「检查本轮更新」或③「开始更新」时,先清空对应界面的可见日志显示区,并写入本轮开始摘要;运行中只追加本轮日志,完成后保留本轮日志。只清空界面文本,不删除 SQLite `run_logs/run_log_events` 或本地 `logs/` 诊断日志。
- 文档:同步 `docs/02-requirements.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`;当前下一个可领取任务改为 T-523a。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-02】T-523a 完成 · ②/③ 本轮日志显示重置
- 代码:`GenerateTab` 和 `ApplyTab` 进入页面后不再自动加载上一轮历史日志,默认显示“本轮日志会在开始运行后显示”;②点击「开始生成」时清空可见日志并写入本轮 AI 生成摘要;③点击「检查本轮更新」或「开始更新」并确认后清空可见日志并写入本轮检查/更新摘要。
- 边界:只清空 GUI 文本显示,不删除 SQLite `run_logs/run_log_events` 或本地 `logs/`;未改 AI HTTP、DB schema、Excel、Shopee/CDP 更新流程。
- 测试:更新 `tests/test_gui.py`,覆盖②/③进入页面不自动混入历史日志,以及②开始生成、③检查/真实更新启动时旧日志被清掉且本轮摘要出现。
- 文档:`docs/06-tasks.md` 将 T-523a 标为 DONE;同步 `docs/current-state.md`,下一个可领取任务更新为 T-524。
- 验证:`python -m unittest discover -s tests -p "test_gui.py"` 通过(72 tests)。
## 【2026-07-02】文档 · T-404b 商品详情页加载失败 toast 自动捕获
- 需求:商品 ID 失效、无权限或店铺不匹配时,Shopee 商品详情页可能只弹出很快消失的错误 toast,例如 `please input correct product id`;用户来不及复制 HTML,当前错误也可能只表现为商品详情页等待超时。
- 方案:新增 T-404b。`open_product()`/等待商品详情页就绪前安装或启用 `.eds-toasts .eds-toast__content` 捕获,保存 toast 文本、`outerHTML`、当前 URL、时间和可见状态;详情页关键元素超时或页面不符合商品编辑页时,把最近错误 toast 上浮到 `tasks.last_error`、`run_logs/run_log_events`、③/①可见日志和本地 `logs/` 诊断日志。可保存失败现场 HTML/toast JSON,禁止记录 Cookie、密码、token。
- 文档:同步 `docs/02-requirements.md`、`docs/04-architecture.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`;当前下一个可领取任务改为 T-404b。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-02】文档补充 · T-404b 商品失效显示边界
- 需求补充:用户确认①导入采集列表可以在商品 ID 失效时显示更直观的“商品失效”,但必须确认不会影响②AI生成、③更新shopee、进度统计和 Excel 回写。
- 决策:只做 UI 显示映射,不新增或改写数据库 `stage` 枚举。捕获到明确商品失效/商品不存在/无权限类 toast 时,任务底层保持 `stage=imported/status=failed/last_error=商品失效:<原始toast>`,①列表“阶段”列显示“商品失效”;网络、CDP、未登录、页面超时、风控等其他打开失败仍显示“失败”。
- 文档:同步 `docs/02-requirements.md`、`docs/04-architecture.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-02】T-404b 完成 · 商品详情页加载失败 toast 自动捕获
- 代码:`app/editor.py` 在 `open_product()` 导航商品详情页前后安装 toast 观察器,缓存最近 toast 的文本、HTML、URL、可见状态和时间;`_wait_ready()` 超时或检测到失效 toast 时,将商品失效/商品不存在/无权限类文案上浮为 `商品失效:<原始toast>`,其他打开失败仍保持普通失败原因。
- GUI:①导入采集任务表只在 `last_error` 明确包含商品失效类错误时把“阶段”列显示为“商品失效”;底层 DB 不新增 stage 枚举,仍保持 `stage=imported/status=failed`,其他超时、网络、登录、风控类错误仍显示“失败”。
- 测试:`tests/test_editor_login.py` 覆盖隐藏 toast 捕获、失效 toast 错误上浮和 open_product 安装观察器;`tests/test_gui.py` 覆盖只有明确失效错误才显示“商品失效”。
- 验证:已通过相关测试;全量验证见本轮最终结果。
## 【2026-07-02】文档补充 · T-404b 商品失效失败 tab 清理
- 问题:商品 ID 失效时,`_wait_ready()` 在 `open_product()` 内部抛出 `商品失效:...`,上层 `collect()` / `apply_task()` 尚未拿到 `cdp`,因此上层 finally 无法关闭本轮自动新建的商品 tab。
- 方案:补充 T-404b 边界。`open_product()` 如果在返回 `cdp` 前失败,要自行清理:本轮自动新建 tab 先断开 CDP 再调用 `close_tab(target_id, host=...)`;复用用户原本打开的 tab 只断开 CDP,不关闭页面。进入编辑页后的更新失败仍按原设计保留现场。
- 文档:同步 `docs/02-requirements.md`、`docs/04-architecture.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-02】T-404b 补丁完成 · 商品失效失败 tab 自动清理
- 代码:`app/editor.py` 新增 `open_product()` 内部失败清理。若 `_wait_ready()` 因商品失效 toast 或其他打开页异常在返回 `cdp` 前抛错,会先断开 CDP;本轮自动新建的商品 tab 继续调用 `close_tab(target_id, host=...)` 关闭,复用用户已有 tab 不关闭。
- 边界:只处理 `open_product()` 内部打开失败导致的残留 tab;进入编辑页后的更新失败仍按原设计保留现场,成功提交后的关闭仍受 ⑤ `close_success_tab` 控制。
- 测试:`tests/test_editor_login.py` 增加自动新建 tab 商品失效时关闭、复用 tab 商品失效时不关闭的覆盖。
- 验证:`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(36 tests)。
## 【2026-07-02】文档 · T-103b 登录检测补充识别 Shopee accounts 登录页
- 需求:打开商品详情页或卖家中心时,未登录账号可能跳转到 `https://accounts.shopee.tw/seller/login...`。④「检测登录」以及①/③执行前账号预检应明确把该 URL 判为未登录,不能依赖模糊规则或误判为普通无 Cookie。
- 方案:新增 T-103b。登录页 URL marker 显式加入 `accounts.shopee.tw/seller/login` / Shopee accounts 域名下 `/seller/login`;`login_status()` 返回 `logged_in=false/reason=LOGIN_PAGE/url=<实际URL>`;④状态列显示“未登录”,①/③预检继续阻断并引导去④。
- 边界:不自动登录、不填密码、不绕过验证码;只增强登录页识别和测试覆盖,不改 CDP 商品页、上传、提交逻辑。
- 文档:同步 `docs/02-requirements.md`、`docs/04-architecture.md`、`docs/routes.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`;当前下一个可领取任务改为 T-103b。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-02】T-103b 完成 · 登录检测补充识别 Shopee accounts 登录页
- 代码:`app/editor.py` 的 `LOGIN_PATH_MARKERS` 显式加入 `accounts.shopee.tw/seller/login`,`_is_login_url()` 增加 `accounts.shopee.*` 域名且路径以 `/seller/login` 开头的判断。
- 行为:`login_status()` 遇到 `https://accounts.shopee.tw/seller/login...` 立即返回 `logged_in=false/reason=LOGIN_PAGE/url=<实际URL>`,④状态列会显示未登录,①/③预检继续阻断并引导去④。
- 边界:不自动登录、不填密码、不绕过验证码;未改商品页、上传、提交逻辑。
- 测试:`tests/test_editor_login.py` 增加真实 accounts 登录 URL 覆盖。
- 验证:`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(37 tests)。
## 【2026-07-03】T-524 完成 · PyInstaller 打包为免安装 exe
- 代码/配置:新增 `cmshopee.spec`、`scripts/build_exe.ps1`、`requirements-build.txt`;`main.py` 在 PyInstaller frozen 模式下把工作目录切到 `cmshopee.exe` 所在目录,确保配置、DB、图片、日志、登录态和提示词在 exe 同级目录生成/读取;`.gitignore` 新增 `build/`、`dist/`、`db.sqlite`、`prompts/`、`title_prompt.txt`。
- 打包安全:`cmshopee.spec` 不声明本地数据 `datas`;构建脚本打包后检查 `dist\cmshopee`,发现 `config.json`、`config/ai_models.json`、`cmshopee.db*`、`db.sqlite`、`chrome_user_data_dir/`、`images/`、`logs/`、`prompts/`、`title_prompt.txt` 会直接失败;脚本会打印当前 Python 版本,低于项目目标 3.10 时给出警告。
- 文档:新增 `docs/packaging.md`,同步 `docs/README.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`docs/current-state.md`;T-524 标为 DONE,下一个可领取任务更新为 T-525。
- 测试:新增 `tests/test_packaging.py` 覆盖 frozen 入口路径、spec 不打包本地数据、构建脚本排除列表。
- 验证:`python -m compileall app main.py` 通过;`python -m unittest discover -s tests -p "test_packaging.py"` 通过(3 tests);`python -m unittest discover -s tests` 通过(170 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示);`powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1` 成功产出 `dist\cmshopee\cmshopee.exe`,脚本确认未混入本地数据。本机当前 `python` 为 3.7.9,构建脚本已提示低于项目目标;正式发布建议切到 Python 3.10+ 环境后重新打包。
## 【2026-07-04】文档修正 · cmhub 网关对接任务定义
- 背景:Claude Code 已新增 `docs/cmhub-integration-design.md` 与 T-526~T-528;本轮从全栈工程角度评审后修正任务边界,避免未配置 cmhub 时破坏现有 direct 生文/生图流程。
- 修正:`docs/cmhub-integration-design.md` 增补 v3 评审结论,明确旧配置缺 `backend` 时按 `direct` 迁移、cmhub Key 固定存 `config/cmhub.json`、新增 `CMHubError` 结构化错误、点数/余额通过事件回调传播、cmhub HTTP 调用需区分 connect/read timeout、`image_url` 下载需安全校验。
- 任务:`docs/06-tasks.md` 将业务优先级调整为 Phase 7 cmhub,对 T-526/T-527/T-528 验收重写;T-525 ruff 顺延到 Phase 8。`docs/current-state.md` 下一个可领取任务改为 T-526。
- 验证:文档-only 更新,未改代码,未运行单元测试。
## 【2026-07-04】文档修正 · cmhub v3.2 六点一致性收口
- 修正:`docs/cmhub-integration-design.md` 将目标接口改为 title/image/balance/models 四类接口;统一 `gen_title`/`gen_cover` 为“返回值与现有调用兼容,可新增可选事件回调参数”;把不存在的 `default_ai_config()` 改为现有 `DEFAULT_CONFIG` / `default_config()` 表述;明确 T-526 只发出计费 metadata 事件,T-528 再负责 GUI run_logs 与余额展示;补充 `image_url` 域名解析后 IP 也要拒绝内网/回环/本机地址。
- 任务:`docs/06-tasks.md` 将 T-526/T-527/T-528 引用统一为 `docs/cmhub-integration-design.md` v3.2,并同步 T-526 的 run_logs 边界与安全下载验收。
- 验证:文档-only 更新,未改代码,未运行单元测试。
## 【2026-07-04】T-526 完成 · `app/ai.py` + `appconfig` 接入 cmhub backend
- 状态:DONE
- 变更:`app/appconfig.py` 在 `config.json` 的 `ai` 段新增 `backend=direct/cmhub` 和 `ai.cmhub` 默认配置,新增 `config/cmhub.json` 的读写/打码 helper;`.gitignore` 增加 `config/cmhub.json`。`app/ai.py` 保留 direct 直连模型清单路径,新增 cmhub 生文、生图和 `/models` 别名发现 helper;`gen_title()` / `gen_cover()` 返回值不变,cmhub 计费 metadata 通过事件回调传出;新增 `CMHubError` 结构化错误、按 code 的重试策略、tuple timeout、生图读超时不自动重发、`image_url` http/https + DNS/IP 内网拦截 + 大小上限下载。
- 文档:同步 `docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/06-tasks.md`、`docs/current-state.md`;T-526 标记 DONE,下一步为 T-527。
- 测试:`python -m py_compile app\appconfig.py app\ai.py tests\test_appconfig.py tests\test_ai.py` 通过;`python -m unittest discover -s tests -p "test_appconfig.py"` 通过(8 tests);`python -m unittest discover -s tests -p "test_ai.py"` 通过(16 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(179 tests)。全量测试仍有本机 PySide6 字体目录提示,不影响结果。
- 决策:全新配置和旧配置缺 `ai.backend` 时都保持 `direct`,避免升级后未配置 cmhub 就破坏现有生成;cmhub Key 固定只进 `config/cmhub.json`,不进 `config.json`;T-526 只传出 metadata,GUI 余额/计费提示留给 T-528。
- 下一步:T-527 ⑤设置 cmhub 网关面板。
## 【2026-07-04】T-527 完成 · ⑤设置 cmhub 网关面板
- 状态:DONE
- 代码:⑤设置页新增 AI 后端选择,`direct` 模式保留现有 `config/ai_models.json` 模型 master-detail 和直连模型测试;`cmhub` 模式显示网关 Base URL、API Key、生文别名、生图别名、刷新别名、测试连接/查余额和余额结果。cmhub Key 单独保存到 `config/cmhub.json`,保存新 Key 时沿用本地明文保存提示,`config.json` 只保存 backend、Base URL、别名、连接超时和余额预检开关。
- 别名:`CMHubSettingsWorker` 后台调用 `fetch_cmhub_models()`,⑤设置页首次显示且已配置 Base URL/Key 时自动刷新一次,也可手动点「刷新别名」;按 `operation_type` 拆分生文/生图,过滤 `pricing_status="unpriced"`,下拉展示点数价格和 `requires_image` 提示;网关暂不可达或已存别名不在可用列表时保留已存值,避免保存设置时清空用户配置。
- 余额:`app/ai.py` 新增 `fetch_cmhub_balance()` 调 `GET /api/v1/balance`,设置页「测试连接/查余额」复用同一个 worker 拉模型和余额;worker 失败会用本地 Key 脱敏后再上浮错误。
- 边界:未改 Shopee/CDP、Excel、DB schema、账号流程;`config/ai_models.json` 在切换 cmhub 时不删除,仍供 direct 模式回退使用。
- 测试:`tests/test_ai.py` 覆盖 cmhub 余额 helper;`tests/test_gui.py` 覆盖 `CMHubSettingsWorker` 成功/失败脱敏、设置页 cmhub backend 切换、保存 `config.json` + `config/cmhub.json`、别名过滤与已存值保留。
- 文档:`docs/06-tasks.md` 将 T-527 标为 DONE;同步 `docs/current-state.md`,下一步为 T-528。
- 验证:`python -m py_compile tests\test_ai.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p "test_ai.py"` 通过(17 tests);`python -m unittest discover -s tests -p "test_gui.py"` 通过(77 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(184 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示)。全量测试仍有本机 PySide6 字体目录提示,不影响结果。
- 下一步:T-528 ②计费错误提示 + 余额展示。
## 【2026-07-04】T-528 完成 · ②计费错误提示 + 余额展示
- 状态:DONE
- 代码:②AI生成页新增 cmhub 余额显示,仅在 `backend=cmhub` 时展示;`GenerateWorker` 读取 cmhub metadata,把 `points_cost`、`points_balance`、`call_id` 以脱敏用户日志写入 `run_logs/run_log_events`,并把最新 `points_balance` 回传给页面。
- 点数不足:`app/ai.py` 的生成错误回调会透传 `CMHubError.code/status/retryable`;`GenerateWorker` 通过 `code=insufficient_points` 识别点数不足,设置本轮停止标记,弹「点数不足,请先充值。本轮未开始任务将停止。」并让未开始任务不再继续,不靠中文错误字符串匹配。
- 边界:只改②AI生成 UI、worker 事件处理和测试;未改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。
- 文档:`docs/06-tasks.md` 将 T-528 标为 DONE;同步 `docs/current-state.md`,下一个可领取任务回到 T-525 ruff。
- 测试:`python -m unittest discover -s tests -p test_gui.py` 通过(80 tests);`python -m unittest discover -s tests -p test_ai.py` 通过(17 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(187 tests);`git diff --check` 通过,仅有本机 LF/CRLF 提示。全量测试仍有本机 PySide6 字体目录提示,不影响结果。
- 下一步:T-525「引入 ruff(lint + format)+ 可选 pre-commit」。
## 【2026-07-04】T-529 完成 · 默认 cmhub 网关并隐藏 AI 后端选择
- 状态:DONE
- 文档:同步 `docs/cmhub-integration-design.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/06-tasks.md`、`docs/current-state.md`;T-529 标为 DONE,下一步回到 T-525 ruff。
- 代码:`DEFAULT_CONFIG.ai.backend` 改为 `cmhub`,`ai_backend()` 缺省跟随默认 backend;⑤设置页隐藏「AI 后端」下拉、direct 模型选择、模型详情和标题/图片角色下拉,普通 UI 直接展示 cmhub 网关配置;保存设置固定写 `ai.backend=cmhub`,允许先保存不完整 cmhub 配置,真正生成时仍由 `app/ai.py` 提示补齐 Base URL/API Key/别名。
- 兼容:direct 代码、`config/ai_models.json`、AI 模型测试 worker 和 direct 单测保留为内部兼容/手工回滚路径;未改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。
- 测试:更新 appconfig/GUI/AI 单测,direct 分支测试显式设置 `backend=direct`;②AI生成页默认显示 cmhub 余额标签。
- 验证:`python -m py_compile app\appconfig.py app\gui\tabs\settings.py app\gui\tabs\generate.py tests\test_appconfig.py tests\test_gui.py tests\test_ai.py` 通过;`python -m unittest discover -s tests -p test_appconfig.py` 通过(8 tests);`python -m unittest discover -s tests -p test_gui.py` 通过(80 tests);`python -m unittest discover -s tests -p test_ai.py` 通过(17 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(187 tests);`git diff --check` 通过,仅有本机 LF/CRLF 提示。全量 GUI 测试仍有本机 PySide6 字体目录提示,不影响结果。
## 【2026-07-06】T-530 完成 · cmhub Base URL 规整 + 404 明确提示
- 状态:DONE
- 代码:新增 `appconfig.normalize_cmhub_base_url()`,并让 `cmhub_request_url()`、`save_config()`、`load_config()`、`cmhub_config()` 使用规整后的网关根;⑤设置页新增 Base URL 网关根提示,保存/刷新别名前会把输入同步规整;`app/ai.py` 将 HTTP 404 / `notfound` 统一映射为 `CMHubError(code="not_found")`,显示中文排障提示。
- 边界:未改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程;direct 兼容路径不变。
- 测试:新增 appconfig URL 规整测试、ai 404 `not_found` 测试、GUI Base URL 提示/保存归一断言。
- 验证:`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(189 tests)。全量 GUI 测试仍有本机 PySide6 字体目录提示,不影响结果。
- 下一步:T-525「引入 ruff(lint + format)+ 可选 pre-commit」。
## 【2026-07-06】文档修正 · T-531 设置未保存提示方案收口
- 背景:Claude Code 已在任务看板新增 T-531,用于解决⑤设置页填写 cmhub Base URL/API Key 后没有未保存提示、切 Tab/关闭窗口会静默丢输入的问题。
- 修正:认可“显式保存 + 脏状态追踪 + 离开确认”方案,不做自动保存;补充 `save_app_settings()` 成功/失败返回语义、放弃必须重新从 `config.json` 与 `config/cmhub.json` 回填、程序化回填不置脏、`currentChanged` 回退需 guard 防递归、刷新别名/测试连接成功只提醒保存不落盘。
- 文档:同步 `docs/06-tasks.md`、`docs/current-state.md`、`docs/routes.md`、`docs/api.md`;下一个可领取任务改为 T-531,T-525 ruff 顺延。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-06】T-531 完成 · ⑤设置未保存状态追踪 + 离开确认
- 状态:DONE
- 代码:⑤设置页新增 dirty 状态、保存按钮旁“● 未保存更改”提示、`is_dirty()`、`discard_unsaved_changes()`,`save_app_settings()` 改为返回成功/失败;保存成功清除 dirty,保存失败保留 dirty;放弃会重新读取 `config.json` 与 `config/cmhub.json` 并回填控件。
- 主窗口:`MainWindow` 在切 Tab 和关闭窗口时检查⑤设置页未保存状态,弹保存/放弃/取消;保存失败或取消会回到⑤设置页;`currentChanged` 回退使用 guard 防止递归。
- cmhub:刷新别名/测试连接继续使用输入框实时值,但不自动保存 URL/API Key;成功文案提醒“记得点『保存设置』持久化”。程序化回填和别名下拉刷新不触发 dirty。
- 边界:未改配置 schema、AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。
- 测试:新增 GUI 单测覆盖 dirty 置位/清除、程序化刷新不置脏、放弃还原 URL/Key、切 Tab 取消、保存失败、放弃分支、关闭窗口取消。
- 验证:`python -m py_compile app\gui\tabs\settings.py app\gui\main_window.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(86 tests)。
- 下一步:T-525「引入 ruff(lint + format)+ 可选 pre-commit」。
## 【2026-07-06】文档规则 · 用户可见文本统一中文
- 规则:所有用户可见文本必须使用中文,覆盖弹窗标题/正文/按钮、窗口标题、按钮、菜单、label、placeholder、tooltip、状态栏、空状态、确认框、运行日志、错误提示和成功提示等。
- 边界:URL、配置键、API 字段、模型别名、第三方/Shopee 原始错误可保留原文,但外围必须给中文解释;不得直接把英文技术报错裸露给用户。
- 文档:同步 `AGENTS.md`、`docs/05-coding-rules.md`、`docs/current-state.md`。
- 代码:同步修正 T-531 设置未保存离开确认框,改用自定义中文按钮“保存 / 放弃 / 取消”,避免系统默认按钮显示英文。
- 验证:`python -m py_compile app\gui\main_window.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(86 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(195 tests);`git diff --check` 通过,仅有本机 LF/CRLF 提示。
## 【2026-07-06】运行修正 · ②AI生成失败状态分阶段提示
- 现象:在②AI生成选择一个批次,列表都是“失败”状态时点击「开始生成」,状态栏提示“当前筛选结果没有可生成任务”。
- 排查:本地最新未删除批次 `20260702_171000_75bd2557` 的 5 条任务均为 `stage=imported/status=failed/collect_attempts=3/generate_attempts=0/apply_attempts=0`,属于①采集失败,不是 AI 生成失败;②没有旧标题/旧封面,不能直接生成。
- 修正:新增 `ai.is_generatable_task()` 统一 GUI/Worker/底层批处理口径;②状态列把失败细分显示为“采集失败 / 生成失败 / 更新失败”;「开始生成」无可重试任务时提示先到①完成旧数据采集。AI 生成失败可重试,③更新失败不会被②误重试。文档补充 `docs/troubleshooting.md` 的同名排查条目。
- 测试:新增 `test_generate_batch_retries_failed_generation_record_after_existing_result`、`test_generate_batch_does_not_retry_apply_failed_records`、`test_generate_tab_explains_collect_failed_records_are_not_generatable`。
## 【2026-07-06】文档建议 · 状态栏语义颜色
- 建议:左下角状态栏按 muted/info/success/warning/danger 做轻量文字色区分,普通/就绪保持 muted 或默认,进行中用 info,成功用 success,需用户处理但可恢复的问题用 warning,失败/阻断用 danger。
- 落地边界:状态栏只做辅助反馈,不做大面积背景,不替代弹窗、空状态、运行日志和按钮禁用;后续代码建议统一 `MainWindow.show_status(message, level)`,逐步替换直接 `statusBar().showMessage(...)`。
- 文档:同步 `docs/ui-color-design.md`、`docs/ux-review.md`、`docs/06-tasks.md`、`docs/current-state.md`。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-06】文档记录 · T-105b 启动登录复用已打开 Chrome
- 现象:④账号管理同一账号重复点击「启动登录」会再次打开 Chrome 窗口,而不是复用已打开账号 Chrome 创建/激活 tab。
- 根因:当前链路 `AccountsTab.launch_login()` → `accounts.launch_for_login()` → `chrome.launch_chrome()` → `subprocess.Popen()` 每次直接启动进程,未使用已有 `chrome.is_running(debug_port)` 做幂等判断;界面上的“已启动”只是显示状态,不是保护逻辑。
- 方案:新增 T-105b,要求④启动登录先检查账号 CDP 端口;已运行时不再 Popen,复用现有 Chrome/CDP 并打开/激活卖家中心登录 tab,写 `chrome_launch result=reused`;未运行时才启动并写 `result=launched`。不自动登录、不填密码、不改变①/③预检不自动启动 Chrome 的规则。
- 文档:同步 `docs/06-tasks.md`、`docs/api.md`、`docs/routes.md`、`docs/04-architecture.md`、`docs/troubleshooting.md`、`docs/current-state.md`。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-06】T-105b 完成 · 启动登录复用已打开 Chrome
- 状态:DONE
- 代码:`app/accounts.py` 的 `launch_for_login()` 改为幂等入口,先用 `chrome.is_running(debug_port)` 检查账号 Chrome/CDP 是否已运行;已运行时复用现有 Chrome,通过 CDP 打开或激活卖家中心登录 tab,不再调用 `subprocess.Popen()`;未运行时才启动 Chrome、等待 CDP 就绪并打开登录 tab。`app/cdp.py` 新增 `activate_tab()` 复用浏览器 target 激活能力。
- GUI:④账号管理启动登录运行日志区分 `result=launched` 与 `result=reused`;复用时状态栏提示“该账号 Chrome 已打开,已复用现有窗口”。
- 边界:不自动登录、不填写密码、不绕过验证码;①/③账号预检仍只检测,不自动启动 Chrome。
- 文档:`docs/06-tasks.md` 将 T-105b 标为 DONE;同步 `docs/api.md`、`docs/troubleshooting.md`、`docs/current-state.md`。
- 验证:`python -m py_compile app\cdp.py app\accounts.py app\gui\tabs\accounts.py tests\test_accounts.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_accounts.py` 通过(9 tests);`python -m unittest discover -s tests -p test_chrome.py` 通过(6 tests);`python -m unittest discover -s tests -p test_gui.py` 通过(88 tests)。
## 【2026-07-06】文档记录 · T-532 cmhub 连接成功提示显示账号名
- 现象:⑤设置页测试 cmhub 连接成功后只显示 `cmhub 连接成功:...`,用户无法确认当前 API Key 属于哪个 cmhub 账号。
- 方案:新增 T-532。成功 payload 中若 `/api/v1/balance` 返回账号名、用户名、邮箱或 id 等信息,结果 label 与状态栏优先显示 `cmhub 账号「<账号名>」连接成功:...`;邮箱脱敏,无账号信息时保留现有兜底文案。不得显示 API Key、token、完整邮箱或原始敏感响应。
- 边界:只改⑤设置页成功提示和 GUI 单测;不改 cmhub HTTP 协议、配置 schema、保存逻辑、AI 生成流程或 Shopee/CDP 流程。
- 文档:同步 `docs/06-tasks.md`、`docs/routes.md`、`docs/api.md`、`docs/current-state.md`。
- 验证:文档-only 更新,未运行单元测试。
## 【2026-07-06】T-532 完成 · cmhub 连接成功提示显示账号名
- 状态:DONE
- 代码:`SettingsTab._on_cmhub_finished()` 成功文案会从 worker payload 的 `balance.user`、`balance.account`、顶层字段中提取 `name/account_name/username/email/id`,优先显示 `cmhub 账号「<账号名>」连接成功:...`;无账号信息时保留 `cmhub 连接成功:...`。
- 脱敏:新增 `appconfig.mask_email()`,`sanitize_for_log()` 对 `email` / `*_email` 字段脱敏,worker 返回和 run log summary 不保存完整邮箱;设置页展示邮箱时也使用脱敏格式。
- 边界:未改 cmhub HTTP 协议、配置 schema、保存逻辑、AI 生成流程或 Shopee/CDP 流程。
- 测试:`python -m py_compile app\appconfig.py app\gui\tabs\settings.py tests\test_appconfig.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_appconfig.py` 通过(9 tests);`python -m unittest discover -s tests -p test_gui.py -k cmhub` 通过(9 tests)。
## 【2026-07-06】T-532 补充 · 兼容 cmhub 当前账号返回结构
- 文档:`docs/06-tasks.md`、`docs/api.md`、`docs/routes.md`、`docs/cmhub-integration-design.md`、`docs/current-state.md` 已记录当前 `/api/v1/balance` 返回结构 `{user, points_balance, account:{username, display_name}}`。
- 代码:`SettingsTab` 账号显示名提取优先读取 `account.display_name`,再兜底 `account.username`、`user`、name/account_name/email/id 等字段;邮箱仍脱敏展示。
- 测试:补充 GUI worker 与设置页成功提示单测,确认当前结构显示 `主账号` 而不是 `cmhub_user`;未改 cmhub HTTP 协议、配置 schema、AI 生成或 Shopee/CDP 流程。
## 【2026-07-06】T-533 完成 · ②增量生成按缺失组件补齐
- 状态:DONE
- 代码:`app/ai.py` 新增 `generation_needs()` / `generation_component_totals()`,`is_generatable_task()` 改为按当前封面开关判断标题/封面缺口;`generate_batch()` 对已有 `new_title` 的任务跳过生文,只在缺 `new_cover_path` 且开启封面时补封面,避免覆盖 T-509 手动标题和重复扣标题点。
- GUI:②开始生成、待生成筛选、`GenerateWorker` 和底部进度改用组件口径;本轮日志会显示已有标题跳过生文,标题/图片进度分别按 `title_total` / `cover_total` 统计;无缺口时提示先完成①采集或勾选封面补封面。
- 边界:未改 cmhub HTTP 协议、配置 schema、DB schema、Excel、Shopee/CDP 流程;全组件齐备任务不纳入本轮生成。
- 测试:新增/调整 AI 与 GUI 单测覆盖只补封面、混合标题/封面缺口、封面关闭时标题态任务不纳入、全组件齐备不重复生成、②入口可启动补封面。
- 验证:`python -m py_compile app\ai.py app\gui\workers.py app\gui\tabs\generate.py tests\test_ai.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_ai.py` 通过(23 tests);相关 GUI 用例通过;`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(207 tests);`git diff --check` 通过,仅有本机 LF/CRLF 提示。
## 【2026-07-06】T-534 完成 · ②按组件批量重置生成结果
- 状态:DONE
- 代码:`db.reset_generated()` 增加 `reset_title` / `reset_cover` 组件开关和仅重置封面时可选的 `delete_file`,重置后保持 `stage=generated/status=success`,让 T-533 能按缺口只补被清掉的标题或封面;已提交线上记录保留 `committed/apply_attempts` 历史。
- AI:`generation_needs()` 只阻断 `status=failed` 且有 `apply_attempts` 的更新失败记录;用户主动重置后的成功态记录即使保留提交历史,也允许按缺口补生成。
- GUI:②任务表改为多选;「重置生成结果」确认框提供「重置标题 / 重置封面 / 重置全部 / 取消」,有选中行时作用于选中任务,没有选中行时作用于当前筛选结果;确认框显示标题/封面数量、运行中跳过数、已提交线上数量和重复提交风险;批量写 `run_type=reset` 日志。
- 边界:不改 cmhub HTTP 协议、配置 schema、Excel、Shopee/CDP 流程;默认不删除本地新封面文件,不触碰 Shopee,不自动回写 Excel。
- 测试:新增/调整 DB、AI、GUI 单测覆盖只重标题保留封面、只重封面保留手动标题、重置全部、多选/筛选范围、committed 警告、运行中禁用、提交历史下补封面。
- 验证:`python -m py_compile app\db.py app\ai.py app\gui\tabs\generate.py tests\test_db.py tests\test_ai.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_db.py` 通过(7 tests);`python -m unittest discover -s tests -p test_ai.py` 通过(24 tests);相关 GUI 用例通过;`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(212 tests)。
## 【2026-07-06】T-535 完成 · cmhub 生成标题读取等待固定 600 秒
- 状态:DONE
- 文档:`docs/06-tasks.md` 新增 T-535,`docs/current-state.md` 记录当前 cmhub 生文请求超时口径。
- 代码:`app/ai.py` 新增 `CMHUB_TITLE_READ_TIMEOUT_SECONDS = 600`,cmhub `gen_title()` 的 `title_request` 读取等待固定 600 秒;连接超时仍取 `ai.cmhub.connect_timeout`,重试次数仍取 `ai.retry`。
- 边界:封面生成和图片下载继续按分辨率读取等待;direct 兼容路径不变;未改配置 schema、cmhub HTTP 协议、DB、Excel、Shopee/CDP 或 GUI 交互。
- 测试:`tests/test_ai.py` 更新 cmhub 标题请求 timeout 断言为 `(connect_timeout, 600)`。
- 验证:`python -m py_compile app\ai.py tests\test_ai.py` 通过;`python -m unittest discover -s tests -p test_ai.py` 通过(24 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(212 tests)。
## 【2026-07-07】T-536 完成 · GUI 按钮圆角全局统一
- 状态:DONE
- 文档:`docs/06-tasks.md` 将 T-536 标记为 DONE,`docs/current-state.md` 记录全局按钮基础样式和语义色按钮叠加规则。
- 代码:`app/gui/widgets.py` 新增 `BUTTON_BASE_STYLE`、`BUTTON_RADIUS_PX`、`_warning_outline_button_style()` 和共享 `_outline_button_style()`;`MainWindow` 全局应用按钮基础 QSS;③「开始更新」改为 warning 语义色叠加,①「删除批次」/④「删除账号」/①「未匹配」继续复用 danger 语义色叠加。
- 边界:只改 GUI 样式层;未改按钮行为、启用/禁用逻辑、DB、Excel、Shopee/CDP 或 AI 流程。
- 测试:`tests/test_gui.py` 补充全局 `QPushButton` 基础样式断言,并确认 warning/danger 按钮保留语义色但不再各自硬写 `border-radius`/`padding`。
- 验证:`python -m py_compile app\gui\widgets.py app\gui\main_window.py app\gui\tabs\apply.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(95 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(212 tests)。
## 【2026-07-07】T-537 完成 · 品牌名显示为蝦皮圈優化助手
- 状态:DONE
- 文档:`docs/06-tasks.md` 将 T-537 标记为 DONE;`README.md` 新增中文品牌入口;`AGENTS.md`、`docs/00-ai-start-here.md`、`docs/README.md`、`docs/01-vision.md`、`docs/ui/` 线框图和 `docs/current-state.md` 已把产品显示名更新为「蝦皮圈優化助手(代号 cmshopee)」。
- 代码:`app/gui/main_window.py` 主窗口标题改为「蝦皮圈優化助手」;`app/gui/__init__.py` 的 PySide6 缺失启动提示带上中文品牌名;`tests/test_gui.py` 补充窗口标题断言。
- 边界:只改显示层和文档层;`cmshopee` 继续作为仓库、包名、exe、数据库、日志、配置、回写后缀和兼容标识符保留;未改任何文件名、schema、AI、Excel、DB、CDP 或 Shopee 更新流程。
- 验证:`python -m py_compile app\gui\main_window.py app\gui\__init__.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_gui.py` 通过(95 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过;`git diff --check` 通过。
## 【2026-07-07】T-538 完成 · 打包产物用户数据收进 data/
- 状态:DONE
- 代码:`app/appconfig.py` 新增 `data_dir` 路径解析、`data/` 默认根、旧顶层布局迁移、冲突保护和可写性检测;`main.py` 移除打包版 `chdir`;GUI 启动时先准备数据目录,不可写时弹中文错误并退出。
- 路径:`config.json`、`config/ai_models.json`、`config/cmhub.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/`、`logs/`、`prompts/`、`title_prompt.txt` 等默认统一落到 `data/`;配置里保存的相对路径仍保持便携,运行时解析为绝对路径。
- 打包:`.gitignore` 新增 `data/`;`scripts/build_exe.ps1` 发布目录校验禁止带出 `data/`;更新方式改为覆盖发布包里的程序文件和依赖文件并保留 `data/`。当前 PyInstaller 5.13.2 为扁平 onedir,不要求存在 `_internal/`。
- 文档:`docs/06-tasks.md` 将 T-538 标记为 DONE;同步 `docs/packaging.md`、架构/技术栈/API/排障/路由/current-state 和 UI 线框图里的数据路径说明。
- 边界:未改 DB schema、AI HTTP 协议、Excel、账号、CDP 或 Shopee 页面操作流程;旧 DB 已存图片路径继续按原路径读取。
- 验证:`python -m py_compile app\appconfig.py app\diagnostics.py app\prompts.py app\editor.py app\gui\__init__.py app\gui\main_window.py app\gui\tabs\generate.py app\gui\tabs\settings.py app\gui\widgets.py main.py tests\test_appconfig.py tests\test_packaging.py tests\test_gui.py` 通过;`python -m unittest discover -s tests -p test_appconfig.py` 通过(14 tests);`python -m unittest discover -s tests -p test_packaging.py` 通过(3 tests);`python -m unittest discover -s tests -p test_gui.py` 通过(95 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(217 tests);`git diff --check` 通过。
## 【2026-07-07】T-538 修正 · 区分数据迁移冲突和目录不可写
- 现象:旧布局数据和 `data/` 里同名数据同时存在时,GUI 标题仍显示“数据目录不可写”,但实际原因是迁移冲突。
- 代码:`appconfig` 新增 `DataMigrationConflictError` 与 `DataDirectoryWriteError`;旧布局同名冲突抛“数据迁移冲突”,目录创建/写入失败才抛“数据目录不可写”。GUI 启动时按错误类型显示不同中文弹窗标题。
- 文档:`docs/packaging.md` 和 `docs/current-state.md` 记录冲突与不可写的区别。
- 边界:只改启动错误分类和提示;不自动合并、不删除、不移动用户旧数据;未改 DB schema、路径解析、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-538 修正 · 打包目录结构按 PyInstaller 5 扁平 onedir 记录
- 现象:实际 `dist/cmshopee` 目录包含 `cmshopee.exe`、DLL/PYD 文件和依赖目录同级,并不是 `cmshopee.exe` + `_internal/` 两个条目。
- 原因:当前 `requirements-build.txt` 锁定 PyInstaller 5.13.2,onedir 默认输出为扁平结构;`_internal/` 属于后续升级 PyInstaller 6.x 后再评估的布局,不是当前验收标准。
- 文档:`docs/packaging.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`docs/current-state.md` 已改为发布整个 `dist/cmshopee` 文件夹,验证重点是不得带出 `data/` 和用户本地数据。
- 代码:`scripts/build_exe.ps1` 打包成功后会提示当前依赖布局;无 `_internal/` 时明确说明扁平结构正常,并提醒发布整个 `dist/cmshopee` 目录。
- 边界:未升级 PyInstaller,未改变 spec 产物布局,未改运行时路径、DB、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-540 文档 · 打包版本号与 release 产物命名统一
- 需求:参考 `D:\chengma\cmbot` 的打包版本控制方式,解决当前 `cmshopee` 没有统一版本源、发布目录不带版本号、GUI 标题和构建产物无法一致追踪的问题。
- 文档:`docs/packaging.md` 新增“版本号与发布目录(T-540 方案)”,定义 `app/version.py` 为唯一版本源、`release\cmshopee-<APP_VERSION>\` 发布目录、`cmshopee-<APP_VERSION>-portable.zip` 便携包、`version.txt` 和中文 README;`docs/06-tasks.md` 新增 T-540;`docs/current-state.md` 记录后续任务口径。
- 决策:本阶段只做手动发版版本号一致性,不搬 `cmbot` 的 `Launcher.exe`、`manifest.json`、SHA-256 下载校验、强制更新或在线更新机制。
- 边界:本轮只更新文档,未改代码、构建脚本、测试、运行时路径、DB、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-540 完成 · 打包版本号与 release 产物命名统一
- 状态:DONE
- 代码:新增 `app/version.py`,统一维护 `APP_NAME`、`APP_CODE_NAME`、`APP_VERSION` 和 `display_name()`;GUI 主窗口标题改为 `蝦皮圈優化助手 v<APP_VERSION>`,PySide6 缺失提示继续读取统一品牌名。
- 打包:`scripts/build_exe.ps1` 从 `app/version.py` 读取版本,最低支持 Python 3.7.9,低于 3.7.9 直接失败;PyInstaller 仍输出 `dist\cmshopee`,随后组装 `release\cmshopee-<APP_VERSION>\`,写入 `version.txt`、中文 `README.txt`,并生成 `release\cmshopee-<APP_VERSION>-portable.zip`;`release/` 已加入 `.gitignore`。
- 兼容:脚本源码保持 ASCII,中文构建提示和 README 文案用 UTF-8 base64 在运行时解码,避免 Windows PowerShell 5.1 误读 UTF-8 无 BOM 导致解析失败。
- 测试:`tests/test_packaging.py` 覆盖版本源、release 命名、zip、`version.txt`、Python 3.7.9 最低版本校验和数据排除脚本逻辑;`tests/test_gui.py` 改为断言窗口标题读取统一版本源。
- 边界:不做 Launcher、manifest、sha256 下载校验、在线更新、强制更新或自动替换程序目录;未改 DB schema、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-540 修正 · 打包兼容 Python 3.7.9
- 需求:用户确认当前项目在 Python 3.7.9 环境可运行,打包文档与脚本不应继续要求 Python 3.10+。
- 代码:`scripts/build_exe.ps1` 的最低版本校验改为 Python 3.7.9;低于 3.7.9 才失败。
- 文档:`docs/packaging.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`docs/current-state.md` 改为当前发布构建兼容 Python 3.7.9+,CI 继续使用 Python 3.11。
- 测试:`tests/test_packaging.py` 改为断言构建脚本包含 Python 3.7.9 最低版本校验,并防止重新写回 Python 3.10+ 阻断。
- 边界:只改打包版本要求、文档和测试;未改运行依赖版本、业务逻辑、DB、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-540 修正 · 打包固定使用 Python 3.10
- 需求:用户已安装 Python 3.10,正式打包需要固定使用 Python 3.10,避免 PATH 上的 Python 3.7.9 或其他版本参与发布构建。
- 代码:`scripts/build_exe.ps1` 改为固定调用 Windows Python Launcher 的 `py -3.10`,读取到的版本必须是 `3.10.x`;`python -m PyInstaller` 改为 `py -3.10 -m PyInstaller`,并在 PyInstaller 退出码非 0 时直接失败。
- 文档:`docs/packaging.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`docs/current-state.md` 改为正式打包固定 Python 3.10;安装打包依赖命令改为 `py -3.10 -m pip install -r requirements-build.txt`。
- 测试:`tests/test_packaging.py` 改为断言构建脚本固定 `py -3.10`,且不再使用裸 `python -m PyInstaller` 或 3.7.9 最低版本口径。
- 验证:`py -3.10 -m pip install -r requirements-build.txt` 成功;`py -3.10 -m compileall app main.py` 通过;`py -3.10 -m unittest discover -s tests` 通过(219 tests);`powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1` 成功,输出显示 `使用 Python 3.10: 3.10.11` 并生成 `release\cmshopee-0.1.0-portable.zip`。
- 边界:只改正式打包解释器选择、文档和测试;未改运行依赖版本、业务逻辑、DB、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-541 文档 · 打包版窗口左上角显示不全
- 现象:运行打包后的 `dist/cmshopee/cmshopee.exe` / release 包时,Windows 10 虚拟机或小分辨率环境可能出现主窗口贴到屏幕左上角、左边和标题栏显示不全,用户看不到品牌标题且无法拖动窗口到中间。
- 原因:`MainWindow.__init__()` 当前只 `resize(1180, 760)`,没有读取 `QApplication.primaryScreen().availableGeometry()` 限制初始尺寸、居中显示,也没有保证窗口 frame/title bar 完整落在可用屏幕内。
- 文档:`docs/06-tasks.md` 新增 T-541;`docs/troubleshooting.md` 增加临时处理(`Alt+Space` → `M` → 方向键移动)和正式修复方案;`docs/packaging.md` 打包验收清单新增标题栏完整可见/可拖动检查;`docs/current-state.md` 记录 T-541 为下一步优先修复。
- 方案:后续代码在主窗口启动时根据屏幕可用区域限制尺寸并居中,保证标题栏可见;小屏继续适配,不保存坏的历史窗口坐标。
- 边界:本轮只更新文档,未改 GUI 代码、打包脚本、业务逻辑、DB、AI、Excel、CDP 或 Shopee 流程。
## 【2026-07-07】T-541 完成 · 打包版主窗口初始位置与小屏适配
- 状态:DONE
- 代码:`app/gui/main_window.py` 新增 `_fit_and_center_window()`,启动时读取 `QApplication.primaryScreen().availableGeometry()`,将主窗口尺寸限制在可用屏幕范围内并居中;首次 `showEvent` 后再应用一次,避免打包版在 Windows 10 VM/小分辨率环境标题栏出屏。
- 文档:`docs/06-tasks.md` 将 T-541 标记为 DONE;`docs/troubleshooting.md`、`docs/packaging.md`、`docs/current-state.md` 更新为当前修复与打包验收口径。
- 测试:`tests/test_gui.py` 新增小屏和大屏 mock geometry 单测,覆盖尺寸限制与居中位置;`scripts/build_exe.ps1` 已重新构建 release,确认 T-541 合入后仍可打包。
- 边界:不保存/恢复窗口历史坐标;未改业务流程、DB、AI、Excel、CDP/Shopee、打包产物结构或用户数据路径。
## 【2026-07-07】T-540 修正 · 当前打包布局统一为 PyInstaller 6
- 现象:T-540 代码和 `requirements-build.txt` 已固定 PyInstaller 6.11.1,构建脚本也会校验 `_internal/`,但 `docs/packaging.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`docs/current-state.md` 仍有当前 PyInstaller 5.13.2 扁平 onedir 的表述。
- 文档:已把当前打包口径统一为 PyInstaller 6.11.1,产物必须包含 `cmshopee.exe` + `_internal/`;用户升级说明改为整体覆盖新版程序文件、`_internal/`、`version.txt`、`README.txt`,并保留旧目录 `data/`。
- 边界:只修正文档一致性;历史 T-538 流水仍保留当时 PyInstaller 5 的记录;未改业务流程、DB、AI、Excel、CDP/Shopee 或用户数据路径。