项目:ZhctWeightingTableYoukate(410 型优卡特称重台)
沉淀日期:2026-09-10
文档状态:方案汇总,待产品、客户端、服务端、硬件和测试共同评审
实施状态:当前仅部分具备基础能力,双层方案尚未完整实现
评审责任人:待指定
一、结论
针对扫码头偶发短暂断码导致同一次取餐被拆成多笔订单的问题,建议采用两层保护:
- **前置防抖:900~1200ms 内不结束订单。**APK 收到空码后进入“疑似断码”状态,在宽限期内保持当前取餐会话;同一餐盘码恢复时继续原订单,只有持续空码超过宽限期才确认离盘。
- **后置纠错:2 分钟断连订单识别与合并。**如果前置防抖仍未拦住,服务端对同设备、同餐盘、同用户、同计重菜品的连续异常明细进行候选检测,满足全部规则后作废重复明细,并按秤体实际重量差生成一条纠正明细。
两层方案不是二选一:前置防抖是主防线,目标是避免错误订单产生;后置纠错是兜底,目标是修复已经产生的异常明细。红外逐步关闭属于硬件策略灰度验证,不能替代任一订单保护机制。
二、问题背景与数据链路
现有会议资料中的硬件口径为:扫码头约每 300ms 产生一次真实扫码结果,香山控制板可能把同一结果按 100ms × 3 或 50ms × 6 重复发送给 APK。这里的每一个 APK 回调不一定代表一次新的真实扫描。
扫码头真实扫描(约 300ms/次)
↓
控制板缓存同一结果
↓
按 100ms×3 或 50ms×6 重复组成复合包
↓
DeviceSDK 解析扫码字段和称重字段
↓
APK 根据有效码/空码判断取餐开始、继续或结束
↓
本地保存取餐记录并上传服务端
如果 APK 把重复上报的空码包当成多次独立扫码失败,就可能过早结束当前订单;同一餐盘随后重新被识别时,又会开始一笔新订单,形成断连拆单甚至重复计费。
三、方案一:900~1200ms 扫码断码宽限
3.1 目标
- 屏蔽扫码头、控制板或通讯链路产生的短暂空码。
- 宽限期内恢复同一餐盘码时继续原取餐会话,不新增订单。
- 餐盘真实移走后仍能在可接受延迟内结束订单。
- 判断不受
100ms×3、50ms×6等重复包数量影响。
3.2 推荐状态机
无订单
│ 收到有效餐盘码
▼
取餐进行中
│ 首次收到空码
▼
疑似断码(保持订单,不结算)
├─ 宽限期内恢复同一餐盘码 → 回到取餐进行中
├─ 宽限期内出现不同餐盘码 → 进入冲突分支,按待确认规则处理
└─ 持续空码达到宽限时间 → 确认离盘,只结束一次订单
3.3 判定规则
- 收到合法餐盘码时,记录最后一次有效餐盘码和单调时钟时间。
- 首次收到空码时,只标记“疑似断码”,不得立刻清空当前订单状态。
- 使用单调时钟计算断码持续时间,避免系统时间被校准后影响判断。
- 宽限期内恢复同一餐盘码时:
- 清除疑似断码状态;
- 保留原取餐起始重量、用户、菜品和会话标识;
- 不上传、不结算、不生成新明细。
- 空码持续达到配置值时,才确认离盘并触发一次订单结束。
- 同一轮离盘确认必须具备幂等保护,避免多个延迟任务重复结束订单。
3.4 900ms 与 1200ms 的含义
| 宽限值 | 对应约 300ms 真实扫描周期 | 优点 | 代价 |
|---|---|---|---|
| 900ms | 约 3 个周期 | 真离盘后的页面恢复和结算更快 | 对较长断码的容忍度较低 |
| 1200ms | 约 4 个周期 | 更能降低偶发断码拆单 | 真离盘后多等待约 300ms |
不建议仅把“空码次数”机械设置为 9 或 12 来替代时间判断,因为 APK 回调频率可配置,控制板还可能重复同一次真实扫描结果。最终默认值应通过 900ms、1200ms 两档真机数据比较后确定。
3.5 当前项目状态
当前源码已经具备连续空码次数确认,但还不是独立的时间窗口:
HomeActivity.handleEmptyScanCode()按连续空码回调次数结束订单。- 离盘确认次数允许设置为 3~10 次;全新安装默认 7 次,存量升级设备默认保留 3 次。
- 称体回调默认 100ms,也支持 50/100/150/200/250ms。
- 因此当前实际宽限时间会随回调间隔和控制板重复包变化,不能等同于稳定的 900~1200ms。
- 设置页已有红外打开、关闭命令入口,但“命令发送成功”目前只证明串口写入成功,不等同于设备执行结果已回读确认。
结论:方案一当前为部分实现,需要从“次数判断”升级为“时间判断”,并补充同一餐盘恢复、不同餐盘冲突和幂等结束规则。
3.6 红外逐步关闭策略
扫码头灵敏度和识读距离增强后,可以评估减少红外依赖,但建议按设备或餐线灰度:
- 选择一台设备或一条餐线关闭红外。
- 记录设备编号、APK 版本、控制板版本、操作时间和回退方式。
- 对比关闭前后的扫码成功率、短暂断码率、误拆单率、真离盘延迟、重启/休眠异常。
- 指标无恶化且连续运行达到约定周期后,再扩大范围。
- 出现扫码下降、订单拆分或无法确认硬件状态时立即恢复红外。
红外灰度必须保留人工回退;在设备未提供状态回读前,不能只根据 APK 的“命令已发送”认定红外已经关闭。
四、方案二:2 分钟断连订单过滤与纠错
4.1 目标
当前置防抖未拦截成功、同一次取餐已经形成多条订单明细时,识别高度疑似的断连拆单,并把计费结果纠正为秤体实际取餐重量。
该方案建议由服务端实施,因为服务端才能统一查询订单、锁定候选明细、处理结算冲正、重新计算金额/营养/库存并保留完整审计链。
4.2 候选触发条件
以新明细产生时间为结束点,向前检查滚动 2 分钟窗口。至少满足以下基础条件才启动深度检测:
- 同一设备;
- 同一餐盘;
- 同一用户;
- 同一菜品且期间没有换菜;
- 菜品计价方式为计重;
- 候选称重/订单明细数量不少于 4 条。
“不少于 4 条”只是进入检测的门槛,不代表等满 2 分钟处理。第 4 条明细产生时即可立即回查过去 2 分钟。
4.3 全量判定条件
候选组必须同时满足以下条件,任一条件不满足都不得自动纠错:
条件 A:菜品与计价方式一致
候选明细必须全部属于同一个计重菜品。换菜、按份、按个或固定价格菜品不能仅按重量合并。
条件 B:重量序列符合断连特征
截图方案要求“各次称重连续递增,单步误差不超过 5g”。建议将公式明确为:
w[i] >= w[i-1] - 5g
这里的 w[i] 必须先明确是“同一次操作的累计取餐重量”。如果现有订单明细保存的是每段独立取餐重量,这条递增规则就不成立,必须改用原始秤重轨迹或累计重量快照,不能直接套用。
条件 C:订单合计与秤体实际重量差明显不一致
实际取餐重量 = |操作开始稳定重量 - 操作结束稳定重量|
订单明细总重量 = Σ 候选明细重量
异常差值 = |订单明细总重量 - 实际取餐重量|
只有异常差值达到或超过 15g 时,才认为存在需要修正的计费偏差。15g 用于排除正常称重波动,但仍需结合秤的检定精度和起跳值验证。
示例:
| 项目 | 数值 |
|---|---|
| 操作开始稳定重量 | 5000g |
| 操作结束稳定重量 | 4900g |
| 实际取餐重量 | 100g |
| 断连明细 | 25g、50g、75g、100g |
| 明细合计 | 250g |
| 异常差值 | 150g |
该组数据满足明显重复累计特征,纠正后的计费重量应为 100g,而不是 250g。
条件 D:期间只有同一用户且没有异常加菜
候选期间不能切换用户。秤体剩余重量也不能出现超出容差的明显上升,否则可能发生补菜、换盆、用户放回菜品、压秤或重新置零,开始重量与结束重量不再足以代表单人取餐量。
4.4 推荐纠错流程
发现候选组
→ 对设备/餐盘/会话加锁
→ 回读并再次校验全部条件
→ 计算实际取餐重量
→ 生成唯一纠错事件 ID
→ 将原明细标记为“断连纠正已作废”
→ 新建一条纠正明细
→ 重算金额、余额、营养和库存
→ 保存原明细与纠正明细的关联关系
→ 写审计日志并释放锁
不建议物理删除原订单明细。更安全的方式是业务作废或冲正:保留原始重量、金额、生成时间、作废原因、纠错规则版本和替代明细 ID,以便财务追溯、问题复盘和重复执行防护。
4.5 当前项目状态
当前 Android 项目没有实现截图中的完整后置规则:
- 没有 2 分钟候选窗口。
- 没有
≥4条分组检测。 - 没有递增序列、5g 单步误差、15g 总差判断。
- 本地记录只有用户、餐盘、取餐时间、菜品、取餐重量等基本字段,没有完整的开始/结束稳定重量和原始称重轨迹。
- 补传任务仅逐条上传,服务端成功后删除本地待上传记录;这不是订单纠错。
- Android 网络接口只有取餐上传,没有服务端明细作废与纠正接口。
结论:方案二当前为未实现,且需要服务端、客户端数据契约和结算链路联合设计。
五、两个方案的关系
| 对比项 | 方案一:前置防抖 | 方案二:后置纠错 |
|---|---|---|
| 发生阶段 | 订单结束前 | 异常明细产生后 |
| 核心目标 | 不让短暂断码拆单 | 修复已产生的重复/错误明细 |
| 主要执行端 | Android APK | 服务端,Android 补充证据字段 |
| 时间尺度 | 900~1200ms | 滚动 2 分钟 |
| 主要依据 | 最后一次有效码与持续空码时间 | 设备、餐盘、用户、菜品、明细序列和实际重量差 |
| 用户感知 | 真离盘结算延迟增加约 0.9~1.2 秒 | 可能涉及已展示金额的撤销、冲正或重新计算 |
| 风险 | 过长会延迟真离盘,过短仍会拆单 | 误判可能改错账,必须强审计和幂等 |
| 定位 | 主防线 | 兜底防线 |
推荐完整链路:
扫码短暂为空
→ APK 启动 900~1200ms 宽限
→ 同码恢复:继续原订单
→ 持续空码:正常结束订单
→ 若仍形成多条异常明细
→ 服务端 2 分钟规则先只检测告警
→ 规则准确率验收后再开启自动作废与纠正
六、职责与数据契约建议
| 责任方 | 建议职责 |
|---|---|
| 扫码头厂商 | 明确真实扫描周期、同码输出和空码行为 |
| 香山控制板 | 明确重复包节奏,最好提供真实采样序号、时间戳或新旧标识 |
| Android APK | 实现时间型宽限、会话连续性、一次性结束、日志和纠错证据字段 |
| 服务端 | 候选检测、并发锁、幂等纠错、业务作废、金额/余额/营养/库存重算和审计 |
| 测试 | 构造断码、重复包、异码、换菜、换人、补菜、置零、网络重试和并发场景 |
| 交付/现场 | 执行红外灰度、记录设备版本和前后指标、保留回退 |
建议新增或确认的数据字段:
pick_session_id:一次连续取餐会话的唯一标识;idempotency_key:上传与纠错幂等键;device_code、tray_code、user_id、dish_id;dish_pricing_type;pick_started_at、pick_ended_at;start_stable_weight、end_stable_weight;calculated_pick_weight;scan_loss_started_at、scan_recovered_at;scanner_sample_seq或控制板新旧包标识(如协议支持);correction_event_id、correction_rule_version、replaced_detail_ids。
七、建议实施顺序
阶段 0:冻结口径
- 由硬件/控制板提供 300ms、100ms×3/50ms×6 和空码语义的书面确认或抓包证据。
- 明确方案二中的
w[i]是累计取餐重量还是分段取餐重量。 - 明确实际重量差的开始、结束稳定点和补菜识别阈值。
- 明确已支付订单采用作废、退款、冲正还是账单更正。
阶段 1:先落地前置防抖
- 把离盘判断从回调次数改为单调时间窗口。
- 支持 900ms、1200ms 两档测试配置。
- 同码恢复继续原会话;超时只结束一次。
- 增加断码开始、恢复、确认离盘和会话 ID 日志。
- 同步开展单设备/单餐线红外关闭灰度。
阶段 2:后置规则只检测不改账
- 服务端按规则识别候选组,仅打标、告警和生成审计报告。
- 用真实样本统计命中率、误报率、漏报率和可解释性。
- 人工复核候选组,修正规则口径。
阶段 3:开启自动纠错
- 只有观察期达到验收标准后,才允许自动业务作废和生成纠正明细。
- 必须具备事务、并发锁、幂等、审计、人工回滚和异常告警。
- 先灰度到指定设备/餐线,再逐步扩大。
八、验收标准
8.1 前置防抖
- 构造
100ms×3和50ms×6重复空码包,订单结束时间只由宽限时间决定,不由包数量决定。 - 断码在 900ms 内恢复同一餐盘码时,订单不结束、不新增明细、取餐起始状态不重置。
- 断码在 900~1200ms 区间恢复时,能验证两档配置的差异。
- 空码持续超过配置值时,只产生一次离盘、一次结算和一条正常明细。
- 宽限期内出现不同餐盘码时,结果符合评审后的冲突规则。
- 真实移盘延迟、误拆单率和漏结算率达到项目约定指标。
8.2 后置纠错
- 同设备、同餐盘、同用户、同计重菜品且不少于 4 条时才进入候选检测。
- 换菜、换人、异常加菜、置零、跨设备、跨餐盘任一场景不得自动合并。
- 5g 和 15g 边界值分别有正反测试。
- 同一候选组重复执行不会产生第二条纠正明细。
- 并发上传和纠错时不会漏单、重复扣费或部分作废。
- 原明细、纠正明细、金额变化和规则版本可完整追溯。
- 观察期误报率达到业务批准标准后,方可启用自动纠错。
8.3 红外灰度
- 每次切换均记录设备、版本、时间、执行结果和回退结果。
- 切换前后对比扫码成功率、断码率、拆单率和设备异常率。
- 任一核心指标恶化时可以立即恢复原策略。
九、P0 待确认项
- 断码宽限最终默认 900ms 还是 1200ms,是否允许现场配置。
- 宽限期内出现不同餐盘码时,旧订单与新订单如何处理。
- 控制板重复包是否具有采样序号、时间戳或新旧标识。
- 方案二的递增重量字段到底是累计重量还是独立明细重量。
- “无异常加菜”的计算窗口、阈值和稳定条件。
- 15g 是否适用于所有秤体、菜品和起跳配置。
- 已扣费/已结算明细的纠错方式和财务审计要求。
- 原明细是否允许物理删除;本方案建议只业务作废,不物理删除。
上述口径未关闭前,方案二不得直接进入自动改账开发。
十、证据等级与边界
| 内容 | 证据等级 | 说明 |
|---|---|---|
| 当前 APK 使用连续空码次数结束订单 | A:源码直接证据 | 依据当前 zymy_ongoing 分支代码核查 |
| 当前可配置 3~10 次、默认回调约 100ms | A:源码直接证据 | 受设备实际保存配置影响 |
| 300ms 真实扫描、100ms×3/50ms×6 | B:会议与人员口径 | 待供应商书面说明或串口抓包确认 |
| 扫码灵敏度和距离已增强 | B:项目人员反馈 | 待现场对比数据确认 |
| 2 分钟、≥4 条、5g、15g 规则 | B:用户提供的业务方案 | 当前项目未实现,规则字段语义仍待冻结 |
| 业务作废、幂等纠错、分阶段上线 | C:工程建议 | 需产品、财务、服务端和测试评审 |
本文件没有修改业务源码,没有认定方案已经上线,也没有将临时聊天截图复制进项目仓库。
十一、来源索引
recordings/transcripts/2026-09-07-liutianhua-scan-interruption/2026-09-07-hardware-packet-return-summary.mdrecordings/transcripts/2026-09-07-liutianhua-scan-interruption/2026-09-07-scanner-interruption-meeting-minutes.mddocs/2026-09-09-tray-removal-confirmation-count-code-locations.mdhardware/MJ-2050/2026-09-09-MJ-2050扫码头性能分析.md- 用户于 2026-09-09~2026-09-10 在当前线程提供的扫码头时序说明与断连订单过滤截图;截图仅作需求上下文,不作为已实施证据归档。
十二、人审状态
- 当前结论:待评审。
- 建议评审角色:产品负责人、Android 开发、服务端开发、香山控制板/扫码头技术、测试、财务或结算负责人、现场交付。
- 评审重点:P0 待确认项、自动改账安全边界、900/1200ms 现场选择、红外灰度指标。
- 已接受经验:前置防抖和后置纠错分层;真实扫描周期与重复包次数分离。
- 禁止吸收的反模式:把回调包数当真实扫描次数;未观察验证就自动删订单;把串口写成功当设备执行成功。