autotrain/docs/screen-adaptation.md
xinxin6623 7a01ed4db9 feat: add invoice batch wrap-up (name extraction + roster annotation) and web ui automation improvements
- add scripts/annotate_roster_invoices.py: match passenger names to invoice PDFs, write status + relative encoded hyperlinks into roster xlsx
- add scripts/add_invoice_passenger_name.py, scripts/calibrate.py
- add .opencode/skills/invoice-wrapup skill
- improve invoice_tool.py / web_ui.py / vision_fallback.py automation
- add device/screen/windows adaptation docs
- ignore out/ (delivered invoice PDFs contain sensitive data)
2026-07-10 00:11:45 +08:00

12 KiB
Raw Blame History

屏幕适配 · 识图点与坐标依赖清单

目的:梳理 H5 开票流程中所有"关键识图点"及其输出,标明哪些依赖具体屏幕分辨率,为适配其他手机做准备。

当前手机分辨率1260 x 2800PNG 头实测,_screenshot_size 读取)

一、识图点全链路(按处理顺序)

每个乘客的完整处理流程经过以下识图点。标注 [屏幕依赖] 的点换手机必须改。

阶段 0 · 进入列表页(仅 precheck 不在列表时触发)

# 识图点 输入 输出给后续环节 屏幕依赖
0a _vision_find_button("扫码开票") 截屏 像素坐标 (x,y) or None → tap 进扫码 0-1000 归一化)
0b _vision_find_button("订单") 截屏 像素坐标 → tap
0c _vision_find_button("电子发票") 截屏 像素坐标 → tap
0d _vision_find_button("扫码开票") 截屏 像素坐标 → tap
0e XML 找"相册"按钮 uiautomator dump bounds → click 否(系统返回)
0f _vision_find_qr_thumbnail() 相册截屏 QR 缩略图坐标 → tap 选图 0-1000 归一化)

阶段 1 · 列表页识行(每轮截屏)

# 识图点 输入 输出给后续环节 屏幕依赖
1 vision_list_rows() 截屏 rows[{masked_name, id_first, id_last, button_bbox:[x1,y1,x2,y2], tap_point:[x,y]}], scroll_position, page_ok → 后续用 tap_point 点击行 0-1000 归一化后 denormalize

防熄屏 tap [屏幕依赖]:每轮开头 tap "630" "1400"invoice_tool.py:2013)— 硬编码像素,换屏幕要改。

阶段 2 · 点击乘客行 → 子流程

用阶段 1 的 tap_point tap进入核验页。

阶段 3 · 等待页面状态 wait_for_page_transition

