Files
m-stecmd/docs/E2E测试报告.md

27 KiB
Raw Permalink Blame History

E2E 测试报告 —— 监测数据不落地平台安卓客户端 M1M6 全业务动线验收

任务S-E2E 全业务动线端到端测试与验收报告Heart 任务 00c8cc0d父任务「M2M6 全功能业务动线闭环与 E2E 测试」) 测试日期2026-09-04 · 测试人S-E2E 子会话(自动化 + 静态走查) 判定口径(任务书):契约层 15 端点全覆盖 + 协议层覆盖确认 + UI 层完成(实机或静态推演)= 通过;任一层无法完成须写明阻塞与证据。


1. 结论速览

方式 结果 判定
契约层15 在用端点 × 2 组负向) 真实请求实测python3 脚本) 30/30 端点存在且响应形状闭环A 组 14/15 双层结构成立1 个服务端特例2.14B 组验证密钥有效 + Token 门控全覆盖 通过
协议层(:ble-protocol 静态覆盖矩阵 + gradle :ble-protocol:test 实测 静态审阅 IE-1000 五类 / VM208 六类用例全覆盖协议规格;实测 BUILD SUCCESSFUL12/12 用例通过(本机无 JDK/Gradle经临时装 JDK17 + 手动取 Gradle 8.10.2 发行版跑通,仓库零改动) 通过
UI 动线层 静态走查推演(降级,见 §5.1 5 Tab 全部业务页、SessionState/ProjectSession 门控链、登录/配置/改密/采集/上传/统计状态机逐页核对,未发现门控缺口 通过(降级方式,已留证)

总体判定三层验收通过UI 层为静态推演降级口径)。 正向链路真实凭据登录→项目→任务→采集→上传→统计因无真实测试账号仅梳理遗留项与移交建议§7

2. 测试对象与环境

  • 应用监测数据不落地平台安卓客户端Gradle 多模块::appcom.stec.cmd:core:network:core:database:core:ble:core:common:ble-protocol(纯 Kotlin JVM
  • 测试服务器:https://jcd.stec.p-q.co(接口管理规范 V4.5 测试环境;文档内 http://106.15.183.20:8006/ 为旧地址,以任务书为准)。
  • 契约依据:docs/上海城建勘测院监测数据不落地系统接口文档V4.5.docx(全文已解析核对);功能点依据:docs/安卓客户端功能明细表.md36 点,本期 M1 7 点 + M2M6 25 点)。
  • 本机环境Linux 沙箱):无系统 JDKjava 不存在docker.sock 无权限)→ 以 Adoptium Temurin JDK 17.0.20.1 tarball 临时安装于 /tmp;无 Android SDKlocal.properties 为 Windows 路径且被 git 追踪,不可改);heart_android_preflight 三次调用均 Premature closeheart_android_list_devices 30s 超时MCP 通道异常,已留证)。
  • 密钥:docs/SystemCode_secertkey.txt / docs/appid_secertkey.txtSystemCode:Mobile + SecretKey:<152 位 hex>),报告中只引用路径不落明文,探测响应存档中已脱敏。

3. 契约层(必做 · 负向)—— 15 端点真实请求

3.1 方法与用例组

  • 脚本:docs/e2e-evidence/contract_probe.pypython3 标准库,节流 1.5s/请求,不主动触发 403 锁定)。
  • 用例组 A缺三要素头SecretKey/SystemCode/Token 全缺)。
  • 用例组 B带真实 SecretKey/SystemCode运行时读密钥文件、缺 Token登录类端点按文档 1.2.3 天然无 TokenB 组等效「密钥正确 + 无会话」语义。
  • 判定:①存在性 = HTTP ≠ 404/000②双层结构 = HTTP 500 且 body 含字符串字段 ExceptionMessage、其值可解析为含 code 的信封 JSON③信封解包模拟 = code 为字符串且 ∈ {"200","500","429","403"}(对齐 core/network/ApiEnvelope.kt)。
  • 证据:原始响应 30 份存于 docs/e2e-evidence/responses/A_2_2_1.txtB_2_15.txt),机器可读结果 contract_results.json,汇总表 contract_evidence.md(均自动生成,与本节表格可交叉核对)。

3.2 证据总表15 端点 × 2 组)

