Files
aerologic-app/.claude/skills/phone-adapt/references/verification.md
YANG JIANKUAN bbc29ca2e1 fix: 出库交接交接状态参数名改为handoverState并完成内网验证
- 按 api-doc 接口文档修正交接状态入参 hoState → handoverState(原参数被后端
  静默忽略,两个 Tab 返回同一批数据);内网实机验证 0/1 过滤与 App 端到端链路
  均生效,平板端回归无影响
- 新增《后端接口对接问题清单.xlsx》:按模块/页面/接口定义/接口地址索引的后端
  对接文档,含 5 项待处理缺口(移库缺航班字段/操作人姓名/数据不回填、交接缺
  交接人入参、pageQueryTotal 出参恒 0)+ 1 项已验证归档
- phone-adapt skill 新增 references/api-doc.md(api-doc MCP 核对流程 + 内网
  直调验证技巧),阶段 2 改为「先查接口文档再问用户」,修正 verification.md
  中「后端不认 hoState」的过时误判结论
- CLAUDE.md / CHANGELOG.md 同步走查与验证结论

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 15:13:43 +08:00

7.1 KiB
Raw Blame History

端到端验证手册

目标:在真实接口下把手机端走通,并确认平板端没退化。只看编译通过是不够的—— 本次适配里"筛选条件被静默清空"和"后端不认新参数"两个问题,都只有跑真机才暴露。

环境

~/Library/Android/sdk/emulator/emulator -list-avds    # Medium_Phone_API_36.1(手机) / Aerologic_Tablet(平板)
adb devices -l
adb -s <serial> shell wm size && adb -s <serial> shell wm density

判定形态:手机 1080×2400@420dpi → PHONE(sw=411dp);平板 1280×800@160dpi → TABLET(sw=800dp)

ui_probe.sh 封装了常用操作,比手写 adb 省事且更可靠:

P=.claude/skills/phone-adapt/scripts/ui_probe.sh
$P <serial> form                  # 打印形态识别结果
$P <serial> texts                 # 当前界面所有可见文字(做断言用)
$P <serial> bounds "已交接"        # 元素 bounds + 中心点
$P <serial> tap-text "确认"        # 按文字点击(自动取中心点)
$P <serial> wait-text "待交接"     # 等页面就绪
$P <serial> wait-gone "请稍候……"   # 等加载弹窗消失
$P <serial> shot /tmp/a.png       # 截图
$P <serial> req pageQuery         # 抓请求/响应体
$P <serial> crash                 # 崩溃次数 + 栈

构建安装

./gradlew assembleDebug 2>&1 | grep -E "^(BUILD|FAILURE)|error:"
adb -s <serial> install -r -d app/build/outputs/apk/debug/app-debug.apk

注意 gradle 输出里大量 e: 注: ARouter::Compiler … 是注解处理器的提示被误标成 e: 不是错误;只看 BUILD SUCCESSFUL / error: 就行。

进入目标页面的两条路

正规路径(优先):登录 → 首页菜单

adb -s <serial> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1

登录页账号密码需要用户提供。填表用 input text,注意 # 等字符要转义(Aa123456\#)。 底部有「首页 / 国内 / 国际 / 我的」Tab国际业务页找对应入口。

首页菜单是按权限过滤的:当前账号角色没有该权限时,入口根本不显示(本次 ADMIN 登录后 国际业务只有「板箱过磅」「进港理货」,没有「出库交接」)。这时走下面的直启。

直启(权限缺失时的替代):冷启动 + am start

