Files
2026-10-22 19:08:46 +08:00

31 KiB
Raw Permalink Blame History

智慧后勤收银终端 · 测试角色手册

项目:pj_supermarket_cloud(智慧后勤 POS 收银终端,Flutter + Android 混合开发) 版本基线:v2.1.3+213 · Floor 数据库 v14 · minSdk 24 · Flutter 3.27.4(fvm) 本文档是本项目测试角色的岗位职责 + 测试策略 + 发版流程三位一体的工作手册。


0. 角色定位:你在为谁把守发布按钮

每按下一次"发版",就是把一个新版本同时推进 100 多家商家的收银台。

收银终端不是普通 App:

  • 它是商家的营业生命线——收银页白屏 10 分钟,商家 10 分钟收不了钱;
  • 它跑在无人值守的真实硬件上——双目摄像头、扫码枪、打印机、副屏,任何一个外设失联都是现场事故;
  • 它一旦装上就很难卸载(Device Owner 模式),发出去的版本没有"撤回"按钮,只有"覆盖再发";
  • 它的 bug 大多有资损属性——少收钱、重复扣款、订单丢失、离线单不回传,都是直接的经济纠纷。

所以这个岗位的三个铁律:

  1. 把每一次发版当成"100 个商家同时投诉"的事故预演。 测试报告里写的不是"用例通过率",而是"我为什么敢让它上生产的 100 台机器"。
  2. P0 主链路(扫码→加购→结算→支付→出票)任何一条不过,版本就没有出门的资格。 性能好看、UI 精致都不能换主链路的确定性。
  3. 灰度不是走过场。 100+ 商家意味着你不可能手工覆盖每一家,灰度挑选 + 观察指标 + 回滚预案是你对全部商家负责的机制,而不是仪式。

1. 被测系统画像(必须烂熟于心)

1.1 技术栈与架构要点

层 技术 测试相关要点
框架 Flutter 3.27.4(必须用 fvm) ~/fvm/versions/3.27.4/bin/flutter,默认 3.29.2 会令 flutter_libserialport 编译失败(Registrar 未解析)
状态/路由 GetX 4.7.2 路由守卫、controller 生命周期竞态是历史 bug 高发区
网络 dio 5.7.0 / http 401/424 守卫、登录页 controller 重建竞态
消息 mqtt_client 10.x clientId/账密创建时固化;配置变化必须重建客户端,否则重连 notAuthorized
本地库 Floor 1.4.2 + sqflite 库文件 cash_register.db,位于外部 Documents 目录 pj_cashier/database/(非 sqflite 默认目录);版本 v14 与原生侧 DatabaseHelper 双方锁定,任何一侧动版本必须两边同改(Floor 遇更高版本会静默跳过迁移,这是数据丢失级陷阱)
原生 facediscern 插件(Java) 人脸识别引擎、双目摄像头、人脸库、证据图写入、副屏插件
双屏 flutter_subscreen_plugin 副屏四模式(SubDisplayCoordinator);sendMsgToViceScreen 永不回包,sendViceMessage 由 MainActivity 解包转发立即回包
串口 flutter_libserialport 扫码枪
打印 drago_pos_printer(项目携带修复版)+ printing USB 小票打印机;项目内修复版是因 pub.dev 版 build.gradle 与 AGP 不兼容
定时任务 cron 0.6.2 终端日志上传、定时探测等后台任务
语音 flutter_tts 4.2.2(钉死,Kotlin 编译器约束) 收银语音播报
更新 background_downloader APK 自动更新;Device Owner 挡卸载,只能覆盖安装
加密 pointycastle 通信加密

1.2 业务模块地图

模块 路径 说明
登录 lib/pages/login http 守卫 + fenix 重建竞态历史坑
启动装载 lib/pages/load 5 步骤装载(含重试防 late final 二次赋值)
收银主界面 lib/pages/panel 超市收银 + 食堂收银,购物车、结算弹框
超市订单 lib/pages/supermarket_order 订单列表/退款
食堂商品 lib/pages/canteen_product 商品卡网格(GridView 紧约束历史坑)
食堂预订/取餐/汇总 canteen_reserve / take_food / canteen_order_total 预订取餐链路
卡激活 lib/pages/card_active 饭卡激活
订单历史 order_history(+detail) 本地+云端订单
设置 lib/pages/setting 人脸管理、离线用户、同步日志、基本设置(识别摄像头选择等)
终端外壳 lib/pages/terminal /terminal 一级路由(顶栏+二级路由),面板经 arguments 进入

