Files
cmautobuy/docs/client/06-quality-security.md
T

241 lines
12 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.
# 06 Client 质量、安全与测试基线
- 文档状态:基线草案,待质量评审
- 适用范围:Client 源码、配置、数据库、自动化和发布产物
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。没有标注的默认是 `[必须]`。看不懂的词查 [术语表](00-glossary.md)。
## 1. 质量目标
- 同一台 Client 不重复执行或重复提交同一个采购任务;崩溃重启后也不重复下单。
- 网络和 Admin 故障不丢失已完成结果。
- 设备或 PDD 页面异常可诊断,不以错误坐标继续执行高风险操作。
- 所有阻塞工作离开 Qt 主线程,窗口在长任务期间保持可用。
- 代码、接口、数据库和诊断产物不泄露凭据及不必要的个人数据。
## 2. 测试分层
### 2.1 单元测试
必须覆盖:
- 任务状态转换和非法转换;
- 金额、数量、价格上限和规格校验;
- Admin DTO 与 `pdd_data` JSON 验证;
- SQLite Repository、迁移和默认排序;
- Outbox 幂等、退避和崩溃恢复;
- 销量、评价和价格文字解析;
- 控件树坐标解析、规格匹配和选中状态判断;
- 订单候选匹配规则。
PDD 解析测试优先使用脱敏的 XML 固件,不要求每次连接真实设备。
### 2.2 集成测试
使用临时 SQLite 和 Mock Admin 覆盖:
- 原子领取和重复领取;
- 提交一个 Admin 侧已取消的任务,仍被接受并标记 `succeeded`;
- 采集/采购任务分派;
- 结果先落库再进入 Outbox;
- Admin 超时后重试相同幂等键;
- 程序在提交前、下单阶段和提交后异常退出;
- 窗口关闭后后台结果不会更新已销毁界面。
### 2.3 界面测试
- Python 语法编译;
- 离屏实例化主窗口;
- 表格模型排序、筛选和增量加载;
- 开始/停止按钮防重复;
- Ctrl+F、Enter 和 Tab 顺序;
- 加载、空、错误、离线、运行和人工处理状态;
- 浅色、深色、高对比度和 100%–200% 缩放。
### 2.4 设备测试
真实设备测试必须显式标记,不应混入默认快速测试:
- PDD 首页、商品页、规格面板和订单列表识别;
- 不同商品规格数量、滚动方式和缺货状态;
- 登录失效、验证码、网络慢、页面变化和弹窗;
- 采集完整性;
- 采购演练及订单核对。
### 2.5 契约和端到端测试
- Mock Gateway 与 Http Gateway 对同一接口测试集通过。
- Admin 提供可测试环境后执行版本、错误和幂等契约测试,含"已取消任务的结果仍被接受"。
- 端到端测试从任务领取开始,以 Admin 确认结果和本地状态一致结束。
## 3. 采购安全门禁
真实下单开关默认关闭。全部满足后才能通过独立工单启用:
1. Admin 原子领取和结果幂等已经通过联合测试。
2. Outbox 断网和重启恢复测试通过。
3. 商品编号、标题、规格、数量、库存和价格保护校验通过。
4. 最终下单前后均有持久化步骤标记。
5. 进程在不可逆阶段退出后只执行订单核对,不会重新下单。
6. 订单匹配能识别唯一候选;多个候选进入人工处理。
7. 演练模式在代表性商品和设备上通过验收。
8. 操作人员明确确认真实下单范围和安全设置。
真实下单不得通过调试参数、默认配置或界面误操作意外开启。
当前版本的 `ClaimCapabilities` 仅接受 `purchase_mode=dry_run`,采购
Adapter 不提供提交订单或付款方法。开发者不能把自动化测试通过当成真实
下单门禁放行;仍需项目负责人和实际操作人员共同确认。
### 3.1 当前自动化安全检查
- 关键手机动作前已持久化 `current_step`。
- 无不可逆标记的中断会先关闭旧运行,新运行使用新 `attempt_id`。
- 有不可逆标记的中断只调用只读核对,采购 Adapter 调用次数为 0。
- 已落库 Outbox 只补交,不重跑手机流程。
- 停止、设备断开、验证码、登录失效和结果不确定都保留稳定错误和诊断。
- `purchase_mode=live` 会在数据对象创建时被拒绝。
- 正式 uiautomator2 采购 Adapter 只点击商品页采购入口和目标规格,
`enter_confirmation` 与 `stop_before_submit` 均为只读检查。
- 单元测试必须断言到达最终确认页后没有产生第二次底部按钮点击。
上述只证明演练和恢复代码的默认安全性,**不代表真实下单已放行**。
## 4. 自动化防护
- 点击前必须确认包名、页面类型、目标语义和坐标边界。
- 高风险点击应在最新控件树上重新定位,不使用长时间缓存的坐标。
- 规格选择后读取最新控件树并验证选中状态。
- 最终下单前重新读取实际价格、数量和规格。
- 页面出现验证码、登录、支付、权限请求或未知模态层时停止并转人工处理。
- uiautomator2 连接由单一工作线程独占,任务间清理临时状态。
- 所有循环具有超时、最大滑动次数和可取消检查。
> **运营提醒(不是代码规则):** Admin 超时重派可能导致同一任务被两台 Client 各下一单,
> 产生重复的未付款订单。人工审核时取消多余订单即可。但未付款订单长期堆积可能触发平台风控,
> 需要操作人员留意,不要放着不管。
>
> 将来若要在程序里防这个,正确的加固点是**最终下单前的最后一次校验**(本节已要求重新读取价格、
> 数量和规格,顺带确认任务有效性即可),**不要**改回周期性回查 Admin 状态。
## 5. 错误分类
建议使用稳定错误代码:
| 分类 | 示例 |
|---|---|
| `ADMIN_*` | 认证失败、超时、请求无效、幂等冲突 |
| `DEVICE_*` | 设备离线、ADB 失败、应用未启动 |
| `PDD_PAGE_*` | 页面超时、未知页面、登录失效、验证码 |
| `PDD_DATA_*` | 标题缺失、规格不完整、价格解析失败 |
| `PURCHASE_*` | 规格缺货、价格超限、下单状态不确定 |
| `ORDER_*` | 未找到订单、多个候选、详情不匹配 |
| `DB_*` | 迁移失败、写入失败、数据库锁超时 |
| `OUTBOX_*` | 提交失败、幂等冲突、结果被拒绝 |
错误反馈必须说明发生了什么、已保留什么以及下一步是什么。原始异常堆栈仅写入诊断日志,不直接展示给普通用户。
## 6. 日志与诊断
日志至少包含:
- 时间、级别、模块和稳定事件名;
- `client_id`、`remote_task_id`、`attempt_id` 和请求编号;
- 当前步骤、持续时间、结果和稳定错误代码;
- Admin 响应状态,但不记录认证头和完整敏感正文。
建议事件:
- `task_claim_started/succeeded/empty/failed`
- `task_claimed`
- `task_step_changed`
- `task_result_persisted`
- `outbox_send_started/succeeded/failed`
- `purchase_irreversible_step_entered`
- `order_reconcile_completed`
失败 Artifact 目录建议:
```text
artifacts/<remote_task_id>/<attempt_id>/
├── metadata.json
├── hierarchy.xml
├── screenshot.png
└── steps.jsonl
```
Artifact 写入前应脱敏,数据库只保存引用。保留周期由设置控制,删除不得影响任务结果和审计字段。
## 7. 凭据与隐私
- Admin 必须使用 HTTPS;生产环境不得允许静默忽略证书错误。
- Token 优先保存到 Windows 凭据管理器或由安全环境注入。
- **`data/` 目录是明文的,且设计上就是"整个拷走",凭据绝不能写进去**,见 [03](03-data-model.md) §2.1。
- 设置页只能显示认证状态或掩码值。
- 日志、数据库、截图、XML、Gitea 工单和 `docs/task` 都不得包含 Token、Cookie、密码或支付信息。
- 只保存完成任务所需的订单编号和时间,不采集无关收货人、地址、电话等个人数据。
- 不实现验证码、风控或平台安全机制绕过。
## 8. 数据可靠性
- 数据库迁移前创建可恢复备份或采用可回滚迁移步骤。
- 所有可写文件只能落在 `data/` 下,路径一律通过 `data_dir()` 取,见 [03 数据模型](03-data-model.md) §2.2。
- 每个任务状态变化和执行记录在同一事务内保持一致。
- `result_pending` 任务必须存在对应结果和 Outbox 记录。
- Outbox 发送成功前不得删除结果正文。
- 迟到的 Admin 响应不得覆盖更新的本地执行状态。
- 数据库损坏、磁盘写满和只读目录必须产生可操作错误,不能继续下单。
### 8.1 在线更新安全
- 清单和更新包只允许 HTTPS,不提供忽略证书错误的开关;URL 不得包含账号密码、
查询参数或片段,避免凭据随地址写入 SQLite 或错误提示。
- 更新包必须与最终清单 URL 同源,并同时校验清单声明的字节大小和 SHA256。
- 清单、压缩包和解压后总大小均有限制;ZIP 只能包含安全的 `app/` 内容,拒绝路径
穿越、绝对路径、反斜杠路径和符号链接。
- 下载和解压只写 `data/update/`;`client.db`、日志、Artifact 和其他设置不得进入
替换范围。
- Launcher 发现主程序仍在运行时不得替换目录;替换失败必须恢复旧 `app`,新版本
未通过健康检查时恢复 `app.old`。
- SHA256 不证明发布者身份。当前发布服务器整体失陷不在保护范围内,正式扩大分发
前应另行评估签名清单和代码签名。
## 9. 性能和响应性
- 表格使用模型/视图和增量加载,不为每个单元格创建 QWidget。
- 搜索、排序和分页使用索引支持的 SQLite 查询。
- 高频进度信号需要节流,避免每次 XML 节点变化都刷新界面。
- 大型 XML 解析、截图和文件写入位于工作线程。
- 目标基线:普通本地搜索在典型数据量下保持即时反馈,任务运行时界面可持续操作。
## 10. 发布门禁
每个版本至少满足下面全部条件。**"谁负责"这一列是为了让你知道哪些不用自己扛**——不是你负责的项,去找对应的人,不要自己拍板放行。
| # | 门禁项 | 怎么验证 | 谁负责 |
|---|---|---|---|
| 1 | 依赖版本已固定,且能重复安装 | `client/requirements.txt` 里每一行都带 `==`;在干净环境执行 `pip install -r requirements.txt` 能装成功 | 开发者 |
| 2 | 全部默认单元测试和集成测试通过 | 跑测试命令,不允许有跳过而没说明的用例 | 开发者 |
| 3 | UI 离屏冒烟测试通过 | 见 [client/AGENTS.md](../../client/AGENTS.md) §验证 第 2 条 | 开发者 |
| 4 | 数据库从上一发布版本迁移成功 | 拿上一版本的 `client.db` 副本启动新版本,数据不丢 | 开发者 |
| 5 | Mock Admin 契约测试通过 | Mock 和 HTTP 两个实现跑同一套测试 | 开发者 |
| 6 | Windows 干净环境启动测试通过 | **未打包版本**:找一台没装过本项目的机器,照 [00 上手指南](00-getting-started.md) 从头走一遍。**已打包版本**:把整个文件夹拷到一台**没装 Python** 的机器上双击 `Launcher.exe` | 开发者 |
| 6b | 升级不丢数据(仅打包版本) | 关闭程序并只换掉 `app/`,保留 `Launcher.exe` 和 `data/`;启动后任务、日志、设置都还在 | 开发者 |
| 6c | 发布清单与产物一致 | `autobuy——manifest.json` 中的版本、文件名、字节大小和 SHA256 与实际压缩包一致,更新包中没有 `data/` | 开发者 |
| 6d | 在线更新和回退通过 | 使用上一版本目录检查新版本、下载、下次启动替换、健康标记、文件占用失败和自动回退;确认数据库未被覆盖 | 开发者 |
| 7 | 日志和产物无敏感信息 | 翻一遍日志和 `artifacts/`,确认没有 token、Cookie、密码、收货人信息 | 开发者 |
| 8 | 开源和商业许可证已确认 | 新增依赖的许可证是否允许本项目的使用方式 | **项目负责人**(不是开发者自己判断) |
| 9 | 真实下单版本额外满足采购安全门禁 | 逐条核对 §3 的 8 项 | **项目负责人 + 操作人员共同确认** |
第 8、9 项开发者**不要自己判断放行**。新增依赖时把包名和许可证类型报给项目负责人;真实下单开关必须由项目负责人和实际操作的人一起确认,见 §3。
## 11. 任务完成定义
单元任务只有同时满足以下条件才算完成:
1. 工单验收标准逐项通过。
2. 代码、迁移、测试和必要文档同步完成。
3. 没有静默跳过的测试或未说明的真实设备假设。
4. 变更不泄露敏感信息,也不扩大自动化权限。
5. Gitea 工单更新最终结果和提交哈希。
6. 完成记录归档到 `docs/task`。