docs: import wiki at afc651f75a3a
+434
@@ -0,0 +1,434 @@
|
||||
<!-- docs-wiki-sync:docs/tasks/T-262.md@afc651f75a3abc2676bb13aa8a80f6aa6a25a72e -->
|
||||
> 同步来源:[`docs/tasks/T-262.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-262.md) · commit `afc651f75a3a`
|
||||
|
||||
---
|
||||
id: T-262
|
||||
title: 为支付边界判定增加命中归因诊断
|
||||
phase: 2
|
||||
deps:
|
||||
- T-261
|
||||
status: DOING
|
||||
created: 2026-07-31
|
||||
context_ref: 3b8a201
|
||||
work_branch: null
|
||||
write_paths:
|
||||
- docs/tasks/T-262.md
|
||||
- docs/current-state.md
|
||||
- android-buyer/app/src/main/**
|
||||
- android-buyer/app/src/test/**
|
||||
---
|
||||
|
||||
## 问题 / 背景
|
||||
|
||||
真机任务三轮**全部在第一步 `pdd_open_app` 被 `PAYMENT_BOUNDARY` 拦下**,所有计数为 0,
|
||||
自动化一步都没跑。设备取证显示拼多多当时停在 `NewPageActivity`(普通商品页),
|
||||
可见文本中包含:
|
||||
|
||||
```
|
||||
使用#微信支付,更换先用后付可0元下单 → 命中标记「微信支付」
|
||||
选择尺码后,提交订单 → 命中标记「提交订单」
|
||||
```
|
||||
|
||||
判定逻辑(`PinduoduoPageClassifier`):
|
||||
|
||||
```kotlin
|
||||
private fun Collection<String>.containsAny(markers: Collection<String>): Boolean =
|
||||
any { text -> markers.any(text::contains) } // 子串匹配
|
||||
|
||||
if (hasCheckoutRoot || visibleTexts.containsAny(paymentMarkers)) → PAYMENT_BOUNDARY
|
||||
```
|
||||
|
||||
`hasCheckoutRoot`(`resourceId == "order_checkout"`)**未命中**,仅子串匹配命中。也就是
|
||||
说:**商品页上的营销横幅和「先选尺码」引导文案,被判成了结算页。**
|
||||
|
||||
### 纠正 T-261 中的错误推断
|
||||
|
||||
T-261 背景中写「`paymentMarkers` 不含『立即购买』,推断第一轮选规格时点进了订单确认
|
||||
页」。**该推断不成立**——当时只考虑了按钮文字是否命中,忽略了普通文案中的子串同样会
|
||||
命中。实际全程未进入结算页。T-261 已实现的清理逻辑本身无害,但它并非本故障的解法。
|
||||
|
||||
### 影响面
|
||||
|
||||
误报会让**任何含支付字样文案的页面**触发安全停止。拼多多商品页、活动页乃至首页
|
||||
banner 都可能出现「微信支付」「先用后付」「提交订单」之类文案,自动化因此无法推进。
|
||||
|
||||
同时 T-261 的清理不会执行——`isSafeForCleanup()` 要求 `safetyStopReason == null`,
|
||||
安全停止路径本就不清理。
|
||||
|
||||
### 为什么先诊断而不是直接改判定
|
||||
|
||||
`paymentMarkers` 是资金安全的最后一道防线。可能的收紧方向有多种:要求命中文本所在
|
||||
元素可点击、要求精确等于而非包含、要求 `hasCheckoutRoot` 与文本同时成立等。**选哪种
|
||||
取决于真实结算页上这些标记以什么形态出现**,目前没有样本。
|
||||
|
||||
盲目放松有让真实结算页漏判的风险,因此本任务**只采集证据,不改任何判定**。
|
||||
|
||||
## 关联需求与交互
|
||||
|
||||
- 功能:F-005、F-007
|
||||
- 用户故事:US-004、US-006
|
||||
- 交互:沿用候选搜索进度与任务详情,不新增采购操作
|
||||
- 架构/API:沿用 T-254/T-255/T-257/T-260/T-261 诊断机制,**后端零改动**
|
||||
|
||||
## 行为契约
|
||||
|
||||
### 一、支付边界命中归因
|
||||
|
||||
当分类结果为 `PAYMENT_BOUNDARY` 时,记录本次判定的归因,并入
|
||||
`CandidateSearchDiagnostics`,按固定顺序追加在既有键之后:
|
||||
|
||||
```
|
||||
payCheckoutRoot=<0|1> payMarkerIndex=<-1|0..7> payMarkerHits=<n> payClickable=<0|1> payExact=<0|1>
|
||||
```
|
||||
|
||||
| 键 | 含义 |
|
||||
| --- | --- |
|
||||
| `payCheckoutRoot` | `resourceId == "order_checkout"` 是否命中 |
|
||||
| `payMarkerIndex` | 首个命中的标记在 `paymentMarkers` 中的下标;无文本命中记 `-1` |
|
||||
| `payMarkerHits` | 命中文本标记的可见文本条数 |
|
||||
| `payClickable` | 首个命中标记所在元素是否 `clickable` |
|
||||
| `payExact` | 该元素文本是否**精确等于**标记(`1`)还是仅包含(`0`) |
|
||||
|
||||
`paymentMarkers` 的下标顺序即其当前定义顺序,必须在本任务文档中固定记录以便解读:
|
||||
|
||||
```
|
||||
0=确认订单 1=提交订单 2=确认支付 3=立即支付
|
||||
4=收银台 5=微信支付 6=找好友支付 7=更多支付方式
|
||||
```
|
||||
|
||||
若后续调整该列表顺序,必须同步更新本表。
|
||||
|
||||
### 二、只记归因,不记页面原文
|
||||
|
||||
**严禁记录命中文本的原文**——只记标记下标、布尔量和计数。商品标题、店铺名、goods_id、
|
||||
链接、价格和任何页面文本一律不得进入诊断 message。沿用 T-255 起的脱敏口径。
|
||||
|
||||
标记下标指向的是本项目自己定义的常量,不是页面内容,因此可以记录。
|
||||
|
||||
### 三、判定行为完全不变
|
||||
|
||||
**这是硬边界。** `PinduoduoPageClassifier` 的判定条件、`paymentMarkers` 内容与顺序、
|
||||
`containsAny` 实现、`hasCheckoutRoot` 规则、安全停止的触发时机与后续行为**一律不改**。
|
||||
|
||||
本任务只在既有判定得出 `PAYMENT_BOUNDARY` 之后额外产出一份归因,不影响任何返回值。
|
||||
必须有回归测试证明:相同输入下分类结果与本任务前逐项一致。
|
||||
|
||||
### 四、阻塞在 `pdd_open_app` 时也要能看到
|
||||
|
||||
本故障发生在候选自动化启动之前,`PinduoduoCandidateAutomation` 的计数全为 0。因此归因
|
||||
必须能在**安全停止发生于任意步骤**时进入诊断 message,包括 `pdd_open_app`。
|
||||
|
||||
未发生 `PAYMENT_BOUNDARY` 时,五个键取默认值(`payMarkerIndex=-1`,其余为 `0`),
|
||||
不得缺省或写空。
|
||||
|
||||
### 五、既有诊断不变
|
||||
|
||||
T-254、T-255、T-257、T-260、T-261 既有诊断键的顺序、名称与取值**逐字节不变**,
|
||||
新键一律追加在末尾。
|
||||
|
||||
## 方案
|
||||
|
||||
1. 在分类得出 `PAYMENT_BOUNDARY` 处额外产出归因数据,随 `PinduoduoUiSnapshot` 暴露;
|
||||
判定表达式本身不动。
|
||||
2. 归因传递到诊断快照,确保安全停止发生在任意工作流步骤时都能带出。
|
||||
3. `CandidateSearchDiagnostics` 追加五个字段与 `auditSuffix()` 输出段。
|
||||
4. 单测覆盖:仅 `hasCheckoutRoot` 命中、仅文本子串命中、精确等于标记命中、
|
||||
可点击与不可点击元素命中、多条文本命中时的计数、未触发时的默认值、
|
||||
以及分类结果与本任务前一致的回归测试。
|
||||
|
||||
## 验收要点
|
||||
|
||||
- [ ] 五个新键出现在诊断 message 末尾且取值符合定义。
|
||||
- [ ] 仅 `hasCheckoutRoot` 命中时 `payMarkerIndex=-1`、`payCheckoutRoot=1`。
|
||||
- [ ] 仅文本子串命中时 `payCheckoutRoot=0`、`payExact=0` 且下标正确。
|
||||
- [ ] 文本精确等于标记时 `payExact=1`。
|
||||
- [ ] 命中元素可点击与否分别反映在 `payClickable`。
|
||||
- [ ] 未触发 `PAYMENT_BOUNDARY` 时五个键取默认值,不缺省。
|
||||
- [ ] 安全停止发生在 `pdd_open_app` 时归因同样出现在 message 中。
|
||||
- [ ] 有回归测试证明分类结果与本任务前逐项一致。
|
||||
- [ ] 诊断 message 不含任何页面文本或商品信息。
|
||||
- [ ] T-254/T-255/T-257/T-260/T-261 既有诊断键顺序、名称、取值不变。
|
||||
- [ ] Android Debug/Release 单测、`lintDebug`、`assembleDebug`、`assembleRelease` 全部通过。
|
||||
- [ ] 真机验证:复现一次 `PAYMENT_BOUNDARY` 阻塞,从 Admin 读到五个归因值并记入执行
|
||||
记录;另在**真实订单确认页**上采集一组对照值,供后续判定规则收紧参考。
|
||||
|
||||
## 边界
|
||||
|
||||
- **不改任何判定逻辑**:`PinduoduoPageClassifier` 判定条件、`paymentMarkers` 内容与
|
||||
顺序、`containsAny` 实现、`hasCheckoutRoot` 规则一律不动。
|
||||
- 不放松、不收紧安全停止的触发条件;本任务不修复误报,只产出定位依据。
|
||||
- 不改 T-261 的清理逻辑与 `isSafeForCleanup` 判定。
|
||||
- 不改 T-256 提交门槛、T-258 后端校验、T-259 安全停止补报、T-260 节奏配置。
|
||||
- 不改候选卡片识别、签名算法、跨轮去重、SKU 硬约束或价格解析。
|
||||
- 不改后端代码、数据库、API 契约或 Admin 模板。
|
||||
- 不启动真实下单,不进入订单提交或支付。
|
||||
|
||||
## 执行记录
|
||||
|
||||
开工后记录修改、命令、结果、环境、决策和 blocker。
|
||||
|
||||
### 2026-07-31 实现与自动化验证
|
||||
|
||||
环境:WSL 无 Android SDK/JDK,全部构建与单测经
|
||||
`/mnt/c/Windows/System32/cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat <目标> --no-daemon --console=plain"`
|
||||
在 Windows 侧执行;未运行 `init.ps1`。
|
||||
|
||||
#### 改动文件
|
||||
|
||||
- `android-buyer/app/src/main/java/com/roubao/autopilot/workflow/WorkflowModels.kt`:
|
||||
新增 `PaymentBoundaryAttribution` 数据类(`checkoutRootHit` / `markerIndex`
|
||||
(-1..7)/ `markerHitCount` / `markerClickable` / `markerExact`,默认值即
|
||||
“未触发”状态);`AutomationResult.Blocked` 追加 `attribution` 字段(默认值,
|
||||
兼容既有单参数构造);`WorkflowReport` 追加 `paymentBoundaryAttribution` 字段
|
||||
(默认值)。
|
||||
- `android-buyer/app/src/main/java/com/roubao/autopilot/workflow/WorkflowRunner.kt`:
|
||||
`Blocked` 分支与 `terminalReport()` 透传 `attribution`/`paymentBoundaryAttribution`,
|
||||
未改变任何状态机分支或重试逻辑。
|
||||
- `android-buyer/app/src/main/java/com/roubao/autopilot/pinduoduo/PinduoduoPageClassifier.kt`:
|
||||
`PinduoduoUiSnapshot` 追加 `paymentBoundaryAttribution` 字段(默认值);`classify()`
|
||||
中既有的 `safetyStop` 判定表达式与 `paymentMarkers`/`containsAny`/`hasCheckoutRoot`
|
||||
逐字节未动,只在其后新增一段只读局部变量:当 `safetyStop ==
|
||||
PAYMENT_BOUNDARY` 时调用新增的私有函数 `computePaymentBoundaryAttribution()`
|
||||
产出归因,否则使用默认值。归因计算:`payCheckoutRoot` 取自既有的
|
||||
`hasCheckoutRoot`;`markerIndex` 按 `paymentMarkers` 当前顺序(0=确认订单
|
||||
1=提交订单 2=确认支付 3=立即支付 4=收银台 5=微信支付 6=找好友支付
|
||||
7=更多支付方式)扫描,取第一个在任意可见元素 `text`/`contentDescription`
|
||||
中命中的标记下标;`markerHitCount` 统计命中任一标记的可见文本条数;
|
||||
`markerClickable`/`markerExact` 取自该首个命中标记所在元素。只使用下标、
|
||||
布尔量与计数,未记录任何页面文本。
|
||||
- `android-buyer/app/src/main/java/com/roubao/autopilot/pinduoduo/PinduoduoSearchAutomation.kt`、
|
||||
`PinduoduoImageSearchAutomation.kt`、`PinduoduoCandidateAutomation.kt`:三处
|
||||
`safetyResult()` 从 `snapshot.safetyStopReason?.let(AutomationResult::Blocked)`
|
||||
改为显式传入 `snapshot.paymentBoundaryAttribution`,使归因在任意工作流步骤
|
||||
(含 `pdd_open_app`)触发安全停止时都能带出;判定触发时机和后续行为未变。
|
||||
- `android-buyer/app/src/main/java/com/roubao/autopilot/procurement/CandidateSearchDiagnostics.kt`:
|
||||
末尾追加 `payCheckoutRoot`/`payMarkerIndex`/`payMarkerHits`/`payClickable`/
|
||||
`payExact` 五个字段(默认值 `0/-1/0/0/0`)及对应 `require` 校验;新增
|
||||
`withPaymentBoundaryAttribution()`;`auditSuffix()` 在既有 `cleanupFailures=`
|
||||
之后追加五个键,既有键顺序、名称、取值未动。
|
||||
- `android-buyer/app/src/main/java/com/roubao/autopilot/MainActivity.kt`:候选搜索
|
||||
成功与失败两处上报 `recordCandidateSearchAttempt` 的 `diagnostics` 构建链上
|
||||
追加 `.withPaymentBoundaryAttribution(report.paymentBoundaryAttribution)`,
|
||||
使阻塞发生在 `pdd_open_app`(此时 `PinduoduoCandidateAutomation` 计数全为 0)
|
||||
时归因仍能进入诊断 message。
|
||||
- 测试:`PinduoduoPageClassifierTest.kt`(新增 9 个用例,覆盖仅 checkoutRoot
|
||||
命中、仅子串命中、精确匹配、可点击/不可点击、多条命中计数、默认值、登录
|
||||
拦截优先级下的默认值,以及一个显式回归用例复核既有支付边界/HOME 场景的
|
||||
`page`/`safetyStopReason` 与本任务前一致)、`CandidateSearchDiagnosticsTest.kt`
|
||||
(更新 3 处精确匹配断言以包含新增尾部默认键,新增 4 个用例覆盖归因追加、
|
||||
仅 checkoutRoot 默认下标、越界校验和默认值不缺省)、
|
||||
`WorkflowRunnerTest.kt`(新增归因透传用例,并给既有“all safety reasons”
|
||||
用例追加默认归因断言)、`PinduoduoSearchAutomationTest.kt`(新增
|
||||
`pdd_open_app` 阻塞即带出归因、以及非支付边界安全停止保持默认归因两个用例)、
|
||||
`PinduoduoCandidateAutomationTest.kt`(`FakeCandidateDriver` 新增可选归因参数,
|
||||
新增一个用例验证候选自动化内部安全停止同样透传归因)。
|
||||
|
||||
#### 命令与结果
|
||||
|
||||
```
|
||||
cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat testDebugUnitTest --no-daemon --console=plain"
|
||||
BUILD SUCCESSFUL;Debug 单测 299 项,0 失败、0 错误。
|
||||
|
||||
cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat testReleaseUnitTest --no-daemon --console=plain"
|
||||
BUILD SUCCESSFUL;Release 单测 299 项,0 失败、0 错误。
|
||||
|
||||
cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat lintDebug --no-daemon --console=plain"
|
||||
BUILD SUCCESSFUL。
|
||||
|
||||
cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat assembleDebug --no-daemon --console=plain"
|
||||
BUILD SUCCESSFUL。
|
||||
|
||||
cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat assembleRelease --no-daemon --console=plain"
|
||||
BUILD SUCCESSFUL。
|
||||
```
|
||||
|
||||
五个目标全部通过。
|
||||
|
||||
#### 偏离任务文档之处及理由
|
||||
|
||||
1. `PaymentBoundaryAttribution` 定义在 `workflow` 包而非 `pinduoduo` 包。任务文档
|
||||
只写“随 `PinduoduoUiSnapshot` 暴露”,未指定归属包。既有依赖方向是
|
||||
`pinduoduo` 依赖 `workflow`(如 `SafetyStopReason`),若把该类型放进
|
||||
`pinduoduo` 包会让 `workflow.AutomationResult.Blocked`/`WorkflowReport`
|
||||
反向依赖 `pinduoduo`,与既有架构方向相反。放进 `workflow` 包可保持单向依赖,
|
||||
且 `PinduoduoUiSnapshot` 正常 import 使用,不影响契约。
|
||||
2. `payMarkerHits` 的口径取“命中任一标记的可见文本条数”(与既有
|
||||
`containsAny` 的判定口径一致),而非“首个命中标记专属的文本条数”。任务
|
||||
文档原文“命中文本标记的可见文本条数”存在两种读法;选择前者是因为它更能
|
||||
反映误判噪声的规模(验收点“多条文本命中时的计数”未强制要求按单一标记
|
||||
过滤),且与 `containsAny` 本身的判定语义直接对应,便于人工核对。
|
||||
3. 归因只在 `PinduoduoPageClassifier.classify()` 自身判定得出 `PAYMENT_BOUNDARY`
|
||||
时产出;`PinduoduoCandidateAutomation.kt` 内部另有几处手工构造的
|
||||
`AutomationResult.Blocked(SafetyStopReason.PAYMENT_BOUNDARY)`(例如规格页
|
||||
跳转订单确认后 `returnFromOrderConfirmation()` 失败),这些不经过一次新的
|
||||
`classify()` 调用产出页面归因,因此保持默认值。这些路径不在“真机任务卡在
|
||||
`pdd_open_app`”这一故障范围内,且任务文档的措辞(“当分类结果为
|
||||
`PAYMENT_BOUNDARY` 时”)本身即指向 `classify()` 的输出,未视为需要修改的
|
||||
范围。
|
||||
4. 未改动 `PinduoduoOrderDryRunAutomation.kt` / `AndroidPinduoduoOrderSubmissionDriver.kt`
|
||||
/ `AndroidPinduoduoOrderDryRunDriver.kt`。这三者不实现
|
||||
`AutomationGateway`/`AutomationResult`(下单试运行走独立的异常式
|
||||
`stop()` 机制),且属于 T-256/T-258/T-259 已冻结的下单流程边界。验收点中
|
||||
“真实订单确认页对照组”需要的是真机上任意一次触达真实订单确认页时
|
||||
`classify()` 产出的归因值,这与 pdd_open_app 阻塞走的是同一条
|
||||
`PinduoduoPageClassifier.classify()` 代码路径,采集时不需要改动下单试运行
|
||||
驱动。
|
||||
|
||||
#### 未完成项(需人工真机验证,state 保持 DOING)
|
||||
|
||||
- 真机复现一次 `PAYMENT_BOUNDARY` 阻塞,从 Admin 读取五个归因值并回填本节。
|
||||
- 真机在真实订单确认页采集一组对照归因值并回填本节,供后续判定规则收紧参考。
|
||||
|
||||
### 2026-07-31 真机取证(主会话,UI dump)
|
||||
|
||||
在 OnePlus PKG110、拼多多 8.17.0 上对**误报现场**做了一次 `uiautomator dump`
|
||||
(`NewPageActivity`,商品规格弹层已选好颜色与尺码,153 个节点),结论如下。
|
||||
|
||||
#### 误报样本的归因实测
|
||||
|
||||
| 命中标记 | 下标 | 精确等于 | 元素 clickable | 所在文本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 微信支付 | 5 | 否 | 否 | 支付方式促销横幅 |
|
||||
| 提交订单 | 1 | 否 | 否 | 规格弹层底部 CTA |
|
||||
|
||||
`hasCheckoutRoot`(`resourceId == "order_checkout"`)**未命中**。即
|
||||
`payCheckoutRoot=0`、`payMarkerIndex=1`、`payMarkerHits=2`、`payClickable=0`、
|
||||
`payExact=0`。
|
||||
|
||||
**该页面是商品规格弹层,不是订单确认页。** 两条命中文本分别是支付方式促销横幅和
|
||||
底部 CTA,均为普通页面文案。
|
||||
|
||||
#### `payClickable` 缺乏区分力(本任务的已知局限)
|
||||
|
||||
`PinduoduoUiElement` 是扁平模型,无父节点引用,因此 `markerClickable` 只能取命中
|
||||
文本元素自身的 `clickable`。实测显示拼多多把文字包在不可点的 `TextView` 中,真正
|
||||
可点的是祖先容器:
|
||||
|
||||
```
|
||||
'提交订单 ¥15.98' TextView clickable=false
|
||||
↑父2 FrameLayout clickable=true bounds=[0,2181][1080,2328]
|
||||
'使用#微信支付…' TextView clickable=false
|
||||
↑父2 ViewGroup clickable=true bounds=[0,2079][1080,2178]
|
||||
```
|
||||
|
||||
曾考虑改为按 bounds 查找可点祖先,但推演后放弃:**促销横幅本身也位于可点容器内**
|
||||
(点击会打开支付方式选择),因此误报样本与真实按钮会得到相同取值,可点击性在本
|
||||
场景不具备区分力。保持现有实现并记录该局限,避免无收益的改动。
|
||||
|
||||
**据此,后续收紧判定时最可能的区分器是 `payCheckoutRoot`**,这使「在真实订单确认页
|
||||
采集对照样本」这一验收项成为决定性依据:若真结算页上该值为 1,收紧规则可以简化为
|
||||
要求其必须命中。
|
||||
|
||||
#### 附带发现(超出本任务范围,另行处理)
|
||||
|
||||
同一页面存在「使用#微信支付,更换**先用后付**可0元下单」。先用后付属信用支付,会
|
||||
产生真实债务且不经过付款确认,下单路径必须显式排除该入口。需另开任务处理。
|
||||
|
||||
同一页面同时出现三条价格文本:`¥21.98`(可被现有 `PRICE_PATTERN` 解析)、
|
||||
`仅1次¥15.98`、`提交订单 ¥15.98`。后两条因前缀导致 `matchEntire` 失败,与 T-257
|
||||
观测到的 `pricePrefix` 占比吻合;且**同屏存在两个不同金额**,其中 `¥15.98` 带
|
||||
「仅1次」限定。这说明价格问题不能靠放宽正则解决,需区分实付价、原价与促销类型,
|
||||
另行开任务。
|
||||
|
||||
### 2026-07-31 真实结算页对照样本(主会话,UI dump)
|
||||
|
||||
在同一设备上手动走到**订单确认/去支付页**后 dump,结果推翻了两个预设的收紧方向。
|
||||
|
||||
#### 对照数据
|
||||
|
||||
| 页面 | Activity | 节点数 | `order_checkout` | paymentMarkers 命中 | 可见文本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 商品规格弹层(误报) | `NewPageActivity` | 147–153 | 未命中 | 2 条(微信支付、提交订单) | 丰富 |
|
||||
| **真实结算页** | `NewPageActivity` | **11** | **未命中** | **0 条** | **几乎为零** |
|
||||
|
||||
真实结算页的完整结构是「若干无文本容器 + 一个占满屏幕的 `WebView`」:
|
||||
|
||||
```
|
||||
FrameLayout bounds=[0,0][1080,2376]
|
||||
LinearLayout bounds=[0,0][1080,2328]
|
||||
FrameLayout rid=content
|
||||
FrameLayout / ViewGroup / FrameLayout rid=pdd
|
||||
WebView bounds=[0,0][1080,2328] ← 页面内容全在这里,无障碍读不到
|
||||
View rid=pdd
|
||||
View rid=navigationBarBackground
|
||||
```
|
||||
|
||||
#### 被否定的两个收紧方向
|
||||
|
||||
1. **要求 `payCheckoutRoot` 命中**——不可行。`resourceId == "order_checkout"` 在真实
|
||||
结算页上**同样未命中**,该常量在当前拼多多版本上似乎已不适用。
|
||||
2. **要求 `payExact`(精确文本匹配)**——不可行。真实结算页**一个可见文本都没有**,
|
||||
任何基于文本的规则都无法识别它。
|
||||
|
||||
#### 判定思路需要改变
|
||||
|
||||
当前逻辑是「见到支付关键词即停」,但实测恰好相反:
|
||||
|
||||
- 商品页文本丰富且含支付字样 → **误报**
|
||||
- 真实结算页几乎无文本、纯 `WebView` → **文本规则完全失效**
|
||||
|
||||
因此**单纯收紧文本规则会两头落空**:误报消除的同时,真实结算页也将无法识别,
|
||||
那是比误报更危险的结果。**在找到可靠的结算页识别特征之前,不得放松现有文本规则。**
|
||||
|
||||
#### 可能的结构性特征(仅一个样本,尚不足以作为判定依据)
|
||||
|
||||
- 整页只有一个占满屏幕的 `WebView`,且语义文本数近乎为零
|
||||
- 节点总数极少(11 vs 商品页 147–153)
|
||||
- `Activity` 与商品页相同(`NewPageActivity`),**不能用作区分依据**
|
||||
|
||||
#### 后续所需对照样本
|
||||
|
||||
在把上述结构特征用于安全判定之前,至少还需确认不会误报或漏报:
|
||||
|
||||
| 待采页面 | 用途 |
|
||||
| --- | --- |
|
||||
| 拼多多首页 | 确认正常页不命中新特征 |
|
||||
| 活动 / 优惠券等 WebView 页 | 确认其他 WebView 页不被误判为结算页 |
|
||||
| 订单列表页 | 常用页面对照 |
|
||||
|
||||
在样本齐备前,**本任务不产出判定规则变更建议**;收紧或替换判定属于后续独立任务。
|
||||
|
||||
### 2026-07-31 页面对照样本汇总(主会话,UI dump)
|
||||
|
||||
在同一设备、同一版本上采集四类页面,用于判断能否以结构特征替代文本关键词判定。
|
||||
|
||||
| 页面 | Activity | 节点数 | 非空文本 | WebView | `order_checkout` | paymentMarkers |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 首页 | `MainFrameActivity` | 227 | 48 | 0 | 未命中 | 无 |
|
||||
| 我的优惠券 | `NewPageActivity` | 457 | **205** | 2(占满屏) | 未命中 | 无 |
|
||||
| 商品规格弹层(误报) | `NewPageActivity` | 147–153 | 多 | 0 | 未命中 | **2 条** |
|
||||
| **订单确认/去支付** | `NewPageActivity` | **11** | **0** | **1(占满屏)** | 未命中 | 0 |
|
||||
|
||||
#### 决定性结论:WebView 本身不阻断无障碍
|
||||
|
||||
优惠券页同样是占满屏的 WebView,其内容 **205 条文本全部可读**;而结算页的 WebView
|
||||
**一条都读不到**。这排除了「所有 WebView 内容都不可读」的可能——结算页读不到内容是
|
||||
该页面特有性质,很可能是拼多多对支付类页面做了无障碍防护。
|
||||
|
||||
因此「**存在占满屏 WebView 且语义文本数近乎为零**」在四个样本中**唯有结算页命中**,
|
||||
具备作为主判据的区分度。误报的商品页文本丰富,天然不命中。
|
||||
|
||||
#### 仍需处理的风险:加载中的普通页面
|
||||
|
||||
任何普通页面在刚打开、WebView 尚未渲染时都可能短暂呈现「WebView + 零文本」。若单次
|
||||
快照即判定,会把加载瞬间误判为结算页——只是把误报从商品页搬到了加载瞬间。
|
||||
|
||||
因此新判据**必须要求连续多次快照稳定命中**才成立,参照既有 `unknownPageLimit`
|
||||
(20 次 × 200ms)的模式,不得单次命中即停。
|
||||
|
||||
#### 未采集到的样本及原因
|
||||
|
||||
「限时秒杀」与「我的订单」两页**无法采集**:两者均有持续动画(倒计时、轮播),
|
||||
`uiautomator dump` 报 `ERROR: could not get idle state`,永远等不到静止状态。
|
||||
|
||||
需要说明的是,**该限制只影响本次取样手段,不影响 Roubao**:`uiautomator` 必须等界面
|
||||
idle 才能快照,而 Roubao 的无障碍服务直接读取节点树,不受动画影响。
|
||||
|
||||
缺失这两个样本不阻塞主判据设计——两页都含大量文本,不可能命中「WebView + 零文本」。
|
||||
|
||||
#### 附带发现:订单列表可能长期被自身安全规则阻挡
|
||||
|
||||
订单列表上的待支付订单会显示「立即支付」,在现行文本规则下必然触发
|
||||
`PAYMENT_BOUNDARY`。而业务规则 22 要求「网络超时或页面不确定时必须先对账」,对账
|
||||
正需进入订单列表。**该路径可能一直被自身安全判定挡住**。本次未能采样验证,需另行
|
||||
确认,不在本任务范围。
|
||||
Reference in New Issue
Block a user