1.3 本地数据(Floor entities)

订单 order、挂单 order_hold_bill、超市 SPU/SKU/条码 supermarket_spu/sku/code、食堂 SPU canteen_spu、分类 classify、人脸档案 face、用户 user、打印历史 print_history、定时任务 scheduled_task(+log)、文件上传记录 file_upload_record、订单凭证上传任务 order_evidence_upload_task、设置 setting、商铺卡 shop_card、广播 broadcast。

测试升级/迁移时,这 16 张表全部要有存量数据。

1.4 外设矩阵

外设 接入方式 测试关键点
双目摄像头(彩色+红外) 原生 facediscern facing 元数据不可靠(排线装反过)→ 人工选择摄像头 + NV21 色度探测(U/V 偏离 128 阈值 2.0)
扫码枪 串口 高频扫码、乱码、拔插
小票打印机 USB 出票内容与格式、断电重连
副屏 flutter_subscreen_plugin 四模式切换、购物车/人脸页副屏内容、10 秒清屏
语音 TTS 播报时机与音量

2. 测试环境建设

2.1 设备矩阵(最低配置)

档位 配置 用途
主力真机(≥1 台) 项目实际下发的 POS 整机(含双摄、扫码枪、打印机、副屏) 一切外设/人脸/性能/稳定性测试的唯一权威环境
次主力真机(≥1 台) 另一批次 POS 整机 兼容性对照
低端 Android 平板 Android 7.x,2GB RAM 性能下限、兼容性
模拟器 Android 14 仅限 UI 走查、普通功能回归;人脸(无摄像头排线)、串口、副屏、Device Owner 在模拟器上不可测或行为失真

铁律:外设相关和人脸相关的结论,只认真机。 模拟器通过 ≠ 真机通过。

2.2 构建红线

# 必须用 fvm 的 3.27.4,且注意 pub get 需国内镜像;gradle daemon 有历史代理坑
~/fvm/versions/3.27.4/bin/flutter analyze          # 静态分析,0 error 才准入
~/fvm/versions/3.27.4/bin/flutter test              # 单元/组件测试
~/fvm/versions/3.27.4/bin/flutter build apk --release   # 发版包
  • 改过 Floor DAO/Entity 后必须重跑 build_runner,否则 .g.dart 与 SQL 不一致(历史坑:DAO SQL 改了没重跑,行为与代码不符)。
  • 签名必须与线上一致(Device Owner 覆盖安装要求签名一致,否则装不上)。

2.3 测试数据准备(每次发版前刷新)

  1. 多商户:至少 3 个测试商家,覆盖 超市模式 / 食堂模式 / 混合。
  2. 大商品量商家:≥ 1 万 SPU(含多级分类、已删除分类下仍挂商品的"脏数据"商家——历史上"全部"标签空白就是这种孤儿分类导致)。
  3. 人脸量:单商家 ≥ 3000 人脸档案(压人脸同步与引擎装载)。
  4. 存量库:一台装着上一版生产数据(订单 ≥ 5000 条、挂单、打印历史齐全)的真机,专用于升级测试。
  5. 钱包类型:饭卡/消费/充值三种钱包类型(walletTypeUniqueCode 1/2/3)都要有测试账户。

2.4 环境分层

环境 用途 要求
dev 日常功能测试 后端可随时重启,允许脏数据
test 集成回归 数据形态贴近生产
预发 发版前全量回归 后端为发版候选版本;后端先行部署(增量同步依赖后端 updateTime 契约,前后端版本必须配套验证)
生产灰度 3–5 家真实商家 见 §6 发布流程

3. 测试类型全景

3.1 功能测试(按模块的核心用例要点)

