Table of Contents
同步来源:
docs/tasks/T-270.md· commitafc651f75a3a
id: T-270 title: 任务阶段结束后一次性切回 Roubao phase: 2 deps:
- T-207 status: DOING created: 2026-08-01 context_ref: 243874a work_branch: null write_paths:
- docs/tasks/T-270.md
- docs/current-state.md
- android-buyer/app/src/main/**
- android-buyer/app/src/test/**
问题 / 背景
Roubao 把拼多多置于前台执行图片搜索、规格匹配或下单核对后,阶段结果虽然已经保存并 准备回传 Admin,手机仍停留在拼多多。采购人员无法直观看到任务已经进入等待确认、失败、 安全停止或待付款状态,容易误以为自动化仍在运行,也会增加手工切换 App 的操作成本。
本任务只处理任务阶段结束后的前台归位。状态持久化、候选/事件 outbox、Admin 回传和 支付边界仍是事实来源;切换失败不能改写业务结果,也不能诱发重复搜索或重复下单。
关联需求与交互
- 功能:F-005、F-006、F-007
- 用户故事:US-004、US-006
- 交互:复用 Android 采购任务页,不新增采购步骤
- 架构/API:Android 本地导航与通知;后端 API、数据库和任务状态机零改动
行为契约
1. 先保存结果,再请求切回
只有本地执行状态或 terminal outbox 已经可靠写入后,才允许请求把 Roubao 放到前台。 切回属于展示副作用,不参与候选成功、执行失败、安全停止或订单核对的事务判断。
允许触发的阶段:
WAITING_ADMIN_CONFIRMATION:本轮候选采集已经封板,等待 Admin 人工确认;- pending terminal
FAIL:失败结果已进入 outbox; AUTHORIZATION_EXPIRED:取消、授权到期或安全停止已持久化;ORDER_SUBMISSION_MANUAL_REVIEW:订单结果无法自动确认,需要人工核对;ORDER_RECONCILED:拼多多订单号和下单时间已经保存并准备/完成回传,等待付款。
以下情况不得触发:单条候选增量回传、第一/二/三轮之间、证据仍在保存、普通 RUNNING、
ORDER_DRY_RUN_RUNNING、ORDER_SUBMISSION_RECONCILING、空闲同步或页面恢复步骤。
2. 每个执行阶段只切回一次
幂等键为 executionId + terminalPhase。设备本地持久化最近的幂等键;同一状态重复发布、
Service 重启、Activity/Service 同时观察到状态时只能有一个调用取得执行权。不同阶段可以
各触发一次,例如先在候选封板时返回,Admin 授权下单后又在订单核对完成时返回。
持久化中只保存幂等键摘要,不记录页面文本、商品信息、token 或设备密钥,并对历史键设置 固定上限,避免无限增长。
3. 使用显式 Intent 返回采购任务页
- 只启动本应用
MainActivity,使用显式 Intent 和FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP | FLAG_ACTIVITY_REORDER_TO_FRONT; MainActivity的既有singleTask保持不变,在onNewIntent()消费内部 action 并切到 采购任务页;不重建登录表单、不清空已填数据;- 不使用 Home/最近任务坐标、Shizuku 或无障碍去点击系统桌面;
- 不携带候选标题、SKU、价格、图片、共享链接或其他业务数据到 Intent。
4. Android 后台启动限制的降级
Android 可能根据系统版本、厂商策略或用户设置拒绝后台 Activity 启动。因此触发时同步发布 一条非持续、可点击的任务结果通知,点击后使用同一个显式 Intent 进入采购任务页。后台拉起 失败不重试抢前台,不标记任务失败,不改变 outbox;通知文案仅包含本应用自有的阶段说明。
5. 安全边界
- 返回 Roubao 后不得继续触发拼多多动作;后续只允许既有 outbox 后台同步;
- 不放宽登录、验证码、风控、结算、提交订单或支付安全停止;
ORDER_RECONCILED仍只提醒采购人员去拼多多确认和付款,Roubao 不点击支付;- App 不在前台、通知权限关闭或系统拦截启动时,业务结果仍必须保持已保存状态。
方案
- 在采购状态模型中派生不含敏感数据的
ForegroundReturnTarget,统一声明允许返回的阶段。 - 用 SharedPreferences 保存有限数量的幂等键摘要,并用进程内同步保证 Activity 和 Service 竞争时只有一个观察者取得该键。
- 增加前台返回协调器:先 claim 幂等键,再发布完成通知并发送显式 Intent。
ProcurementExecutionService观察状态,保证 Activity 已被系统回收或不活跃时仍能处理;MainActivity同时观察以缩短在拼多多前台执行完成后的响应时间,二者由同一幂等门保护。MainActivity.onNewIntent()只处理应用内部 action,并把 Compose 导航切到采购任务页。
验收要点
- 候选封板并持久化后,Roubao 最多请求前台一次并显示采购任务页。
- 同一 execution、同一阶段的重复状态发布或 Service 重启不会重复抢前台。
- 同一 execution 从等待候选确认进入订单核对完成时,两个不同阶段各允许返回一次。
- pending terminal
FAIL、授权到期/安全停止、人工核对、订单已核对均有明确返回目标。 - 中间轮次、单条候选、普通运行和下单核对过程中不会返回 Roubao。
- 系统不允许后台启动时,可通过结果通知进入同一采购任务页;业务状态不受影响。
- Intent、通知和日志不含任务商品数据、页面原文或认证秘密。
- Android Debug/Release 单测、lint 和 APK 构建通过。
- 真机让拼多多停留前台,分别验证候选封板与订单核对完成后的返回行为;若系统拦截, 验证通知降级且不重复启动。
边界
- 不修改 Admin、Go API、数据库 migration、候选匹配、三轮搜索预算或下单自动化。
- 不承诺绕过 Android/厂商对后台 Activity 启动的系统限制。
- 不新增自动支付、免密支付或绕过登录/验证码/风控的能力。
- 本任务不关闭 T-265/T-268/T-269 的独立真机验收项。
执行记录
- 2026-08-01:根据阶段完成后手机仍停在拼多多的问题创建任务,状态
TODO;先提交行为 契约,尚未修改生产代码。 - 2026-08-01:领取任务,状态改为
DOING,开始 Android 实现。 - 2026-08-01:新增
ProcurementForegroundReturnPolicy,只从已持久化的候选等待确认、 pendingFAIL、授权结束、人工对账和订单已核对状态派生返回目标;普通运行、各轮搜索、 dry-run 和提交对账中间状态均返回 null。ProcurementUiState只暴露 execution ID 与自有 阶段枚举,不带商品和页面数据。 - 2026-08-01:新增进程共享的
ProcurementForegroundReturnCoordinator。先以SHA-256(executionId + phase)同步提交最多 32 条本地 receipt,再发布可点击的采购结果 通知并发送显式NEW_TASK | SINGLE_TOP | REORDER_TO_FRONTIntent;Activity 与前台 Service 同时观察同一状态,但共享 gate 保证只消费一次。MainActivity.onNewIntent()只切换到既有采购任务页,未重建登录输入;Service 在同步前后也检查目标,避免 STOP 时 来不及返回。 - 修改文件:
App.kt、MainActivity.kt、ProcurementExecutionService.kt、ProcurementForegroundReturn.kt、ProcurementModels.kt、ProcurementRepository.kt、ProcurementForegroundReturnTest.kt、本任务和docs/current-state.md。 - 自动化验证:首次运行
.\gradlew.bat :app:compileDebugKotlin因当前 shell 未设置ANDROID_HOME,在依赖解析前失败;设置$env:ANDROID_HOME="$env:LOCALAPPDATA\Android\Sdk"和同值ANDROID_SDK_ROOT后,:app:compileDebugKotlin成功。 - 自动化验证:运行
.\gradlew.bat :app:testDebugUnitTest :app:testReleaseUnitTest :app:lintDebug :app:assembleDebug :app:assembleRelease成功;Debug/Release 各 417 项单测,0 failure、 0 error,lint 成功,app-debug.apk与app-release-unsigned.apk均生成。 - 仓库验证:在项目根运行
.\init.ps1成功;Android 全量测试和 Debug APK 构建通过, Go 后端所有 package 测试通过。 - 真机 smoke:设备 PKG110,Android 16 / SDK 36,Roubao 1.4.18 (23),拼多多 8.17.0。
通过
adb install -r ...\app-debug.apk覆盖安装成功;用包管理器解析并显式启动拼多多后,topResumedActivity为com.xunmeng.pinduoduo/.activity.NewPageActivity;再发送com.roubao.autopilot.action.SHOW_PROCUREMENT到.MainActivity,前台变为com.roubao.autopilot/.MainActivity。该 smoke 未操作商品、订单或支付。 - 未验证范围:尚未用一条真实任务让候选封板/订单核对状态自动触发返回,也未在系统实际
拦截后台 Activity 启动的场景验证通知点击降级。因此状态保持
DOING,不改为DONE。