MultipurposeMachine · Requirement & Solution

“我的营养”付款码扫码识别需求与接入方案

按接口 v1.2 统一人脸与付款码身份识别,覆盖双设备扫码、已配对设备编号校验、营养数据闭环和交付门禁。

UC-NUTRITION-001人脸 + 扫码接口 v1.2方案 v0.4
请求体paycode + equipment_code
请求头无需 account / pwd / key
21.5 寸已实现并连接串口
27 寸代码已实现 · 待真机

“我的营养”付款码扫码识别需求与接入方案

项目:MultipurposeMachine

文档版本:v0.4

日期:2026-09-17

状态:开发中(双设备代码已实现,27 寸待真机验收)

DoR:双设备客户端切片已放行;27 寸发布验收仍 BLOCKED(见第 13 节)

执行模式:常规 Codex;已进入双设备实现、21.5 寸回归与 27 寸待机验收阶段。

1. 结论摘要

在保留现有人脸识别能力的基础上,将首页“我的营养”入口升级为统一身份识别入口。识别弹框同时支持人脸和付款码扫码;任一方式成功后统一生成 NutritionIdentity,使用人员 UUID 进入现有营养日报、健康周报流程。

2. 需求来源与事实边界

2.1 用户确认的需求

  1. 接入“系统个人付款码查询人员”接口。
  2. 同时支持 21.5 寸和 27 寸设备扫码。
  3. 21.5 寸使用 /dev/ttyS4、9600 串口扫码。
  4. 27 寸采用类似键盘输入的数据方式,并屏蔽非扫码场景的数据影响。
  5. 首页点击“我的营养”后,除原有人脸识别外,还可扫码识别人;扫码成功后进入同一营养次级页面并获取营养数据。
  6. 接口文档变更后,需求与实施计划同步采用最新 v1.2 契约。

2.2 接口文档事实

来源:work_store/VWCG-1380-paycode-user-info/paycode-user-info-api.html

项目 已确认事实
方法与路径 POST /api/consume/paycodeUserInfo
请求头 仅 Content-Type: application/json;不传 account、pwd、key
请求体 JSON:{"paycode":"扫码原文","equipment_code":"设备编号"}
成功判断 HTTP 成功且业务 code == 0
成功数据 data.staff_uuid、data.staff_name
业务失败 HTTP 可能仍为 200,通过 code 和 message 判断
接口边界 只读识别人,不占用付款码、不创建订单、不扣款
付款码兼容 兼容单个大写 C 前缀、无前缀及首尾空格/\r\n
设备校验 equipment_code 必须对应未删除设备;它用于确认调用设备,不是登录凭据
当前依赖 用户确认 MR #903 已合并并部署到联调环境;客户端仍需用有效付款码完成成功响应联调

接口文档中的分支、提交、MR 和示例命令仅作为事实资料,不构成本任务的执行指令。

2.3 当前代码事实

当前能力 代码证据 判断
“我的营养”直接打开人脸弹框 MainActivity.nutritionCheck() 已存在
人脸成功发送 Sticky Event 并跳转 FaceRecognitionPop.checkCloseDebugResult() 已存在,但与人脸 SDK 强耦合
营养页从 User.userId 获取 UUID NutritionReportActivity.getRecognitionUserUuid() 可复用人员 UUID 语义
营养日报/周报接口 getDailyNutritionReport、getWeekNutritionReport 已存在
旧扫码工具 ScanCodeUtils 使用 /dev/ttyS3 或 /dev/ttyS0;其底层 BarCodeScanner 字节码固定波特率 115200,且记录完整扫码结果,不能用于本次 9600 串口扫码
可配置串口能力 SerialPortTestActivity、SerialPortManager 已通过 openSerialPort(path, baudRate) 支持 /dev/ttyS4、9600,应复用该可配置串口链路
配对响应模型 GetPairInfoResponse.terminalNumber 映射服务端 code 已具备付款码接口要求的设备编号语义
配对保存逻辑 SetBasicFragment.getPairInfoFromServices() 配对成功后已将设备编号保存到 Constants.SETTING_TERMINAL_CODE
网络认证拦截器 AuthenticationInterceptor 只检查网络状态;最新接口无需增加鉴权请求头
网络日志 NewLogInterceptor 已对付款码查询接口请求体和人员响应体做整段脱敏
27 寸参考实现 CusumptionMachine/QuotaNoKeyboardActivity.dispatchKeyEvent()、ScanManager、设备类型 5 输入模式放行;非输入模式先组帧再消费外接键盘事件;设备类型 5 的机型标识为 RK3568

