消费页一卡通服务同步弹框设计

1. Use Case 概要

2. 背景、目标与范围

2.1 背景

项目已经具备一卡通连接验证、每批 1000 人全量拉取、本地 SQLite 快照以及向业务服务器分批上传的底层能力,但当前实现把 SQLite 分批写入放在拉取关键路径中,业务上传又依赖成功完成的数据库快照。数据库写入耗时会直接延后配置服务器转发,同时消费页尚未调用和展示该链路。

2.2 目标

进入消费页时自动执行一次“连接验证—全量拉取—可靠 JSON 暂存—业务上传”流程;操作人员能够查看连接与同步进度、最近结果和毫秒级操作时间,也能从页面右侧入口手动再次同步。本地 SQLite 降级为辅助存储与必要查询,由独立后台任务导入,不参与主同步成功判定,也不得阻塞配置服务器转发。

2.3 In Scope

2.4 Out of Scope

3. 连接状态语义

一卡通 WebService 使用 HTTP/SOAP,connect() 通过 GetCustomerCount 验证 URL、AppID、网络和认证,并非建立需要持续保持的 Socket。同步结束后客户端会清除本地连接参数。

因此,右侧入口展示“最近一次连接验证结果”,而不是 OnecardWebServiceClient.isConnected() 的瞬时值:

最近验证结果图标与颜色文案
成功绿色一卡通服务可用
失败或从未验证红色一卡通服务不可用

连接验证结果和人员同步结果相互独立:连接验证成功但业务服务器上传失败时,入口仍为绿色,弹框中的同步结果为失败。

4. 页面与交互

4.1 页面结构

4.2 自动弹框

  1. ConsumptionActivity.onCreate() 完成现有初始化后显示弹框。
  2. 每个页面实例只自动启动一次,不因重复 onStart() 重复同步。
  3. 同步运行在应用级后台任务中,不阻塞餐盘识别、读卡和消费流程。
  4. 运行期间允许用户主动关闭弹框,后台同步继续。
  5. 用户可以点击右侧状态按钮重新打开并查看当前进度。

4.3 弹框内容

弹框标题为“一卡通服务同步”,包含:

弹框视觉必须复用项目现有设置类弹框规范:1000dp 白色圆角容器、蓝色渐变标题栏、白色 40sp 标题、灰色描边内容区、蓝色描边关闭按钮、蓝色渐变手动同步按钮,以及与 pop_bac_color 一致的遮罩透明度。运行期间手动同步按钮使用禁用态灰色背景。

可见时间统一格式为 yyyy-MM-dd HH:mm:ss.SSS

4.4 手动同步

4.5 自动关闭

5. 主流程

步骤系统行为可见状态数据变化验收
M1页面完成现有初始化弹框出现,显示准备连接读取最近状态AC-001
M2调用 GetCustomerCount 验证一卡通服务正在连接一卡通服务记录本轮开始AC-002
M3连接验证成功显示连接成功、人数和毫秒时间;右侧变绿持久化最近连接成功结果AC-003
M4按账号游标每批最多 1000 人拉取、解析和校验显示批次及已拉取/总人数生成不可变人员批次AC-004
M5每批写入应用私有 JSON 文件显示正在暂存强制刷盘、SHA-256 校验、原子提交批次AC-005
M6收齐全部批次并核对总人数显示全量准备完成原子标记本次快照 READYAC-006
M7启动远端上传和后台数据库导入显示上传进度;本地显示后台更新中两个消费者只依赖 JSON,不彼此等待AC-007
M8按业务接口上限每批最多 1000 人上传显示批次、已处理和失败数原子记录远端已确认批次并汇总结果AC-008
M9全部上传完成且失败数为 0显示主同步成功、时间和人数持久化最近主同步成功结果AC-009
M10展示主同步终态 1 秒保留最终结果;数据库可继续后台运行AC-010
M11自动关闭弹框消费页继续运行AC-010
M12新数据库快照完整并通过人数校验本地存储显示已完成原子切换活动快照并清理旧快照AC-011

6. JSON 持久化队列

6.1 存储位置与目录

临时快照放在 Context.getNoBackupFilesDir() 下的应用私有目录,不使用可能被系统回收的 cacheDir,也不进入系统云备份:

onecard_sync_spool/<syncId>/
├── manifest.json
├── batch-00001.json
├── batch-00002.json
└── ...

JSON 保存本地查询和业务转发所需的完整人员原始字段。单批元数据至少包含 syncId、批次号、人员数、起止 customerId、文件字节数和 SHA-256。

