Files
cmautobuy/docs/client/01-requirements.md
T

299 lines
16 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.
# 01 Client 产品需求基线
- 文档状态:基线草案,待需求评审
- 适用范围:`client/`
- 产品类型:Windows 桌面自动化客户端
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。没有标注的默认是 `[必须]`。看不懂的词查 [术语表](00-glossary.md)。
## 1. 背景与目标
Client 从 Admin 获取拼多多任务,在一台已授权的 Android 设备上通过 uiautomator2 执行商品数据采集或采购操作,并将结果可靠地提交回 Admin。
产品目标:
1. 在统一界面中查看、搜索和跟踪**本机已领取**的全部 PDD 任务(含正在做的和做完的)。
2. 自动领取并串行执行采集任务和采购任务。
3. 在 Admin 或网络暂时不可用时保存结果并安全重试,不能丢失采集结果或重复采购。
4. 为失败、异常页面和不确定订单提供可诊断、可恢复的人工处理入口。
5. 在 Admin 尚未完成时,通过本地模拟接口完成大部分 Client 开发和测试。
## 2. 用户与外部系统
- **操作人员:**配置 Admin 和 Android 设备,启动或停止自动获取任务,查看状态和详情,处理异常。
- **Admin:**创建任务、分配任务、维护任务状态,并接收 Client 执行结果。
- **Android 设备:**登录并运行拼多多应用,由 Client 独占一个自动化执行会话。
- **拼多多应用:**被自动化操作的外部系统,不属于本项目控制范围。
## 3. 产品范围
Client 顶级导航仅包含:
1. **PDD 任务**:本机已领取任务的展示、搜索、自动执行和详情查看。
2. **设置**:Admin、Android 设备、自动化、安全和诊断设置。
设备连接不作为独立顶级模块,统一放入设置页。
**Client 不显示 Admin 任务池。** 本地只保存本机已领取的任务(含执行中和已完成),
不缓存"待领取"的任务。要拿新任务只有一条路——由任务引擎向 Admin 领取。
操作人员想知道队列里还有多少活没派下来,去 Admin 自己的界面看。
这条决定的完整理由见 [03 数据模型](03-data-model.md) §3.1。
## 4. 任务类型
### 4.1 采集任务
输入至少包含远程任务编号和商品链接。Client 应采集:
- 商品编号、商品链接和商品标题;
- 店铺名称(无障碍树未提供时允许留空并保存诊断证据);
- 已拼数量及原始显示文字;
- 评价数量及原始显示文字(首屏没有时,在进入规格面板前有限次缓慢向下浏览补采;到达页面底部或次数上限后仍没有时允许留空);
- 所有规格维度及其可见值;
- 每个颜色对应的价格、币种,以及当前控件树显示的规格可用状态;
- 采集时间、设备及必要诊断产物引用。
当前业务按颜色采价:进入规格面板后逐个选中颜色并立即读取稳定价格;尺码只
收集文字和当前显示的可用状态,不逐个选择、不单独采价。提交时仍以 SKU 规格
组合保存颜色与尺码关系,同一颜色的组合复用该颜色价格,
`price_observed_at` 只记录实际选中的颜色,不得虚构已选尺码。以后需要按尺码
区分价格或组合库存时,必须另行确认并改用 `price_granularity=sku`。
颜色已售罄、无法选中或未采到稳定价格时,不重试采价,也不阻断后续流程;该
颜色的价格相关字段留空。所有颜色处理完成后仍须采集尺码、保存本地结果并提交
Admin。规格 `available` 只反映页面控件状态,不能根据价格是否为空推断。
对于“1.2 万+”等近似数值,同时保存规范化数值、原始文字和近似标记。
### 4.2 采购任务
输入至少包含:
- 远程任务编号;
- 商品编号或商品链接;
- 目标颜色和尺码;
- 采购数量;
- 预期价格或最高允许价格;
- 任务版本及幂等信息。
Client 应执行:
1. 校验商品、规格、数量、库存和价格保护条件。
2. 选择指定颜色、尺码和数量。
3. 进入订单提交前状态并再次校验价格。
4. 在允许真实下单时提交订单。
5. 获取并核对订单编号和下单时间。
6. 保存本地结果并提交 Admin。
仅凭“我的订单”列表中的最新一条记录不能认定为当前任务订单。订单页必须读到
非空订单编号、有效下单时间和待付款状态;下单时间须位于本地
`order_submitted_at` 前后 5 分钟,且窗口内只能有一个候选。商品、规格、数量和金额
使用下单前已持久化的确认快照,不要求订单页重复提供。存在多个候选时进入人工处理
状态,不得猜测或重新下单。
## 5. PDD 任务页需求
页面分为三个稳定区域。
### 5.1 顶部命令区
- “获取任务”是持续任务引擎的主操作,启动后按钮文字切换为“停止获取”。
- 停止只停止领取新任务;当前任务应运行到安全停止点。
- 搜索条件至少支持任务类型、任务状态和关键词。
- 关键词搜索任务编号、商品编号和商品标题。
- “搜索”只查询本地数据库,不触发任务执行。
### 5.2 中部任务表格
表格显示的是**本机已领取的全部任务**,包括正在做的和早已做完的。已完成的任务永久保留,不会被清理。
表格要让操作人员一眼看出:这是什么任务、买的什么、什么规格、多少钱、现在到哪一步了。
**具体列有哪些、每列怎么显示,以 [05 界面交互规范](05-ui-specification.md) §5.1 为准**(那是唯一权威定义,改列只改那一处)。
本文只规定不可动摇的业务要求:
- 默认按 `updated_at DESC, id DESC` 稳定排序。
- 所有任务可通过增量加载访问,禁止为每个单元格创建常驻复杂控件。
- `pdd_data` 是详情数据,不作为隐藏表格列整体加载;表格模型只保留稳定任务编号,查看详情时从数据库读取。
- 单击行只选择,双击任务行或按 Enter 打开任务详情。
### 5.3 底部状态区
持续显示:
- 自动获取是否开启;
- 当前任务编号和类型;
- 当前执行步骤;
- 最近一次领取到任务的时间;
- 最近一次成功或可恢复错误摘要。
重要错误应同时使用持久信息条提供重试或进入设置的入口,不能只依赖自动消失提示。
## 6. 设置页需求
设置分为以下组:
1. **Admin:**服务地址、Client 编号、认证状态、请求超时和测试连接。
2. **Android 设备:**ADB 地址、拼多多包名、设备测试连接。
3. **自动化:**轮询周期、任务超时和演练模式。
4. **安全与诊断:**价格允许偏差、最大购买数量、日志目录、截图/XML 保留周期。
5. **软件更新:**当前版本、清单地址、账号、密码、保存/检查按钮和稳定状态文字。
密码和访问令牌不得以明文写入普通 SQLite 设置或日志。
## 7. 任务状态
任务共有 8 个标准状态:`claimed`、`running`、`result_pending`、`retry_wait`、`manual_review`、`succeeded`、`failed`、`cancelled`。
`retry_wait` 只为兼容旧数据和采集进程异常退出保留;自动任务不会选择它,也不会
自动重试失败任务。
没有"待领取"状态——任务在领取成功那一刻才写进本地库,见 [03 数据模型](03-data-model.md) §3.1。
**每个状态的中文显示、含义和允许的转换,以 [03 数据模型](03-data-model.md) §7 为准**(那是唯一权威定义,写代码和写测试都看那一张表)。
本文只强调两条产品级红线:
- 任何自动流程都不许把 `manual_review` 改回执行中状态,必须由人处理。
- Client 拿到任务就做完。中途 Admin 取消了这个任务,Client **不管**,照做完照提交,由人工审核时处理。
- 程序崩溃后,处于不可逆采购阶段的任务只能执行订单核对,**不能重新下单**。
## 8. MVP 范围
MVP 包含:
- 两模块 Fluent 界面;
- SQLite 数据库和迁移;
- 本地任务表格、搜索、筛选和详情;
- Mock Admin 接口;
- 任务领取与串行执行;
- 后台任务协调器、轮询、停止、重试和结果待提交队列;
- 真实商品采集及 SKU 数据组装;
- 采购演练模式,完成规格和数量选择但不执行最终下单;
- 重启恢复、断网、设备断开和重复提交测试。
MVP 之后:
- 接入真实 Admin HTTP 接口;
- 经过安全验收后启用真实订单提交;
- 完成订单核对和人工处理流程;
- 打包成 exe 并做生产环境验证(策略见 §8.1)。
### 8.1 打包策略
**打包动作属于 MVP 之后**,但策略现在就定下来,避免写代码时留下不好打包的结构。
**工具:** PyInstaller 6.x,**one-dir 模式**(不是 one-file)。
one-file 每次启动都要解压到临时目录,启动慢,出错几乎没法排查;one-dir 缺什么文件一眼就能看见。
**产出目录结构:**
```text
CMAutoBuy/ 整个文件夹拷到任何机器都能用
├── Launcher.exe 双击这个启动;它只负责启动主程序
├── app/ 主程序和运行时依赖,升级时整体替换
│ ├── CMAutoBuy.exe
│ ├── version.txt
│ └── dependencies/
│ ├── python310.dll
│ ├── PyQt5/Qt5/
│ │ ├── bin/ Qt5Core.dll / Qt5Gui.dll / Qt5Widgets.dll …
│ │ └── plugins/platforms/qwindows.dll
│ ├── qfluentwidgets/ qss 样式、字体、图标资源
│ ├── vendor/android-platform-tools/windows/
│ │ ├── adb.exe
│ │ ├── AdbWinApi.dll
│ │ └── AdbWinUsbApi.dll
│ └── …
└── data/ 本地数据,升级时保留(见 [03 数据模型](03-data-model.md) §2.1)
├── client.db
├── logs/
└── artifacts/
```
主程序使用 PyInstaller one-dir,内部依赖目录通过 `--contents-directory dependencies`
固定命名。根目录的 `Launcher.exe` 是独立轻量启动器,为后续“关闭主程序后再替换
`app/`”保留边界;当前版本不联网、不下载,也不自动更新。
**当前升级方式:** 关闭程序后只替换 `app/`,**`Launcher.exe` 和 `data/` 原样不动**。
数据库靠 `PRAGMA user_version` 自动迁移,见 [03](03-data-model.md) §2.3。完整便携包
用于首次安装;更新包只包含 `app/`,不得包含 `data/`。
每次构建在 `client/release/` 生成以下发布文件:
- `CMAutoBuy_<版本号>.zip`:在线更新包;
- `自动采集采购工具_<版本号>.zip`:带版本号的完整便携包;
- `自动采集采购工具.zip`:与带版本号便携包内容完全相同的下载别名;
- `autobuy_manifest.json`:Client 固定读取的在线更新清单;
- `autobuy_manifest_<版本号>.json`:与固定清单内容完全相同的归档副本。
清单中的更新包引用带版本号的文件名,便携包固定引用
`自动采集采购工具.zip`,并记录版本、字节大小和 SHA256;SHA256 用于发现下载
损坏,不等同于发布者身份认证。
**在线更新:**设置页默认显示已确认的发布清单地址和账号,密码由操作人员首次
填写并保存到 Windows 凭据管理器;URL 和账号作为非敏感设置保存在 SQLite,密码
不回填明文。保存完整配置后,Client 启动时在后台检查一次,也可手动检查。发现
新版本后必须由用户确认下载;下载、校验和安全解压在后台线程完成,只写入
`data/update/`。程序不会强制退出,用户下次通过 `Launcher.exe` 启动时才替换
`app/`。Launcher 保留一份 `app.old/`,目录移动失败或新版本没有写入健康标记时
恢复旧版本。更新包必须与清单同源且重定向不得跨源。一般地址只允许 HTTPS;当前
固定发布主机按已确认方案临时允许默认端口 HTTP 和 Basic Authentication。URL
不得包含账号密码、查询参数或片段,认证密码不得写入源码、Git、SQLite 或日志。
`0.1.0` 的 Launcher 没有应用在线更新的能力,因此第一次升级到带在线更新的
`0.2.0` 仍需人工替换一次完整程序;从 `0.2.0` 开始才可使用上述流程。当前没有
清单数字签名,发布服务器整体失陷不在 SHA256 的保护范围内,签名需要另建工单。
**已知风险点**(打包工单必须逐项验证):
| 风险 | 症状 |
|---|---|
| Qt 平台插件没收集到 | 启动即报 `could not find or load the Qt platform plugin "windows"` |
| `qfluentwidgets` 资源没收集到 | 能启动,但界面全白、控件没样式 |
| 项目内置 ADB 或配套 DLL 没收集到 | 启动时弹窗提示,设备操作保持中断 |
| `uiautomator2` 初始化流程 | 打包后没有 `python -m uiautomator2 init` 这条命令,需确认辅助 App 怎么装 |
| 杀软误报 | 空白版本信息的 exe 最容易被拦。必须给 exe 加图标和版本信息(产品名、版本号)。根治需要代码签名证书 |
| 装到 `C:\Program Files\` | 无写权限,数据被重定向到 VirtualStore,设置改了不生效 |
具体的 PyInstaller 参数和 hook 配置跟依赖版本强相关,**不在本文档固化**,
由打包工单在真实 Windows 环境上验证后写进归档。
## 9. 非目标
- 不开发 Admin 管理界面或 Admin 数据库。
- 不自动注册、登录或绕过拼多多验证码、风控和安全机制。
- 不自动完成支付;当前采购范围止于创建订单并获取订单信息。
- MVP 不支持一台 Client 同时并行控制多台设备。
- 不保证拼多多未公开界面在所有版本中保持兼容,必须通过版本化适配和诊断产物处理变化。
## 10. 非功能需求
- 所有网络、数据库长操作和 uiautomator2 操作不得阻塞 Qt 主线程。
- 同一设备同一时间只执行一个任务。
- 任务领取、结果提交和采购操作必须具有去重或幂等保护。
- 结果先本地持久化,再提交 Admin;Admin 暂时不可用不能导致重新采购。
- 时间在数据库和接口中使用带时区的 ISO 8601,内部优先使用 UTC,界面转换为本地时间。
- 金额以人民币分的整数保存,禁止使用浮点数作为业务金额。
- 任务、日志和诊断信息不得包含密码、访问令牌、Cookie 或不必要的个人信息。
## 11. 待确认事项
这些还没定下来,但**不许因此停工**。先按"临时默认值"做,等定了再按工单改。
| # | 待确认什么 | 临时默认值(先这么做) | 定下来之前会卡住什么 |
|---|---|---|---|
| 1 | Admin 的认证方式和 Client 注册方式 | Mock Gateway 不做认证;HTTP 实现预留 `Authorization: Bearer` 请求头,token 从设置读 | 只卡真实 HTTP 联调,不卡 MVP |
| 3 | 价格允许偏差 | 默认 0 分(即实际价格必须等于预期价格才继续),可在设置页改 | 不卡。先按最严的来 |
| 4 | 是否存在颜色、尺码之外的第三个规格维度 | **按"可能有任意多个维度"实现**,用 `dimensions` 数组,不要写死两个 | 不卡。写死才会返工 |
第 4 条特别注意:即使目前见到的商品都只有颜色和尺码两个维度,也**不许**把数据结构写成两个固定字段。见 [03 数据模型](03-data-model.md) §8.1。
**已定案(不再是待确认项):**
- Admin 新建采购任务固定为 `execution_mode=live`,不再提供演练选择;Client 不提供手工 live 授权,身份、已选 Android 设备和真实采购 Adapter 就绪时自动声明 live 并领取任务。
- Admin 只分配任务给指定 Client,Client 不读全局任务池。见 [04](04-admin-api-contract.md) §5.1。
- **没有租约、没有心跳、没有状态回查。** 任务流程只有领取、提交结果、提交失败三个调用;设置页另有无任务副作用的幂等 Client 登记调用。见 [04](04-admin-api-contract.md) §1、§4.1。
- 本地只存已领取任务,已完成任务**永久保留**。诊断产物(截图、XML、日志)仍按 `diagnostics.retention_days` 清理,两者不是一回事。见 [03](03-data-model.md) §3.1。