A. 登录与启动(P0)

  • 冷启动 → Load 页 5 步骤装载(任何一步失败重试后可恢复;不出现 late final 二次赋值崩溃)。
  • 登录成功/失败/断网登录;token 过期后 401/424 守卫(登录页自我替换 + fenix 重建竞态:表单有字仍提示"请输入账号"就是复发了)。
  • 三处登录后跳转路径都带 currentRoute 守卫。

B. 收银主链路(P0,最高优先级)

  • 扫码枪扫码加购(串口数据流)、手工选商品加购、数量步进器 +/-、改价、折扣。
  • 食堂模式:餐段限制、菜谱日期范围、预订取餐核销。
  • 购物车边界:0 元单、大数量、清空、快速连续加购(连击去抖)。

C. 结算弹框专项(本项目历史 bug 密集区,必须逐条过)

检查项 要点
金额快照 弹框金额是冻结快照;识别用户重算后必须 updateCheckoutSnapshot 推送(body 订阅 revision)
会员恢复 重开弹框时会员必须恢复(initialUser 种子化+刷钱包);挂单恢复时会员也要恢复
钱包类型 饭卡(1)/消费(2)/充值(3) 三种类型展示正确;食堂内嵌按饭卡展示
数量步进器 只有传 onQuantityChanged 才有 +/−;回调必须返回新快照
额外支付 额外支付金额计入快照;_computedAmountMealPrice 不得残留旧值
行内确认 破坏性操作是行内二次确认(5 秒超时还原),不是再套弹框
就地注册人脸 弹框内只做本地引擎装载(rebuildFaceLibrary + registrationStream 进度),绝不拉取远端

D. 支付(P0)

  • 现金、扫码支付(被扫/主扫)、人脸支付(识别→比对→扣款→凭证图留存)。
  • 支付成功→副屏成功页→10 秒清屏;打印小票。
  • 支付失败/超时/取消的三态展示与重试。
  • 资损专项:同一单不重复扣款;断网瞬间支付结果未知时,重试不产生双单;退款金额 ≤ 原单。

E. 人脸(P0,混合开发重点)

  • 人脸同步三模式:full / incremental / surgical(带 spuIds 定向);同步进度条两阶段语义正确。
  • 人脸档案管理页:删除/停用后引擎库即时生效。
  • 人脸质量上报:V2 历史数据(updateTime=null)装新包后点一次 V3 同步即回填修复。
  • 识别链路:活体(RGB/IR)、防伪、识别后会员信息与钱包正确。
  • 副屏盲区专项:结算弹框打开时副屏购物车间歇更新——人脸消息曾被 isAvailable 门禁静默丢弃,验证按需补探测生效。

F. 商品同步(P0)

  • 增量同步三模式 + 墓碑(逻辑删除):后台改价/上新/删除 → 终端 ≤ 1 个同步周期内生效(删除是逻辑删除,须先补 update_time 才能被增量感知)。
  • 超市"全部"标签不空白(孤儿分类口径:商品挂已删除分类时 INNER JOIN classify 后的行为)。
  • 扫码枪路径不过分类过滤,条码直查必须命中已删除分类下的商品(这是口径例外,验证它没被"顺手修掉")。
  • 同步日志页可查每次同步的结果与耗时。

G. 离线与回传(P0)

  • 断网收银正常落本地 → 网络恢复后离线订单回传,不丢不重。
  • sysType 陷阱回归:设备切模式后,离线订单按订单自身 sysType 回传,不得按设备当前模式过滤(历史 bug:过滤导致查空假成功——回传"成功"但后台查无此单)。

H. 挂单/取单

  • 挂单(含会员)→ 取单恢复购物车+会员+额外支付;挂单列表分页。

I. 订单与退款

  • 订单历史(本地/云端)、订单详情、退款(整单/部分)、退款凭证。

J. 设置与终端

  • 基本设置:识别摄像头选择(自动/彩色/红外,CameraSelectionStore -1=自动,探测只在 FACE_EXECUTOR)、打印设置、语音设置。
  • 终端外壳 /terminal:顶栏状态栏、二级路由;panel 经 arguments 进终端(内层路由不能用 Get.arguments)。
  • 离线用户管理、同步日志查看。