6.2 原子写入协议

  1. 写入 batch-xxxxx.json.tmp
  2. 执行缓冲区 flush()FileDescriptor.sync()
  3. 重新校验人员数、文件长度和 SHA-256;
  4. 在同一目录原子改名为 batch-xxxxx.json
  5. 使用 Android AtomicFile 原子更新 manifest.json
  6. 上传和数据库消费者只读取 manifest 已登记的完整批次,永不读取 .tmp

6.3 状态与投递语义

7. 分支、异常与恢复

编号条件系统处理结果与恢复
A1@M1已有最近结果弹框先展示历史结果,再切换为本轮运行态历史结果不会阻止自动同步
A2@M1用户关闭运行中的弹框只关闭界面,不取消后台任务右侧入口可重新打开
A3@M8单批返回部分失败人员累计失败数且不确认该批最终整体标记同步失败;后续整批幂等重传
E1@M2URL、AppID、网络、认证或响应校验失败记录失败时间和原因,不执行拉取及上传右侧变红;可手动重试
E2@M4拉取或解析失败本轮保持非 READY,不上传业务服务器丢弃未完成 .tmp,下次重新全量拉取
E3@M5JSON 写入、刷盘或校验失败标记本轮失败且禁止上传保留诊断信息;空间恢复后重新全量拉取
E4@M8业务 API 网络、HTTP、业务码或响应数量异常停止后续上传,记录已处理数量和失败原因一卡通入口保持最近连接验证颜色;保留 JSON 后续恢复
E5@M7SQLite 导入失败不改变主同步结论,不切换活动快照继续查询旧快照;保留 JSON 并后台重试
E6进程在远端成功后、本地记账前退出重启后重传未确认批次服务端按 customerid 幂等处理,不重复生成人员
E7JSON 被截断或校验和不一致隔离损坏文件,禁止上传和入库重新执行完整拉取,不静默跳过人员
E8页面销毁解除 UI 观察和延迟关闭,不取消应用级任务不持有已销毁 Activity 或弹框;任务可恢复
E9主任务运行时重复点击手动同步按钮保持禁用,不创建第二个任务当前任务继续

8. 业务规则

9. 数据库完整快照

数据库在逻辑上增加快照元数据、候选快照和活动快照指针:

10. 组件职责

组件职责
ConsumptionActivity绑定右侧入口,显示/关闭弹框,接收主线程状态,不承载同步算法
ConsumptionPersonSyncController连接页面与应用级同步运行时、拒绝主任务并发、生成统一可见状态
ConsumptionPersonSyncCoordinator编排连接、全量拉取、JSON 快照完成、远端上传和断开,并报告明确阶段
ConsumptionPersonSyncState表达运行阶段、进度、终态、时间和人数,保持为可单元测试的纯 Java 状态
ConsumptionPersonSyncStatusStore使用现有 MMKV 保存和恢复最近连接及同步终态
ConsumptionPersonSyncDialog只渲染状态并转发手动同步/关闭事件,不发起网络和数据库操作
CustomerBatchSpool原子写入/读取批次 JSON,校验长度、人数和 SHA-256,不解释业务状态
CustomerSyncManifestStore使用 AtomicFile 维护拉取、远端和数据库处理进度
BusinessPersonSpoolUploader从完整 JSON 快照分批映射并上传,记录远端确认和汇总,不读取 SQLite
CustomerSnapshotImportWorker低优先级后台导入 JSON,校验并原子激活新数据库快照
SQLiteCustomerRepository保存多版本完整快照并使现有查询透明读取活动版本

现有 OnecardWebServiceClient、字段解析/映射、业务网关和网络鉴权继续复用。CustomerSyncServiceBusinessPersonSyncService 的数据源边界调整为 JSON 队列,但不复制已有协议和字段规则。

11. 持久化数据

持久化分为 UI 汇总、可恢复 JSON 和数据库活动快照:

存储内容清理规则
MMKV 最近连接验证SUCCESS/FAILURE、完成时间、服务端人数、失败摘要新结果覆盖旧结果
MMKV 最近人员同步SUCCESS/FAILURE、完成时间、拉取数、处理总数、成功数、失败数、失败摘要新结果覆盖旧结果
noBackupFilesDir JSON完整人员批次、校验元数据和处理进度远端确认且数据库已激活,或被另一个已激活的更新快照标记为 SUPERSEDED 后删除
SQLite 活动快照本地必要查询使用的完整人员数据新快照激活后异步删除旧版本

进程退出后不把旧 UI 运行态直接恢复为“正在同步”;由 manifest 扫描结果决定显示“恢复上传”或“后台入库重试”。

12. 生命周期与并发

13. 日志与安全

14. 验收标准

