410 二维码改进 · 等待方案 Review

410二维码改进:冻结合同与执行门禁

1. 版本、边界和优先级

2. UC执行切片与状态

沿用已有UC-YKSCAN-001(取证)、002(解析质量)、003(取餐会话)、004(区域内移动)、005(夹子计量)。不创建重复UC。原草案整体BLOCKED拆分为以下执行切片;完整物理业务UC仍保留对应门禁。

Slice 关联UC/步骤 角色与触发 主流程 异常/恢复 AC/测试 DoR
E00 全部前置 执行Codex开始任务 预检→隔离→准备既有构建栈→读测试XML 来源不符保留差异;依赖失败归BUILD_ENV,不改版本绕过 ENV-01 READY
A01 001 M2;002 M3/M4/E1;003 E4 SDK回调/错误 复制公开字段→分类→更新重量→按二维码质量路由→诊断 非法/来源错误使空候选失效,保留身份和基线;合法报空仍保留旧策略 QR-01–07 READY
A02 003 E5;004 A1/R1 查人请求返回 发请求时冻结代次→UI线程入口验证→才改人员/事件/告警 旧请求/菜品变化/同码新会话丢弃,当前失败正常反馈 ASYNC-01–03 READY
A03 003 A5 A02等认领收到普通有效码 先清观察计数/标在位→再看inflight与去重 不改管理卡/A03前置路由;B未查询不写lastQueried=B A02-01–03 READY
A04 005 BR-W01/AC-W01 用户保存勺重 解析整数→put→回读→显示 空/溢出不改旧值;取消不写,不自动补偿 TOOL-01 READY
A05 003 R3;004 M1/M2 重量显示/同实例回前台 旧算法等价模型;仅无观测缺口时恢复既有视图/基线 实际onStop缺口只诊断;恢复/收尾改变待C01 WEIGHT-01;UI-01 模型与无缺口UI READY;缺口恢复GATED
A06 001 M2;003/004状态准备 同一观察流旁观 reducer只输出候选状态/effects→比对诊断 无质量/无在位不产生确定结束;效果禁止业务副作用 SESSION-01–04 READY_OBSERVE
B01 002 M1/M2/E1/E2/R1 SDK候选包交付 冻结包→真实注入入口→正确断言回放→ABI/设备集成 入口未交付标WAIT_VENDOR_ENTRY,不用旧私有方法名断言质量 SDK-01–07 验收准备READY;候选/设备部分GATED
B02 002完整读取链 厂家路线无法满足且选择旁路 一次性后端选择→唯一owner→原始流→新parser→统一写命令 协议/端口/native关闭未证实不启动;不双读/双开 TRANSPORT-01–02 GATED:G-SDK/G-PORT
C01 003/004完整流程 新会话内核要接管 按已批准SessionPolicy判离开/续餐/换盘 模糊归属待处理;中途不自动退回旧引擎结算 LIVE-01–03 G-POLICY必需;选在位判据时另需G-PRESENCE
C02 005 M1–M5/E1/E2 夹重补偿要参与计量 校准工具及两端状态→计算补偿或UNRESOLVED 不知道夹子在哪就不自动补偿;湿/搭边/手压另判 TOOL-02–03 GATED:G-TOOL
C03 003 M4/M5/R2;004 M5 明确结束申请要入库/真机发布 单事务幂等本地写→原补传→设备动作验收 失败保留待处理;重启不重复写;旧API语义不改 DB-01–03;DEVICE-01 本地准备READY_OBSERVE;LIVE需C01及计量合格,发布另需G-RELEASE

性能与安全共通:A阶段只处理现有回调,不创建无界线程/队列,不记录完整人员/二维码;纯策略不sleep、不调用网络。A06诊断环形内存上限256条、单事件摘要上限512字节,禁无限日志;该数值是本计划的资源约束,不是硬件性能事实。真机P95/P99延迟、容忍失读时长和重量误差只在G-POLICY/G-RELEASE批准后作为验收门槛。

3. 唯一观察合同(A01后冻结,不允许各子任务重定义)

新增类放 app/src/main/java/com/cpt/zhct/weighting/hardware/,均为App自有类,不与厂商同名。