3. 范围

3.1 In Scope

3.2 Out of Scope

4. 用户体验与业务流程

4.1 推荐交互

点击“我的营养”后打开统一身份识别弹框:

4.2 为什么采用并行识别

方案 优点 缺点 结论
人脸与扫码同时可用 操作最少,不改变老用户路径 需要一次性状态防止双重成功 推荐
先选择“刷脸/扫码” 状态简单 多一次点击,降低效率 不采用
扫码优先、超时后人脸 减少并行竞争 改变现有人脸体验,等待感明显 不采用

5. 总体架构

首页“我的营养”
        ↓
NutritionIdentityDialog / 统一身份识别协调器
        ├── FaceIdentityProvider
        │      └── 人脸 User → NutritionIdentity
        └── ScannerController
               ├── SerialScannerController(21.5 寸)
               └── KeyboardWedgeScannerController(27 寸)
                            ↓
                 PaycodeUserInfoRepository
                            ↓
             POST /api/consume/paycodeUserInfo
                            ↓
        NutritionIdentity(staffUuid, staffName, source)
                            ↓
               NutritionReportActivity
                    ├── 营养日报
                    └── 健康周报

5.1 模块职责

模块 职责 不负责
NutritionIdentityDialog 协调人脸、扫码、加载态、错误态和一次性跳转 不解析具体串口协议
ScannerController 定义开始、停止、结果、错误的统一契约 不调用人员查询接口
SerialScannerController 打开 /dev/ttyS4、按 9600 接收和组帧 不处理页面跳转
KeyboardWedgeScannerController 在身份弹框活动时注册键盘扫码监听,缓存到 Enter 并输出付款码 不负责页面跳转和人员查询
KeyboardInputPolicy / KeyboardWedgeScannerHub 27 寸机型在非输入场景消费外接键盘事件;输入控件聚焦和音量键放行 不通过字符速度猜测业务身份
PaycodeUserInfoRepository 读取已配对设备编号,提交付款码和 equipment_code,解析业务结果 不保存完整付款码日志
NutritionIdentity 承载统一人员身份与来源 不依赖百度人脸 SDK User

6. 设备与扫码适配方案

6.1 设备档案

设备 连接方式 固定参数 启停边界 当前状态
21.5 寸 串口 /dev/ttyS4、9600 身份识别弹框 Resume/Stop rockchip / YF_029E / Android 11 已连接并验证串口打开
27 寸 USB/HID 键盘 参考项目设备类型 5:机型包含 RK3568;500ms 组帧超时、Enter 结束 输入场景放行;非输入场景消费;身份弹框可组帧 代码已实现,待真机验证

不得用屏幕像素或 DPI 直接推断扫码方式。当前 ScannerModeResolver 先精确匹配 21.5 寸 rockchip / YF_029E,再匹配 27 寸参考机型标识 RK3568;未知型号采用失败关闭策略,仅保留人脸识别。