app/src/debug/AndroidManifest.xml 里把调试页临时 exported="true",就能直接拉起。 必须冷启动(先 force-stop否则会撞上既有的 LoadingModel 崩溃:

adb -s <serial> shell am force-stop com.lukouguoji.aerologic
sleep 2
adb -s <serial> shell am start -n com.lukouguoji.aerologic/<完整类名>

登录态存在 SharedPreference 里,冷启动直启同样带 token接口能正常返回真实数据。

先经首页再 am start 会崩在 LoadingModel.showLoading → XPopup popupInfo is null 这是既有缺陷(未改动的页面也复现),不是本次改动引入的。

点击前先等就绪

加载弹窗("请稍候……")会盖住整屏并吞掉 input tap,表现为"点了没反应"。 本次验证就因此白跑了几轮。所以:

$P <serial> wait-text "待交接"      # 等目标元素出现
$P <serial> tap-text "已交接"
$P <serial> texts | grep -c "全选"  # 用文字变化确认点击确实生效

点完一定要断言效果(用 texts 或截图),别假设点中了。

逐项走查清单

每一步做完都跑一次 crash(必须为 0

  • 列表加载:卡片字段、各种状态形态(如待处理 / 已完成两种卡片)
  • Tab 切换:指示条位置、角标数字、底部条按设计显隐
  • 筛选弹层:打开、各控件取值、重置、确认后重新查询
  • 下拉字典:真实取到数据(不是空列表)
  • 多选:单选、全选联动、"已选 N 项"计数
  • 详情页/二级页:字段绑定、条件显示的区块
  • 扫码入口能拉起

核对请求参数是否真的生效

新增的查询字段要确认两件事:客户端发出去了,服务端

$P <serial> req pageQuery

OkHttp 日志是多行 pretty JSON。看到请求体里有你的字段"handoverState": "0")只说明客户端对了。 服务端是否生效要用行为验证:切到另一个 Tab看返回数据是否真的变了。

⚠️ 行为验证失败时先怀疑参数名,再怀疑后端:出库交接第一版传 hoState,两个 Tab 返回同一批数据, 一度误判为「后端没做过滤」实际是参数名错了api-doc 里是 handoverState),后端对不认识的参数 静默忽略。正确顺序:查 references/api-doc.md 核对参数名 → 用 token 直调后端做 A/B 对比 (同条件只改一个参数看 total 变化)→ 仍不生效才定性为后端缺陷并记入 documents/后端接口对接问题清单.xlsx

无数据时怎么核对 UI

服务端当天可能没数据(列表空)。放宽条件(清空日期)再查;仍为空时,临时注入样例 Bean

// 临时:覆盖各种形态各一条,验证后删除
binding.rv.postDelayed({
    binding.rv.commonAdapter()?.refresh(listOf(/* 待处理 / 危险品 / 已完成 各一条 */))
}, 2500)

截图核对完必须删除,然后重新构建 + grep 确认无残留:

grep -rn "injectMockForVisualCheck\|TODO 临时" module_base/src module_gjc/src | wc -l   # 应为 0

屏幕方向验证

新增/修改 Activity 后,确认手机端不会「先横屏再转竖屏」:

# 冷启动时连续采样:应全程 mRotation=0
adb -s <phone> shell am force-stop com.lukouguoji.aerologic; sleep 2
adb -s <phone> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1
for i in $(seq 1 10); do adb -s <phone> shell dumpsys window | grep -m1 -oE "mRotation=[0-9]+"; sleep 0.4; done

平板则应首屏就是横屏(截图宽 > 高)。若手机出现横屏帧,检查该 Activity 的 Manifest 是否写成了别的值——必须是 android:screenOrientation="unspecified"(写死 userLandscape 或用 @integer/... 资源引用都会出问题,详见 SKILL.md 阶段 4

Pad 端回归(必做)

adb -s <tablet> install -r -d app/build/outputs/apk/debug/app-debug.apk
adb -s <tablet> shell am start -n com.lukouguoji.aerologic/<同一个类名>
$P <tablet> shot /tmp/tablet.png
$P <tablet> form     # 应为 TABLET(sw=800dp…)

对比改造前后:搜索条数量与顺序、日期默认值、按钮(平板可能比手机多按钮)、底部统计文案。 改过 module_base 里的公共基类或 Pad 系组件时,这一步不能省,且要多看几个页面。

定位"值被神秘修改"类问题

在 setter 里打带堆栈的日志,一次就能看到调用来源:

android.util.Log.d("DBG", "value '$field' -> '$v'", Throwable("trace"))
adb -s <serial> logcat -d -s DBG | grep -A14 "'旧值' -> ''" | grep "at com.lukouguoji\|at android"

本次靠这招定位到 TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged 即复合自定义 View 的 id 串档(见 components.md)。排完删日志。