public final class ScaleObservation {
    public enum CodeKind { VALID_QR, SDK_REPORTED_NO_CODE, INVALID_QR, SOURCE_ERROR }
    public enum Presence { PRESENT, ABSENT, UNKNOWN }
    public enum Freshness { UNKNOWN, RECEIVER_TIMESTAMP_ONLY, DEVICE_VERIFIED }
    public final boolean weightAvailable; // 无可信重量的SOURCE_ERROR为false
    public final int weightState;       // 沿用现有稳定/不稳定/超重映射
    public final int weightGrams;       // 沿用当前SDK净重单位和符号处理
    public final int sdkDeclaredCodeLength;
    public final String sdkCode;        // 已被SDK trim,不宣称原始串口字节
    public final String normalizedCode;
    public final CodeKind codeKind;
    public final String reason;         // 固定原因码,不拼人员数据
    public final long hostSequence;     // 本应用观察序,不是设备帧序
    public final long callbackAtElapsedMs;
    public final Long readAtElapsedMs;  // 旧SDK为null
    public final Long sourceSequence;   // 旧SDK为null
    public final Long sourceAtMs;       // 旧SDK为null,不能混用时钟域
    public final Freshness freshness;   // 旧SDK为UNKNOWN
    public final Presence presence;     // 旧SDK为UNKNOWN
    // 构造器按上述字段赋值;拒绝负hostSequence。对象发布后不可修改。
}

SdkScaleObservationAdapter.from(int state, int grams, int declaredLen, String sdkCode, long hostSeq, long callbackAt)是纯Java入口,weightAvailable=true。另提供fromError(String reason, long hostSeq, long callbackAt):SOURCE_ERROR、weightAvailable=false、presence/freshness UNKNOWN、源元数据null;重量int仅占位,不得消费。Android层只传真实SDK公开字段。保持当前6字符规则,不新增全数字正则、不自动补齐/截断。分类次序冻结如下:

SDK数据对象缺失/SDK onError → SOURCE_ERROR(不生成伪重量0样本)
sdkCode为空或全边界空白 且 declaredLen>0 → INVALID_QR / DECLARED_NONEMPTY_BUT_BLANK
sdkCode为空或全边界空白 且 declaredLen==0 → SDK_REPORTED_NO_CODE
declaredLen<0 或含内嵌控制字符 → INVALID_QR / FIELD_OR_CONTROL_INVALID
其它非空码:只沿用当前传输边界trim;规范化后长度非6 → INVALID_QR / CODE_LENGTH_REJECTED
其它规范化6字符 → VALID_QR(管理码仍交原UnlockCodePolicy分流)

declaredLen != normalizedCode.length()本身不能判协议错误:SDK可能已trim字节。declaredLen=8,sdkCode="123456"可保留现有有效码兼容;该场景不证明设备采样新鲜。遇declaredLen=0但非空值也记录不一致诊断;在未确认厂商含义前保持既有六字符可读性,不凭空当空盘。

重量字段与码质量分开:有可信SDK重量字段时,即使码非法仍继续称重显示;码质量不能通过旧兼容回调再次退化成空字符串送入Home。HardwareListener新增默认 onScaleObservation(ScaleObservation o);兼容旧显示listener仅在weightAvailable时调用旧onWeightData。Home必须覆盖类型化入口,只执行一次重量处理和扫码路由。同步检查WeightingActivity、DebugWeigherPop。

Home新增handleWeightObservation(ScaleObservation o):无可信重量直接返回;有重量转四参handleWeightData(state,grams,code,allowPresenceInference)。保留原普通重量更新次序,仅将原1961–1978附近A02/A03/NoTrayPickDetector在位相关分支包在allowPresenceInference判断内。INVALID/SOURCE_ERROR时不运行这些分支,且对非null的NoTrayPickDetector调用resetBaseline,使无盘取菜候选不能跨异常区间累计。VALID/SDK_REPORTED_NO_CODE仍沿旧推断,A阶段尚未解决正常报空与物理离开的区别。

INVALID_QR/SOURCE_ERROR:清除普通/A03空候选、调用新增 A02TrayClaimCodePolicy.invalidateEmptyCandidate()清该策略候选,并对非null的NoTrayPickDetector调用resetBaseline,包括weightAvailable=false的SOURCE_ERROR。不得跨没有可信重量/码证据的间隔累计无盘取菜。不清lastQueriedTrayCode、不释放报警、不换身份、不清起始重量、不保存取餐。这是证据不连续处理,不是确认餐盘仍在。 SDK_REPORTED_NO_CODE在A阶段保持旧N策略,诊断明确“SDK报空,物理在位未知”;只有C01可替换旧离盘策略。