3.2 性能测试

指标与参考门槛(基线由首次全量测量建立,此后每版对比,回归 > 20% 必须给出解释):

指标 参考门槛 测量方法
冷启动 → 登录页可见 ≤ 3 s adb shell am start -W + 录屏逐帧
登录 → 可收银(含人脸库装载) ≤ 10 s 录屏 + 应用内日志时间戳
加购点击 → 购物车刷新 ≤ 200 ms(P95) 录屏逐帧 / DevTools timeline
结算弹框打开 ≤ 500 ms 同上
人脸识别(刷脸→出会员) ≤ 1.5 s 现场秒表 + 日志
扫码→商品上屏 ≤ 300 ms 高频连扫 100 次取分布
打印指令→出纸 ≤ 3 s 现场实测
滚动流畅度 FPS ≥ 50,无连续丢帧 adb shell dumpsys gfxinfo com.example.pj_supermarket_cloud framestats
内存 长跑 24h 无持续增长趋势 adb shell dumpsys meminfo com.example.pj_supermarket_cloud,每小时采样
ANR 0 次 logcat + /data/anr/
1 万 SPU 增量同步 ≤ 60 s(基线待建) 同步日志页耗时

工具链:

# 内存
adb shell dumpsys meminfo com.example.pj_supermarket_cloud
# 帧渲染
adb shell dumpsys gfxinfo com.example.pj_supermarket_cloud framestats
# 帧率/卡顿(Perfetto)
~/Library/Android/sdk/cmdline-tools/latest/bin/adb shell perfetto ...   # 或 Android Studio Profiler
# 真库拉取后用 sqlite3 实测慢查询(先 adb root)
adb root && adb pull /storage/emulated/0/Android/data/com.example.pj_supermarket_cloud/Documents/pj_cashier/database/cash_register.db /tmp/
sqlite3 /tmp/cash_register.db ".tables"
sqlite3 /tmp/cash_register.db "EXPLAIN QUERY PLAN <待测SQL>"

外加:Flutter DevTools(Performance / Memory / CPU Profiler,重点看 build 帧与 Isolate 占用)。

本项目性能地雷区(新版本改动到这些区域,性能测试自动升级为 P0):

  1. 人脸链路:历史上 getFaceStatus 在主线程全表扫描导致 ANR;现状契约是状态查询绝不 SELECT faceModel 大字段、重活进 FACE_EXECUTOR、占线短路(进度键 total/successCount)。凡动 face 相关 DAO/原生代码,必做 3000+ 档案真机压测。
  2. 结算链路:后端 createOrder 历史上是 9 次串行 Feign 的大事务;前端虽已去重+内存缓存,但任何弹框重算逻辑的改动都可能把耗时打回来。结算弹框打开耗时每版必测。
  3. 同步链路:拼音转 Isolate、详情分批 100、删除分批 500——改动分批参数/ Isolate 策略要回归 1 万 SPU 商家。
  4. DB 迁移:升级路径必然触发迁移,必须带存量库测启动耗时。

3.3 混合开发专项(Flutter ↔ Android 通道)

这是本项目区别于普通 Flutter 项目的测试核心:

  • MethodChannel 契约测试:FlutterFaceDiscernPlugin / FlutterFaceSubScreenPlugin / FlutterTool / FlutterFaceReceiver 每个对外方法的入参、返回、异常路径逐一验证;协议字段名(如 updateCard 系列)一字不能改,通道两侧是弱契约,编译器不查。
  • 线程纪律:原生重活必须走 FACE_EXECUTOR;任何"顺手"在主线程做的事(DB 查询、图片 IO)都是 ANR 候选。
  • 双库版本锁:Floor v14 与原生 DatabaseHelper v14 必须一致。发版前 diff 两边版本号;Floor 遇到更高版本号会静默跳过迁移,这会直接造成数据结构错位。
  • 生命周期:人脸页副屏自建 Texture 的创建/销毁;App 后台→前台、跳转打断识别(isRecognition 复用为锁 + dispatchId 令牌防迟到回调)。
  • 消息回路:副屏 sendViceMessage(MainActivity 解包转发、立即回包)与 sendMsgToViceScreen(永不回包)行为不同,别混用验收标准。
  • 原生日志:logcat 过滤 facediscern 标签,验证引擎状态迁移。

