7.2 KiB
7.2 KiB
id, title, phase, deps, status, created, context_ref, work_branch, write_paths
| id | title | phase | deps | status | created | context_ref | work_branch | write_paths | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| T-216 | 设备下单命令投递、确认与恢复 | 2 |
|
DONE | 2026-07-28 | 827afc7 |
null |
|
问题 / 背景
T-215 只生成 PENDING_DELIVERY 下单授权。Android 仍停留在旧的手机人工选择流程,
也没有可靠方式知道 Admin 已授权哪个 candidate。直接在 Admin 浏览器推送或收到 HTTP
响应后立即执行都无法处理 Android 后台限制、断网、进程重启和响应丢失。
本任务把授权作为同一 execution 的可重放设备命令,由 Roubao 的现有前台服务主动 拉取。App 必须先把完整命令写入 Keystore-backed 加密状态,再确认收妥。T-216 不 执行拼多多页面动作。
冻结合约
拉取而非服务端推送
- App 在非终态 execution 的 30 秒 heartbeat 后,以及用户点击“立即同步”时调用
POST /api/v1/tasks/{task_id}/commands/next。不增加 WebSocket、FCM 或浏览器到 手机的直连通道。 - 请求必须携带 BUYER bearer、
X-Claim-Token,并提交当前device_id、execution_id、claim_generation。服务端校验 task、user、device、execution、 generation、token、有效租约、WAITING_CONFIRMATION和未请求取消。 - 没有命令返回
204。当前 active authorization 为PENDING_DELIVERY/DELIVERED/ACKNOWLEDGED时返回同一命令;一个 execution 不会 返回第二条 active 命令。
命令内容与持久身份
命令 schema_version=1、type=CREATE_PENDING_ORDER,只包含服务端快照和受控
observation,不包含第三方 URL:
{
"id": "authorization-uuid",
"schema_version": 1,
"type": "CREATE_PENDING_ORDER",
"authorization_version": 1,
"task_id": "uuid",
"execution_id": "uuid",
"task_content_sha256": "64-char-hex",
"original_sku": "雾霾蓝 / L",
"quantity": 2,
"candidate": {
"candidate_key": "64-char-hex",
"observed_ordinal": 1,
"title": "实际观测标题",
"sku_text": "雾霾蓝 / L",
"price_text": "39.80",
"card_signature": "64-char-hex",
"detail_signature": "64-char-hex",
"detail_evidence_sha256": "64-char-hex",
"specification_evidence_sha256": "64-char-hex"
},
"command_sha256": "64-char-hex",
"authorization_status": "DELIVERED"
}
observed_ordinal只用于重新定位提示,不能直接作为点击索引;T-217 必须重新核对 card/detail/SKU/证据。command_sha256是 schema v1 除自身和投递状态外的 canonical JSON SHA-256。 首次投递在事务内写入该 hash、delivered_at和ORDER_AUTHORIZATION_DELIVERED事件;重投不新增事件。- 已
ACKNOWLEDGED但 App 本地状态丢失时仍允许同一身份重新拉取,以便安全恢复; 不允许因此改写 hash 或创建新命令。
App 先持久化后确认
- 新增加密
PendingOrderCommand,保存完整快照、command hash、ACK 幂等键和确认 状态。先验证 schema/type、task/execution/content hash、数量和所有指纹,再一次store.save;保存失败不得发送 ACK。 - 保存成功后调用
POST /api/v1/tasks/{task_id}/commands/{command_id}/ack,请求包含 execution、 generation 和 command hash,并带 claim token 与Idempotency-Key。 - ACK 在事务内把
DELIVERED -> ACKNOWLEDGED,写acknowledged_at、幂等记录和ORDER_AUTHORIZATION_ACKNOWLEDGED事件。相同 key/body 或同 command/hash 重试 返回相同结果;hash、归属、状态或身份不匹配返回冲突。 - HTTP ACK 响应丢失时 App 保留同一 ACK key 重试。只有后端确认后本地
acknowledged=true,UI 显示“后台已授权,等待订单核验”;进程重启继续 ACK, 不能重新采集或领取其他任务。
旧手机人工确认收口
- 后台任务非空候选上传成功后,App 进入
WAITING_ADMIN_CONFIRMATION,不再显示 本地接受/拒绝候选按钮,也不提交旧的HUMAN_REVIEW + COMPLETE。 - 空候选仍可按现有安全终态回传;独立本地探针不受后台任务模式影响。
- 前台服务在等待 Admin 时继续 heartbeat、同步 outbox 和拉取命令;租约到期、 取消、认证失效或命令校验失败时保持安全停止。
数据模型
v9:
order_authorizations增加不可变command_sha256和投递计数/最后投递时间;device_order_command_ack_requests保存 device、幂等键、请求 hash、command 和时间;task_events增加ORDER_AUTHORIZATION_DELIVERED与ORDER_AUTHORIZATION_ACKNOWLEDGED。
存在命令投递/ACK 数据或新事件时 destructive down 必须失败。
验收要点
- 只有原 user/device/execution/generation/token 且租约有效时能拉取命令。
- 首次拉取原子进入
DELIVERED;网络重放返回相同 command/hash 且不重复事件。 - App 严格校验并先加密持久化,存储失败不 ACK,重启可恢复同一命令。
- ACK 幂等进入
ACKNOWLEDGED,错误 hash/command/设备和并发请求被拒绝。 - 后台候选流程等待 Admin,不再允许手机本地选择并提前完成任务。
- App 等待/已授权状态可见,但不打开拼多多、不选择 SKU/数量、不提交订单。
- v9 migration、Go test/race/vet、Android test/Debug/Release 和根验证通过。
边界
- 不重新打开图片搜索或定位 Admin 选择的商品;属于 T-217。
- 不选择 SKU/数量、进入确认订单页或比较最终金额;属于 T-217。
- 不点击确认购买、读取订单号或处理付款;属于 T-218/T-219。
- 不新增 FCM/WebSocket,不把第三方 URL 当命令,不授权支付。
执行记录
- 2026-07-28:T-215 实现提交
827afc7后领取。复用现有 30 秒前台 heartbeat 和 Keystore-backed 状态,选择 pull + durable-before-ACK,避免 Android 后台推送和 HTTP 不确定结果造成命令丢失。 - 2026-07-28:新增 v9、命令 pull/ACK API、同一授权重放和投递/确认审计事件。 HTTP 集成覆盖错误 hash、响应丢失后的相同 key 重试、ACK 后恢复拉取、事件不重复 及带数据禁止降级。
- 2026-07-28:Android
1.4.13 (18)增加命令 canonical hash 校验、加密持久化、 固定 ACK key、重启恢复和等待/已授权 UI。后台任务有候选后不再出现手机接受/ 拒绝入口,也禁止再次采集;空候选仍保留安全终态。 - 2026-07-28:Go/Kotlin 固定哈希向量一致;Android 测试证明存储失败时 ACK 调用数
为 0。
go test ./...、go test -race ./...、go vet ./...、Androidtest、 Debug/Release assemble 和根.\init.ps1全部通过。APK 已构建,未在本任务中 覆盖安装或执行拼多多页面动作。