# 识图点 输入 输出给后续环节 屏幕依赖
3a xml_page_kind(nodes) uiautomator dump page kind: verify/invoice_info/invoice_confirm/no_ticket/success/other → 状态机分发
3b vision_detect_page()(仅 screenshot_wait=True 截屏 {page, summary} → 补充页面判断

阶段 4 · 核验页 - 输入身份证后 8 位

# 识图点 输入 输出给后续环节 屏幕依赖
4 XML 找 EditText 输入框 uiautomator dump bounds → click 输入框 → 输入 id8

阶段 5 · 点核验按钮

# 识图点 输入 输出给后续环节 屏幕依赖
5a XML 找"核验"按钮 uiautomator dump bounds → click
5b _vision_find_button("核验")XML 找不到时 fallback 截屏 像素坐标 → tap

阶段 6 · 等待 after_verifynetwork_sensitive2s + 12s

输出 page kind。超时返回 other → 进阶段 7。

阶段 7 · [after_verify 超时 other] 发票管理页纠偏

# 识图点 输入 输出给后续环节 屏幕依赖
7a XML 找"查看"按钮 uiautomator dump 无(找不到)
7b _vision_find_button("查看") 截屏 found:false → 触发探测
7c vision_detect_page() 截屏 v_page=invoice_info, summary → 判定为误判
7d _vision_find_button("开具") 截屏 像素坐标 (1071,1047) → tap 开具

阶段 8 · 发票信息页 - 点提交

# 识图点 输入 输出给后续环节 屏幕依赖
8a XML 找"提交"按钮 uiautomator dump bounds [31,2418][1228,2576] → click
8b _vision_find_button("提交")fallback 截屏 像素坐标 → tap

阶段 9 · 等待 after_submit_invoice_info → page=invoice_confirm

阶段 10 · 发票确认页 - 点确认

# 识图点 输入 输出给后续环节 屏幕依赖
10a XML 找"确认"按钮 uiautomator dump H5 页面常无 text
10b _vision_find_button("确认") 截屏 像素坐标 (850,2153) → tap

阶段 11 · 等待 after_invoice_confirmnetwork_sensitive→ 经过 loading → page=success

阶段 12 · 成功页 - 下载发票

# 识图点 输入 输出给后续环节 屏幕依赖
12a XML 找"发票预览/下载" uiautomator dump bounds [31,1942][1228,2096] → click
12b XML 找"下载" uiautomator dump bounds [52,1372][1207,1526] → click → download_pulled

阶段 13 · 成功页 - 找继续开票按钮回列表

# 识图点 输入 输出给后续环节 屏幕依赖
13a XML 找"继续开票" uiautomator dump
13b _vision_find_button("继续开票") 截屏 found:false → sub_step_error
13c 兜底XML 找"确定" uiautomator dump bounds [192,1591][1067,1724] → click
13d 兜底XML 找"继续开票" uiautomator dump bounds [28,2128][1232,2289] → click

阶段 14 · 回列表

keyevent 4 (back) × N -> 列表页。back 后 H5 页 scroll 重置到顶部,每轮重新截屏。


二、滑动设计详解([屏幕依赖] · 换屏必改)

滑动是最大的屏幕依赖点:起止 y 坐标 + 幅度 + duration 都绑定 1260×2800换屏不改会导致翻页异常跳过行 / 重复行 / 超出屏幕)。

2.1 滑动参数全量H5 主线 android-h5-page-run

参数 当前默认值 占屏比例 (2800 高) 用途 代码位置
--h5-scroll-x 630 50%(中线) 滑动 x 坐标(起止相同,垂直滑动) :3182
--h5-scroll-start-y 2400 85.7%(屏幕下方) 向下滚动时的起点 / 回顶时的终点 :3183
--h5-scroll-end-y 600 21.4%(屏幕上方) 向下滚动时的终点 / 回顶时的起点 :3184
--h5-scroll-duration-ms 800 - 单次滑动耗时ms越长越慢越可控 :3185
--h5-scroll-after 2.0 - 滑动后等待页面稳定(秒) :3186
--h5-scroll-top-burst 5 - 回顶时先连续下拉次数(不等识图) :3179
--h5-scroll-top-max 12 - 回顶时带签名确认的最大下拉次数 :3180

2.2 滑动方向与幅度

向下滚动_h5_scroll_down:2306)——看更多下方内容:

swipe (x, start_y=2400) -> (x, end_y=600)    # 手指上滑
幅度 = 2400 - 600 = 1800px = 屏高的 64.3%

回顶下拉h5_scroll_to_top:2233)——回到顶部:

swipe (x, end_y=600) -> (x, start_y=2400)    # 手指下滑
幅度 = 同 1800px方向相反
先连续 5 次burst再带 vision_list_rows 签名确认到顶

2.3 幅度为什么是屏幕相关的

屏幕 屏高 幅度 1800px 占比 问题
当前 1260×2800 2800 64.3% 正常
小屏 1080×2400 2400 75.0% 滑太多可能跳过行start_y=2400 已贴底)
大屏 1440×3200 3200 56.3% 滑太少,翻页慢,同屏签名重复判定到底
超大 1080×3120 3120 57.7% 同上

根因start_y / end_y 是绝对像素,不随屏高缩放。换屏后:

  • start_y=2400 在 2400 高的屏幕上 = 最底部swipe 起点贴边可能无效
  • 幅度 1800px 在小屏占比过大 -> 一次滑过太多行 -> 跳过乘客
  • 幅度 1800px 在大屏占比过小 -> 每次只滑一点 -> 效率低 + 签名去重误判

2.4 duration 也受影响

duration_ms=800 对 1800px 幅度 = 2.25 px/ms。换屏后若幅度变大比如 2400px同样 800ms 会更快 -> Android 可能触发 fling惯性滚动不可控。应保持速度恒定duration = 幅度 / 目标速度

