2026-08-28 补充确认:日志最终统一保存到设备外部存储根目录
/Log/;新写入 JSONL 的核心字段名使用中文,查看器继续兼容旧英文 key。本文后续关于“应用内部独立目录”和英文 JSON 示例的早期内容,以本补充确认及实施记录 V1.1 为准。调整原因是便于现场统一查找日志,并让导出的原始文件可直接阅读。
版本:V1.0
日期:2026-08-28
适用范围:ConsumptionActivity 放盘、订单查询、一卡通交易、本地落库、接口 C 前台反馈及后台补传
新增独立的 ConsumptionFlowLog 业务流水日志类型,以“一张餐盘对应的一笔订单会话”为主线,完整记录:
放盘 → 查询订单 → 金额分流 → 等待卡片 → 消费前读卡 → 扣款 → 消费后读卡 → 本地保存 → 接口 C → 后台补传 → 会话结束
业务流水只记录可读、可检索的业务摘要,不复制一卡通协议原文和 HTTP 完整报文。现有日志继续承担各自职责:
| 日志层 | 主要用途 | 是否新增 | 关联方式 |
|---|---|---|---|
ConsumptionFlowLog |
按订单查看完整业务时间线 | 是 | flowId、orderNo |
ReaderLog |
USB、寻卡、APDU、钱包扣款等硬件诊断 | 否 | readerSession、时间戳 |
| 网络原始日志 | HTTP URL、请求、响应和传输异常 | 否,但需脱敏检查 | apiName、时间戳、reportAttemptId |
consumption_records.db |
交易事实、接口 C 原请求和可靠补传 | 否 | orderNo |
记录原因:当前关键信息分散在 Activity、读卡协议日志、HTTP 日志和本地数据库中,不能直接回答“一笔订单从放盘到反馈经历了什么”。独立业务流水既能解决现场可读性,也不会把调试噪声和敏感原文混入主链路。
flowId:餐盘首次识别成功时创建,直到统一复位才结束。orderNo 允许为空,成功后每个事件必须带订单号。consumption_records.db。flowIdWAITING_TRAY 接收到第一张有效餐盘。flowId。| 字段 | 来源 | 用途 |
|---|---|---|
orderNo |
接口 B | 关联订单、交易记录和接口 C |
trayCode |
餐盘扫码器 | 关联放盘与订单查询;日志中按配置决定原值或掩码 |
readerSession |
ReaderLog.getSession() |
跳转到一卡通协议明细 |
reportAttemptId |
每次接口 C 调用生成 | 区分前台首次上传和后台第 N 次补传 |
eventSequence |
每个 flowId 单调递增 |
保证同毫秒及异步回调仍可正确排序 |
后台补传可能发生在原页面会话结束或应用重启后,因此待上传记录需要持久化 flowId;数据库版本升级时为旧记录允许生成 legacy-{orderNo} 兼容标识。
每行至少包含以下字段:
| 字段 | 必填 | 说明 |
|---|---|---|
schemaVersion |
是 | 首版固定为 1 |
timestamp |
是 | ISO-8601 本地时间,精确到毫秒 |
elapsedRealtimeMs |
是 | 设备单调时钟,用于计算耗时,不受系统校时影响 |
eventSequence |
是 | 当前业务会话事件序号 |
level |
是 | INFO、WARN、ERROR |
event |
是 | 稳定事件码,禁止直接使用页面中文文案 |
flowId |
是 | 业务会话标识;全局网络事件可为 GLOBAL |
orderNo |
条件必填 | 订单查询成功后的事件必须有值 |
trayCode |
条件必填 | 放盘、移盘、订单查询事件使用 |
stage |
否 | TRAY、ORDER、CARD_BEFORE、DEBIT、CARD_AFTER、LOCAL_SAVE、REPORT、RESET |
result |
否 | STARTED、SUCCEEDED、FAILED、PENDING、SKIPPED、UNKNOWN |
durationMs |
否 | 阶段结束事件耗时 |
amount |
否 | 订单应付金额,单位分 |
completedAmount |
否 | 实际确认扣款金额,单位分 |
errorCode |
否 | 稳定错误码或服务端业务码 |
errorMessage |
否 | 清洗换行并限制长度的错误摘要 |
apiName |
否 | ACQUIRE_BY_PLATE、ONE_CARD_REPORT |
traceId |
否 | 服务端返回的链路标识 |
reportSource |
否 | FOREGROUND、SCHEDULED_RETRY、APP_START、NETWORK_RECOVERY |
reportAttemptId |
否 | 单次反馈尝试标识 |
readerSession |
否 | 关联硬件日志会话 |
details |
否 | 仅允许白名单字段的 JSON 对象 |
示例:
{"schemaVersion":1,"timestamp":"2026-08-28T10:20:31.256+08:00","elapsedRealtimeMs":8732140,"eventSequence":12,"level":"INFO","event":"DEBIT_RESULT","flowId":"e806...","orderNo":"O202608280001","stage":"DEBIT","result":"SUCCEEDED","durationMs":736,"amount":1250,"completedAmount":1250,"readerSession":4,"details":{"walletCount":2}}
| 事件码 | 触发时机 | 关键字段 |
|---|---|---|
FLOW_LOG_INITIALIZED |
日志系统初始化 | 目录、保留天数、单文件限制 |
FLOW_LOG_WRITE_FAILED |
文件写入异常并降级 Logcat | 错误摘要;需要限频 |
NETWORK_STATUS_CHANGED |
网络可用性状态变化 | connected、validated、available |
| 事件码 | 触发时机 | 关键字段 |
|---|---|---|
FLOW_CREATED |
第一张有效餐盘建立会话 | flowId、trayCode |
TRAY_DETECTED |
有效餐盘识别成功 | trayCode |
TRAY_CODE_IGNORED |
会话锁定期间收到重复码或其他码 | 原因;限频 |
TRAY_REMOVED |
物理移盘 | 当前状态、是否提前移走 |
ORDER_REQUEST_STARTED |
调用接口 B 前 | apiName、设备编号是否已配置 |
ORDER_REQUEST_RESULT |
接口 B 返回 | result、durationMs、业务码、traceId |
ORDER_VALIDATED |
订单字段校验结束 | orderNo、amount、result |
PAYMENT_DECISION |
按金额分流 | ZERO_AMOUNT 或 CARD_PAYMENT |
| 事件码 | 触发时机 | 关键字段 |
|---|---|---|
CARD_ZONE_NOT_CLEAR |
第三步发现遗留卡 | result=PENDING |
CARD_ZONE_READY |
已确认无卡,可等待新卡 | readerSession |
PAYMENT_ARM_RESULT |
订单武装读卡交易 | result、orderNo、amount |
CARD_DETECTED |
本次支付检测到新卡 | readerSession;不记录完整 UID |
CARD_BEFORE_STARTED |
消费前读卡开始 | readerSession |
CARD_BEFORE_RESULT |
消费前读卡结束 | result、durationMs、错误码 |
DEBIT_STARTED |
扣款命令开始 | amount |
DEBIT_RESULT |
扣款结束 | completedAmount、walletCount、result、错误码 |
CARD_AFTER_STARTED |
消费后复读开始 | readerSession |
CARD_AFTER_RESULT |
消费后复读结束 | result、durationMs、错误码 |
CARD_CONSISTENCY_RESULT |
前后卡及金额校验 | result、校验失败类型 |
TRANSACTION_CLASSIFIED |
最终交易分类 | DEBIT_SUCCEEDED、PRE_DEBIT_FAILED、DEBIT_UNKNOWN |
ConsumptionCardTransactionExecutor 的三个真实阶段作为开始事件;ConsumptionTransactionOutcome 统一产生三个结果事件,避免在底层和 Activity 重复记同一业务事件。
| 事件码 | 触发时机 | 关键字段 |
|---|---|---|
LOCAL_RECORD_SAVE_STARTED |
订单或交易准备落库 | recordType |
LOCAL_RECORD_SAVE_RESULT |
落库结束 | result、durationMs、错误码 |
REPORT_ENQUEUED |
接口 C 原请求已持久化 | reportStatus、nextRetryAt |
REPORT_REQUEST_STARTED |
每次接口 C 调用前 | reportSource、reportAttemptId、attemptNumber |
REPORT_REQUEST_RESULT |
接口 C 返回 | result、durationMs、业务码、traceId、错误摘要 |
REPORT_MARK_RESULT |
本地标记上传成功或失败 | UPLOADED 或 PENDING |
RETRY_ROUND_STARTED |
后台补传轮次开始 | trigger、batchSize、maxPerRound |
RETRY_ITEM_RESULT |
单条补传结束 | orderNo、attemptId、result |
RETRY_ROUND_FINISHED |
补传轮次结束 | attempted、uploaded、failed、skipped、durationMs |
| 事件码 | 触发时机 | 关键字段 |
|---|---|---|
FLOW_TERMINAL |
进入成功、待补传或交易异常终态 | terminalState、trayPresent、cardPresent |
FLOW_RESET_WAITING |
仍等待餐盘或卡片移走 | waitingFor |
FLOW_FINISHED |
本轮安全结束 | finalState、totalDurationMs、reportStatus |
FLOW_RESET |
返回等待放盘 | result=SUCCEEDED |
ACTIVITY_BACKGROUND_DURING_TRANSACTION |
硬件交易在途时进入后台 | 当前交易阶段 |
files/consumption_flow_logs/。consumption-flow-YYYY-MM-DD.jsonl,超过单文件上限后增加序号。consumption_records.db 或待补传数据。eventSequence 保序;Activity 不直接执行文件 I/O。FLOW_TERMINAL、REPORT_REQUEST_RESULT、FLOW_FINISHED 后执行一次队列 flush;进程异常退出时允许丢失尚未执行的普通日志,但交易事实已由数据库保护。不直接复用 LocalFileRecorder,原因是其日志混合保存在外部 /Log/、缺少结构化字段和业务会话序号,并通过剩余空间循环删除其他类别文件,不适合作为交易主链路日志。
在管理员日志入口新增“消费流水”类型:
eventSequence 展示一条完整时间线,并显示各阶段耗时。readerSession 的事件提供“查看读卡明细”提示;不在业务页显示原始 APDU。Access-Token、Authorization、Cookie 等认证信息。同时需要修复现有 NewLogInterceptor 的空请求体 reqBody.toString() 风险,并对 headers/body 制定脱敏策略;该修复属于网络日志安全加固,不改变接口报文。
范围: 新增事件模型、上下文、JSON 编码、顺序写入、轮转和保留策略。
验收标准:
验证: 纯 Java 单测覆盖序列、转义、轮转、保留和写入失败。
依赖: 无。
预计范围: M,4至5个新增文件。
范围: 在状态机、订单协调器和 Activity 接入 flowId,记录放盘、移盘、订单请求、金额分流及复位。
验收标准:
FLOW_CREATED。flowId。验证: 扩展状态机与订单协调器单测,核对固定事件序列。
依赖: Task 1。
预计范围: M,3至4个文件。
范围: 记录遗留卡、等待新卡、前读卡、扣款、后读卡、卡一致性和交易分类;消除 Activity/Manager 重复的交易开始日志。
验收标准:
DEBIT_UNKNOWN 且不出现自动重试事件。验证: 扩展 ConsumptionCardTransactionExecutorTest 和状态事件序列测试。
依赖: Task 1、Task 2。
预计范围: M,3至5个文件。
范围: 记录落库结果、前台接口 C、补传触发来源、每条补传及轮次汇总;持久化 flowId。
验收标准:
reportAttemptId 和开始/结果事件。flowId、orderNo。验证: 仓库升级测试、补传Runner测试及重启恢复测试。
依赖: Task 1、Task 2。
预计范围: M,4至5个文件。
范围: 记录网络状态变化;修复空请求体风险;对认证头和敏感字段脱敏。
验收标准:
验证: 网络状态去重测试、拦截器空Body和脱敏测试。
依赖: Task 1。
预计范围: M,3至4个文件。
范围: 管理员日志入口增加消费流水类型,支持筛选、时间线和导出。
验收标准:
验证: 查询/过滤单测、页面手工检查、导出文件解析检查。
依赖: Task 1至5。
预计范围: M,4至5个文件。
:app:testDebugUnitTest :app:assembleDebug 通过。git diff --check、敏感字段静态扫描和日志文件解析检查通过。建议严格按 Task 1 → 2 → 3 → Checkpoint A → 4 → 5 → 6 → Checkpoint B 推进。Task 4 与 Task 5 在日志契约稳定后可以独立开发,但合并验证必须串行。
本地代码、单测、文档和 Debug 构建预计需要1至2个开发日;真机至少需要一次成功交易、三次阶段移卡、一次断网补传和一次应用重启恢复测试。实际资金卡测试仍由用户提供设备和环境协助。
随机拿到一个 orderNo 后,无需同时翻查多个文件,就能在“消费流水”中回答:
能够完整回答以上8个问题,且业务流水中没有敏感原文,才算全链路日志实施完成。