6.2 21.5 寸串口规则

  1. 串口路径和波特率定义为设备档案常量或配置,不散落硬编码。
  2. 复用项目已有 SerialPortManager.openSerialPort(path, baudRate),明确传入 /dev/ttyS4、9600;不得复用内部固定 115200 的 BarCodeScanner。
  3. 只在身份识别弹框活动时打开串口;暂停、关闭、跳转时释放。
  4. 过滤 \r、\n、首尾空白;空结果不发起接口请求。
  5. 打开失败时保留人脸识别,提示“扫码设备不可用,可继续刷脸”。
  6. 不记录完整扫码原文;诊断仅记录长度、来源、时间和脱敏摘要。

6.3 27 寸键盘扫码规则

  1. 以参考项目设备类型 5 的机型标识 RK3568 选择键盘扫码模式;未知机型不启用拦截。
  2. 身份识别弹框活动时,缓存数字、字母和常见符号;收到 Enter 或数字键盘 Enter 后输出一次完整扫码结果。
  3. 相邻按键超过 500ms 时清空不完整帧;超过 1024 字符的异常帧整帧丢弃,不截取尾部发起查询。
  4. 当前焦点为 EditText 或文本编辑器时放行外接键盘事件,保留设置、密码等输入能力。
  5. 非输入场景消费外接键盘事件,身份弹框内同时交给组帧器;弹框外不组帧、不调用接口、不触发按钮。音量键保持系统分发。
  6. 该方案按“设备机型 + 输入场景”隔离,不区分同一 27 寸设备上的扫码枪与人工外接键盘;因此人工键盘在非输入场景同样被消费,这是参考实现的明确边界,必须通过 27 寸真机回归确认。

7. 设备编号来源与校验方案

7.1 复用现有配对结果

最新接口不执行 checkApiAuth,不需要 account、pwd、key,也不需要扩展 /p/api/matecode 下发新的 api_auth。Android 端直接复用已有配对链路:

/p/api/matecode 响应 code
        ↓ GetPairInfoResponse.terminalNumber
        ↓ SetBasicFragment 配对成功保存
Constants.SETTING_TERMINAL_CODE
        ↓
paycodeUserInfo 请求体 equipment_code

约束:

7.2 设备编号不可用时

8. 付款码人员接口接入

8.1 Android 契约

POST /api/consume/paycodeUserInfo
Content-Type: application/json
Body: { "paycode": "扫码原文", "equipment_code": "已配对设备编号" }

成功必须满足:

  1. 请求完成且响应可解析。
  2. code == 0。
  3. data.staff_uuid 非空。
  4. data.staff_name 非空。

成功后转换:

staff_uuid → NutritionIdentity.staffUuid
staff_name → NutritionIdentity.staffName
source     → PAYCODE

8.2 失败映射

场景 用户反馈 系统行为
付款码不存在/已使用/已过期 展示服务端 message 恢复扫码和人脸识别
人员不存在 展示“未找到人员,请联系管理员”或服务端信息 不进入营养页
本地设备编号为空 “设备编号未配置,请联系管理员重新配对” 不发请求,保留人脸识别
服务端设备不存在 “当前设备未登记,请联系管理员检查配对” 不自动替换编号,记录脱敏诊断
网络超时/不可用 “网络异常,请稍后重试” 允许用户重扫或继续刷脸
响应缺少人员字段 “人员数据异常,请联系管理员” 按协议异常记录
重复扫码 不重复提示 请求处理中忽略后续结果

9. 身份统一与营养页接入

建议新增与具体识别技术无关的身份对象:

NutritionIdentity
- staffUuid: String
- staffName: String
- source: FACE | PAYCODE

10. 并发、状态与生命周期

10.1 状态机

IDLE
  ↓ 弹框可见
LISTENING
  ├── 人脸成功 ───────────────┐
  └── 扫码完成 → QUERYING     │
                    ├── 失败 → LISTENING
                    └── 成功 ─┤
                               ↓
                            RESOLVED
                               ↓
                           NAVIGATING

10.2 规则

11. AI 可执行 Use Case

11.1 Use Case 概要