2.5 原生列表滑动(android-vision-page-run,非主线但保留)

参数 当前默认 占屏比例 代码位置
--swipe-x 630 50% :3146
--swipe-start-y 2300 82.1% :3147
--swipe-end-y 200 7.1% :3148
--swipe-duration-ms 700 - :3149
--after-swipe 1.5 - :3150

幅度 = 2300-200 = 2100px = 75%,比 H5 更大。同样的问题。


三、[屏幕依赖] 硬编码坐标清单(换手机必须改)

这些是像素字面量,绑定 1260×2800 分辨率。换屏幕不改就会 tap/swipe 到错误位置。

位置 当前值 用途 代码位置
防熄屏 tap (630, 1400) 每轮开头轻点唤醒屏幕 invoice_tool.py:2013
截图尺寸 fallback 1260 × 2800 PNG 头读取失败时的兜底尺寸(极少触发) invoice_tool.py:1280
H5 滑动 x / start_y / end_y 630 / 2400 / 600 H5 列表页滑动(见第二节详解) --h5-scroll-* 默认 :3182-3184
原生滑动 x / start_y / end_y 630 / 2300 / 200 原生列表页滑动 --swipe-* 默认 :3146-3148

滑动幅度是最大风险点:换屏幕后 start_y 可能超出屏高,或幅度比例不对导致跳行/重复/翻页异常。


四、[屏幕无关] 自动适配的识图点

以下点使用 0-1000 归一化坐标系Qwen 返回归一化坐标后,用实际截图尺寸 denormalize 到像素,自动适配不同分辨率:

  • _vision_find_button(text) → 截图宽高 × (Qwen 返回的 0-1000 值 / 1000)
  • vision_list_rows() → 同上button_bbox 和 tap_point 都已 denormalize
  • _vision_find_qr_thumbnail() → 同上
  • XML bounds → 来自 uiautomator dump,系统返回的本身就是当前屏幕正确坐标
  • vision_detect_page() → 只返回页面类型,无坐标

结论:视觉识图点本身是屏幕无关的。问题集中在硬编码像素坐标(防熄屏 tap + 滑动参数)。


五、适配方案建议

5.1 启动时读屏幕分辨率(一次性)

# adb shell wm size -> "Physical size: 1260x2800"
SCREEN_W, SCREEN_H = read_screen_size()  # 缓存,全流程复用

5.2 防熄屏 tap 改为比例

(630, 1400) -> (SCREEN_W // 2, SCREEN_H // 2)

5.3 滑动坐标 + 幅度改为比例(核心)

当前值反推比例(以 2800 高为基准):

参数 当前像素 比例 动态计算
scroll_x 630 W×50% SCREEN_W // 2
start_y 2400 H×85.7% int(SCREEN_H * 0.857)
end_y 600 H×21.4% int(SCREEN_H * 0.214)
幅度 1800 H×64.3% start_y - end_y(自动随比例缩放)

换屏后幅度自动适配:

  • 2400 高 -> 幅度 = 2400×(0.857-0.214) = 1543px仍是 64.3%,行为一致)
  • 3200 高 -> 幅度 = 3200×0.643 = 2058px同比例

5.4 duration 按幅度等比缩放(保持滑动速度恒定)

当前:幅度 1800px / duration 800ms = 2.25 px/ms

换屏后若仍用 800ms 但幅度变了,速度会变 -> 可能触发 fling。应保持速度恒定

SWIPE_SPEED = 2.25  # px/ms, 当前调好的值
duration_ms = max(300, int(幅度 / SWIPE_SPEED))  # 下限 300ms 防过快
  • 2400 高 -> 幅度 1543 -> duration = 686ms
  • 3200 高 -> 幅度 2058 -> duration = 915ms

5.5 截图尺寸 fallback 改用动态值

return 1260, 2800 -> return SCREEN_W, SCREEN_H(启动时读的值)

5.6 保留 argparse override

默认值改用动态计算(比例 × 屏幕尺寸),但 --h5-scroll-* / --swipe-* 参数仍保留,特殊机型可手动覆盖动态值。

5.7 原生列表滑动同理

--swipe-* 参数start_y=2300→82.1%, end_y=200→7.1%)也按同样方式改比例 + duration 缩放。