1
T-263
ila edited this page 2026-08-07 16:37:05 +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-263.md · commit afc651f75a3a


id: T-263 title: 以页面结构判定支付边界并按页面类型收敛文本规则 phase: 2 deps:

  • T-262 status: DOING created: 2026-07-31 context_ref: 0c2e423 work_branch: null write_paths:
  • docs/tasks/T-263.md
  • docs/current-state.md
  • android-buyer/app/src/main/**
  • android-buyer/app/src/test/**

问题 / 背景

T-262 采集的四个真机样本证明:现行「见到支付关键词即停」的判定,在误报和漏报两端 同时失效。

页面 Activity 节点数 非空文本 WebView order_checkout paymentMarkers
首页 MainFrameActivity 227 48 0 未命中 无
我的优惠券 NewPageActivity 457 205 2(占满屏) 未命中 无
商品规格弹层 NewPageActivity 147–153 多 0 未命中 2 条 → 误报
订单确认/去支付 NewPageActivity 11 0 1(占满屏) 未命中 0 → 漏报

两端同时失效

误报:商品规格弹层上的「使用#微信支付,更换先用后付可0元下单」(促销横幅)和 「提交订单 ¥14.9」(底部 CTA)被子串匹配命中。这是正常购物页面,自动化必须能在 上面读取规格,却被拦在第一步,导致连续多次真机任务三轮全败。

漏报:真实结算页一条可见文本都没有,order_checkout 也未命中。现行规则 完全无法识别它——之所以从未出事,是因为自动化根本走不到那一步。

根本原因:混淆了「在支付页上」与「看得见通往支付的按钮」

商品规格弹层上本来就有结算入口按钮,这是正常的。自动化需要停止的是已经身处支付 页面,而不是看见了支付相关字样。文本关键词无法区分这两者。

决定性证据:WebView 本身不阻断无障碍

优惠券页同为占满屏 WebView,其 205 条文本全部可读;结算页的 WebView 一条读不 到。因此「结算页读不到内容」是该页面特有性质(很可能是支付页的无障碍防护), 而非 WebView 的普遍限制。

据此,「存在占满屏 WebView 且语义文本数近乎为零」在四个样本中唯有结算页命中。

关联需求与交互

  • 功能:F-005、F-007
  • 用户故事:US-004、US-006
  • 交互:不新增采购操作
  • 架构/API:沿用 T-262 归因诊断,后端零改动

行为契约

一、总原则:只增强,不削弱

新判据是追加,不是替换。三条判据任一成立即判 PAYMENT_BOUNDARY:

结构判据(新)  或  hasCheckoutRoot(保留)  或  文本判据(收敛后保留)

paymentMarkers 列表内容与顺序不得删改。hasCheckoutRoot 规则不得删改。

二、结构判据(新增主判据)

同时满足以下全部条件时判定为支付边界:

  1. 前台包名为拼多多;
  2. 存在占满屏的 WebView(宽高各不小于屏幕的 90%);
  3. 可见语义文本数不超过阈值(常量定义,初值 2,覆盖极少量装饰性文本);
  4. 连续 PAYMENT_STRUCTURE_CONFIRMATIONS 次快照均满足上述条件,初值 3。

第 4 条是硬要求。任何普通页面在 WebView 尚未渲染时都会短暂呈现「WebView + 零文本」, 单次命中即判定会把页面加载瞬间误判为结算页。参照既有 unknownPageLimit (20 次 × 200 毫秒)的模式,不得单次命中即停。

连续计数在页面结构不再满足条件时立即清零,不得跨页面累积。

三、文本判据按页面类型收敛

仅当页面未被正面识别为已知安全页面类型时,文本关键词命中才强制停止。

  • 页面已被分类器正面识别为商品详情、规格弹层、搜索结果、图片搜索结果或首页时, 文本命中不再强制停止,但必须完整记录(见第四节)。
  • 页面为 UNKNOWN 或任何未正面识别的类型时,文本命中维持现有强制停止行为。

依据:在已确认的正常购物页面上出现支付字样属预期现象(促销横幅、结算入口按钮); 在无法识别的页面上出现支付字样则是危险信号,必须保守处理。

登录、验证码、风控三类安全停止的判定与优先级完全不变,不受本节影响。

四、可审计:记录被放行的文本命中

新增诊断计数,追加在 T-262 五个键之后:

payStructureHit=<0|1> payStructureStreak=<n> payTextSuppressed=<n>
键 含义
payStructureHit 本轮是否由结构判据触发过支付边界
payStructureStreak 结构条件连续命中的最大次数
payTextSuppressed 因页面被正面识别而未强制停止的文本命中次数

payTextSuppressed 是本次变更的安全审计口径:它记录了在旧规则下会停止、新规则下 放行的次数,用于事后评估变更影响。若真机上该值异常偏高,说明收敛过度,需回头收紧。

沿用既有脱敏口径:只记计数,不记页面文本或商品信息。

五、回归测试必须基于真实样本

必须用 T-262 记录的四个真机样本构造测试夹具,逐一断言判定结果:

样本 期望
首页(227 节点 / 48 文本 / 0 WebView / 无命中) 不判支付边界
优惠券页(457 / 205 / 2 WebView / 无命中) 不判支付边界
商品规格弹层(147 / 多文本 / 0 WebView / 2 条命中) 不判支付边界(本任务修复目标)
结算页(11 / 0 文本 / 1 占满屏 WebView / 无命中) 判支付边界(连续 3 次后)

另需覆盖:结算页结构仅命中 1 次和 2 次时不得判定;hasCheckoutRoot 单独命中时 仍判定;UNKNOWN 页面文本命中时仍判定;登录/验证码/风控优先级不变。

六、既有行为不变

  • 不改登录、验证码、风控的检测与优先级。
  • 不改 paymentMarkers 内容与顺序、containsAny 实现、hasCheckoutRoot 规则。
  • 不改 T-262 五个归因键的顺序、名称与取值,也不改 T-254/T-255/T-257/T-260/T-261 既有诊断键。
  • 不改候选识别、签名算法、跨轮去重、SKU 硬约束、价格解析、三轮预算或成功判定式。
  • 不改下单 dry-run、订单提交、授权价格策略或支付边界之后的处置逻辑。

方案

  1. PinduoduoPageClassifier 增加结构判据计算(占满屏 WebView + 文本数阈值), 阈值与连续次数以具名常量定义。
  2. 连续确认状态由调用方持有并传入,分类器保持无副作用的纯函数特性;结构不满足时清零。
  3. 文本判据增加「页面已正面识别」的抑制条件,抑制时记录计数而非停止。
  4. 三个新诊断键并入 CandidateSearchDiagnostics,追加在 T-262 键之后。
  5. 用四个真机样本构造测试夹具并逐一断言,另覆盖连续次数边界与各优先级场景。

验收要点

  • 四个真机样本的判定结果与上表逐一相符。
  • 结算页结构连续命中不足 3 次时不判定,达到 3 次后判定。
  • 结构条件中断后连续计数清零,不跨页面累积。
  • hasCheckoutRoot 单独命中仍判定支付边界。
  • UNKNOWN 页面上文本命中仍强制停止。
  • 登录、验证码、风控的判定与优先级未变,有回归测试。
  • paymentMarkers 内容与顺序未删改。
  • 三个新诊断键出现且不含页面文本或商品信息。
  • T-254/T-255/T-257/T-260/T-261/T-262 既有诊断键顺序、名称、取值不变。
  • Android Debug/Release 单测、lintDebug、assembleDebug、assembleRelease 全部通过。
  • 真机验证:同一商品重跑采购任务不再被拦在 pdd_open_app,能进入候选采集; 并从 Admin 读取 payTextSuppressed 确认放行次数在合理范围。

边界

  • 不删除任何现有判据:paymentMarkers、hasCheckoutRoot 全部保留。
  • 不改登录、验证码、风控的检测逻辑与优先级。
  • 不改支付边界判定成立之后的处置逻辑(立即停止、不清理、不点击)。
  • 不改 T-261 清理逻辑与 isSafeForCleanup。
  • 不改 T-256 提交门槛、T-258 后端校验、T-259 安全停止补报、T-260 节奏配置。
  • 不改候选识别、价格解析或下单相关任何逻辑。
  • 不改后端代码、数据库、API 契约或 Admin 模板。
  • 不处理订单列表页可能被自身规则阻挡的问题(见 T-262 附带发现),另行开任务。

已知局限

真实结算页样本只有一个。 结构判据基于该单一样本设计,可能存在盲区:

  • 若某些拼多多版本或路径使用原生结算页(有可读文本),结构判据不会命中。此时 仍依赖 hasCheckoutRoot 与 UNKNOWN 页面上的文本判据兜底。
  • order_checkout 这个 resourceId 在本次采集的结算页上并未命中,其在当前版本 上的有效性存疑,但按「只增强不削弱」原则仍予保留。

因此本任务不宣称覆盖全部支付页面形态,只解决已证实的误报并补上已证实的漏报。 后续若发现新的结算页形态,应继续追加判据而非放松现有判据。

执行记录

开工后记录修改、命令、结果、环境、决策和 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 追加三个字段——structureHit(本次 PAYMENT_BOUNDARY 是否由结构判据触发)、structureConfirmations(产出本次 归因时的结构连续确认次数)、textSuppressedCount(本次判定时因页面被正面 识别而放行的命中文本条数);均为末尾追加、默认值 false/0/0,init 增 加对应非负校验。既有五个字段与其含义/顺序未动。
  • android-buyer/app/src/main/java/com/roubao/autopilot/pinduoduo/PinduoduoPageClassifier.kt:
    • 新增具名常量 PAYMENT_STRUCTURE_TEXT_THRESHOLD = 2、 PAYMENT_STRUCTURE_CONFIRMATIONS = 3、私有 FULL_SCREEN_WEBVIEW_RATIO = 0.9。
    • classify() 追加末尾可选参数 priorStructureConfirmations: Int = 0(调用方 持有并传入的连续确认状态,分类器本身仍是无副作用纯函数);新增私有函数 hasFullScreenWebViewWithFewTexts():屏幕范围取当前可见元素包围盒(根容器 通常等于屏幕大小,避免新增屏幕尺寸入参改动既有调用方签名),WebView 宽高 各不小于该包围盒的 90% 且可见非空文本数不超过阈值时判定单次快照满足结构 条件;structureConfirmations = if (met) prior+1 else 0, structureHit = structureConfirmations >= PAYMENT_STRUCTURE_CONFIRMATIONS。
    • 将原来紧跟在 visibleTexts 之后计算的 page 判定块整体上移到 safetyStop 判定之前(page 的计算只依赖 hasCheckoutRoot/normalized/ visibleElements 等既有局部变量,与 safetyStop 无关,纯粹重排不改变 page 本身的判定逻辑),使文本判据可以引用已确定的 page 值。
    • safetyStop 表达式改为 structureHit || hasCheckoutRoot || (textMarkerHit && !textMarkerSuppressed), 其中 textMarkerHit = visibleTexts.containsAny(paymentMarkers)(containsAny 实现、paymentMarkers 内容与顺序、hasCheckoutRoot 规则逐字节未动); textMarkerSuppressed = textMarkerHit && page in {PRODUCT_DETAIL, SPECIFICATION_PANEL, SEARCH_RESULTS, IMAGE_SEARCH_RESULTS, HOME}。登录/ 验证码/风控三类判定仍在同一个 when 的前三个分支,优先级和触发逻辑未动; 结构/文本判据只影响 LoginBlocker.NONE/UNKNOWN 分支内部。
    • PinduoduoUiSnapshot 追加两个字段:structureConfirmations: Int = 0(本 次调用后的连续确认计数,供调用方在下一次调用时回传)、 textMarkerSuppressed: Boolean = false(本次快照是否发生文本命中放行, 与 PAYMENT_BOUNDARY 是否触发无关,始终计算,供调用方做会话级审计)。
    • computePaymentBoundaryAttribution() 追加三个入参并透传进 PaymentBoundaryAttribution 的三个新字段,其余标记扫描逻辑未动。
  • android-buyer/app/src/main/java/com/roubao/autopilot/accessibility/BuyerAccessibilityService.kt: 新增私有实例字段 pinduoduoStructureConfirmations;classifyPinduoduoRoot() (全仓库唯一的真机快照来源,其余 snapshotPinduoduoUi()、 clickPinduoduoSearchEntry() 等数十处调用均经此函数)将该字段作为 priorStructureConfirmations 传入 classify(),再用返回快照的 structureConfirmations 回写该字段,从而让结构判据的连续确认覆盖所有轮询 循环与一次性动作调用;snapshotPinduoduoUi() 在前台包名不是拼多多的分支 显式清零该字段,确保离开拼多多前台或结构条件中断时立即清零、不跨页面累积。
  • android-buyer/app/src/main/java/com/roubao/autopilot/pinduoduo/PinduoduoCandidateAutomation.kt: 新增两个会话内累计字段 maxStructureConfirmations、textSuppressedCount (reset() 一并清零);新增私有 observeSnapshot() 作为本类唯一的快照读取 入口(原有约 11 处 driver.snapshot() 全部替换为该函数),每次快照后更新 两个累计字段的最大值/计数,不改变 driver.snapshot() 的返回值或任何既有 判定/重试/清理行为;diagnostics() 追加 payStructureStreak = maxStructureConfirmations、 payTextSuppressed = textSuppressedCount。
  • android-buyer/app/src/main/java/com/roubao/autopilot/procurement/CandidateSearchDiagnostics.kt: 末尾追加 payStructureHit/payStructureStreak/payTextSuppressed 三个字段 (默认值 0)及对应 require 校验;withPaymentBoundaryAttribution() 追加 设置 payStructureHit(直接取自本次归因),payStructureStreak 改为 maxOf(既有值, 归因中的 structureConfirmations)(取大合并,不覆盖候选自动化 自身轮询中已观察到的更高计数);payTextSuppressed 不在此方法中设置, 完全由调用方(PinduoduoCandidateAutomation.diagnostics())持续累积后写入, 避免被本方法在未触发 PAYMENT_BOUNDARY 的常见场景下清零或覆盖(原因见下方 「偏离与决策」第 2 条)。既有五个键与 auditSuffix() 中它们之前的所有键均 逐字节未动,三个新键追加在 payExact= 之后。
  • 测试:
    • PinduoduoPageClassifierTest.kt 新增 9 个用例:基于 T-262 四个真机样本的 首页/优惠券页/规格弹层/结算页判定(规格弹层用例直接复现 T-262 记录的误报 文案,断言不再判定且 paymentBoundaryAttribution 保持默认值)、结算页 结构连续命中 1/2 次不判定、第 3 次判定、结构中断后清零再重新计数、 hasCheckoutRoot 单独命中且结构计数为 0 时仍判定、UNKNOWN 页面文本命中 仍强制停止、登录/验证码/风控在已有结构连续计数时优先级不变。
    • CandidateSearchDiagnosticsTest.kt 更新 6 处既有精确匹配断言以包含三个新 增尾部默认键,新增 4 个用例覆盖非法计数拒绝、结构命中归因追加、 payStructureStreak 取大合并两种方向、payTextSuppressed 不被 withPaymentBoundaryAttribution 清零。
    • PinduoduoCandidateAutomationTest.kt 的 FakeCandidateDriver 新增 structureConfirmations/textMarkerSuppressed 两个可选构造参数并透传进 快照;新增一个用例验证候选自动化经 observeSnapshot() 累积的 payStructureStreak/payTextSuppressed 出现在 diagnostics() 中,且 reset() 后归零。

命令与结果

cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat testDebugUnitTest --no-daemon --console=plain"
BUILD SUCCESSFUL;Debug 单测 313 项,0 失败、0 错误(较 T-262 的 299 项新增 14 项)。

cmd.exe /c "cd /d D:\chengma\cmroubao\android-buyer && gradlew.bat testReleaseUnitTest --no-daemon --console=plain"
BUILD SUCCESSFUL;Release 单测 313 项,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。

五个目标全部通过。

四个真机样本的断言结果(单测,非真机)

样本 断言 结果
首页(无 WebView、46+2 条文本、无标记命中) page=HOME,safetyStopReason=null,structureConfirmations=0 通过
优惠券页(2 个占满屏 WebView、205 条文本、无标记命中) safetyStopReason=null,structureConfirmations=0(文本数量守住占满屏 WebView 不误判) 通过
商品规格弹层(0 WebView、命中“提交订单”“微信支付”2 条) page=SPECIFICATION_PANEL,safetyStopReason=null(本任务修复目标),paymentBoundaryAttribution 为默认值,textMarkerSuppressed=true 通过
结算页(1 个占满屏 WebView、0 条文本) 连续 1/2 次快照 safetyStopReason=null;第 3 次 safetyStopReason=PAYMENT_BOUNDARY,paymentBoundaryAttribution.structureHit=true、structureConfirmations=3、checkoutRootHit=false、markerIndex=-1 通过

另覆盖:结构条件中断后连续计数清零、hasCheckoutRoot 单独命中仍判定、 UNKNOWN 页面文本命中仍强制停止、登录/验证码/风控在已有结构连续计数时优先级 不变——均通过。

偏离任务文档之处及理由

  1. 结构判据的“屏幕范围”不是新增入参,而是取当前可见元素包围盒。 任务文档 要求“宽高各不小于屏幕的 90%”,但 classify() 现有签名不含屏幕尺寸, PinduoduoUiElement 也不含屏幕尺寸字段。改为在可见元素集合内取 boundsLeft/Top/Right/Bottom 的包围盒作为屏幕范围的近似——真机根容器 (FrameLayout)的 bounds 本就等于屏幕大小(T-262 结算页 dump 亦如此: FrameLayout bounds=[0,0][1080,2376]),且无障碍节点树采集时根节点必然 被收录(BuyerAccessibilityService.collectNodes() 用 BFS 且根节点最先入 队)。这样既满足“宽高各不小于屏幕的 90%”的判定意图,又不改变 classify() / PinduoduoUiElement 对既有调用方的签名,风险更低。
  2. payTextSuppressed 的口径改为调用方持续累积,而非像 T-262 五个键那样只 在 PAYMENT_BOUNDARY 触发时产出。 这是本任务中分量最重的一个偏离, 理由:文本判据被放行(textMarkerSuppressed=true)几乎总是意味着 PAYMENT_BOUNDARY 没有触发(正是本任务要修复的常见场景——自动化在 规格弹层等页面继续正常浏览)。若沿用 T-262 的“只在触发时产出”模式, payTextSuppressed 在绝大多数真实场景下都会读到默认值 0,验收要点 “若真机上该值异常偏高,说明收敛过度”将无法被观察到,审计口径名存实亡。 经推演证实:hasCheckoutRoot 恒定强制 page=ORDER_CONFIRMATION(不在可 抑制页面集合内),结构判据要求非空文本数 <=2 而可抑制的五类页面 (HOME/SEARCH_RESULTS/IMAGE_SEARCH_RESULTS/PRODUCT_DETAIL/ SPECIFICATION_PANEL)现有识别规则均需要 >=2 个独立于标记文本之外的 语义文本,二者在现有启发式下几乎互斥——即“归因触发时刻”与“文本判据被 放行”事实上不会同时发生。因此改为:PinduoduoUiSnapshot 新增 textMarkerSuppressed(与 structureConfirmations 一样,每次快照都计算, 不依赖 PAYMENT_BOUNDARY 是否触发);PinduoduoCandidateAutomation 通过 新增的唯一快照入口 observeSnapshot() 在自身轮询过程中持续累积 textSuppressedCount(会话内计数,reset() 清零),随 diagnostics() 一并暴露;CandidateSearchDiagnostics.withPaymentBoundaryAttribution() 不再覆盖或清零该键。 已知局限:该累积目前只覆盖 pdd_browse_candidates 步骤(候选浏览, 规格弹层重复出现的主要场所),不覆盖 PinduoduoSearchAutomation/ PinduoduoImageSearchAutomation 所在的 pdd_open_app/pdd_enter_query/ pdd_submit_query 等更早步骤(这两个类当前没有诊断累积基础设施,为其新增 等价机制需要更大改动面)。原故障恰好是“在 pdd_open_app 单次命中即误判”, 修复后该步骤会正常通过并进入候选浏览,绝大多数后续文本放行会发生在 pdd_browse_candidates 内并被正确计数;但如果放行只发生在更早步骤且之后 再未出现,payTextSuppressed 会读到偏低的值。这是本次为控制改动面而接受 的范围边界,如需覆盖全流程应另开任务为 PinduoduoSearchAutomation/PinduoduoImageSearchAutomation 补充等价的 累积与暴露机制。
  3. payStructureStreak 由 withPaymentBoundaryAttribution() 与 PinduoduoCandidateAutomation.diagnostics() 共同产出,取二者较大值,而非 单一来源。 归因中的 structureConfirmations 只反映触发 PAYMENT_BOUNDARY 那一刻的读数(可能来自 pdd_open_app 阶段,PinduoduoCandidateAutomation 完全不知情);候选自动化自身累积的 maxStructureConfirmations 只覆盖它 自己轮询到的快照。二者是互补而非互斥的信息源,取大合并可以同时覆盖“结构 判据在更早步骤触发”与“候选浏览阶段观察到接近阈值但未触发”两类场景,且不 丢失任何一方已经观察到的信息。
  4. 文本判据可抑制的页面集合按任务文档字面五类严格取值 (PRODUCT_DETAIL、SPECIFICATION_PANEL、SEARCH_RESULTS、 IMAGE_SEARCH_RESULTS、HOME),未包含 SEARCH_RESULTS_OTHER_QUERY、 IMAGE_SEARCH、IMAGE_SEARCH_RETRY_DIALOG、ORDER_LIST 等其余非 UNKNOWN 类型。任务文档原文列举的是“商品详情、规格弹层、搜索结果、图片 搜索结果或首页”,理解为这五个具名类型;其余非正面列举的类型按“任何未正面 识别的页面”对待,维持强制停止,偏保守方向,符合“遇到不确定页面停止”的 业务硬边界。
  5. PinduoduoOrderDryRunAutomation/AndroidPinduoduoOrderDryRunDriver (T-217 订单 dry-run)一个字节未改,但由于二者最终都经同一个 BuyerAccessibilityService.classifyPinduoduoRoot() 获取快照,新增的结构 判据在共享的 classify() 层面对它们同样生效:若 dry-run 流程在 expectingOrderConfirmation 变为 true 之前意外连续 3 次观察到“占满屏 WebView + 语义文本 ≤ 2”的结构(现有 PinduoduoCollapsedCheckoutPolicy 本就是为识别这一结构而设计,只是原先只在该布尔量被显式置位后才转换为 PAYMENT_BOUNDARY),classify() 会独立触发 PAYMENT_BOUNDARY,比 dry-run 驱动原有的门控逻辑更早/更广地停止。这是共享判据修复误报/漏报后 的必然连带效应,方向上只会让安全停止更早/更确定,不会绕过或削弱既有 dry-run 停止点,且不修改 dry-run 文件本身的任何代码,因此判断为符合 “只增强不削弱”原则、未违反“不改下单 dry-run”的边界,但作为可能影响未来 dry-run 行为的连带效应在此明确记录,供后续如遇异常提前停止时排查参考。

未完成项(需人工真机验证,state 保持 DOING)

  • 真机复现同一商品重跑采购任务,确认不再被拦在 pdd_open_app 且能进入候选 采集。
  • 从 Admin 读取 payTextSuppressed,结合上方「偏离与决策」第 2 条的范围边界 确认放行次数在合理范围;若异常偏高需回头收紧文本判据的可抑制页面集合或 结构判据阈值。
  • 若条件允许,在真实结算页上验证连续 3 次快照后确实触发 PAYMENT_BOUNDARY 且 payStructureHit=1。