1
T-246
ila edited this page 2026-08-07 16:36:56 +08:00
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.

同步来源:docs/tasks/T-246.md · commit afc651f75a3a


id: T-246 title: 自动接单与 goods_id 直达采购闭环 phase: 2 deps:

  • T-218
  • T-245 status: DONE created: 2026-07-29 context_ref: 97b2a18 work_branch: null write_paths:
  • docs/tasks/T-246.md
  • docs/current-state.md
  • docs/api.md
  • docs/04-architecture.md
  • backend-api/migrations/**
  • backend-api/internal/domain/**
  • backend-api/internal/usecase/**
  • backend-api/internal/repository/sqlite/**
  • backend-api/internal/transport/httpapi/**
  • backend-api/internal/transport/webui/**
  • android-buyer/app/src/main/**
  • android-buyer/app/src/test/**

需求 / 背景

低客单价采购不能依赖采购人员在手机上反复领取、开始、重新搜索和定位商品。Admin 创建 采购任务后,Roubao 应在登录、设备就绪且无其他有效任务时自动领取并开始图片搜索,将 最多 5 个候选的拼多多商品身份、链接、本地图片、标题、规格和价格保存到后台。采购员 只在 Admin 选择一次商品;之后 Roubao 自动进入该商品页、选择规格和数量、提交一笔 待付款订单并回传订单号与下单时间,付款仍由人员完成。

现有实现有四个缺口:

  1. IDLE 只在点击“获取任务”后调用 claim,CLAIMED 仍需人工确认开始。
  2. RUNNING/WAITING_CONFIRMATION 在授权过期后仍永久占用设备;App 的 SAFE_STOPPED 手动同步在真正请求后端前返回,后台取消无法完成。
  3. 候选 observation 虽预留 product_url/image_url,Android 实际始终提交空字符串; 候选图片只有受控详情/规格证据。
  4. 下单授权和设备命令没有 goods_id 或规范 URL,dry-run 会再次图片搜索,不能直接 进入 Admin 已确认的商品。

参考 Obsidian:

  • ila/项目文档/cmroubao/cmroubao-候选商品链接回传与goods_id方案.md
  • Projetos/cmroubao/Regras/goodsid.md

修订契约

自动调度

  • App 登录且无本地任务、无后台有效任务、设备 readiness 通过时,自动 heartbeat 并 claim 最早的 PENDING 任务;无任务按有界退避轮询。
  • claim 后完成参考图下载、SHA-256 校验和 2 秒稳定窗口,自动开始 execution;本地安全 VLM 配置可用时采用 AI_ASSISTED,否则使用确定性 MANUAL_FIRST,不能因模型配置 缺失阻塞采购。
  • 自动流程同一时刻只允许一个 claim、execution、候选探针或下单动作;进程恢复复用原 幂等 key,不得重复领取、重复开始或重复提交订单。
  • 当前任务进入后台确认、终态或安全失败后停止外部动作;终态清理完成后自动领取下一条。
  • “获取任务”和“立即同步”保留为故障重试,不再是正常流程必需操作。

过期任务恢复

  • CLAIMED 仍可按现有租约重新领取;RUNNING/WAITING_CONFIRMATION 授权过期后不得继续 自动化,也不得永久占用设备。
  • 后台已请求取消且 claim 已过期时,后端在受控恢复事务中直接转 CANCELED、结束未完成 execution、清空 claim 所有权并记录审计事件。
  • App 的 SAFE_STOPPED 手动/自动同步必须真正查询后台;后台任务已终止或不再属于当前 设备时清理本地加密任务并回到自动接单。
  • 不把过期的执行中任务重新排入 PENDING,避免在未知外部状态下重复执行。

候选商品身份和图片

  • 每个真实候选提取纯数字 goods_id,并生成唯一规范 URL: https://mobile.yangkeduo.com/goods.html?goods_id=<goods_id>。
  • 首选无障碍语义中的“商品编号/goods_id”,本地 OCR 仅作受控兜底;不能可靠提取时保持 空值、不得猜测。缺少 goods_id 的候选可以作为审计观察,但不能创建自动下单授权。
  • 候选回传保存 goods_id、规范 URL、标题、当前选中规格摘要、颜色/尺码目标、组合价格、 采集时间、PDD 版本、卡片/详情指纹以及 DETAIL/SPECIFICATION 本地证据资产。
  • Admin 展示本地鉴权图片并提供规范外链。外链使用新窗口及 noopener/noreferrer; 后端只执行严格语法/域名/路径/参数校验,绝不请求 PDD URL 或保存浏览器会话。
  • image_url 只允许作辅助字段;主要候选图片继续由本地证据 asset 提供,外链失效不影响 审计和授权。

授权和直达下单

  • candidate_key 仍是 Admin 选择主键;新身份版本同时绑定 execution、ordinal、 goods_id、详情指纹和规格证据 hash。
  • Admin 授权快照、设备命令和 command SHA-256 必须不可变地包含 goods_id、规范 URL、 原始 SKU、数量、候选规格/价格及现有证据指纹;URL 或 ordinal 不能单独授权。
  • App 收到授权后使用显式拼多多包名的 ACTION_VIEW 打开规范 URL。只能在确认进入 PRODUCT_DETAIL 且重新提取的 goods_id 与授权一致后继续。
  • 直达失败可使用现有参考图搜索作一次受控兜底,但最终仍必须唯一复核 goods_id; 不一致、跳转、下架或提取失败立即停止。
  • 颜色、尺码和数量仍按唯一可访问控件选择,每次动作后复读状态;价格、订单确认页商品 身份和总额必须通过现有 dry-run。
  • 最终继续使用 T-218 submission fence,最多点击一次“提交订单”,只回读待付款订单, 不点击支付、不保存 PDD Cookie/Token。

数据与接口方案

  1. 新迁移为 observation/identity/authorization 增加结构化 goods_id 和规范 URL; 历史记录以空值兼容,新授权只接受完整身份。
  2. 候选请求 schema 增加 goods_id,product_url 改为严格 PDD 规范 URL;候选键升级 cmroubao-candidate-v2。
  3. order authorization 与 device command schema 升级,command hash 覆盖新增字段。
  4. Android 安全存储升级并兼容旧 schema;新字段在 ACK 前落盘。
  5. Admin API/Web 展示 goods_id、外链和本地证据图;人工理由与 candidate key 流程不变。
  6. App 自动调度使用进程内单协程、有界退避和现有 repository mutex;不引入 FCM、 WebSocket 或后台常驻推送服务。

验收要点

  • 新 Admin 任务在 App 已登录且 readiness 通过时无需点击即可 claim、start 并搜索。
  • 无任务轮询有界;单设备/并发恢复不重复 claim、start、探针或下单。
  • 过期 WAITING_CONFIRMATION 加后台取消可原子终止并释放设备,App 清理旧任务后继续。
  • Top 5 每项保存标题、SKU、价格、两张本地证据;可读项同时保存纯数字 goods_id 和唯一规范 PDD URL。
  • 非规范域名、路径、额外参数、非数字 ID 和伪造 URL 被拒绝;后端不 fetch 外链。
  • Admin 可查看本地图片并安全打开候选商品;无 goods_id 候选不能授权自动下单。
  • 授权和设备 command hash 覆盖 goods_id/URL,网络重放仍返回同一不可变命令。
  • Roubao 优先直达商品页并复核相同 goods_id,再唯一选择颜色、尺码和数量。
  • 直达失败或 ID 未能复核时安全停止,不下单。
  • 一次性 submission fence、待付款回读和禁止支付边界不回归。
  • 上下迁移、Go 标准/race/vet/入口构建和 Android 单测/Debug/Release 构建通过。

边界

  • 不承诺始终返回 5 个候选;只能返回实际曝光且证据完整的 0..5 项,禁止凑数。
  • 不使用分享短链、推广参数、剪贴板账号信息或服务端 PDD 抓取。
  • 不把 product_url 当作商品身份,不绕过 candidate key、Admin review 或授权版本。
  • 不因 VLM 输出直接点击规格;VLM 只辅助语义映射,动作仍需确定性唯一匹配和复读。
  • 不自动付款,不在订单已提交状态不确定时重复点击。
  • Admin 工作站浏览器登录 PDD 是外部运营前提,本系统不保存其凭证。

执行记录

  • 2026-07-29:读取 Obsidian 方案并复核当前代码。确认 observation 已有 URL 字段但 Android 两条候选构造路径均未赋值,Admin 只渲染本地证据;授权/设备命令没有商品 ID/URL, dry-run 使用参考图重新搜索。确认需要后端契约和迁移,不能按旧结论“后端零改动”实施。
  • 2026-07-29:现场数据库确认旧 WAITING_CONFIRMATION claim 已过期且继续占用设备; 新任务保持 PENDING。App SAFE_STOPPED 同步提前返回,后台取消不能被确认,构成自动 接单前必须修复的恢复死锁。
  • 2026-07-29:新增 v18 候选商品/授权商品快照,候选键升级为 v2;Admin 只允许授权 完整 goods_id 候选,schema v2 设备命令和哈希绑定规范 URL。App 增加语义+本地 OCR ID 提取、自动 claim/start/VLM 或确定性候选链路,以及显式 PDD ACTION_VIEW 直达。
  • 2026-07-29:后端 heartbeat 已原子恢复“后台请求取消且租约过期”的旧执行,App AUTHORIZATION_EXPIRED 会真实 heartbeat 并清理服务端已结束任务。单测覆盖恢复后 同设备继续领取下一任务。
  • 2026-07-29:go test ./...、go test -race ./...、go vet ./... 和三个 Go 入口 构建通过;Android testDebugUnitTest、Debug/Release 构建通过。Debug APK 已通过 adb install -r 安装到 PKG110,保留本地数据;未在迁移 v18 应用前自动启动真实流程。