diff --git a/docs/task/73-采购任务安全恢复.md b/docs/task/73-采购任务安全恢复.md new file mode 100644 index 0000000..c165945 --- /dev/null +++ b/docs/task/73-采购任务安全恢复.md @@ -0,0 +1,77 @@ +# 73 完善采购任务崩溃恢复与安全门禁验证 + +- 类型:需求 +- 父级大工单:#1 +- 所属 MVP / 版本:#2 Client 可运行任务闭环(MVP) +- 状态:已完成,待用户验收 +- 日期:2026-08-10 +- Gitea 工单:http://ilaer.eicp.net:8418/chengma/cmautobuy/issues/73 + +## 背景与目标 + +采购演练可以执行和领取后,还需要明确处理进程退出、设备断开、结果 +提交失败和结果不确定。本任务用 SQLite 已提交事实作为唯一恢复依据, +并证明只要 `irreversible_action_at` 有值,程序就不会再次执行采购。 + +## 最终方案 + +- 每个采购关键动作前,先同时保存任务和执行记录的 `current_step`。 +- 启动时发现无不可逆标记的中断采购演练,先关闭旧执行记录,再把任务 + 改回待执行;新演练必须创建更大的 `attempt_no` 和新 `attempt_id`。 +- 有不可逆标记时,任务和运行改为 `manual_review / reconcile_purchase`。 +- 新增独立 `PddPurchaseReconcileAdapter` 和 `PurchaseReconcileService`,只提供读取 + 订单候选能力,没有选规格、设数量、提交订单或付款方法。 +- 唯一匹配、未找到、多候选和未知结果均保存到执行诊断,任务仍等待人工确认。 +- 采购的设备断开等可重试技术错误也先转人工处理,不隐式重跑采购。 +- Outbox 继续优先补交;结果已落库时不再访问 PDD。 +- `ClaimCapabilities` 只接受 `dry_run`,`live` 在数据对象创建时就被拒绝。 +- 界面为采购演练、结果待提交、只读核对和人工处理步骤提供中文说明。 + +## 改了哪些文件 + +- `client/src/task_repository.py`:采购中断恢复、最新执行记录、只读核对结果。 +- `client/src/pdd_purchase_reconcile_adapter.py`:只读核对边界。 +- `client/src/purchase_reconcile_service.py`:核对条件、读取、记录与人工处理。 +- `client/src/task_dispatcher.py`、`client/src/pdd_ui_event.py`:恢复分支优先于普通采购。 +- `client/src/purchase_task_service.py`:保留服务步骤和诊断,采购错误不自动重跑。 +- `client/src/admin_gateway.py`:从数据边界禁止 `live`。 +- `client/src/task_detail_view.py`:采购和恢复步骤的中文文案。 +- `client/test/`:崩溃步骤、重启、不可逆恢复、设备断开、重复轮询和界面文案测试。 +- `docs/client/`:同步架构、数据模型、界面和质量安全基线。 + +本任务没有修改数据库表结构,直接使用已有的 `current_step`、 +`irreversible_action_at` 和 `diagnostics_json`。 + +## 验收结果 + +| 验收标准 | 结果 | +|---|---| +| 采购关键步骤在动作前持久化 | 通过 | +| 无不可逆标记时关闭旧运行后才恢复 | 通过 | +| 不可逆标记非空时 purchase 调用次数为 0 | 通过 | +| 已落库 Outbox 只补交,不重跑手机流程 | 通过 | +| 重复启动和重复轮询不会创建并发采购运行 | 通过 | +| 停止、设备断开、验证码、登录失效和未知结果安全停止 | 通过 | +| 采购继续禁止“重新执行” | 通过 | +| 采购与恢复步骤使用明确中文 | 通过 | +| `purchase_mode=live` 不能通过参数开启 | 通过 | +| 语法、全量测试和离屏界面冒烟 | 通过 | + +## 测试 + +- 执行的命令: + - `C:/Python310/python.exe -m unittest discover -s test -p "test_purchase*.py" -v` + - `C:/Python310/python.exe -m unittest discover -s test -p "test_task_repository.py" -v` + - `C:/Python310/python.exe -m unittest discover -s test -p "test_task_detail_view.py" -v` + - `C:/Python310/python.exe -m unittest discover -s test` + - `C:/Python310/python.exe -m py_compile src/admin_gateway.py src/pdd_ui_event.py src/pdd_purchase_reconcile_adapter.py src/purchase_reconcile_service.py src/purchase_task_service.py src/task_detail_view.py src/task_dispatcher.py src/task_repository.py` + - 按 `client/AGENTS.md` 设置 `QT_QPA_PLATFORM=offscreen` 并创建 `MainWindow` +- 结果:采购专项 12 项通过;Repository 14 项通过;Client 全量 207 项通过; + 语法检查通过;离屏冒烟输出 `OK`。Qt 同时输出环境缺少字体目录警告,不影响本次结果。 +- **未验证到的部分**:未连接真实 Admin、Android 设备或 PDD 页面;没有执行 + 真实崩溃进程杀死,崩溃点由可注入 Adapter 抛错和 SQLite 历史状态构造验证。 + 本工单不代表真实下单安全门禁已放行。 + +## 相关提交 + +- `c86c748` 完善采购任务安全恢复