3.4 稳定性测试

  • 长跑(Soak):真机收银页 24–48h:每 10 分钟一次自动加购+结算(可用 adb 脚本或人工节奏),期间跑 3 轮人脸同步。验收:零 crash、零 ANR、内存曲线平稳、副屏内容不漂移。
  • Monkey:adb shell monkey -p com.example.pj_supermarket_cloud --throttle 300 -s <seed> 100000,仅限冒烟后(人脸页、设置页单独跑),验收零 ANR、crash 可归因。
  • 异常注入(每版必做一轮):
    • 收银过程中拔网线 / 关 WiFi / 热点切换(弱网可用 adb shell tc qdisc 限速/加丢包);
    • 扫码枪/打印机/副屏中途拔插;
    • 支付进行中按返回/进后台/锁屏;
    • 断电重启(非正常关机):重启后本地订单完好、离线单可回传、无 double init;
    • 后端接口 500/超时/返回畸形 JSON。
  • MQTT 稳定性:改密/换地址后重连(历史坑:clientId 固化导致 notAuthorized 拒绝);后台 pull 指令 10 分钟内到达;publish 断连时是静默 return 且恒返成功,不能只看 App 日志判断发送成功,要以后台收到为准。

3.5 兼容性测试

  • 机型:≥2 批次 POS 整机 + 1 台低端平板 + 1 台主流手机(用于老板/运维装 App 看后台的场景)。
  • 系统:Android 7 / 9 / 12+(minSdk 24 到 targetSdk 全区间至少抽 3 档)。
  • 副屏分辨率:不同分辨率/长宽比副屏下布局不错位(副屏内容是独立渲染路径)。
  • 大字号:主题字号运行时缩放 1.2–1.5 倍(forui 字号被 sizeScalar 缩放),全页面走查无裁切/溢出——本项目已有多起字号裁切历史(DropdownButton、商品卡、输入框)。
  • 深浅色/横竖屏(若支持)。

3.6 网络与离线测试

  • 弱网矩阵:200ms 延迟 / 5% 丢包 / 30KB 带宽,核心链路(登录、加购、结算、同步)逐一过。
  • 断网下可用功能清单明确(本地商品、本地收银、挂单、订单历史),不可用功能有明确友好提示而非转圈。
  • 恢复网络后:离线订单自动回传(不丢/不重/顺序合理)、增量同步自动追平、MQTT 自动重连且鉴权成功。
  • 账密错误/设备未授权/token 过期的错误路径展示。

3.7 升级与数据迁移测试(100+ 商家场景的重中之重)

  • 覆盖安装:上一生产版(带真实存量数据)→ 候选版,验证:
    1. 安装成功(Device Owner 下只能覆盖装,签名必须一致);
    2. 启动后 DB 迁移正确:16 张表数据完整(订单/挂单/人脸/设置逐一核对条数);
    3. Floor 与原生两侧版本一致,无静默跳迁移;
    4. 登录态/设置项保留。
  • 跨版本升级:至少支持 n-2 → n(很多商家不会及时更新)。
  • 首启动修复逻辑:V2 人脸数据(updateTime=null)在装新包后点一次 V3 同步被回填。
  • 降级禁止:明确记录不支持降级;若需回滚,走"覆盖安装回旧版 + 旧版能读新库结构吗"的预研结论(通常不能,所以灰度观察窗口才是主防线)。
  • 自动更新链路:background_downloader 下载 → 安装提示 → 覆盖安装成功;下载中断续传、磁盘不足提示。
  • 旧 APK 窗口判别:灰度观察期区分"设备没更新"(后台看在线设备 APK 版本)与"更新了但坏了"。

3.8 外设专项

