Files
cmshoppe/progress.md
T
chengma 3a79e34eab docs: 记录AI模型category启动报错排查
- 新增 troubleshooting 文档,说明 category 缺失导致启动失败的原因

- 补充安全修复步骤,避免泄露或提交 config/ai_models.json 中的 API Key

- 更新文档导航、技术栈、当前状态和进度记录
2026-06-29 10:37:27 +08:00

663 lines
80 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。