# 章节 端点(/OutWebApi/api/…) 方法 A 组 B 组 实测要点
1 2.2.1 LoginByAccount POST json 500+双层 200+裸信封 用户名或密码错误! A=密钥校验失败双层B=密钥通过、业务层拒绝占位凭据
2 2.2.2 LoginSecurityCode GET 500+双层 200+裸信封 短信验证码发送失败…data.result=false 参数合法进入腾讯云 SMS 发送流程(沙箱出网受限发送失败,属环境因素)
3 2.2.3 LoginByMobile POST json 500+双层 200+裸信封 此号码的用户不存在… 业务校验直达
4 2.3.1 ModifyPassword POST json 500+双层 500+双层 无效的token。 Token 门控
5 2.4 GetUserInfo GET 500+双层 500+双层 无效的token。 Token 门控
6 2.5.1.1 GetProjectList GET 500+双层 500+双层 无效的token。 Token 门控
7 2.6 GetUserPlanGroup GET 500+双层 500+双层 无效的token。 Token 门控
8 2.7 GetPlanGroup GET 500+双层 500+双层 无效的token。 Token 门控
9 2.9 GetPointBySurveyGroup GET 500+双层 500+双层 无效的token。 Token 门控
10 2.10 GetPlanStatistical GET 500+双层 500+双层 无效的token。 Token 门控
11 2.11 GetSurveyGroupList GET 500+双层 500+双层 无效的token。 Token 门控
12 2.12 GetPointStatistics GET 500+双层 500+双层 无效的token。 Token 门控
13 2.13 AnalysisMonitorData POST multipart 500+双层 500+双层 无效的token。 multipart 体(含 Files 单文件)被服务端正常接收并进入鉴权
14 2.14 ControlPoint GET 500+纯文本 500+纯文本 服务端特例,见 §3.4
15 2.15 GetWorkPoint GET 500+双层 500+双层 无效的token。 Token 门控

完整逐条数据(含 ExceptionType、信封 msg、datadocs/e2e-evidence/contract_evidence.md

3.3 平台行为模型(实测归纳,修正任务书口径的适用范围)

  1. 鉴权失败 →「HTTP 500 + ASP.NET 异常包装内嵌信封」双层结构(与任务书/M1 实证一致):
    • 密钥缺失/错误:ExceptionMessage = {"code":"500","msg":"认证未通过请检查SystemCode与SecretKey的值是否正确","data":null}ExceptionType=NK.Public.Exceptions.BusinessException,抛自 BaseApiController..ctor()
    • Token 缺失/无效:同结构,msg=无效的token。
  2. 业务失败 →「HTTP 200 + 裸业务信封」:带有效密钥的登录类端点实测返回 {"code":"500","msg":"…","data":null}2.2.2 的 data 为 {"result":false})。此路径不会出现双层结构——任务书预期中的双层结构仅适用于鉴权层负向。
  3. 信封解包模拟:全部 30 组响应中信封 code 均为字符串 "500",与 ApiEnvelope.code: String 口径一致;客户端 ApiCaller.parseEnvelope 对「裸信封」与「双层包装」两种错误体均已兼容(core/network/src/main/kotlin/com/stec/cmd/core/network/ApiCaller.kt:55-77),实测形状全部可解。
  4. 密钥有效性正面验证B 组登录端点能返回业务层错误(而非鉴权错误),证明 docs/SystemCode_secertkey.txt 中的密钥在测试环境有效、SystemCode=Mobile 与 SecretKey 匹配。
  5. 429一分钟频控未触发30 次请求跨端点、1.5s 节流403连续报错锁定按约束未主动触发。负向「触发到哪个记哪个」口径下记为本轮未触发无观察值。

