熟食称重“先放后选”交互设计
1. 目标与冻结流程
把当前“先选或先称,再点加入购物车”改为唯一主路径,并保证每件商品都完成取走和归零闭环。
用户主动归零并等待稳定 → 放置商品 → 等待重量稳定 → 点击商品 → 直接加入购物车
→ 取走商品 → 用户主动归零 → 等待稳定零位 → 开始下一件
2. 已确认决策
- 采用独立状态机方案 B。
- 称重前必须收到有效、稳定、协议确认的零位数据。
- 正重量稳定后点击商品直接入车,不再二次点击“加入购物车”。
- 首次成功点击后立即锁定本轮,防止连续点击重复入车。
- 提示包含商品、重量、本次新增金额、取走商品和手动归零指引;同商品合并时不显示累计行金额。
- 实时价格布局不改,未选择商品时保持
0.00 元。 - 系统绝不自动归零;页面归零命令只能由用户明确点击归零入口触发。
- 称重未闭环时,定值商品、手动金额和结算入口暂时不可用。
- 串口重量、状态转换和 UI 更新统一在主线程处理。
- 默认拒绝去皮和非
kg单位;无活动称重周期时秤故障不阻断定值、手动金额及已完成订单结算。 - 账单打开和真正发起支付前都检查称重周期。
- 购物车、支付、订单和上传协议不变。
3. 范围与非目标
本次只调整称重熟食的操作顺序、状态判断、商品点击、提示、线程一致性和必要门禁,并补充状态机测试。
- 不改变定值商品的按份计价、数量加减规则。
- 不改变手动金额的累计、编辑和删除规则。
- 不改变购物车合并、账单核对、支付、离线记账及上传协议。
- 本期不增加“撤销最近一次入车”;误选仍通过现有购物车删除能力处理。
4. 状态机
| 状态 | 含义 | 商品点击结果 |
|---|---|---|
WAITING_SCALE | 尚未收到有效秤数据 | 提示设备未就绪 |
WAITING_ZERO_STABLE | 未确认稳定零位 | 提示保持秤盘为空、手动归零并等待稳定 |
READY_TO_PLACE | 稳定零位,可以放商品 | 提示先放置商品 |
WEIGHT_CHANGING | 正重量但尚未稳定 | 提示等待重量稳定 |
READY_TO_SELECT | 正重量且稳定 | 立即加入购物车 |
WAITING_REMOVE_ZERO | 本轮已入车,等待取走和用户归零 | 提示取走商品并手动归零,不重复入车 |
SCALE_ERROR | 无效、负重、去皮、单位异常、溢出、超时或断开 | 禁止称重入车;无活动周期时可继续定值流程 |
稳定零位
必须同时满足:数据有效、BCC 有效、未溢出、符号为正或零、单位为 kg、非去皮模式、快照未过期、isWeightStable() 为真、isZeroWeight() 为真,并且规范化业务重量为 0g。页面展示仅是输出,不参与状态判定。
状态转换
WAITING_SCALE → WAITING_ZERO_STABLE → READY_TO_PLACE
WAITING_ZERO_STABLE --有效但未稳定归零→ 保持当前状态
WAITING_ZERO_STABLE --用户主动归零→ 保持当前状态并等待新秤帧
READY_TO_PLACE → WEIGHT_CHANGING → READY_TO_SELECT
READY_TO_SELECT --点击商品并入车→ WAITING_REMOVE_ZERO
WAITING_REMOVE_ZERO --用户主动归零→ 保持当前状态并等待新秤帧
WAITING_REMOVE_ZERO --稳定零位→ READY_TO_PLACE
任意有效业务状态 --异常帧→ SCALE_ERROR
5. 点击即入车
- 检查支付会话未进行中,且状态严格为
READY_TO_SELECT。 - 读取同一份不可变重量快照。
- 先切换到
WAITING_REMOVE_ZERO,立即关闭本轮后续点击。 - 按商品和快照计算本次新增金额,新建或合并购物车行;合并时以购物车总额差值作为提示金额。
- 更新合计并提示:卤牛肉 250g 已加入,¥12.50;请取走商品并手动归零。
- 后续点击只提示取走商品并手动归零,不再次使用本轮重量。
- 入车成功和进入等待归零状态均不得自动调用归零命令。
若购物车写入发生异常,必须回滚并恢复到待选商品状态,不能留下半完成状态。
6. 页面行为
- 商品卡片保持可见;只有稳定正重量时点击才入车。
- 原“加入购物车”按钮隐藏,避免存在第二条入车路径。
- 实时价格不重做,未选择商品时继续显示 0.00 元。
- 入车后可在当前商品区展示最近加入商品,用户归零且收到稳定零位后清空。
- 没有活动称重周期时允许定值商品和手动金额;即使秤未就绪也可降级使用。
- 购物车有金额但秤上仍有未处理商品时,阻止结算,防止漏记。
- 账单核对弹窗确认支付时再次检查称重周期,防止弹窗打开后新增秤上商品。
7. 用户主动归零规则
- 系统只负责提示和监听,不在入车、重量回调、状态转换、定时任务或页面生命周期中主动归零。
- 页面归零命令只能由用户明确点击“归零”入口触发;使用秤体实体键时,应用仅监听结果。
- 入车后持续提示用户先取走商品,再主动归零;稳定零位前不解除本轮锁定。
- 命令发送成功不等于完成归零;只有收到新的、有效的、稳定的零位数据后才能进入下一轮。
- 归零命令失败时保持当前状态并提示重试;连续点击不得并发发送多条命令。
本方案不再规定“任何非零读数都禁止发送归零命令”。是否允许特定重量下归零,由用户主动操作和秤体自身安全能力共同决定。
8. 线程一致性
串口先按协议边界组装完整帧,再生成带规范化整数克、单位、去皮、稳定、零位和采集时间的不可变重量快照;重量字段、状态转换、商品点击和 UI 刷新统一在主线程执行。过期快照不能入车,状态机不引用 Activity、View、数据库或串口对象。
9. 验收重点
- 稳定零位前不能入车;启动时秤上有物必须先取走、由用户主动归零。
- 零重量点击商品提示先放商品;不稳定时提示等待稳定。
- 稳定后点击一次立即入车,提示商品、重量、本次新增金额、取走商品和手动归零。
- 同一轮连续点击多个商品,购物车只能增加一次。
- 系统不会自动归零;用户未归零或尚未收到稳定零位时不能开始下一轮。
- 负重量、溢出、无效帧和校验失败不能变成可选商品状态。
- 去皮、非 kg 单位和过期快照不能入车;无活动周期时秤故障不阻断定值流程。
- 账单打开后再放商品,确认支付必须被二次检查拦截。
- 称重未闭环时,定值、手动金额和结算均被阻止。
- 稳定归零后,定值、手动金额、购物车和支付行为保持原样。
10. 测试设计
- 状态机:覆盖初始无数据、稳定零位、重量变化、稳定待选、重复点击、等待取走、用户归零、归零失败、异常帧及恢复。
- 业务回归:覆盖 250g/500g/750g 计价、同商品跨轮累计、不同商品分行,以及定值和手动金额。
- 真机验收:覆盖慢放、快放、抖动、连续点击、取走一半、未手动归零、归零重复点击、启动压秤、断开恢复及完整支付链路,并确认系统不会自动发送归零命令。
11. 影响范围
代码变更预计集中在 CookedFoodActivity、新增纯状态机类、熟食布局、字符串/语音资源和单元测试。数据库、订单明细与支付协议不变。
12. 风险与回滚
- 零位标志可能因设备固件不同而有差异,必须用真实秤帧验证,不能只靠展示重量兜底。
- 商品点击与串口回调可能竞争,通过主线程单写和不可变快照规避旧重量入车。
- 直接入车后的误选首版依赖清晰成功提示和现有购物车删除,不增加二次确认。
- 用户可能忘记手动归零,页面必须持续提示并保持称重周期锁定,系统不得代替用户归零。
- 用户可能连续点击归零入口,通过请求进行中锁避免命令并发。
- 状态机只影响称重熟食入口;购物车模型和支付协议未变,可回退页面交互且无需迁移数据。
13. 书面复核
方案 B 及确认项已写入本文。归零口径为:系统不自动归零,只提示用户;归零必须由用户主动触发;收到新的有效稳定零位后才进入下一轮。代码已按本设计实施,并通过 90 个 JVM 单元测试和 Debug 构建;真实秤体协议、快照超时及刷卡、扫码、人脸支付二次门禁仍待真机验收,验收结果以对应变更记录为准。