A02 未放餐盘报警认领补录设计

项目:ZhctWeightingTableYoukate · 日期:2026-08-25 · 状态:代码已实施,待真机/服务端联调验收

范围仅限 A02「未放餐盘取餐」正式报警:现场人员可选择刷管理卡解除,或放餐盘认领并复用现有本地取餐记录与后台上传链路。

1. 目标

  1. 刷管理卡解除:只解除报警,不补录取餐记录。
  2. 放餐盘认领并补录:识别餐盘绑定人员,以认领前最后一次有效的实时逃单重量复用现有本地取餐记录和后台上传链路。

报警发生后,未点击任一按钮前,不响应放盘、普通餐盘码或解除卡;这些操作不能解除报警,也不能进入正常打餐流程。

2. 范围与非目标

本次范围

  • 仅改造 A02 报警页和 A02 报警期内的扫码处理。
  • 记录报警时间、时长、报警重量、解除方式及认领/刷卡信息。
  • 复用 PickMealRecord、本地 DAO、调度器和上传 Worker。
  • 在日报警分类日志及既有对应日志中保留记录。

非目标

  • 不改 A01、A03 的规则或页面。
  • 不新增服务端补录接口、订单事件 ID 或远程管理员页面。
  • 不改变现有上传接口、补传策略和服务端计价口径。
  • 不做进程被杀后的 A02 操作态恢复,沿用现有中断审计。

3. 业务流程

A02 稳定减重达到阈值 -> 固定报警前基准重量,展示 A02 报警页 -> 在无餐盘、稳定状态下持续刷新当前逃单重量 -> 若当前重量恢复至基准重量或更高,自动解除报警 -> 默认状态:未选择解除方式 - 忽略普通餐盘码、解除卡和放盘动作 -> 点击「刷管理卡解除」 -> 等待有效管理卡 -> 记录卡值、解除时间和持续时长 -> 解除报警并返回取餐前页 -> 点击「放餐盘认领并补录」 -> 等待有效餐盘码 -> 查询该餐盘绑定人员 -> 用认领前最后一次有效实时逃单重量保存本地取餐记录(共 3 次) -> 三次保存失败:显示保存异常,仅保留管理卡解除 -> 保存成功后立即上传(网络类失败共重试 3 次) -> code=0 且本地删除成功:解除报警并刷新当前餐盘累计 -> 三次网络失败:进入定时上传队列、提示累计可能未同步并离线解除 -> code=1:提示先绑定餐盘,移开并重新放盘后再次认领 -> 其他业务或删除失败:报警继续锁定

4. 关键规则

4.1 报警基准与实时逃单重量

当前 A02 检测器以“稳定重量相对无餐盘基线的减重达到阈值”触发报警。触发瞬间保存 baselineWeighttriggerCurrentWeighttriggerPickWeight = baselineWeight - triggerCurrentWeight 作为证据。报警触发后基准重量保持不变,但在报警解除前,currentWeightalarmPickWeight = max(0, baselineWeight - currentWeight) 必须实时更新。仅在取餐前页、无餐盘码、秤体稳定时采纳当前重量;餐盘码出现后保留出现前最后一次有效实时值供认领使用。

报警页必须持续展示:本单逃单重量:{alarmPickWeight}g。未选择、等待管理卡和等待餐盘认领时均随有效实时重量更新;餐盘已识别后展示认领前最后一次有效值。

4.2 重量恢复自动解除

当 A02 尚未解除且秤体稳定、仍在取餐前页、无餐盘码,并且 currentWeight >= baselineWeight 时,自动解除报警。该条件表示重量已完全恢复到报警前基准,不使用报警阈值或容差代替。写入 WEIGHT_RECOVEREDRESOLVE|method=WEIGHT_RECOVERED,记录基准、触发重量、恢复重量、0g 逃单重量和时长;不创建取餐记录、不调度上传。

4.3 按钮门控

模式可响应输入行为
NONE忽略任何餐盘码、解除卡和放盘动作
WAIT_MANAGER_CARD有效管理卡记录卡值并解除报警
WAIT_TRAY_CLAIM有效普通餐盘码查询人员并执行补录