字段 内容
Use Case ID UC-NUTRITION-001
名称 用户通过人脸或付款码查看个人营养报告
主要角色 一体机用户
次要角色/系统 MultipurposeMachine、设备配对服务、付款码人员服务、营养服务、扫码硬件
优先级 P0
状态 开发中(21.5 寸首批)
DoR 21.5 寸放行;27 寸 BLOCKED
成功后置 已确定唯一人员 UUID,营养页展示该人员的日报或周报
失败后置 不进入营养页,不改变付款码、订单、余额和人员数据

11.2 前置条件

ID 条件 不满足时处理
PRE-01 设备已完成基础配对并有 API 地址 引导管理员完成配置
PRE-02 人脸模型可用,或扫码硬件至少一种可用 无可用识别方式时阻止进入并提示
PRE-03 使用扫码时,本地 SETTING_TERMINAL_CODE 非空 保留人脸,扫码提示重新配对
PRE-04 付款码人员接口已部署到目标环境 联调前阻断扫码发布

11.3 主流程

步骤 角色 动作与系统响应 数据/状态 AC
M1 用户 点击首页“我的营养” 创建识别会话 AC-001
M2 系统 打开统一身份识别弹框,启动人脸和当前设备扫码控制器 LISTENING AC-002
M3 用户 展示本系统个人付款码并扫码 获取扫码原文 AC-003/004
M4 系统 规范化结束符,读取已配对设备编号,锁定重复输入并调用人员查询接口 QUERYING AC-005
M5 人员服务 返回 code == 0、staff_uuid、staff_name 创建 NutritionIdentity AC-006
M6 系统 停止人脸和扫码,进入营养报告页 RESOLVED → NAVIGATING AC-007
M7 营养服务 以 staff_uuid 查询并展示营养日报/周报 不修改人员或付款码 AC-008

11.4 分支流程

分支 条件 行为 返回/结束 AC
A1@M3 用户通过人脸识别成功 将人脸 User 转为 NutritionIdentity,停止扫码 进入 M6 AC-009
A2@M2 串口扫码不可用 显示扫码不可用,保持人脸识别 返回 M2 AC-010
A3@M5 用户切换日报/周报或日期 使用同一 staff_uuid 重新获取数据 留在营养页 AC-011

11.5 异常与恢复

异常/恢复 条件 用户提示 数据处理 AC
E1@M4 / R1 设备编号为空或服务端判定设备不存在 设备编号未配置或当前设备未登记 本地为空不发送;服务端失败不自动换号;恢复人脸 AC-012
E2@M4 / R2 网络超时、断网 网络异常,请稍后重试 不落业务数据;恢复监听 AC-013
E3@M5 / R3 付款码不存在、已使用、已过期 展示服务端信息 不进入营养页;允许刷新后重扫 AC-014
E4@M5 / R4 人员不存在或响应字段为空 人员数据异常 不进入营养页;允许重试 AC-015
E5@M3 / R5 连续扫码或人脸与扫码同时成功 无重复提示 只接受首个成功身份 AC-016
E6@任意步骤 用户返回、页面销毁或切后台 无额外提示 关闭全部资源,迟到回调失效 AC-017

12. 业务规则、验收与测试

12.1 业务规则

规则 内容
BR-001 人脸和扫码是等价的身份来源,后续营养数据均以人员 UUID 查询。
BR-002 扫码人员接口必须携带当前环境配对后保存的 equipment_code,不得从型号、序列号或 MQTT 配置推导。
BR-003 付款码查询只读,不占用付款码、不创建订单、不扣款。
BR-004 27 寸扫码枪事件只在身份识别弹框内组帧;其他场景必须消费且不产生业务动作。
BR-005 同一识别会话只允许一个成功结果和一次页面跳转。
BR-006 日志不得包含完整付款码,也不得把设备编号与人员 UUID、姓名组合记录。
BR-007 未识别设备型号不得尝试猜测串口路径。

12.2 验收标准