外设 必测点
双目摄像头 彩色/红外自动探测标签正确(U/V 偏离 128 阈值 2.0);人工选择后重启仍生效;排线装反的机器上靠人工选择兜底
扫码枪 高频连扫、破损码、含中文码、扫码中拔出(不 crash,插回自动恢复)
打印机 小票内容字段齐全(商家名/单号/明细/金额/时间)、缺纸提示、连续打印 100 单不掉 USB
副屏 四模式切换、收银副屏跟车、支付成功页 10 秒清屏、副屏拔插
TTS 播报内容、与系统音源冲突(放音乐时播报)

3.9 安全测试

  • 日志脱敏:人脸照片、密码、token 在日志中只出现脱敏格式(历史规范:密码只打脱敏格式)。
  • 本地 DB 中人脸特征/用户钱包数据的存储边界;DB 文件 pull 下来检查敏感字段。
  • 通信加密(pointycastle)生效性抽验(抓包不可读业务明文——脱敏字段除外)。
  • 证据图(人脸支付对比凭证、OSS 上传)不越权可访问;OSS 上无空文件/损坏图(历史坑:oss 有空文件图)。
  • 更新包完整性:APK 下载校验。

3.10 自动化与单元测试

现状:test/ 下 3 个测试文件(terminal_toolbar、mq_supermarket_product_msg、search_highlight_tool)。

  • CI 门槛:flutter analyze 0 error + flutter test 全绿,作为提测准入。
  • 建议逐步补齐(按资损优先级):
    1. 金额计算纯函数(快照重算、额外支付、钱包优先级);
    2. 离线订单回传的 sysType 归类逻辑;
    3. 增量同步三模式的本地合并/墓碑逻辑;
    4. MethodChannel 消息的序列化/反序列化 round-trip(可用原生侧单测)。
  • 自动化是辅助,主链路验收永远是真机手工(外设决定了无法完全自动化)。

3.11 UI/UX 走查

  • 大字号缩放 1.2/1.5 两档全页面走查(见 3.5)。
  • 弹框类:showAppDialog 内 controller 生命周期(历史上 await 返回后立刻 dispose 会崩,弹窗内输入框 controller 收进弹窗 StatefulWidget)。
  • 图片:appImg 有宽度契约(width 透传),Row 中图片无界会 "forces an infinite width" 崩溃——新页面必查。
  • 加载态/空态/错误态三态齐全;Toast 长文案不截断(需 Flexible)。

4. P0 回归红线(发版冒烟集,30 分钟内完成)

每版发版前在一台带全部外设的 POS 真机上,用生产形态的测试商家过完以下清单。任何一条 FAIL 即停发。

  1. 冷启动 → Load 装载 → 登录成功。
  2. 扫码枪扫 3 个不同商品 → 购物车正确(名称/价格/数量)。
  3. 修改数量(步进器+直输)→ 金额重算正确。
  4. 挂单 → 继续收银新单 → 取单 → 购物车+会员+额外支付完整恢复。
  5. 选会员(刷脸识别会员)→ 会员价生效 → 钱包余额正确显示(三种钱包类型各一次)。
  6. 现金支付一单 → 出票 → 订单历史可见。
  7. 扫码支付一单(真实支付通道/沙箱)→ 支付成功页 → 副屏 10 秒清屏 → 出票。
  8. 人脸支付一单 → 识别 ≤ 1.5s → 扣款 → 证据图留存可在后台看到。
  9. 断网收银一单 → 恢复网络 → 订单回传后台可查(去后台亲眼确认,不信本地"成功"日志)。
  10. 退款一单 → 金额正确 → 小票/后台同步。
  11. 后台改价一个商品 → 触发/等待同步 → 终端价格更新;后台删一个商品 → 终端不再可售。
  12. 超市"全部"标签有数据、食堂商品页正常。
  13. 重启 App → 登录态/设置/本地订单保留。
  14. 覆盖安装(上一版→本版,带存量库真机)→ 启动无迁移报错,订单/挂单/人脸档案条数不丢。
  15. 弱网(限速)下完成一次完整收银。
  16. 副屏四模式切换一遍,内容正确。
  17. logcat 过滤 30 分钟无未归因 crash/ANR。
  18. 终端日志上传到后台可见(本版日志)。