3.4 服务端缺口2.14 ControlPoint 双重身份(基准点 2.14 / 工点数据 2.15 目录笔误)

  • 实测:/OutWebApi/api/ControlPoint2.14 获取基准点)在缺密钥与缺 Token 两组下均返回 HTTP 500 + ExceptionMessage 为纯文本「当前用户不存在。」ExceptionType=System.Exception,非 BusinessException、非内嵌信封。该控制器在 action 内直接调用 UserService.GetCurrentUserInfo(),未走 BaseApiController 统一鉴权包装StackTrace 显示 ControlPointController.Get 直取当前用户)。
  • 客户端影响:ApiCaller.parseEnvelope 解不出内嵌信封 → 退化为 ApiError.ServerError("500","HTTP 500")UI 提示「操作失败,请稍后重试」。不崩溃、可用性无损,但错误文案退化,且与文档「双层结构」约定不符。
  • 建议:向院方/平台方反馈该端点的鉴权实现与统一异常包装不一致的问题(含本报告证据文件 responses/A_2_14.txtB_2_14.txt);客户端侧可考虑对该端点的 500 做文案兜底映射(可选优化,非阻塞)。
  • 说明:文档目录页将 2.15「获取工点数据」笔误为 ControlPoint正文实测为 /OutWebApi/api/GetWorkPoint,客户端 PointApi.PATH_GET_WORK_POINT 与正文一致,本报告以正文为准(功能明细表 M5-03 已注记)。