4. 请求与会话合同

新内核入口冻结为:QrSessionReducer.reduce(SessionSnapshot old, SessionEvent event, SessionPolicy policy) -> Transition。Transition含不可变snapshot和effect列表;旁观执行器只能记录,不能执行effect。

最小阶段:IDLE、ACTIVE、GAP_PENDING、CONFLICT、FINALIZE_PENDING、CLOSED。政策包含 mode=OBSERVE|LIVE、policyVersion、approvedBoundaryMode、可空的 gapWindowMs/exitWindowMs;OBSERVE缺参数允许输出不确定状态,LIVE缺参数必须拒绝启动,不用0代替。

当前/输入 新内核约束
ACTIVE/同二维码且连续性证据满足 保持epoch/起始重量;不再查人/新建记录
ACTIVE/SDK报空、质量丢失、UI解绑观测中断 GAP_PENDING;旧身份仅作历史,缺口重量待归属
任意/非法、来源错误、无新数据 标质量UNKNOWN,不将时间流逝当ABSENT
ACTIVE或GAP/B码或曾见B再回A CONFLICT;不覆盖起始重量,不因最终再见A自动归整段重量
GAP/再次A但没有完整连续性证据 可保留原会话,仍不自动确认缺口重量
新鲜可信在位ABSENT持续达批准规则 仅提出结束候选;身份/末重/夹子条件不足则待确认
明确结束候选重复、存库失败重试 相同finalizationId,不生成第二份本地记录

UI恢复和物理恢复分开。A05仅修无观测缺口的同实例返回展示;真实onStop缺口的视图/计量接管归C01。A阶段该路径保持旧行为并标为未解决,不能为了看起来连贯就恢复旧基线再自动归缺口重量。新内核不能给缺口补造身份;进程恢复依赖C03,不凭最后条码猜。

5. 夹子计量合同

设置保存与补偿独立。A04只修真实保存,不改默认20g、不替换现场实测夹重,不将其自动扣入订单。

toolState = ON_SCALE / OFF_SCALE / UNKNOWN
Adjustment evaluate(rawDelta, startState, endState, calibratedToolMass)
  任一状态UNKNOWN / 校准不可用 → UNRESOLVED
  已知且模型适用 → foodDelta = rawDelta - (startOn - endOn) * mass

真实应用中ON/OFF必须来自已确认传感/人工动作;不能凭重量接近夹重猜测。夹子在回位架之外,不等于在手里;可在盆里/搭边。湿夹/带菜/补菜/手压/部分承重使模型不成立时保留原始差与不确定原因。

6. 本地订单与兼容门禁

7. 只阻断相关步骤的明确门禁

Gate 缺少的具体输入 交付者/关闭证据 阻断什么 不阻断什么
G-SDK 新SDK/公开可测入口/帧与响应协议/修复范围 厂家;新版本SHA、变更说明、可运行注入测试或授权串口回放台 新包验收完成、旁路生产parser协议确认 A阶段、B01验收素材准备
G-PORT 实际串口配置、唯一owner、native安全close/reopen 厂家+硬件测试;设备FD/线程与重复开关测试 B02真实接管 纯解析/fake测试(选择旁路后)
G-PRESENCE 独立红外/区域状态、健康、新鲜度、覆盖区域 厂家+测试;字段说明和移动/拿走同步记录 宣称可靠物理离盘 旧回调异常防护、OBSERVE
G-POLICY 二维码单通道时结束依据,gap/exit时间,A→B→A归属,报警与模糊操作规则 产品+业务负责人;版本化SessionPolicy和测试oracle C01 LIVE A01–A06
G-TOOL 是否机械隔离,实际夹重,开始/结束承重观测,湿/带菜/搭边规则 产品+硬件+测试;四状态已知重量验证 C02自动补偿及不确定重量自动最终计量 A04保存修复
G-RELEASE 现场APK/目标分支、设备/固件、允许区域、时延/误差目标与样本量、升级回退 项目负责人+测试;真实动作/日志/订单验收与当次发布授权 C03发布、声称彻底修复现场 全部本地准备

这些是决策输入,不是让执行Codex在第一步就向用户重复问所有问题。先完成READY工作,记录门禁证据缺项;到相关步骤再集中交付需要确认的具体方案。截止日期当前未约定,不捏造。