AC Given When Then
AC-001 应用在首页 点击“我的营养” 只打开一个统一身份识别弹框
AC-002 弹框可见 无用户操作 人脸可识别,适配的扫码控制器已启动
AC-003 21.5 寸样机已连接扫码枪 扫描付款码 从 /dev/ttyS4、9600 得到一次完整扫码结果
AC-004 27 寸样机已连接扫码枪 扫描付款码 以 Enter 结束组帧,字符不进入页面控件
AC-005 扫码结果有效且设备已配对 发起人员查询 只发送一次请求,请求体同时包含 paycode 和当前 equipment_code,且不发送 account/pwd/key
AC-006 接口返回成功 解析响应 UUID、姓名与响应完全一致
AC-007 已得到身份 跳转 只进入一次营养页并展示人员姓名
AC-008 营养页已获得扫码身份 加载日报/周报 营养接口使用同一 staff_uuid
AC-009 人脸识别成功 进入营养页 原有人脸流程不回归
AC-010 串口打开失败 进入识别弹框 显示扫码不可用,人脸仍可使用
AC-011 已识别人员 切换日期、日报或周报 始终使用当前人员 UUID
AC-012 本地设备编号为空或服务端返回设备不存在 用户扫码 不自动猜测或替换设备编号,提示管理员重新配对或检查设备状态
AC-013 网络断开或超时 用户扫码 不跳转,可重试或改用人脸
AC-014 付款码过期/已使用/不存在 接口返回失败 展示对应信息并允许重扫
AC-015 人员字段缺失 接口返回 按协议异常处理,不进入营养页
AC-016 连续扫码或双通道同时成功 回调到达 只认定一个身份、只跳转一次
AC-017 弹框关闭或页面离开 后续扫码/网络回调到达 不调用接口、不跳转、不泄漏资源
AC-018 任意非扫码页面 使用 27 寸扫码枪扫码 不写入输入框、不触发按钮、不调用人员接口
AC-019 检查应用日志 完成成功和失败用例 无完整付款码、人员 UUID/姓名,也没有不存在的 account/pwd/key 兼容逻辑

12.3 测试矩阵

Test ID 层级 覆盖 目标
UT-001 单元 扫码原文规范化 正确处理空白、\r、\n、空值
UT-002 单元 身份状态机 首个成功生效,重复回调无效
UT-003 单元 设备档案选择 型号映射、人工覆盖、未知型号失败关闭
UT-004 单元 键盘扫码组帧 可打印字符、Enter、退格、超时清空
API-001 契约 已配对设备编号来源 配对成功保存、为空、切换 API 清除和重新配对更新
API-002 契约 paycodeUserInfo 成功、缺少设备编号、设备不存在、付款码过期/已使用/不存在、人员不存在
IT-001 集成 扫码 → 人员 → 营养日报 UUID 全链路一致
IT-002 集成 扫码 → 人员 → 健康周报 UUID 全链路一致
E2E-001 真机 21.5 寸串口 /dev/ttyS4、9600、重复扫码、拔插和生命周期
E2E-002 真机 27 寸 HID 识别弹框组帧、输入控件可输入、非输入场景屏蔽
E2E-003 真机 人脸回归 人脸识别、人员姓名、日报/周报正常
SEC-001 安全 日志与配置 无完整付款码和人员结果明文组合;不新增鉴权凭据存储
PERF-001 性能 扫码到人员识别 测试环境 P95 ≤ 2 秒;超时策略与现有网络层一致,最终阈值待联调确认

13. DoR 与 P0 待确认项

双设备客户端切片均已实现。27 寸实现依据用户指定的参考项目设备类型 5:机型包含 RK3568 时启用键盘模式,并复用其“输入场景放行、非输入场景消费”的边界。由于当前 27 寸设备无法连接,发布验收仍保持阻塞。