两个按钮互斥;进入任一模式后禁用另一个按钮,避免同一报警同时走两条解除链路。处于等待管理卡或等待餐盘码且尚未接收有效输入时,页面显示“返回重新选择”:它只取消本次等待态、停止 60 秒计时并回到 NONE,报警、声光、实时逃单重量和累计时长保持不变,日报警分类日志写入 ACTION_CANCEL。餐盘码已接收、查人或上传正在进行时,以及 LOCAL_SAVE_FAILED 状态下不显示该入口。默认页的“刷管理卡解除”和“放餐盘认领并补录”必须左右等宽地显示在同一行,中间保留 12dp 间距;按钮文字在各自按钮内水平居中,允许最多两行显示,不能因横向排布被截断。进入任一等待态后,继续沿用两个禁用按钮的同一横向位置。

4.4 操作超时

选择任一解除方式后,等待对应输入 60 秒:点击“刷管理卡解除”后等待有效管理卡 60 秒;点击“放餐盘认领并补录”后等待有效餐盘码 60 秒。超时不解除报警、不停止声光报警、不清除报警重量;仅回到 NONE 状态和“请选择解除方式”页面。报警持续时间从触发时起连续累计。日报警分类日志写入 ACTION_TIMEOUT;现场也可在尚未接收有效输入时通过“返回重新选择”提前回到选择页,记录 ACTION_CANCEL

4.5 放盘认领

4.6 刷管理卡解除

仅在 WAIT_MANAGER_CARD 模式接受现有管理卡范围,写入实际扫描卡值、解除时间和持续时长;不创建取餐记录,不上传取餐明细。

5. 本地记录与上传复用

从当前 HomeActivity.uploadPickInfo(int) 抽出可复用私有方法:

savePickMealRecord(String pickerUuid, String trayCode, String pickTime,
                   String dishUuid, int pickWeight, String source, String alarmId)

正常取餐调用时使用 source=NORMAL;A02 认领使用 source=ANTI_ESCAPE_A02 和报警流水号。为让后台上传结果回写日报警分类日志,pick_record 增加可选 sourcealarm_id 两列。数据库升级必须采用保留历史数据的 ALTER TABLE 迁移,不能删除旧表重建。

UploadPickMealRecordWorker 保持原上传接口和删除策略,仅对 ANTI_ESCAPE_A02 来源追加日报警分类日志,并通过现有 EventBus 回传 alarmIdpickRecordId 与结果。只有 code=0 且本地删除成功的结果能正常解除匹配的 A02;网络类失败三次后则以已持久化记录进入定时队列作为离线兜底。延迟上传成功后,如果认领餐盘仍是当前餐盘,必须重新查询并刷新人员累计;正常取餐不接收该回传。

6. 日志要求

事件必填字段
TRIGGERtype=A02、alarmId、triggerAt、baselineWeight、triggerCurrentWeight、triggerPickWeight
WEIGHT_RECOVEREDalarmId、baselineWeight、triggerPickWeight、recoveredCurrentWeight、recoveredPickWeight=0、durationMs
ACTION_SELECTalarmId、action=MANAGER_CARD/TRAY_CLAIM
ACTION_CANCELalarmId、action=MANAGER_CARD/TRAY_CLAIM、cancelAt
ACTION_TIMEOUTalarmId、action、waitDurationMs=60000、timeoutAt
RESOLVE(刷卡)alarmId、method=MANAGER_CARD、cardCode、durationMs、resolvedAt
CLAIMalarmId、trayCode、staffUuid、staffName、pickWeight、pickRecordId
LOCAL_SAVE_ATTEMPTalarmId、attempt=1/2/3、trayCode、staffUuid、pickWeight、result
LOCAL_SAVE_FAILEDalarmId、attempts=3、lastError、trayCode、staffUuid、pickWeight
UPLOAD_SUCCESSalarmId、pickRecordId、serverCode=0、message
UPLOAD_DEFERRED_NETWORKalarmId、pickRecordId、attempts=3、lastNetworkError
NEED_TRAY_BINDalarmId、pickRecordId、serverCode=1、trayCode
RESOLVE(放盘)alarmId、method=TRAY_CLAIM_UPLOAD_SUCCESS/TRAY_CLAIM_DEFERRED_NETWORK、durationMs、resolvedAt
RESOLVE(保存异常)alarmId、method=LOCAL_SAVE_FAILED_ADMIN_OVERRIDE、cardCode、lastError、durationMs
UPLOAD_RETRY/FAILalarmId、pickRecordId、serverCode、message
ANTI_ESCAPE|event=TRIGGER|type=A02|alarmId=A02-...|baselineWeight=1000|triggerPickWeight=186|triggerAt=...
ANTI_ESCAPE|event=WEIGHT_RECOVERED|type=A02|alarmId=A02-...|recoveredCurrentWeight=1000|recoveredPickWeight=0
ANTI_ESCAPE|event=ACTION_SELECT|type=A02|alarmId=A02-...|action=TRAY_CLAIM
ANTI_ESCAPE|event=ACTION_TIMEOUT|type=A02|alarmId=A02-...|action=TRAY_CLAIM|waitDurationMs=60000|timeoutAt=...
ANTI_ESCAPE|event=CLAIM|type=A02|alarmId=A02-...|trayCode=123456|staffUuid=...|staffName=张三|pickRecordId=42|pickWeight=186
ANTI_ESCAPE|event=UPLOAD_SUCCESS|type=A02|alarmId=A02-...|pickRecordId=42|serverCode=0
ANTI_ESCAPE|event=RESOLVE|type=A02|alarmId=A02-...|resolveMethod=TRAY_CLAIM_UPLOAD_SUCCESS

