同步来源:
docs/tasks/T-265.md· commitafc651f75a3a
id: T-265 title: 恢复购买入口作为唯一规格入口并依赖订单确认页识别安全回退 phase: 2 deps:
- T-264 status: DOING created: 2026-07-31 context_ref: d856c0b work_branch: null write_paths:
- docs/tasks/T-265.md
- docs/current-state.md
- android-buyer/app/src/main/**
- android-buyer/app/src/test/**
问题 / 背景
T-264 移除「免拼购买」兜底点击后,真机三轮每一个候选都无法打开规格面板:
| 轮次 | attempts | specOpenFailed | specEntryAbsent | reason |
|---|---|---|---|---|
| 1 | 2 | 2 | 2 | CANDIDATE_SPECIFICATION_UNAVAILABLE |
| 2 | 4 | 4 | 4 | CANDIDATE_SPECIFICATION_UNAVAILABLE |
| 3 | 3 | 3 | 3 | CANDIDATE_SPECIFICATION_UNAVAILABLE |
specEntryAbsent 恒等于 attempts,即覆盖损失 100%。
真机取证:详情页根本没有独立的规格入口
对一个真实服装商品详情页 dump(190 节点),全部可见文本中没有任何 「选择规格」「请选择规格」「颜色分类」「颜色款式」「选择颜色」「选择尺码」,也没有 以「请选择:」或「已选:」开头的文本。整页只有标题、价格、优惠券、拼单信息、店铺、 分享/收藏/客服,以及底部的:
'已优惠6元 免拼购买'
当前拼多多版本的商品详情页不提供独立的规格入口——「免拼购买」就是唯一入口,点击 后才弹出规格面板供选择颜色和尺码。
T-264 的判断错误
T-264 依据纸巾案例(点「免拼购买」直接进入订单确认页)推断该兜底本身危险,予以移除。 但真实情况是:
| 商品 | 点「免拼购买」后 |
|---|---|
| 正常服装(有颜色/尺码) | 弹出规格面板,流程正常 |
| 纸巾(无可选规格)+ 促销 | 直接进入订单确认页 |
问题不在于点击该入口,而在于点击后落到订单确认页时无法识别、无法退出。 后者已由 T-264 的原生订单确认页识别修复。
openPinduoduoSpecifications() 的既有安全入口匹配(SAFE_ENTRY_TEXTS /
SAFE_ENTRY_PREFIXES)仍然保留,只是当前版本页面不命中;未来版本若恢复独立入口,
该路径将优先生效。
关联需求与交互
- 功能:F-005、F-007
- 用户故事:US-004、US-006
- 交互:不新增采购操作
- 架构/API:沿用既有诊断机制,后端零改动
行为契约
一、恢复购买入口,但仅作为次优先路径
openPinduoduoSpecifications() 恢复「免拼购买」入口点击,顺序不变:
- 优先匹配
SAFE_ENTRY_TEXTS/SAFE_ENTRY_PREFIXES的安全入口; - 仅当第 1 步找不到唯一目标时,才使用「免拼购买」入口;
- 两者都找不到时返回
ENTRY_ABSENT,维持 T-264 的跳过行为。
除「免拼购买」外,不得新增任何其他购买、下单、拼单、支付语义控件作为入口。
二、落到订单确认页即判定该候选无可选规格
点击入口后 awaitSpecificationEntry() 返回 ORDER_CONFIRMATION 时:
- 调用既有
returnFromOrderConfirmation()退出(沿用其现有实现与有界次数); - 将该候选判定为「无可选规格」,跳过并继续下一个候选,不得对同一候选重试点击;
- 退出失败时维持既有
Blocked(SafetyStopReason.PAYMENT_BOUNDARY)行为。
依据:任务的 SKU 硬约束要求颜色与尺码,而点击规格入口却直接进入下单流程,说明该商品 不存在可选规格,不可能满足硬约束。
三、订单确认页上零点击
这是硬边界。 在订单确认页上,除既有 returnFromOrderConfirmation() 的返回动作外,
不得点击任何控件,尤其不得点击提交订单、立即支付、免密支付、先用后付或任何扣款
语义控件。必须有单测证明该路径不调用任何此类动作。
四、诊断可见
追加计数,置于既有键之后:
specEntryViaPurchase=<n> specLandedOnCheckout=<n>
| 键 | 含义 |
|---|---|
specEntryViaPurchase |
通过「免拼购买」入口打开规格的次数 |
specLandedOnCheckout |
点击入口后落到订单确认页并被跳过的候选数 |
specLandedOnCheckout 是本次恢复的安全审计口径:它直接度量自动化到达订单确认页的
频率。若真机上该值偏高,说明大量候选无可选规格,应在更早阶段过滤(如品类判断),
而非依赖事后回退。
五、既有安全能力一律不放松
- 不改 T-264 的原生订单确认页识别与
hasOrderConfirmation任一判据。 - 不改 T-263 结构判据、
hasCheckoutRoot、paymentMarkers及其抑制名单。 - 不改登录、验证码、风控的检测逻辑与优先级。
- 不改
returnFromOrderConfirmation()的既有实现与有界次数。 - 不改 T-261 清理逻辑与
isSafeForCleanup。 - 不改 T-256/T-258/T-259/T-260 的成果。
- 既有诊断键顺序、名称、取值逐字节不变,新键追加在末尾。
方案
openPinduoduoSpecifications()恢复「免拼购买」为第二优先入口,返回值区分 经由哪条入口打开,ENTRY_ABSENT语义不变。PinduoduoCandidateAutomation在ORDER_CONFIRMATION分支将候选计为specLandedOnCheckout并跳过,不重试。- 两个新计数并入诊断快照与 message 尾部。
- 单测覆盖:安全入口存在时优先使用且不点购买入口;安全入口缺失时使用购买入口;
两者皆无时返回
ENTRY_ABSENT;落到订单确认页时只调用返回动作并跳过候选; 订单确认页路径不调用任何提交/支付语义动作;退出失败仍返回Blocked(PAYMENT_BOUNDARY)。
验收要点
- 安全入口存在时优先使用,不点击购买入口,有单测证明。
- 安全入口缺失时使用「免拼购买」入口,能打开规格面板。
- 两者皆无时返回
ENTRY_ABSENT,行为与 T-264 一致。 - 落到订单确认页时调用既有返回动作、跳过该候选、不重试,有单测证明。
- 订单确认页路径不调用任何提交订单/支付/扣款语义动作,有单测证明。
- 退出订单确认页失败时仍返回
Blocked(PAYMENT_BOUNDARY)。 specEntryViaPurchase与specLandedOnCheckout计数正确并出现在诊断 message。- T-263/T-264 的支付边界与订单确认页判据未被削弱,有回归测试。
- 登录、验证码、风控判定与优先级未变。
- 既有诊断键顺序、名称、取值不变。
- Android Debug/Release 单测、
lintDebug、assembleDebug、assembleRelease全部通过。 - 真机验证:重跑采购任务能打开规格面板并进入 SKU 匹配;从 Admin 读取
specEntryViaPurchase与specLandedOnCheckout记入执行记录。
边界
- 除「免拼购买」外不新增任何购买语义控件作为入口。
- 订单确认页上除既有返回动作外零点击,绝不提交订单或支付。
- 不改 T-261/T-262/T-263/T-264 的任何安全判据与清理逻辑。
- 不改候选卡片识别、签名算法、跨轮去重、价格解析或三轮预算。
- 不改后端代码、数据库、API 契约或 Admin 模板。
- 不在本任务实现品类过滤。若
specLandedOnCheckout偏高,应另开任务在候选筛选 阶段引入品类判断(可考虑接入既有候选评估 VLM),而非在此处堆规则。
附带记录(供后续价格任务参考)
同一次商品详情页取证显示价格文本形态为:
'券后¥15.98' 前缀「券后」
'券后 ¥ 15.98' 含空格
'¥' '15.98' 货币符号与数字被拆成两个独立节点
与 T-257 观测到的 pricePrefix 占比吻合,并新增一个此前未知的因素:价格被拆成多个
节点,这是 PRICE_PATTERN.matchEntire 失败的另一独立原因。价格解析任务需同时处理
前缀与节点拆分两种情况。
该页面同时显示 # 先用后付 # 支持0元下单,确认收货后再付款,说明先用后付在正常服装
商品上同样存在,不能以其存在与否作为跳过依据;下单路径排除信用支付需另行设计。
执行记录
- 2026-07-31:恢复唯一「免拼购买」次优先入口。入口选择先按
SAFE_ENTRY_TEXTS/SAFE_ENTRY_PREFIXES得到唯一安全目标;仅无唯一安全目标时 才接受唯一且文本以「免拼购买」结尾的目标,其它购买、下单、拼单和支付文本仍拒绝。 - 2026-07-31:
PinduoduoSpecificationOpenOutcome区分安全入口与购买入口; 候选循环对购买入口和落到订单确认页分别累计specEntryViaPurchase/specLandedOnCheckout。订单确认页继续只调用既有returnFromOrderConfirmation();成功返回后跳过当前候选,失败保持Blocked(PAYMENT_BOUNDARY)。 - 2026-07-31:两个诊断键追加在既有
payPage之后;旧键顺序、名称和格式不变。 单测按动作发生页面证明ORDER_CONFIRMATION上唯一动作是返回,返回到商品详情后 才执行回搜索结果页;同一候选不重试,下一候选可继续进入规格匹配。 - 2026-07-31:Debug/Release 各 336 项 App 单测零失败,
lintDebug、assembleDebug、assembleRelease均通过;根init.ps1以及全部 Go 后端测试 通过。 - 2026-07-31:
adb devices -l无已连接设备,因此不能执行最后一项真机采购任务 重跑和 Admin 诊断读取。任务保持DOING,不伪造specEntryViaPurchase/specLandedOnCheckout真机结果。 - 2026-07-31:设备重新连接后,通过 USB 在 OnePlus PKG110 Android 16/API 36
覆盖安装 T-265 Debug APK;项目未在本任务修改版本号,设备仍显示
1.4.18 (23),包更新时间为2026-07-31 17:20:18。MainActivity正常启动, 采购无障碍、悬浮窗、通知和 Shizuku 权限均保持启用。采购任务重跑和 Admin 两个 诊断计数仍待验证。