5. 缺陷管理与取证规范

5.1 分级

级别 定义 处置
S1 资损风险(重复扣款/丢单/金额错)、无法收银(白屏/崩溃/主链路卡死)、数据丢失 立即停发/回滚;24h 内修复验证
S2 主链路降级(某支付方式不可用、同步停摆、副屏黑屏)、ANR 阻塞发版
S3 次要功能缺陷、特定机型问题 可带病发版但需灰度期确认不恶化,记录在发版说明
S4 文案/体验 排期修复

5.2 取证标准(缺一不可)

  1. 构建指纹:版本号 + git commit + 构建时间。复测前核对堆栈行号/build 号确认装的是新包——历史上有"修了没生效,其实装的是旧 APK"的事故。
  2. 录屏:从进入页面到 bug 发生完整过程。
  3. 日志:app 内日志文件(pj_cashier/ 下日志目录,注意带 ANSI 色码需 sed 清洗)+ logcat 原始文件。
  4. DB 副本:事发后立刻 adb root && adb pull 真库(很多订单类 bug 的真相只在库里)。
  5. 复现步骤(含前置数据:商家/会员/商品)、机型/系统/网络环境。
  6. 偶现问题给出复现概率(n 次中出现 m 次)。

5.3 测试报告(每版必产出)

  • 版本号/commit/包来源;
  • 冒烟集 18 条结果表;
  • 性能对比表(本版 vs 上版基线,标注 >20% 回归项);
  • 缺陷清单(级别/状态/是否阻塞);
  • 发版建议:Go / No-Go / Go with conditions(列条件与观察项);
  • 灰度观察计划(挑哪些商家、看什么指标、多长时间)。

6. 发布流程:对 100+ 商家负责的机制

提测准入(analyze+test 全绿) → 全量回归(§3+§4) → 测试报告(Go 建议)
  → 灰度发布(3–5 家) → 观察窗口 24–48h → 达标 → 全量推送
                                      ↘ 异常 → 停止推送 + 回滚预案

6.1 灰度商家挑选原则(覆盖风险面,不是随机)

  1. 1 家关系友好、反馈快的商家(首发哨兵);
  2. 1 家大商品量(≥1 万 SPU)超市;
  3. 1 家食堂模式 + 1 家人脸支付高频商家;
  4. 1 家上一版就没及时更新的商家(覆盖跨版本升级路径)。

6.2 观察窗口必看指标(每天早晚各巡一次)

  • 后台设备在线率与 APK 版本分布(区分"没更新成功"和"更新了坏了");
  • 终端日志上传是否持续可见(日志断流 = 终端或 MQTT 出事);
  • 后台订单量/金额曲线与该商家日常对比(掉量 = 收银受阻);
  • 离线订单回传成功率;
  • 主动联系哨兵商家 1 次:开机速度、扫码、刷脸、打印有无异常。

6.3 停发/回滚触发条件(事先约定,不临场辩论)

  • 任一 S1;或灰度商家中出现无法归因的 crash;
  • 在线率异常下跌(对照历史同时段);
  • 回滚手段:停止推送 + 向灰度设备覆盖安装上一稳定版 APK(签名一致可覆盖;注意降级后 DB 结构兼容性预研结论)。

6.4 版本节奏约定

  • 后端先行:涉及同步契约(updateTime/ids/墓碑、MQTT topic 40 spuIds)的版本,后端先部署,前端后发;
  • 收尾字段:发版说明必须写清"本版改动的外设/通道/DB 版本"三项,供测试和售后对照。

7. 历史事故库(本项目专属回归清单)

这些坑都在本仓库真实发生过。改动相关区域时,对应条目自动进入本版回归范围。

