# 执行进度记录 > 本文件是只追加的历史流水,记录任务执行过程、验证命令、阻塞点和关键决策。 > 当前目录、当前命令、下一个可领取任务等可覆盖快照,写入 [`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//_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_`;`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:/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//_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:///portal/` 改为 `https:///`,默认即 `https://seller.shopee.tw/`;更新 `tests/test_editor_login.py`、`docs/api.md`、`docs/04-architecture.md`、`docs/current-state.md`。 - 决策:商品编辑页 URL 仍保持 `/portal/product/?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/` 关闭浏览器 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//_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`。