ACGivenWhenThen证据
AC-001消费页首次创建完成现有初始化自动显示弹框且主消费流程仍可运行页面检查/代码测试
AC-002无任务运行自动或手动同步开始只创建一个后台任务并执行连接验证单元测试
AC-003GetCustomerCount 成功或失败返回结果记录毫秒时间并正确刷新红绿入口单元测试/真机日志
AC-004服务端有 1001 人执行全量拉取生成 1000 和 1 两个完整批次,人数、游标和跨批唯一性正确单元测试
AC-005单批 JSON 写入正常完成或进程在任意写入点退出只消费刷盘、校验并原子登记的完整文件故障注入测试
AC-006拉取中途失败或全量人数不一致协调器结束不标记 READY、不调用业务上传、不改变活动数据库快照单元测试
AC-007JSON 快照已 READY同时启动上传和数据库导入上传不调用、不等待 SQLite;数据库低优先级后台执行单元/并发测试
AC-008完整快照有 1001 人上传业务服务器以 1000 和 1 两批顺序上传并原子记录确认进度单元测试
AC-009上传全部成功、存在失败数或数据库失败主流程结束主结果仅按远端结果显示;数据库失败只显示等待重试单元测试
AC-010本轮主流程进入终态且弹框可见等待 1 秒自动关闭;历史查看不自动关闭;数据库任务可继续状态/调度测试、真机检查
AC-011新数据库快照未完成或已完成执行本地查询未完成时始终查旧完整快照,完成后一次切换到新快照仪器测试
AC-012远端成功后、本地确认前进程退出应用恢复任务允许整批重传且服务端无重复人员故障注入/联调
AC-013JSON 被截断、篡改或磁盘空间不足扫描或写入明确失败并禁止跳过、上传或激活损坏数据单元/仪器测试
AC-014同步运行中重复点击手动同步手动按钮禁用且不创建并发主任务单元测试
AC-015应用重新进入消费页读取 MMKV 和 manifest恢复最近终态及未完成上传/入库任务,再按门禁决定自动同步单元测试/真机检查
AC-016目标设备写入单批 1000 人重复执行安全落盘tempWriteMs + fsyncMs 的 P95 不超过 100 ms真机性能日志

15. 测试矩阵

Test ID层级覆盖对象预期运行方式
UT-001单元状态模型连接、拉取、上传、成功、失败状态及人数正确:app:testDebugUnitTest 定向测试
UT-002单元状态存储编解码最近连接和同步结果可往返恢复:app:testDebugUnitTest 定向测试
UT-003单元并发门禁自动/手动任务不并发:app:testDebugUnitTest 定向测试
UT-004单元失败判定部分失败整体为失败,成功数正确:app:testDebugUnitTest 定向测试
UT-005单元自动关闭策略本轮终态延迟 1 秒,历史查看不关闭:app:testDebugUnitTest 定向测试
UT-006单元JSON spool.tmp 不可消费,SHA-256/人数/长度校验和原子登记正确:app:testDebugUnitTest 定向测试
UT-007单元manifest 恢复各阶段重启后只恢复未确认的远端和数据库工作:app:testDebugUnitTest 定向测试
UT-008单元幂等重传远端成功、本地未记账时重传同批且进度不倒退:app:testDebugUnitTest 定向测试
IT-001集成协调器连接成功后拉取、完整 JSON 后上传、最终断开现有协调器测试扩展
IT-002仪器数据库快照导入期间旧快照可查,校验后原子切换,失败不切换:app:connectedDebugAndroidTest
IT-003仪器故障恢复写文件、远端确认、数据库导入和切换点杀进程后恢复目标设备故障注入
PERF-001性能JSON 安全落盘单批 1000 人 P95 不超过 100 ms目标设备日志统计
BUILD-001构建Java 与 Android 资源单元测试和 Debug APK 构建通过:app:testDebugUnitTest:app:assembleDebug
DEVICE-001人工页面与真实服务自动弹框、右侧入口、幂等恢复、快照切换、毫秒时间和人数符合设计目标设备联调

16. 非功能要求

17. 风险与待确认项

ID风险应对
RISK-001全量同步时间较长,弹框可能遮挡消费操作允许随时关闭,任务后台继续,右侧入口可恢复查看
RISK-002真实服务返回脏数据或部分业务失败保留完整日志和人数汇总,不把部分成功误报为成功
RISK-003远端成功后进程退出导致同批重传服务端已确认按 customerid 幂等,采用至少一次投递并记录确认游标
RISK-004数据库持续失败造成候选文件积压数据库唯一任务重试,只保留最新可用候选;新同步前检查空间,不删除未被替代的有效数据
RISK-005JSON 含人员和卡片信息使用 noBackupFilesDir、最小文件权限、禁止正文日志并在双消费者完成后立即删除
RISK-006页面销毁时同步调用仍在阻塞UI 观察与应用级任务隔离,页面销毁不取消任务,设备联调验证恢复

P0 待确认项:无。

18. 实施边界