# 事故 根因摘要 触发回归的条件
1 人脸页 ANR getFaceStatus 主线程全表扫描 faceModel 改 face DAO/原生查询
2 登录页"请输入账号"假提示 http 守卫对 /login 自我替换 + fenix 重建空实例竞态 改 http 守卫/登录跳转
3 MQTT 重连 notAuthorized clientId/地址创建时固化,复用旧客户端带新账密 改 MQTT 配置/重连逻辑
4 离线单"回传成功"但后台查无 按设备当前 sysType 过滤致查空假成功 改离线回传/模式切换
5 离线单假成功(同上族) 日志只落 warning+,本地看不到 排查手法:真库核对
6 超市"全部"标签空白 商品挂已删除分类,视图按活跃分类分组(口径不一致) 改 classify/商品查询 SQL(必须重跑 build_runner)
7 结算弹框金额不对 弹框是冻结快照,识别重算后未推送 改弹框重算逻辑
8 弹框会员丢失 payUser 是运行时态被清空,业务层 payUser 才是事实来源 改弹框开关/挂单恢复
9 钱包类型显示错 本地 WalletType 枚举缺 TOPUP,未同步后端 code 后端加钱包类型/改展示
10 副屏停在购物车间歇 人脸消息被 isAvailable 门禁静默丢弃(仅定时任务置 true) 改副屏消息链路
11 人脸质量不上报 V2 写入 updateTime=null 永不上报 改人脸同步/上报
12 "修复没生效" 装的其实是旧 APK(复测未核对 build 号) 每次复测
13 双库静默跳迁移 Floor 遇更高版本号跳过迁移,与原生版本错位 任何一侧动 DB 版本(两侧必须同改 v14→vN)
14 覆盖安装失败 签名不一致 + Device Owner 挡卸载 换签名/发版渠道
15 商品卡溢出/字号裁切 GridView 紧约束 + forui sizeScalar 运行时缩放 改卡片布局/字号
16 图片页崩溃 appImg 无界 Row(width 契约被破坏) 新增带图页面
17 编译失败(Registrar 未解析) 用了非 fvm 的 Flutter 版本 本机/CI 环境变更
18 后台拉日志无反应 旧 APK 窗口 或 publish 断连静默 return 恒返成功 改 MQTT 发送/定时任务

8. 附录

8.1 发版 Checklist(打印贴工位)

  • flutter analyze 0 error、flutter test 全绿
  • fvm 3.27.4 构建 release 包,签名与线上一致
  • P0 冒烟集 18 条全过(真机+全外设)
  • 覆盖安装升级测试过(n-1 和 n-2 存量库)
  • Floor/原生双库版本核对一致
  • 性能对比无 >20% 回归(或已归因说明)
  • 24h 长跑零 crash/ANR
  • 灰度商家名单已定(含哨兵/大商品量/食堂/人脸高频/滞后更新各 1)
  • 回滚 APK 与预案就绪
  • 测试报告已发(含 Go/No-Go 结论)
  • 发版说明含外设/通道/DB 版本三项变更声明

8.2 灰度观察日报模板

日期:    版本:    观察人:
灰度商家在线情况:(设备数/版本分布/与昨日对比)
日志上传:正常/断流(断流设备:xxx)
订单与回传:总单数 xx,离线回传失败 x 笔(明细)
crash/ANR:0 起 / xx 起(归因:xxx)
哨兵商家回访:xxx
结论:继续观察 / 全量 / 停发回滚(理由)

8.3 常用命令速查

FLUTTER=~/fvm/versions/3.27.4/bin/flutter
PKG=com.example.pj_supermarket_cloud

$FLUTTER analyze && $FLUTTER test && $FLUTTER build apk --release
adb install -r build/app/outputs/flutter-apk/app-release.apk

adb shell dumpsys meminfo $PKG | head -30          # 内存
adb shell dumpsys gfxinfo $PKG framestats          # 帧渲染
adb logcat -d | grep -E "FATAL|ANR|facediscern"    # 崩溃/ANR/原生
adb shell monkey -p $PKG --throttle 300 100000     # Monkey

# 真库取证
adb root && adb pull /storage/emulated/0/Android/data/$PKG/Documents/pj_cashier/database/cash_register.db /tmp/

# 弱网注入
adb shell tc qdisc add dev wlan0 root netem delay 200ms loss 5%
adb shell tc qdisc del dev wlan0 root

本文档随版本演进持续维护:每次事故复盘后更新 §7;每次性能全量测量后更新 §3.2 基线。文档本身过时 = 测试体系过时。