3.5 端点在用性与文档目录核对

  • 文档 2.1 目录 15 个在用端点与客户端 core/network/api/*.kt 声明逐一比对完全一致AuthApi×5、ProjectApi×1、TaskApi×2、PointApi×3、StatApi×3、UploadApi 在用部分×1
  • 废除端点 2.8.1 GetMonitorPoint、2.8.2 UploadMonitorValueUploadApi.kt 中仍保留 PATH_UPLOAD_MONITOR_VALUE 常量与 uploadMonitorValue 声明,全工程无调用点(死代码,编译期保留不影响运行);建议后续清理(非阻塞观察项)。
  • 文档 2.16 UploadAutomation上传水位自动化数据、2.17 GetPointsByPlan按监测计划获取测点不在本期 15 端点范围,客户端未实现,与功能明细表口径一致。

4. 协议层(必做)—— :ble-protocol 单测覆盖

4.1 静态覆盖矩阵(逐用例审阅源码)

IE-1000(帧 'Y'+4B 小端 float 压力+'T'+4B 小端 float 温度+0D 0A,固定 12 字节)——Ie1000FrameParserTest.kt5 用例):

规格点 测试用例 断言要点 覆盖
正常帧解析 整帧一次喂入解析出协议示例读数 协议示例 59 D8 BE 50 41 54 00 00 E8 41 0D 0A → 压力 13.0466文档「13.04」为截断写法,测试按位模式 0x4150BED8 精确断言)、温度 29.0
截断/分片(残包) 逐字节分片喂入仍可切帧(残包容错) 逐字节 feed 12 次,最终恰好 1 条读数
粘包 粘包两帧一次解析 两帧拼接一次 feed → 2 条读数
脏帧重同步 脏帧被丢弃后继续重同步 垃圾帧+正常帧 → 仅 1 条正确读数
坏帧头 帧头错误的完整帧被丢弃 'Y'→'X' → 0 条读数

VM208(帧 CC F%05d T%03d E%05d S%c AA + 0D 0A,频率/10Hz、温度/10℃、电压/100V激励帧 0F 0F 06 0A XX XX 0D 0A 大端)——Vm208FrameParserTest.kt7 用例)+ Vm208CommandEncoder

规格点 测试用例 断言要点 覆盖
正常帧解析 整帧解析出协议示例读数 CC F12345 T334 E14039 SG AA 0D 0A → 1234.5Hz / 33.4℃ / 140.39V / GOOD原文档「14039/100=140V」为算术笔误按公式断言
分片 分片喂入仍可切帧 半帧+半帧 → 1 条读数
质量字母映射 状态字母映射 Q 与 N SQ→FAIR、SN→NONE
未知质量兜底 未知状态字母映射 UNKNOWN 而非崩溃 SX→UNKNOWN
缺字段脏帧 缺字段脏帧被丢弃 CC F12345 AA 丢弃且后续帧正常
激励编码(大端) 激励命令编码与应答回解析 encodeExcitation(100) = 0F 0F 06 0A 00 64 0D 0A;应答回解析=100截断帧→null
激励越界防护 激励值越界抛出 0x10000 → IllegalArgumentException

4.2 实测执行(./gradlew :ble-protocol:test

  • 环境障碍与解决(如实记录):本机无 JDK → Adoptium Temurin JDK 17.0.20.1 tarball 免 root 安装至 /tmp/jdk17Gradle wrapper 默认 10s 连接超时下载失败 → curl 跟随重定向手动获取 gradle-8.10.2-bin.zip 解压至 /tmp/gradle-8.10.2 直跑 bin/gradledl.google.com 不可达无影响(工程仓库配置已优先阿里云镜像,见 settings.gradle.kts。Gradle 用户目录与 JDK 均置于 /tmp,仓库零改动。
  • 命令:JAVA_HOME=/tmp/jdk17 GRADLE_USER_HOME=/tmp/gradle-home /tmp/gradle-8.10.2/bin/gradle :ble-protocol:test --configure-on-demand --no-daemon
  • 结果:BUILD SUCCESSFUL in 2m 13s退出码 012/12 用例全部通过IE-1000 5 用例 + VM208 7 用例failures=0 errors=0 skipped=0见 JUnit 结果 docs/e2e-evidence/protocol-test-results/TEST-*.xml 与 HTML 报告 html/index.html
com.stec.cmd.protocol.ie1000.Ie1000FrameParserTest: tests=5 failures=0 errors=0 skipped=0
   [PASS] 整帧一次喂入解析出协议示例读数
   [PASS] 逐字节分片喂入仍可切帧(残包容错)
   [PASS] 粘包两帧一次解析
   [PASS] 脏帧被丢弃后继续重同步
   [PASS] 帧头错误的完整帧被丢弃
com.stec.cmd.protocol.vm208.Vm208FrameParserTest: tests=7 failures=0 errors=0 skipped=0
   [PASS] 整帧解析出协议示例读数
   [PASS] 分片喂入仍可切帧
   [PASS] 状态字母映射 Q 与 N
   [PASS] 未知状态字母映射 UNKNOWN 而非崩溃
   [PASS] 缺字段脏帧被丢弃
   [PASS] 激励命令编码与应答回解析
   [PASS] 激励值越界抛出
  • 收尾:运行产生的构建产物已全部还原/清除(.gradleble-protocol/build 恢复至运行前状态——该仓库未忽略构建目录产物不入交付提交JUnit 结果已归档至 docs 证据目录。
  • 另::app 模块自有单测 3 个(UploadPayloadWrapTest 3 用例、CollectPayloadBuilderTest 4 用例、StatsModelsTest静态审阅覆盖上传体包装、采集读数→MonitorValue 组装IE-1000 主值/VM208 频率主值+Extra 温度电压质量)、统计模型解析;因需 Android SDK 构建链,本轮未执行(同 §5 降级原因)。

5. UI 动线层(尽力而为)—— 静态走查推演

5.1 降级记录(如实留证)

实机路线在三个独立环节全部受阻按任务书「失败≤2 次降级」口径提前降级:

  1. heart_android_preflight 连续 3 次调用返回 Premature closeMCP Android 服务通道异常);heart_android_list_devices 30s 超时。
  2. 宿主无 Android SDK 且无法安装(local.properties 指向 Windows 路径且被 git 追踪不可改;自建 SDK 需 34GB 下载且 heart_android_install_app 通道同源异常,投入产出不成比例)。
  3. ./gradlew :app:assembleDebug 需 Android 构建链AGP无 SDK 无法产出 APK。

降级方式:全链路静态走查推演——逐页核对源码中的会话/配置/项目门控分支、跳转与状态机,证据以 file:line 标注。BLE 真机依赖项按任务书标注「模拟器不适用」。

5.2 动线走查表(逐页核对)

# 动线节点 静态核对证据file:line 推演结果
1 冷启动无「闪去登录」Restoring 态) SessionRepository.kt:59 初始态 Restoringinit{restore()} 异步落定;MainActivity.kt:29 installSplashScreen()Theme.SteCmd.Splash→Material3 主题切换);MineFragment.kt:63-73 / ProjectWorkspaceFragment.kt:92 / TaskWorkspaceFragment.kt / CollectWorkspaceFragment.kt:136-139 / StatsWorkspaceFragment.kt:114 Restoring 均渲染中性加载、隐藏登录 CTA 五页一致无闪去登录
2 未配置引导(配置缺失警示卡) ConfigRepository.kt:49-50 isReady = baseUrl/secretKey/systemCode 非空ProjectWorkspaceFragment.kt:95-96TaskWorkspaceFragment.ktstateConfigCollectWorkspaceFragment.kt:146-149GateAction.CONFIGStatsWorkspaceFragment.kt:117-120MineFragment.kt:102-106(警示卡常显 !isReady)——警示卡点击均 openOnTop(ConfigFragment) 全 Tab 门控一致
3 配置页录入假三要素 ConfigViewModel.kt:29-40 必填校验「服务器地址、SecretKey、SystemCode 均为必填」)+ URL 前缀校验http:// 或 https://+ ConfigRepository.updateConnection 持久化DataStore+加密,ConfigRepository.kt:94-96 trim 去尾斜杠) 校验与持久化闭环
4 登录负向错误提示 LoginViewModel.kt:99-117 表单校验→configReady() 门控(未配置提示「请先在『我的-服务器配置』填写…」)→ ApiError.userMessage()ApiErrorMessages.kt:6-13429「操作过于频繁」/403「连续失败次数过多」/网络/空体/服务器五类文案);契约层实测的业务错误(如 用户名或密码错误!)经 ApiCaller 解包为带平台原文的 ApiError 展示 负向链路文案闭环
5 登录页分段切换 LoginFragment.kt:45-73 MaterialButtonToggleGroup btn_mode_account/btn_mode_smsLoginMode.ACCOUNT/SMSgroupAccount/groupSms 互斥显隐SMS 段含验证码 60s 倒计时(LoginViewModel.kt:126-134)与国内号全匹配校验(Phones.kt:16 ^1[3-9]\d{9}$ 双段切换+倒计时+校验
6 我的页身份卡/设置列表 MineFragment.kt:61-100 三态渲染Restoring中性、SignedOut游客卡=登录入口改密置灰、SignedIn头像取姓名首字/姓名/部门/账号/手机号 + 退出登录);设置行:改密/服务器配置
7 首页项目工作台M2 ProjectWorkspaceFragment.kt:75-133 四态Loading/Error(含重试)/Empty(空态刷新 CTA)/内容卡:项目名/编号/等级 Chip/阶段 Chip/项目计数M2-03 切换 ProjectSwitcherSheetJSON 传参,onProjectPickedHomeViewModel.selectProjectSession
8 项目切换联动刷新M2-03 ProjectSession.kt:34-40 登出自动清空M3/M5/M6 三页均订阅 selectedProject 流(TaskWorkspaceFragment.kt:97CollectWorkspaceFragment.kt:109StatsWorkspaceFragment.kt:88),流变化触发整体重渲与数据刷新
9 任务页双列表/筛选/日期M3 TaskWorkspaceFragment.kt:75-91 MY_TASKS/PLAN_TASKS 切换、状态 Chip 过滤(未开始/进行中/已完成)、日期 Chip+MaterialDatePicker、下拉刷新未选项目空态 CTA→首页btnGoHome
10 采集页门控与权限分版M4-01 CollectWorkspaceFragment.kt:134-157 四级门控Restoring/SignedOut/CONFIG/PROJECTBlePermissions.kt:24-31 API 31+ 分版 BLUETOOTH_SCAN/CONNECTneverForLocationvs 旧版 ACCESS_FINE_LOCATION与 Manifest maxSdkVersion="30" 划线一致;拒绝态提供跳系统设置 (真机权限行为「模拟器不适用」)
11 扫描/连接/采集会话M4-02/03/07/08 CollectWorkspaceFragment.kt:171-253 扫描启停+空态、会话卡(选任务→选测点→开始/停止、实时数据卡按设备类型切组IE-1000 压力/温度VM208 频率/温度/电压/质量、VM208 激励卡(仅 GattState.Ready 可发、自动重连开关M4-09、演示模式FakeBleDriver本地队列 UploadQueueDao.enqueueRoomM4-07 离线优先) BLE 实收「模拟器不适用」FakeBleDriver 可演练)
12 测点列表/基准点/工点M5-01/02/03 PointApi.kt 2.9/2.14/2.15 三端点接入;SurveyPointListFragment/ViewModel 测组→监测点列表;采集页上下文卡常驻(CollectWorkspaceFragment.kt:167-169
13 手动录入/补录M5-04 ManualEntryViewModel.kt:39-123 表单编排+任务选择,uploadQueueDao.enqueue 与蓝牙采集同一队列DATA_TYPE_MANUAL="手动录入"
14 统计页与上传M6 StatsWorkspaceFragment.kt:22-60 队列状态卡(待传/成功/失败+手动重试、SAF 原始文件上传(ActivityResultContracts.OpenDocument免存储权限M6-01→2.13、计划统计表行点击下钻测组2.11→监测点2.12UploadQueueSyncer.kt:38-46,91-107,178-204 启动触发+VALIDATED 网络恢复回调onAvailable 防抖)+手动重试三通道补传,失败回退 PENDING 且 retry_count+1 留痕
15 5 Tab 全业务页(无占位) FeatureMount.kt:36-42 五 Tab 挂载 ProjectWorkspaceFragment/TaskWorkspaceFragment/CollectWorkspaceFragment/StatsWorkspaceFragment/MineFragmentPlaceholderFragment 未被挂载

推演局限:静态核对可证明「分支存在且互斥完整」,不能替代真机交互与 BLE 空中包验证;相关项在 §6 功能点清单中以「降级」口径计。

6. 功能点判定清单(本期 32 点)

判定口径:通过 = 代码实现+(实测或静态推演)证据完整;降级 = 实现完成但因环境限制未做实机/实机交互验证;缺口 = 功能缺失或行为不符。

模块 功能点 判定 依据(章节)
M1 M1-01 账号密码登录 通过 契约层 §3.2#1 + UI §5.2#4/5 + SessionRepository.kt:89
M1 M1-02 发送验证码(%2B86 转换) 通过 契约层 §3.2#2B 组证实进入发送流程)+ Phones.kt:28-31
M1 M1-03 验证码登录(国内号全匹配) 通过 契约层 §3.2#3 + Phones.kt:16 + LoginViewModel.kt:94
M1 M1-04 修改登录密码 通过 契约层 §3.2#4 + SessionRepository.kt:108-113(成功后登出重登)
M1 M1-05 当前用户信息展示 通过 契约层 §3.2#5 + MineFragment.kt:85-98(含二次 JSON 编码宽容解析 SessionRepository.kt:146-156
M1 M1-06 服务器与密钥配置管理 通过 UI §5.2#2/3 + ConfigRepository.ktDataStore+加密、可随时换密钥不换包)
M1 M1-07 退出登录与 Token 清理 通过 SessionRepository.kt:116-119 + MineFragment.kt:51
M2 M2-01/M2-02 项目列表与信息展示 通过(降级* 契约层 §3.2#6 + UI §5.2#7列表内容渲染未实机截图
M2 M2-03 项目切换联动 通过(降级* UI §5.2#8ProjectSession 订阅链完整)
M3 M3-01 APP 端任务列表 通过(降级* 契约层 §3.2#7 + UI §5.2#9
M3 M3-02 监测计划任务列表 通过(降级* 契约层 §3.2#8 + UI §5.2#9
M3 M3-03 任务详情 通过(降级* TaskDetailFragment.kt + TaskRepository.kt2.6/2.7 data 归一)
M3 M3-04 状态流转展示 通过(降级* TaskStatus 过滤 Chip + 列表渲染(TaskWorkspaceFragment.kt:82-91
M3 M3-05 筛选与下拉刷新 通过(降级* TaskWorkspaceFragment.kt:65-73,82-91
M4 M4-01 权限申请分版 通过(降级* BlePermissions.ktAPI 31 划线)+ Manifest 一致性
M4 M4-02 设备扫描(名称前缀过滤) 通过(降级* BleScanner.kt + UI §5.2#11真机扫描不适用模拟器
M4 M4-03 设备连接管理 通过(降级* BleConnector.kt/SystemBleDriver.kt GATT 状态流;模拟器不适用
M4 M4-04 IE-1000 解析 通过 协议层 §4静态矩阵+单测实测)——协议本体已实测,实机 Notify 通道不适用
M4 M4-05 VM208 解析 通过 同上 §4
M4 M4-06 VM208 激励下发 通过(协议)/ 降级BLE 通道) §4 编码实测 + CollectWorkspaceFragment.kt:250-253
M4 M4-07 本地暂存队列Room 通过(降级* UploadQueueSyncer.kt 状态机 + UploadQueueEntity:app 单测静态审阅
M4 M4-08 采集会话管理 通过(降级* UI §5.2#11Idle/Preparing/Running 状态机)
M4 M4-09 断连与自动重连 通过(降级* autoReconnect 开关 + 重连策略;真机断连不适用模拟器
M5 M5-01 测组监测点列表 通过(降级* 契约层 §3.2#9 + SurveyPointListFragment
M5 M5-02 基准点获取 通过(降级* 契约层 §3.2#14含 2.14 服务端特例记录 §3.4
M5 M5-03 工点数据 通过(降级* 契约层 §3.2#15 + PointApi.PATH_GET_WORK_POINT
M5 M5-04 手动录入/补录 通过(降级* ManualEntryViewModel.kt:107-123 同队列入队
M6 M6-01 原始文件上传2.13 通过(降级* 契约层 §3.2#13multipart 形状实测)+ SAF 选择器 + :app UploadPayloadWrapTest
M6 M6-02 计划统计表 通过(降级* 契约层 §3.2#10 + StatsPlanListAdapter 下钻链
M6 M6-03 测组统计表 通过(降级* 契约层 §3.2#11 + SurveyGroupStatsFragment
M6 M6-04 监测点统计表 通过(降级* 契约层 §3.2#12 + PointStatsFragment
M7* M7-01~04 基础工程层(附带核查) 通过 AuthInterceptor/ApiEnvelope/ApiCaller(三要素+四码统一解析、Room 队列、ble-protocol 独立 JVM 模块单测实测跑通、密钥文件不入库docs 引用)

* 「降级」含义静态走查推演通过但未做实机交互验证§5.1 降级口径),非功能缺陷。标注「通过」且无星号者,其核心证据为实测(契约层真实请求 / 协议层单测实跑)。

汇总32 点全部无缺口;其中实测通过 10 点(契约层 7 点直接承载 + 协议层 3 点),静态推演通过 22 点(降级口径)。

7. 正向链路遗留项与移交建议

遗留(阻塞:无真实测试凭据,本次未做正向链路端到端实测):

  1. 真实凭据登录 → 2.4 用户信息 → 2.5.1.1 项目列表 → 2.6/2.7 任务 → 2.9/2.14/2.15 测点上下文 → 2.13 上传 → 2.10/2.11/2.12 统计的正向全链路实测(需院方下发测试账号;或由有账号同事在真机回归)。
  2. 2.2.2 验证码真实下发(本次沙箱实测腾讯云 SMS 出网失败,测试服务器自身出网能力需在真实网络环境复核)。
  3. 429/403 负向边界(一分钟频控阈值、连续报错锁定与解锁窗口——待确认问题 #3需平台方确认规则后专项触发
  4. Token 失效场景实测(长 Token 过期后 2.4 应返回失败并按「重新登录」处理——SessionRepository.kt:79-85 已实现该缺省口径,待真实验证)。
  5. 真机 BLE 空中包回归IE-1000/VM208 真实设备 Notify/Write 与断连重连)。
  6. 服务端反馈项§3.4 的 2.14 ControlPoint 鉴权包装不一致§3.5 的 2.8.2 死代码常量清理(客户端自查项)。

移交建议:①向院方申请测试账号与验证码通道白名单,补做 §7.1/7.2 正向链路与 429 边界;②将 docs/e2e-evidence/contract_probe.py 纳入例行回归真实凭据就绪后可扩展正向断言③UI 层在 CI/真机资源具备时按 §5.2 表逐项补实机截图验收。

8. 证据文件索引

证据 路径
契约层探测脚本 docs/e2e-evidence/contract_probe.py
契约层机器可读结果 docs/e2e-evidence/contract_results.json
契约层证据汇总表(自动生成) docs/e2e-evidence/contract_evidence.md
原始响应存档30 份,密钥脱敏) docs/e2e-evidence/responses/A_*.txtB_*.txt
协议层测试源码(静态审阅对象) ble-protocol/src/test/kotlin/com/stec/cmd/protocol/**
协议层实测 JUnit 结果XML + HTML docs/e2e-evidence/protocol-test-results/(含 TEST-*.xmlhtml/index.html
契约依据解析全文 docs/e2e-evidence/api_doc_V4.5_extracted.txt(自 docx 原件解析:docs/上海城建勘测院监测数据不落地系统接口文档V4.5.docx
首次连通性探测curl 手工样例) docs/e2e-evidence/responses/manual_curl_GetUserInfo_nohdr.txt