普通本地日志记录按钮选择、查人、落库和异常原因;取餐记录日志沿用明细并增加来源/报警流水号;上传日志沿用 Worker 日志,A02 来源同时追加日报警分类日志。

7. 代码改动范围

文件/模块改动
antiescape/NoTrayPickDetector返回或暴露触发时基准重量、当前重量和减重值
antiescape/AntiEscapeAlarm增加 A02 报警流水号、固定基准重量、触发重量证据和实时逃单重量
activity/HomeActivity增加 A02 实时重量更新、重量恢复自动解除、按钮门控、认领扫码分支和日志记录
widget/AntiEscapeAlarmDialogFragmentA02 增加实时逃单重量、两种解除按钮、等待与认领结果状态展示;A01/A03 不变
PickMealRecord、DAO、DB Helper增加 sourcealarm_id 并做非破坏性迁移
UploadPickMealRecordWorkerA02 同步结果追加日报警分类日志,并回传成功、需绑盘或重试结果
资源与单元测试新增实时重量、自动恢复、按钮、等待、失败文案,并覆盖门控、重量恢复、解除与上传日志分支

8. 验收口径

  1. A02 报警页持续展示“本单逃单重量:xxg”;在无餐盘、稳定状态下按固定基准重量减当前重量实时变化。
  2. 当前稳定重量恢复至报警前基准重量或更高时,A02 自动解除,记录 WEIGHT_RECOVERED,且不生成取餐记录或上传。
  3. 未点击任何按钮时,放餐盘、普通餐盘码和管理卡均不解除报警、不进入正常打餐页。
  4. A02 默认页的两种解除按钮左右等宽、同一行显示,间距为 12dp;按钮文案完整可读,最多在按钮内换两行。
  5. 刷管理卡仅解除报警,日志包含卡值、报警时长和最终实时逃单重量,不产生取餐记录。
  6. 选择任一方式后等待 60 秒,超时仅返回选择页;尚未接收有效输入时可“返回重新选择”,报警仍锁定且日报警日志分别出现 ACTION_TIMEOUTACTION_CANCEL
  7. 本地保存共三次;三次失败时只显示管理卡解除、停止响应放盘并写 LOCAL_SAVE_FAILED
  8. 本地保存成功后,网络类上传失败三次可进入定时队列离线放行,显示累计暂未同步提示;非网络失败保持报警。
  9. 延迟上传成功时当前餐盘仍在打餐页则主动刷新累计,否则下次放盘读取最新累计。
  10. code=1 时删除候选记录但不解除报警,页面提示先绑定餐盘后重新认领;绑定后移开并重新放盘可生成新记录。
  11. A01、A03、正常取餐、既有解除码范围和补传策略回归通过。

9. 实施前提