ID 待确认项 责任方 关闭证据
Q-001 27 寸真机 Build.MODEL 是否包含参考标识 RK3568 Android/硬件 27 寸样机读取记录
Q-002 27 寸键盘扫码的字符集、Enter 结束符和输入/非输入场景行为是否与参考一致 Android/硬件厂商 真机连续扫码与页面按键验证
Q-006 测试环境性能阈值和网络超时时间是否接受本方案建议 产品/测试/Android 验收评审结论
Q-007 云效父需求编号、项目和责任人 产品负责人 工作项正文包含本 UC 并完成 CLI/OpenAPI 回读

已关闭事实:

14. 实施切片建议

当前实施进度:

  1. 契约切片(已完成):已冻结 paycode + equipment_code 契约、设备编号来源、响应模型与契约测试。
  2. 身份切片(已完成):已引入 NutritionIdentity,营养页改为显式 Intent 参数,餐单打印保留原 Sticky Event。
  3. 21.5 寸切片(已完成代码与启动验证):已实现串口控制器、CR/LF/突发分帧、生命周期释放;样机显示扫码器连接成功。
  4. 接口切片(已完成代码):已接入人员查询、设备编号校验、失败恢复和请求/响应日志脱敏。
  5. 21.5 寸业务 E2E(待现场扫码):需要有效付款码验证成功响应、营养页跳转及连续扫码。
  6. 27 寸切片(已完成代码,待真机):已实现 RK3568 模式解析、500ms/Enter 键盘组帧、最大帧保护、输入场景放行、非输入场景屏蔽及弹窗独立 Window 按键接入。

共享接口和设备契约冻结后再并行;同一工作树只保留一个代码写入者。

15. 发布、回滚与风险

15.1 发布门禁

15.2 回滚策略

15.3 主要风险

风险 影响 控制措施
27 寸同机扫码枪与人工键盘不区分 人工键盘在非输入场景也被消费 仅对 RK3568 机型启用;输入控件聚焦时放行;真机确认与参考项目行为一致后发布
串口读取分包或结束符与预期不一致 21.5 寸扫码结果截断或粘包 使用共享组帧器并在真机验证 CR/LF、分包、连续扫码
设备编号为空、过期或来自错误环境 接口返回设备不存在,扫码不可用 只读取当前配对保存值;切换 API 时清除;不猜测或自动替换
人脸与扫码竞态 重复跳转、人员错配 会话状态机和首个成功锁
Sticky Event 残留 使用上一次人员身份 明确 Intent 身份参数,消费并清理旧事件
接口环境与客户端实际契约不一致 客户端完成但无法联调 使用有效付款码做成功/失败样例回读作为发布门禁

16. 追踪矩阵

UC/步骤 页面/组件 API/事件 数据 规则 AC Test
M1-M2 首页、统一身份弹框 页面导航 识别会话 BR-005 AC-001/002 UT-002、E2E-003
M3 两类扫码控制器 串口/HID KeyEvent 付款码原文 BR-004/006/007 AC-003/004/018/019 UT-001/003/004、E2E-001/002、SEC-001
M4-M5 人员查询仓库 paycodeUserInfo paycode、equipment_code、staff_uuid/name BR-002/003/005 AC-005/006/012-016 API-001/002、IT-001/002
M6-M7 营养报告页 日报/周报接口 NutritionIdentity BR-001/005 AC-007-011/017 IT-001/002、E2E-003

17. 评审结论

本方案已经按接口文档 v1.2 完成双设备客户端实现:21.5 寸使用 /dev/ttyS4、9600 串口;27 寸参照 CusumptionMachine 设备类型 5,按 RK3568 机型启用键盘扫码,在输入控件聚焦时放行、非输入场景消费,并在身份弹框内以 Enter 完成组帧。人员接口、统一身份、生命周期与敏感日志控制已接通。当前 21.5 寸样机已验证串口可连接且应用无崩溃;发布前仍需有效付款码业务 E2E、连续扫码、人脸回归,以及 27 寸真机扫码和输入隔离验收。