docs/:系统梳理、架构优化方案、CR-02 三端功能设计修订(含 §15 三端一致性同步与平台统计分析)、公寓端/应用端/平台端分册、公寓统计分析与排名报表、公寓端小步迭代开发计划(IT-0~IT-8) dev/:RuoYi-Vue-master(后端)与 RuoYi-Vue2-master(前端)官方上游基线,未改动
51 KiB
12 消费、充值与菜品管理
文档编号:
docs/公寓端/12。性质:设计稿(Design Only),不是接口契约冻结稿;阶段二契约冻结评审通过后才落 DDL。 落地权威表述:本文件是「人员消费、人员线下充值、公寓菜品管理」三项业务流程的唯一权威表述(CF-0112-34、图 12-1CF-03、表 12-112-4);口径裁决、流程增量、施工单与验收登记见17 + 23~25)、契约CR-02 §14。 上位依据:docs/10-架构优化方案-三端拆分.md、docs/backend/05-数据模型.md§4 / §6、docs/backend/04-核心业务流程.md§6、docs/设计变更/03-三端功能设计修订(CR-02).md(A-04、CR-C03、表 3-15LEDGER_OFFLINE、表 3-20offlineQuota、表 3-30apt_ledger_offline、F-D)、docs/平台端/01表 C-1、docs/平台端/03表 3-7、docs/平台端/04表 4-9、docs/公寓端/03表 3-6。 硬约束:跨端仍为 **20 个接口编号(1v2-draft、平台不回调公寓;不新增公寓本地业务表(仍 59 张)、不新增跨端编号、不新增bizType;apt.ledger.local是公寓本地账务唯一写入模块,platform-ledger是平台余额主账唯一写入口;金额一律decimal(12,2)/ 字符串两位小数、扣减用负数、禁止浮点。 CR-02 §15 关联(三端一致性 / 统计数据源):本文件仍是三流程唯一权威表述;三端状态映射与数据同步机制以 CR-02 §15 表 3-66 为唯一权威,四能力业务规则 / 交互 / 权限 / 异常三端对照见 §15 表 3-65。平台docs/平台端/10以gyid分类统计时,本流程 #10LEDGER_OFFLINE的拆分字段(allowanceAmount/cashAmount/rechargeChannel)分别供补贴消费与现金充值(计为收入)**取数;评价情况与房间利用周转另由 #13ARCHIVE摘要payload分组计数提供(docs/公寓端/09§0 表 C-9)。统计结果不回写本流程任何状态,本端不新增上报接口与bizType;变更点与影响范围见 CR-02 §15 表 3-63。
表 12-1 文档信息
| 项 | 内容 |
|---|---|
| 范围 | CF-01 人员消费、CF-02 人员线下充值、CF-03 公寓菜品管理;覆盖完整流程、状态流转、数据结构、接口定义(请求 / 响应字段)与异常场景 |
| 本端职责 | 消费终端受理与额度查询、apt.ledger.local 双额度 CAS 扣减、线下充值申请与管理员核对、apt-report #10 上报、食谱只读下发与终端选单 |
| 协同端 | 平台端(platform.ledger 余额主账与日对账、platform.catering 食谱主数据)、应用端(管理员核对入口) |
| 编号规则 | 功能项 CF-01CF-03;表 表 12-y、图 图 12-y 篇内连续;端内接口 N-48N-58(承接 docs/公寓端/11 N-47);决策点 Q-41Q-45;裁决 CR-C31CR-C35、流程 F-L / F-M / F-N(见 CR-02 §14) |
| 术语 | 表名一律小写(apartment/db/mysql/*.sql 实际写法),平台库沿用大写存量表名;枚举全大写下划线;时间统一 ts_ms / ISO-8601 带时区 |
表 12-2 修订记录
| 版本 | 日期 | 修订内容 |
|---|---|---|
| v1.0 | 2026-11-10 | 首次形成:三项流程端到端说明、四条状态链、双额度扣减规则、复用表与补列结构、N-48LEDGER_OFFLINE 扩展字段、异常与权限、Q-41 |
1. 定位与边界
1.1 三项流程与三端职责
表 12-3 流程、主体与落点
| 功能项 | 用户动作 | 主责端 / 模块 | 协同端 | 主要落表 | 跨端编号 |
|---|---|---|---|---|---|
| CF-01 人员消费 | 食堂选餐 → 消费终端添加菜品显示金额 → 查补贴/现金额度 → 补贴优先扣款、不足部分现金补足 → 上报公寓端 → 同步平台落账户 | 消费终端 → apt.ledger.local → apt-report |
平台端(platform.ledger) |
gy_ykt_remainder、gy_ykt_consume、apt_ledger_offline、apt_report_outbox |
#10(LEDGER_OFFLINE)、#1 |
| CF-02 人员充值 | 扫公寓线下二维码 → 签到终端点"现金充值"输入金额 → 管理员核对 → 计入本人现金充值额度 → 保存公寓端现金额度 → 同步平台落账户 | 签到终端 → 值班台 / 管理端 → apt.ledger.local → apt-report |
平台端(platform.ledger) |
gy_ykt_cash_request、gy_ykt_xjczjl、gy_ykt_remainder、apt_ledger_offline |
#10(LEDGER_OFFLINE)、#1 |
| CF-03 公寓菜品管理 | 公寓管理员发布阶段性菜品食谱 → 消费人员在消费终端查看菜品 → 点击加入选择菜单;预留远程点菜入口 | 公寓管理员 → platform.catering → apt.catering(只读下发) → 消费终端 |
平台端(食谱主数据) | YKT_MEALS(平台库,补列)、YKT_MEALS_ORDERING / YKT_MEALS_ORDERS_MX(预留) |
#4(recipes[])、#1(features.remoteOrdering) |
1.2 与既有流程的关系
- 账务闭环不重写:CF-01 / CF-02 的"离线受理 → 本地余额立即可用 → 先派 outbox → #10 逐条上报 → 平台归日 → 日对账通过置
RECONCILED"完整沿用 CR-02 F-D 六步;本文件只补齐"设备侧选菜 / 扫码申请 / 管理员核对"三个前置环节与字段。 - 主账唯一写入口不变:
platform-ledger是GY_YKT_REMAINDER与PL_LEDGER_FLOW的唯一写者;platform-catering与消费终端对余额表零写权限(docs/平台端/01D1、docs/平台端/03)。 - 菜品/订单表不重写:复用
YKT_MEALS/YKT_MEALS_ORDERING/YKT_MEALS_ORDERS_MX/PL_CATERING_SETTLE(docs/平台端/03表 3-7)与/platform/v1/admin/catering(docs/平台端/04表 4-9);"阶段性食谱"以YKT_MEALS补列实现,不新增表。 - 注册与入住不重写:充值需校验人员有效在寓与注册态,沿用
docs/公寓端/10§4 与docs/公寓端/11§4;本文件不新增人员核验链路。 - 远程点菜不实现:仅保留特性开关与接口入口(本期恒关闭),见 §6.4。
1.3 不变式
- 跨端 20 个编号、方向、路径不变;新增数据只走既有
payload+ 既有bizType(LEDGER_OFFLINE已占 #10,CR-02 表 3-12)。 - 平台零回调;#1 / #4 由公寓 GET 拉取,#10 由公寓 POST 上行;#16 日对账本期只提供公寓可主动调用形态(P-02)。
- 公寓本地账务表唯一写入模块是
apt.ledger.local(docs/公寓端/03表 3-6);设备侧只提交请求,不直改余额。 GY_YKT_REMAINDER是唯一余额表,不新造同义账户表;gy_card全库不存在,卡号沿用GY_YKT_RY.card_id。GY_YKT_CASH_REQUEST.request_state沿用三值PENDING/COMPLETED/CANCELLED,且申请表本身不存金额(金额落apt_ledger_offline.amount)。offlineScope三值LOCAL_ONLY/SYNCED/RECONCILED沿用 CR-02 表 3-15 / 表 3-21;LOCAL_ONLY不是匹配状态。- 幂等
idempotencyKey = hash(gyid + bizType + bizId + bizVersion);bizId = flowId、业务 UK(gyid, type, flow_id);归日以serverReceivedAt为准;count按biz_id去重(R-C3)。 - outbox 是队列、
apt_report_history是终态流水,两者语义不互换(CR-C10);本流程的跨端上报态一律沿用 CR-02 表 3-21 的reportState正式五值PENDING/SENT/FAILED/DEAD/RETRIED(outbox 与 history 共用),本地 outbox 内部状态机仍按docs/公寓端/03表 3-11。 - 设备侧一律主动拉取;抓拍、录音、签到现场图不出公寓;
gyid只从凭据解析、所有 SQL 必带gyid。 - 仅线下充值:不接任何线上支付渠道、不产生支付订单与回调;扫公寓线下二维码只是公寓识别与入口引导,不构成资金流。
2. 状态流转总览
图 12-1 三项流程数据流向
flowchart LR
subgraph DEV["公寓内设备"]
CT["消费终端 CONSUME<br/>查看食谱 · 选单 · 显示金额"]
CK["签到终端 CHECKIN<br/>扫码 · 现金充值申请"]
end
subgraph AP["公寓服务系统"]
QUO["apt.ledger.local<br/>额度查询 / 双额度 CAS 扣减"]
REQ["gy_ykt_cash_request<br/>充值申请与管理员核对"]
LED["gy_ykt_* / apt_ledger_offline<br/>本地账务流水"]
OUT["apt-report / outbox"]
CAT["apt.catering<br/>食谱只读下发 / 水位"]
end
subgraph DUTY["值班台 / 管理端"]
ADM["管理员核对现金充值"]
RCP["公寓管理员发布食谱"]
end
subgraph PL["平台端"]
CATM["platform.catering<br/>YKT_MEALS 食谱主数据"]
ING["platform-ingest<br/>#10 接收"]
LEDM["platform-ledger<br/>GY_YKT_REMAINDER / PL_LEDGER_FLOW"]
end
RCP -->|发布| CATM
CATM -->|#4 recipes[](拉)| CAT
CAT -->|CF-03 N-48 食谱| CT
CT -->|CF-01 N-49 额度查询| QUO
CT -->|CF-01 N-50 消费提交| QUO
QUO -->|补贴优先 + 现金补足| LED
CK -->|CF-02 N-52 充值申请| REQ
REQ -->|待核对| ADM
ADM -->|N-54 确认| REQ
REQ -->|计入现金额度| LED
LED -->|#10 LEDGER_OFFLINE| OUT
OUT --> ING
ING --> LEDM
LEDM -->|日对账结论| OUT
表 12-4 四条状态链(互不替代)
| 链 | 字段 / 载体 | 取值 | 唯一写入口 | 说明 |
|---|---|---|---|---|
| 消费流水态 | apt_ledger_offline.ledger_state(补列,流程态) |
SUBMITTED / DEDUCTED / REPORTED / RECONCILED / REJECTED / REVERSED |
apt.ledger.local |
描述一笔消费从终端提交到平台对账;不是余额状态字段 |
| 充值申请态 | gy_ykt_cash_request.request_state |
PENDING / COMPLETED / CANCELLED |
apt.ledger.local |
沿用存量三值(docs/backend/05 §4.7),不新造 |
| 同步生命周期 | apt_ledger_offline.offline_scope + reportState(outbox / history 共用)+ reconcile_state |
LOCAL_ONLY / SYNCED / RECONCILED;PENDING / SENT / FAILED / DEAD / RETRIED;PENDING / RECONCILED / DIFF |
apt.ledger.local / apt.report.outbox |
沿用 CR-02 表 3-15、表 3-21、表 3-30 |
| 食谱发布态 | YKT_MEALS.publish_state(平台库补列) |
DRAFT / PUBLISHED / EXPIRED / REVOKED |
platform.catering |
阶段性食谱按 recipe_id + recipe_version 成组发布 |
表 12-5 全流程状态流转
| 序 | 流程 | 状态 | 触发 | 写入口与落表 | 后继 / 异常回退 |
|---|---|---|---|---|---|
| 1 | CF-03 | DRAFT |
公寓管理员新建阶段性食谱 | YKT_MEALS(补列) |
菜品下架 / 价格改 → 新 recipe_version,不改已发布版 |
| 2 | CF-03 | PUBLISHED |
管理员发布且 effective_from ≤ now ≤ effective_to |
YKT_MEALS.publish_state、#4 增量 |
到期 → EXPIRED;撤回 → REVOKED |
| 3 | CF-01 | 终端选单(不落库) | 消费终端 N-48 拉食谱、点击加入选择菜单 | 设备本地选单缓存 | 仅本地状态,退出/超时清空 |
| 4 | CF-01 | SUBMITTED |
N-50 提交消费(flowId 幂等) |
apt_ledger_offline(ledger_state=SUBMITTED) |
校验失败 → REJECTED,不扣款 |
| 5 | CF-01 | DEDUCTED |
双额度 CAS 扣减成功、balance_before/after 留痕 |
gy_ykt_remainder、gy_ykt_consume、apt_ledger_offline |
CAS 冲突重读重试一次,再失败转人工 |
| 6 | CF-01 | REPORTED |
#10 LEDGER_OFFLINE 入 outbox 并 SENT |
apt_report_outbox / apt_report_history |
断网 → outbox PENDING,恢复后复用原 idempotencyKey 续报 |
| 7 | CF-01 | RECONCILED |
平台日对账通过 | offline_scope=RECONCILED、local_balance_version 更新 |
差异 → DIFF,PF-5003 阻断调账 |
| 8 | CF-02 | PENDING |
N-52 扫码后输入金额提交申请 | gy_ykt_cash_request、apt_ledger_offline(RECHARGE, PENDING) |
重复 deviceEventId 返回原 flowId |
| 9 | CF-02 | COMPLETED |
管理员 N-54 核对确认(二次确认) | gy_ykt_xjczjl、gy_ykt_remainder.xjye、apt_ledger_offline.balance_after |
金额不符 → N-55 CANCELLED,写 reject_reason |
| 10 | CF-02 | REPORTED → RECONCILED |
同序 6 / 7 | 同序 6 / 7 | 同序 6 / 7 |
| 11 | 通用 | REVERSED / CANCELLED |
冲正或作废 | apt_ledger_offline、duty_operation_log |
已 RECONCILED 不得直接改,走 POST /adjustments 冲正 |
图 12-2 双额度扣减决策
flowchart TD
A["N-50 消费提交 amount"] --> B{"额度查询<br/>allowanceQuota / cashQuota"}
B --> C{"amount ≤ allowanceQuota?"}
C -->|是| D["allowanceAmount = amount<br/>cashAmount = 0"]
C -->|否| E["allowanceAmount = allowanceQuota<br/>cashAmount = amount - allowanceQuota"]
E --> F{"cashAmount ≤ cashQuota?"}
F -->|否| G["REJECTED 余额不足<br/>PF-5001 / 40933"]
F -->|是| H["CAS 扣减两条额度<br/>balance_before/after"]
D --> H
H --> I["一条 CONSUME 流水<br/>payload 带拆分字段"]
I --> J["#10 LEDGER_OFFLINE"]
一条流水原则(CR-C31):一笔消费只产生一条
flowId/type=CONSUME流水,补贴与现金的拆分作为allowanceAmount/cashAmount字段随 payload 上报,不额外生成ALLOWANCE_DEDUCT流水,避免平台sum(amount)双计。ALLOWANCE_DEDUCT保留给"独立退补助 / 补助调整"场景。
3. 数据模型
不新增公寓本地业务表(仍 59 张):账户额度、消费流水、充值记录全部复用
gy_ykt_*五张预留表与apt_ledger_offline;食谱主数据复用平台库YKT_MEALS并补列。所有补列均为设计定义,DDL 待阶段二契约冻结评审(Q-43)。
表 12-6 复用表清单
| 表 | 库 | 唯一写入模块 | 本流程用途 | 上报 |
|---|---|---|---|---|
gy_ykt_remainder |
apartment | apt.ledger.local |
账户额度本地快照(knye / xjye),CAS 扣减 |
否(经 #10 流水) |
gy_ykt_consume |
apartment | apt.ledger.local |
消费流水(xfje / xfsj / deviceCode / xfly) |
是(#10) |
gy_ykt_allowance |
apartment | apt.ledger.local |
补助发放流水(本流程只读,作为额度来源) | 是(#10) |
gy_ykt_cash_request |
apartment | apt.ledger.local |
现金充值申请与 request_state 三值 |
否 |
gy_ykt_xjczjl |
apartment | apt.ledger.local |
现金充值记录(flow_id / czje / czsj) |
是(#10) |
apt_ledger_offline |
apartment | apt.ledger.local |
离线/在线统一的本地账务流水、CAS 留痕、同步生命周期 | 是(#10) |
apt_report_outbox / apt_report_history |
apartment | apt.report.outbox / apt-report |
上行队列与终态留痕 | 是 |
GY_YKT_REMAINDER / PL_LEDGER_FLOW |
platform | platform-ledger |
平台余额主账与归集流水(公寓端零写权限) | — |
YKT_MEALS / YKT_MEALS_ORDERING / YKT_MEALS_ORDERS_MX |
platform | platform.catering |
菜品主数据、阶段性食谱补列、选单 / 订单(后两者本期预留) | — |
duty_manual_action / duty_operation_log |
duty | 应用端 | 管理员核对、食谱发布留痕 | 否 |
表 12-7 账户额度 gy_ykt_remainder 列级结构与补列
| 列 | 类型 | 约束 | 来源 | 说明 |
|---|---|---|---|---|
ryid / gyid |
string(64) / string(32) | UK (gyid, ryid) |
存量 | 账户主键;gyid 打头隔离 |
xm |
string(128) | 可空 | 存量 | 姓名快照 |
knye |
decimal(12,2) | 默认 0.00 |
存量 | 补贴可用额度(逻辑名 allowanceQuota,沿用 docs/backend/05 §4.3 与 backend/11 实测口径) |
xjye |
decimal(12,2) | 默认 0.00 |
存量 | 现金可用额度(逻辑名 cashQuota) |
xfze / bzze / xfxjje / xfbzje |
decimal(12,2) ×4 | 默认 0.00 |
存量 | 消费总额 / 补助总额 / 现金消费额 / 补助消费额,随每笔消费累加 |
local_balance_version |
bigint | 默认 1 |
补列 | CAS 版本;与 apt_ledger_offline.local_balance_version 一致,冲突重读重试一次 |
last_reconciled_at / reconcile_state |
datetime(3) / enum_s | 可空 / 默认 PENDING |
补列 | 最后对账日期与 PENDING / RECONCILED / DIFF(平台侧同名三值 RECEIVED / RECONCILED / DIFF) |
quota_ref |
string(64) | 可空 | 补列 | #1 下发 offlineQuota 批次引用,断网额度校验用 |
updated_at |
datetime(3) | NOT NULL | 补列 | 只追加更新,不物理删除 |
knye/xjye到"补贴 / 现金"的映射为设计映射,阶段二冻结前须按backend/11存量数据复核(写入 Q-43 一并拍板);本流程不新造同义余额字段。
表 12-8 消费流水 gy_ykt_consume / apt_ledger_offline 列级结构与补列
| 表 | 列 | 类型 | 来源 | 说明 |
|---|---|---|---|---|
gy_ykt_consume |
tid / flow_id / ryid / gyid |
string ×4 | 存量 / 补列 | flow_id 为 #10 bizId,UK (gyid, flow_id) |
xfje / xfsj / device_code / xfly |
decimal(12,2) / datetime / string / enum_s | 存量 | 消费金额(正数)、时间、受理设备、消费来源 | |
knye / xjye / xfze / bzze |
decimal(12,2) ×4 | 存量 | 扣款后两条余额与累计额快照 | |
allowance_amount / cash_amount |
decimal(12,2) ×2 | 补列 | 本笔拆分:allowance_amount + cash_amount = xfje,一条流水不双计 |
|
recipe_id / recipe_version / items |
string / bigint / json | 补列 | 关联阶段性食谱与选单明细(meal_id / name / price / qty),供对账与退单 |
|
apt_ledger_offline |
id / gyid / type / flow_id |
id_ulid / string / enum_s / string | 存量 | type = CONSUME / RECHARGE / ALLOWANCE_DEDUCT;UK (gyid, type, flow_id) |
amount / balance_before / balance_after |
decimal(12,2) ×3 | 存量 | 扣减用负数、充值用正数;CAS 前后余额留痕 | |
offline_scope / reconcile_state |
enum_s ×2 | 存量 | LOCAL_ONLY / SYNCED / RECONCILED;PENDING / RECONCILED / DIFF |
|
local_balance_version / quota_ref |
bigint / string | 存量 | 本地余额版本与离线额度批次 | |
ledger_state |
enum_s | 补列 | 表 12-4 消费流水态(流程态,不写 gy_jcxx 状态字段) |
|
allowance_before/after / cash_before/after |
decimal(12,2) ×4 | 补列 | 双额度各自 CAS 前后值,与 §2 决策图一致 | |
occurred_at / synced_at / created_at |
datetime(3) ×3 | 存量 | 归日以平台 serverReceivedAt 为准 |
表 12-9 充值记录 gy_ykt_xjczjl / gy_ykt_cash_request 列级结构与补列
| 表 | 列 | 类型 | 来源 | 说明 |
|---|---|---|---|---|
gy_ykt_xjczjl |
flow_id / ryid / gyid |
string ×3 | 存量 | PK flow_id;与 apt_ledger_offline.flow_id 一致 |
czje / czsj |
decimal(12,2) / datetime(3) | 存量 | 充值金额(正数)与时间 | |
channel / confirmed_by / confirmed_at |
enum_s / string / datetime | 补列 | channel 恒 OFFLINE_CASH(仅线下);管理员核对留痕 |
|
gy_ykt_cash_request |
id / ryid / gyid / device_code |
string ×4 | 存量 | 申请单;本身不存金额(金额在 apt_ledger_offline.amount) |
request_state |
enum_s | 存量 | PENDING / COMPLETED / CANCELLED,不新造状态 |
|
flow_id / device_event_id |
string ×2 | 补列 | 关联账务流水;UK (gyid, device_code, device_event_id) 幂等 |
|
requested_at / confirmed_by / confirmed_at / reject_reason |
datetime ×2 / string / datetime / string | 补列 | 申请、核对与作废留痕 | |
qr_code_id |
string(64) | 补列 | 公寓线下二维码标识,只作公寓识别,不含支付信息 |
表 12-10 食谱与菜品 YKT_MEALS 补列(平台库)
| 列 | 类型 | 来源 | 说明 |
|---|---|---|---|
meal_id / gyid / name / price / enabled |
string / string / string / decimal(12,2) / tinyint | 存量 | 菜品主键、公寓、名称、单价、启停(docs/平台端/03 表 3-7) |
recipe_id / recipe_version |
string(64) / bigint | 补列 | 阶段性食谱批次与版本;同批次所有菜品共享,version 单调 |
meal_period |
enum_s | 补列 | BREAKFAST / LUNCH / DINNER / NIGHT / ANY |
effective_from / effective_to |
datetime(3) ×2 | 补列 | 食谱生效区间;到期 EXPIRED |
publish_state |
enum_s | 补列 | DRAFT / PUBLISHED / EXPIRED / REVOKED |
published_by / published_at |
string(64) / datetime(3) | 补列 | 公寓管理员发布留痕 |
category / sort_no / image_ref |
string / int / string | 补列 | 分类、排序、图片引用占位;本期不落任何图片本体,#4 下行只带引用字符串,不进对象存储(平台侧仍只有 person-photo / report-export 两个桶,见 docs/平台端/07 §4.1) |
YKT_MEALS_ORDERING/YKT_MEALS_ORDERS_MX本期不写入:消费终端的"选择菜单"是设备本地选单,确认后才按 CF-01 生成一条消费流水;远程点菜启用后再落订单表(Q-44)。
表 12-11 枚举字典
| 枚举 | 取值 | 来源 / 说明 |
|---|---|---|
flowType |
CONSUME / RECHARGE / ALLOWANCE_DEDUCT |
CR-02 表 3-15,沿用不新增 |
offlineScope |
LOCAL_ONLY / SYNCED / RECONCILED |
CR-02 表 3-15 / 表 3-21 |
reportState |
PENDING / SENT / FAILED / DEAD / RETRIED |
CR-02 表 3-21,outbox 与 history 共用 |
requestState |
PENDING / COMPLETED / CANCELLED |
docs/backend/05 §4.7 |
ledgerState |
SUBMITTED / DEDUCTED / REPORTED / RECONCILED / REJECTED / REVERSED |
本流程补列(流程态) |
publishState |
DRAFT / PUBLISHED / EXPIRED / REVOKED |
本流程补列 |
mealPeriod |
BREAKFAST / LUNCH / DINNER / NIGHT / ANY |
本流程补列 |
rechargeChannel |
OFFLINE_CASH |
唯一取值;不接线上支付(CR-C32) |
source |
DEVICE / DUTY / SELF |
CR-02 表 3-14 |
4. CF-01 人员消费流程
4.1 触发条件与数据范围
表 12-12 触发与前置
| 项 | 口径 |
|---|---|
| 触发 | 消费终端(gy_sb.device_class=CONSUME)按当前 PUBLISHED 食谱展示菜品;人员点击加入选择菜单,终端实时显示合计金额;确认后 N-50 提交 |
| 数据范围 | gyid(凭据解析)、ryid、deviceCode、deviceEventId、选单 items[]{mealId, name, price, qty}、amount、recipeId / recipeVersion、occurredAt |
| 前置 | 人员已注册且有效在寓(docs/公寓端/11 §2);食谱为 PUBLISHED 且在生效区间;设备凭据、HMAC、±5 min 时间窗、nonce 通过 |
| 不触发 | 仅浏览食谱 / 仅加入选单(未确认)、食谱 EXPIRED / REVOKED、菜品 enabled=0、人员已离寓 |
表 12-13 双额度扣减规则(CR-C31)
| 步 | 规则 | 字段 | 异常 |
|---|---|---|---|
| 1 | 查询补贴额度与现金额度 | allowanceQuota = gy_ykt_remainder.knye、cashQuota = xjye |
账户不存在 → 40934;额度版本落后 → 重拉 #1 后重试 |
| 2 | 优先全额补贴 | amount ≤ allowanceQuota → allowanceAmount = amount、cashAmount = 0 |
— |
| 3 | 补贴不足则现金补足 | allowanceAmount = allowanceQuota、cashAmount = amount - allowanceQuota |
cashAmount > cashQuota → REJECTED + PF-5001 / 40933,不部分扣款 |
| 4 | CAS 扣减 | where ryid=? and local_balance_version=?;更新 knye / xjye 与四个累计额 |
冲突重读重试一次,再失败转人工(40935) |
| 5 | 记一条流水 | type=CONSUME、amount = -xfje(payload 负数)、allowanceAmount + cashAmount = xfje |
不生成第二条 ALLOWANCE_DEDUCT,不双计 |
| 6 | 断网额度校验 | offlineScope=LOCAL_ONLY 时校验 #1 offlineQuota.{singleLimit, dailyLimit, maxOutstanding} |
超限 40913,本地余额立即可用但不写平台主账 |
| 7 | 上报与对账 | #10 LEDGER_OFFLINE;平台按 serverReceivedAt 归日;日对账通过置 RECONCILED |
PF-5002 重复流水、PF-5003 对账未结清 |
4.2 接口
表 12-14 N-49 / N-50 / N-51 请求 / 响应字段
| 编号 | 方法 + 路径 | 权限 / 凭据 | 幂等 | 请求字段 | 响应字段 |
|---|---|---|---|---|---|
| N-49 | GET /open/v1/ledger/quota?ryid&deviceCode |
设备凭据 + HMAC | L0 | ryid(须与设备可见范围匹配)、deviceCode |
ryid、allowanceQuota / cashQuota(字符串两位小数)、localBalanceVersion、quotaRef、reconcileState、lastReconciledAt、serverTime |
| N-50 | POST /open/v1/ledger/consume |
设备凭据 + HMAC | L2 (gyid, deviceCode, deviceEventId) |
ryid、items[]{mealId, name, price, qty}、amount、recipeId / recipeVersion、deviceEventId、occurredAt、nonce / signature |
flowId、ledgerState=DEDUCTED、allowanceAmount / cashAmount、allowanceAfter / cashAfter、offlineScope、localBalanceVersion、reportState、serverTime |
| N-51 | GET /open/v1/ledger/flows/{flowId} |
设备凭据 | L0 | flowId |
flowId、type、ledgerState、offlineScope、reportState、reconcileState、amount / allowanceAmount / cashAmount、syncedAt |
表 12-15 消费状态机(条件更新带 ledger_state 期望值)
| 当前态 | 事件 | 目标态 | 落表 | 失败 / 补偿 |
|---|---|---|---|---|
| — | N-50 提交通过校验 | SUBMITTED |
apt_ledger_offline |
校验失败 → REJECTED,不扣款 |
SUBMITTED |
CAS 扣减成功 | DEDUCTED |
gy_ykt_remainder、gy_ykt_consume、apt_ledger_offline |
CAS 冲突重试一次,再失败转人工 |
DEDUCTED |
#10 入 outbox 并 SENT |
REPORTED |
apt_report_outbox / apt_report_history |
断网 PENDING 排队,恢复复用原幂等键 |
REPORTED |
平台日对账通过 | RECONCILED |
offline_scope / reconcile_state / local_balance_version |
差异 DIFF + PF-5003,阻断调账 |
REPORTED / RECONCILED |
人工冲正 | REVERSED |
duty_manual_action、POST /adjustments |
不直接改余额表,走 platform-ledger 冲正 |
图 12-3 消费端到端时序
sequenceDiagram
autonumber
participant CT as 消费终端 CONSUME
participant AP as apt.ledger.local
participant AC as apt.catering
participant L as gy_ykt_* / apt_ledger_offline
participant O as apt-report / outbox
participant P as platform-ingest → platform-ledger
CT->>AC: CF-03 N-48 拉当前食谱(设备主动拉取)
AC-->>CT: recipes[] + version(终端缓存)
CT->>CT: 点击菜品加入选择菜单,显示合计金额
CT->>AP: N-49 查询补贴额度与现金额度
AP-->>CT: allowanceQuota / cashQuota / localBalanceVersion
CT->>AP: N-50 提交消费(flowId, items, amount)
AP->>AP: 补贴优先 → 现金补足(图 12-2)
AP->>L: CAS 扣减 + 记一条 CONSUME 流水(DEDUCTED)
AP->>O: 生成 #10 LEDGER_OFFLINE
O->>P: 上报(idempotencyKey = hash(gyid+bizType+flowId+bizVersion))
P->>P: 按 serverReceivedAt 归日,落 PL_LEDGER_FLOW / GY_YKT_REMAINDER
P-->>O: 接收回执 / 日对账结论
O->>L: REPORTED → RECONCILED(offline_scope / local_balance_version)
AP-->>CT: N-51 可查流水状态
表 12-16 CF-01 异常与错误码
| 场景 | 处理 | 不做什么 |
|---|---|---|
| 余额不足(补贴 + 现金仍不足) | REJECTED + PF-5001 / 40933 |
不部分扣款、不透支(内部人员透支口径见 Q-45) |
| 补贴刚好为 0 | allowanceAmount=0,全额走现金 |
不生成 ALLOWANCE_DEDUCT 流水 |
| CAS 版本冲突 | 重读额度重试一次,再失败 40935 转人工 |
不覆盖较新版本、不丢 balance_before/after |
断网且超 offlineQuota |
40913,本地余额立即可用但不写平台主账 |
不伪造 SYNCED |
重复提交 deviceEventId |
返回原 flowId 与快照 |
不产生第二笔扣款 |
| 平台重复流水 / 幂等重放 | PF-5002 业务重复 vs PL-IDEMPOTENT-REPLAY 幂等重放分别处理 |
不把两者混为一谈 |
| 食谱过期 / 菜品下架 / 价格变更 | 40936 / PF-6001,要求终端重拉食谱 |
不用旧价格扣款 |
| 已离寓人员消费 | 40937 拒收 |
不改 finish_ly / ryzt |
已 RECONCILED 需冲正 |
走 POST /adjustments + 审批留痕 |
不直改 GY_YKT_REMAINDER |
5. CF-02 人员充值流程
5.1 线下充值边界
表 12-17 "仅线下充值、仅记账"口径(CR-C32)
| 项 | 口径 |
|---|---|
| 扫码含义 | 公寓线下二维码只携带 gyid + qrCodeId,用于识别公寓并引导到签到终端;不携带支付链接、不发起任何线上支付 |
| 资金动作 | 现金由管理员线下收取并核对;系统只做记账,不接微信/支付宝/网银、不建支付订单、不处理支付回调 |
| 金额载体 | gy_ykt_cash_request 不存金额(沿用存量口径);金额落 apt_ledger_offline.amount(type=RECHARGE,正数)与 gy_ykt_xjczjl.czje |
| 额度落点 | 管理员确认后由 apt.ledger.local 更新 gy_ykt_remainder.xjye(现金额度)并记 balance_before/after |
| 同步 | 与 CF-01 共用 #10 LEDGER_OFFLINE + F-D 六步;平台 platform-ledger 落 GY_YKT_REMAINDER / PL_LEDGER_FLOW |
| 断网 | 本地记账受 #1 offlineQuota 约束(缺省全 0 即断网不开放,Q-21 / Q-42),超限 40913 |
表 12-18 充值申请状态机(复用 request_state 三值)
| 当前态 | 事件 | 目标态 | 落表 | 失败 / 补偿 |
|---|---|---|---|---|
| — | N-52 扫码后输入金额提交 | PENDING |
gy_ykt_cash_request、apt_ledger_offline(SUBMITTED) |
金额非法 → 40938;重复 deviceEventId 返回原 flowId |
PENDING |
管理员 N-54 核对确认(duty:ledger:confirm + 二次确认) |
COMPLETED |
gy_ykt_xjczjl、gy_ykt_remainder.xjye、apt_ledger_offline(DEDUCTED) |
金额与实收不符 → N-55 CANCELLED |
PENDING |
管理员 N-55 作废 / 超时 | CANCELLED |
reject_reason、duty_operation_log |
不写额度、不生成充值记录 |
COMPLETED |
#10 上报 / 日对账 | REPORTED → RECONCILED |
apt_report_outbox / history |
同 CF-01 序 6 / 7 |
| 终态 | 任何再次确认 | 拒绝 40939 |
— | 返回原态快照,不重复入账 |
表 12-19 N-52~N-55 请求 / 响应字段
| 编号 | 方法 + 路径 | 权限 / 凭据 | 幂等 | 请求字段 | 响应字段 |
|---|---|---|---|---|---|
| N-52 | POST /open/v1/ledger/recharge-requests |
签到终端设备凭据 + HMAC | L2 (gyid, deviceCode, deviceEventId) |
ryid、amount(字符串两位小数,正数)、qrCodeId、deviceEventId、occurredAt、nonce / signature |
flowId、requestState=PENDING、amount、qrCodeId、requestedAt、serverTime |
| N-53 | GET /duty/ledger/recharge-requests?state&cursor |
duty:ledger:local |
L0 | state=PENDING、cursor、limit |
items[]{flowId, ryid, workNo, name, amount, qrCodeId, requestedAt, deviceCode}、cursor、total |
| N-54 | POST /duty/ledger/recharge-requests/{flowId}/confirm |
duty:ledger:confirm + 二次确认 |
L2 commandId |
flowId、expectedAmount、confirmedAmount、expectedRequestState、commandId |
flowId、requestState=COMPLETED、cashQuotaAfter、localBalanceVersion、confirmedBy / confirmedAt、reportState |
| N-55 | POST /duty/ledger/recharge-requests/{flowId}/cancel |
duty:ledger:confirm |
L2 commandId |
flowId、reason、commandId |
flowId、requestState=CANCELLED、rejectReason |
表 12-20 管理员确认后的数据变更清单
| # | 对象 | 变更 | 约束 |
|---|---|---|---|
| 1 | gy_ykt_cash_request |
request_state=COMPLETED、回填 confirmed_by / confirmed_at |
条件更新带期望态 PENDING,并发冲突 40939 |
| 2 | apt_ledger_offline |
type=RECHARGE、amount=+confirmedAmount、ledger_state=DEDUCTED、balance_before/after、cash_before/after |
金额必须与 N-52 一致;不一致走 N-55 |
| 3 | gy_ykt_xjczjl |
新增 flow_id / czje / czsj、channel=OFFLINE_CASH、confirmed_by |
一笔申请一条充值记录 |
| 4 | gy_ykt_remainder |
xjye += confirmedAmount、local_balance_version + 1、更新 updated_at |
CAS 条件更新;只增现金额度,不改 knye 补贴额度 |
| 5 | apt_report_outbox |
生成 #10 LEDGER_OFFLINE 上报项(bizId=flowId) |
断网 PENDING 排队,恢复复用原 idempotencyKey |
| 6 | duty_manual_action / duty_operation_log |
记核对人、commandId、申请金额 / 实收金额、前后额度 |
二次确认留痕,越权 40301 |
图 12-4 线下充值端到端时序
sequenceDiagram
autonumber
participant CK as 签到终端 CHECKIN
participant AP as apt.ledger.local
participant D as 管理员(值班台 /duty)
participant L as gy_ykt_* / apt_ledger_offline
participant O as apt-report / outbox
participant P as platform-ledger
CK->>AP: 扫公寓线下二维码 → N-52 现金充值申请(amount)
AP->>L: request_state=PENDING(申请表不存金额)
AP->>D: 推待办 recharge.request
D->>AP: N-53 查看待核对列表
D->>AP: N-54 核对实收金额并二次确认
AP->>L: COMPLETED + xjye 增加 + gy_ykt_xjczjl 记录
AP->>O: 生成 #10 LEDGER_OFFLINE(RECHARGE)
O->>P: 上报并归日
P-->>O: 日对账结论 → RECONCILED
AP-->>CK: 可查额度与流水状态
Note over CK,P: 全程无线上支付渠道、无支付订单与回调
表 12-21 CF-02 异常与错误码
| 场景 | 处理 | 不做什么 |
|---|---|---|
| 金额为 0 / 负数 / 超上限 / 小数位 > 2 | 40938 拒收(沿用 backend/04 "金额 > 0 且 < 100") |
不建申请单、不排队 |
二维码无效 / 跨 gyid |
40301 / 40939 拒收并记审计 |
不解析任何支付参数 |
| 重复扫码提交 | 返回原 flowId(deviceEventId 幂等) |
不生成第二笔充值 |
| 实收金额与申请不符 | N-55 CANCELLED + reject_reason,重新发起申请 |
不按申请金额直接入账 |
| 并发确认 | 条件更新冲突 40939 |
不重复入账、不改终态 |
断网超 offlineQuota |
40913 |
不伪造已同步、不写平台主账 |
已 RECONCILED 需冲正 |
POST /adjustments + 审批 |
不直改余额表、不改申请单终态 |
| 线上支付请求(若误接) | 直接拒绝,不提供该入口 | 不建支付订单、不监听回调 |
6. CF-03 公寓菜品管理
6.1 发布与查看
表 12-22 食谱发布状态机(平台库 YKT_MEALS.publish_state)
| 当前态 | 事件 | 目标态 | 落表 / 编号 | 失败 / 补偿 |
|---|---|---|---|---|
| — | 公寓管理员新建食谱(选菜品、餐次、生效区间) | DRAFT |
YKT_MEALS 补列 |
菜品 enabled=0 不得加入 |
DRAFT |
发布(二次确认) | PUBLISHED |
recipe_version 单调、published_by/at;#4 增量 |
区间重叠 → PF-6004(规则重叠沿用 PF-5004 语义)或拒绝发布 |
PUBLISHED |
到达 effective_to |
EXPIRED |
定时任务置态 | 终端重拉后不再展示 |
PUBLISHED |
管理员撤回 | REVOKED |
duty_manual_action |
已发生的消费流水不受影响 |
| 任意已发布态 | 改价 / 下架菜品 | 新 recipe_version |
YKT_MEALS 新版本 |
不改已发布版本,避免历史对账错配 |
主数据归属(CR-C33):食谱主数据在平台库
YKT_MEALS(补列),由公寓管理员经/platform/v1/admin/catering的recipes资源发布并按gyid隔离;公寓端apt.catering通过 #4 下行recipes[]拉增量与水位,食谱缓存落在消费终端本地,不落公寓本地业务表(59 张不变)。Q-43 未决前按此默认口径执行。
表 12-23 N-48 / N-57 / N-58 请求 / 响应字段
| 编号 | 方法 + 路径 | 权限 / 凭据 | 幂等 | 请求字段 | 响应字段 |
|---|---|---|---|---|---|
| N-48 | GET /open/v1/catering/recipes?deviceCode&version |
消费终端设备凭据 | L0(版本) | version(可选,来自上次响应) |
recipes[]{recipeId, recipeVersion, mealPeriod, effectiveFrom, effectiveTo, publishState, items[]{mealId, name, price, category, sortNo, enabled, imageRef}}、version、serverTime;版本未变返回 notModified=true |
| N-57 | GET /apt/catering/recipes、POST /apt/catering/recipes、PUT /apt/catering/recipes/{recipeId}/publish、PUT /apt/catering/recipes/{recipeId}/revoke |
apt:catering:recipe:* |
L2 recipeId + version |
recipeId、mealPeriod、effectiveFrom/To、items[]、expectedVersion |
recipeId、recipeVersion、publishState、publishedBy/At、itemCount、serverTime |
| N-58 | POST /open/v1/catering/orders(远程点菜预留入口) |
设备凭据 | L2 orderId |
ryid、items[]、recipeId、deliveryType |
本期恒返回 40940 FEATURE_DISABLED 且 features.remoteOrdering=false;不落订单、不扣款 |
N-57 是公寓端管理入口,后端代理到平台餐饮管理端
/platform/v1/admin/catering/recipes;权限点apt:catering:recipe:list/create/publish/revoke,前端v-hasPermi与后端@PreAuthorize同码并登记sys_menu.perms。
表 12-24 阶段性食谱数据结构
| 字段 | 类型 | 必填 | 校验规则 |
|---|---|---|---|
recipeId / recipeVersion |
string(64) / bigint | 是 | 服务端生成;version 单调,同 recipeId 不允许覆盖已发布版 |
gyid |
string(32) | 是 | 只从会话解析,不接受请求参数 |
mealPeriod |
enum_s | 是 | BREAKFAST / LUNCH / DINNER / NIGHT / ANY |
effectiveFrom / effectiveTo |
datetime(3) ×2 | 是 | from < to;同 gyid + mealPeriod 区间重叠拒绝发布 |
publishState |
enum_s | 是 | 表 12-22 状态机 |
items[] |
array | 是 | 至少 1 项;每项 mealId 存在且 enabled=1、price > 0 且两位小数、qty 上限按 Q-45 |
imageRef |
string(512) | 否 | 只存引用占位;本期不上传 / 不下载图片本体,不新增对象存储桶,docs/平台端/07 §4.1「只有两个桶」的边界不变 |
publishedBy / publishedAt |
string / datetime | 发布时 | 二次确认留痕 |
6.2 选单与远程点菜预留
- 选择菜单:消费终端本地维护(设备主动拉取食谱 → 点击加入 → 实时显示合计金额),确认前不落库;确认后只按 CF-01 生成一条消费流水。终端退出 / 超时(默认 5 min)清空选单。
- 远程点菜入口:#1 下行
features.remoteOrdering(默认false)+ N-58 接口骨架 +YKT_MEALS_ORDERING/YKT_MEALS_ORDERS_MX两表预留;本期不实现下单、不落订单、不扣款,启用条件与配送 / 后厨 / 结算规则见 Q-44 与平台端/09P-07。 - 价格一致性:N-50 提交时服务端按
recipeId + recipeVersion重算金额,与终端amount不一致 →40936要求重拉食谱,防止改价期间错扣。
表 12-25 CF-03 异常与错误码
| 场景 | 处理 | 不做什么 |
|---|---|---|
| 食谱未发布 / 已过期 / 已撤回 | 40936 + 终端重拉 N-48 |
不展示、不允许加入选单 |
| 菜品下架 / 价格变更 | PF-6001 / 40936,按服务端 recipeVersion 重算 |
不用终端旧价扣款 |
| 区间重叠发布 | 拒绝发布并提示调整 effectiveFrom/To |
不产生第二个同时生效的同餐次食谱 |
| 改价影响已发布版 | 生成新 recipe_version |
不改历史版本、不改已发生流水 |
| 远程点菜误调用 | 40940 FEATURE_DISABLED |
不落订单、不扣款、不改额度 |
| 设备食谱缓存落后 | N-48 按 version 增量刷新 |
不用过期缓存计算金额 |
7. 提报与平台同步(CF-01 / CF-02 共用)
表 12-26 #10 POST /platform/v1/report/ledger(bizType=LEDGER_OFFLINE)payload 字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
envelope |
object | 是 | contractVersion=v2-draft、gyId、bizType=LEDGER_OFFLINE、bizId=flowId、idempotencyKey=hash(gyid+bizType+flowId+bizVersion)、occurredAt、serverReceivedAt |
flowId / type |
string / enum | 是 | 沿用 CR-02 表 3-15;CONSUME / RECHARGE(ALLOWANCE_DEDUCT 不用于消费拆分) |
amount |
string(2) | 是 | 字符串十进制两位小数;扣减用负数、充值用正数,禁止浮点 |
ryid / cardNo / deviceCode |
string ×3 | 是 | 账户、GY_YKT_RY.card_id、受理设备 |
offlineScope / localBalanceVersion / quotaRef |
enum / bigint / string | 是 | 沿用 F-D;断网期间恒 LOCAL_ONLY |
allowanceAmount / cashAmount |
string(2) ×2 | CONSUME 必填 |
本流程扩展:拆分金额,allowanceAmount + cashAmount = abs(amount) |
allowanceBefore/After / cashBefore/After |
string(2) ×4 | 是 | 本流程扩展:双额度 CAS 前后值,供平台对账 |
recipeId / recipeVersion / items[] |
string / bigint / array | 否 | 本流程扩展:消费关联食谱与选单明细,不含图像本体 |
rechargeChannel / qrCodeId / confirmedBy |
enum / string / string | RECHARGE 必填 |
本流程扩展:OFFLINE_CASH + 线下二维码标识 + 管理员核对留痕 |
syncedAt |
datetime | 否 | 归日仍以平台 serverReceivedAt 为准(CR-02 表 3-11) |
表 12-27 平台侧处理链路与落表
| 步 | 主体 | 动作 | 编号 | 落表 |
|---|---|---|---|---|
| 1 | platform-ingest |
按 idempotencyKey 去重,落接收日志 |
#10 | PL_RECEIVE_LOG |
| 2 | platform-ledger |
按 serverReceivedAt 归日;业务 UK (gyid, type, flow_id) 去重;sum(amount) 对账 |
#10 | PL_LEDGER_FLOW(reconcile_state = RECEIVED / RECONCILED / DIFF) |
| 3 | platform-ledger |
唯一写入口更新余额主账;消费按 allowanceAmount / cashAmount 分别冲减 knye / xjye,充值只增 xjye |
内部 | GY_YKT_REMAINDER |
| 4 | 公寓 apt-report |
日对账通过后把 offline_scope 置 RECONCILED、更新 local_balance_version |
#16(本期公寓主动调用形态) | apt_ledger_offline、gy_ykt_remainder |
| 5 | 值班台 | 推 ledger.offline WS 消息(flowId / offlineScope / localBalanceVersion / quotaRemain / seq) |
端内 | duty_operation_log |
表 12-28 对账与冲正规则(沿用,不新造)
| 项 | 口径 |
|---|---|
| 三维比对 | count(按 biz_id 去重,R-C3)/ sum(amount) / max(watermark);差异落 PL_RECONCILE_DIFF |
| 归日 | 平台 serverReceivedAt;公寓 occurredAt 不参与日切 |
| 重复语义 | PF-5002 业务重复流水 vs PL-IDEMPOTENT-REPLAY 幂等重放,两者不同 |
| 阻断 | 当日 sum(amount) 不等即 P0,PF-5003 阻断所有调账入口 |
| 冲正 | 只允许 POST /adjustments(带原因 + 审批人)经 platform-ledger 冲正,不改余额表字段、不改申请单终态 |
| 续报 | 断网恢复先派 outbox 再拉 #7(CR-01 §2.9),复用原 idempotencyKey,不产生新业务事实 |
8. 接口清单汇总
表 12-29 端内接口 N-48~N-58(均不计入跨端 20 个编号)
| 编号 | 方法 + 路径 | 权限 / 凭据 | 幂等 | 流程 | 用途 |
|---|---|---|---|---|---|
| N-48 | GET /open/v1/catering/recipes |
消费终端设备凭据 | L0(版本) | CF-03 / CF-01 | 拉取当前有效阶段性食谱与菜品 |
| N-49 | GET /open/v1/ledger/quota |
消费终端设备凭据 | L0 | CF-01 | 查询补贴额度与现金额度 |
| N-50 | POST /open/v1/ledger/consume |
消费终端设备凭据 + HMAC | L2 (gyid, deviceCode, deviceEventId) |
CF-01 | 提交选单与金额,触发双额度扣减 |
| N-51 | GET /open/v1/ledger/flows/{flowId} |
设备凭据 | L0 | CF-01 / CF-02 | 流水状态与拆分金额查询 |
| N-52 | POST /open/v1/ledger/recharge-requests |
签到终端设备凭据 + HMAC | L2 (gyid, deviceCode, deviceEventId) |
CF-02 | 扫码后发起线下现金充值申请 |
| N-53 | GET /duty/ledger/recharge-requests |
duty:ledger:local |
L0(游标) | CF-02 | 管理员待核对列表 |
| N-54 | POST /duty/ledger/recharge-requests/{flowId}/confirm |
duty:ledger:confirm + 二次确认 |
L2 commandId |
CF-02 | 核对实收金额并计入现金额度 |
| N-55 | POST /duty/ledger/recharge-requests/{flowId}/cancel |
duty:ledger:confirm |
L2 commandId |
CF-02 | 作废申请 |
| N-56 | GET /apt/ledger/accounts/{ryid} |
apt:ledger:list |
L0 | CF-01 / CF-02 | 账户额度、流水与对账状态查询 |
| N-57 | GET/POST /apt/catering/recipes、PUT .../{recipeId}/publish、PUT .../{recipeId}/revoke |
apt:catering:recipe:* |
L2 recipeId + version |
CF-03 | 公寓管理员发布 / 撤回阶段性食谱 |
| N-58 | POST /open/v1/catering/orders |
设备凭据 | L2 orderId |
CF-03 | 远程点菜预留入口,本期恒 40940 |
通用约定(前缀、HMAC + 时间窗 +
nonce、错误码分段、data.reason/data.traceId、gyid锁定)见docs/公寓端/04§1;既有 N-09 / N-10(duty:ledger:local)保持不变,本文件不重复登记。
表 12-30 跨端接口(沿用既有编号,不新增、不重排)
| # | 接口 | 方向 | 本流程扩展 | 字段表 |
|---|---|---|---|---|
| 1 | GET /platform/v1/master/apartments/{gyId} |
下行(拉) | offlineQuota 沿用;新增 features.remoteOrdering(默认 false) |
CR-02 表 3-20 |
| 4 | GET /platform/v1/master/rules?gyId |
下行(拉) | 响应增 recipes[]{recipeId, recipeVersion, mealPeriod, effectiveFrom/To, items[]} + recipeWatermark |
表 12-24 |
| 10 | POST /platform/v1/report/ledger |
上行 | LEDGER_OFFLINE payload 扩展(拆分金额 / CAS 值 / 食谱 / 线下充值标记) |
表 12-26 |
| 16 | POST /platform/v1/reconcile/run?gyId&date |
补偿(本期公寓主动调用形态,P-02) | 日对账结论回写 RECONCILED |
表 12-28 |
9. 异常、一致性与权限
表 12-31 异常总表(三项流程一致口径)
| 场景 | 处理 | 不做什么 |
|---|---|---|
| 设备断网 | 本地额度内受理,offlineScope=LOCAL_ONLY、outbox PENDING,恢复复用原 idempotencyKey 续报 |
不伪造 SYNCED、不产生新业务事实 |
| 超离线额度 | 40913 APT_OFFLINE_QUOTA_EXCEEDED(#1 offlineQuota 缺省全 0 即断网不开放) |
不放行本地扣款 |
| 余额不足 | REJECTED + PF-5001 / 40933 |
不部分扣款、不透支 |
| CAS 冲突 | 重读重试一次,再失败 40935 转人工 |
不覆盖新版本、不留半条余额 |
| 金额非法 | 40938 拒收(> 0、< 100、≤ 2 位小数) |
不建申请单 / 流水 |
| 并发确认 / 重复提交 | 条件更新 + commandId / deviceEventId 幂等,冲突 40939 |
不重复入账、不重复扣款 |
| 平台重复 / 幂等重放 | PF-5002 与 PL-IDEMPOTENT-REPLAY 分别处理 |
不混用两种语义 |
| 对账差异 | DIFF + PF-5003 阻断调账,人工走 POST /adjustments |
不直改余额表 |
| 食谱 / 价格失效 | 40936 / PF-6001,终端重拉 N-48 |
不用旧价 / 已下架菜品扣款 |
| 已离寓 / 未注册 | 40937 拒收 |
不改 finish_ly / ryzt、不改人脸主档 |
| 远程点菜误调用 | 40940 FEATURE_DISABLED |
不落订单、不扣款 |
越权 / 跨 gyid |
40301 拒收并记审计 |
不接受请求参数指定 gyid |
表 12-32 权限与审计
| # | 要求 |
|---|---|
| 1 | 双校验:/apt/** 走 RuoYi 会话 + @PreAuthorize,前端 v-hasPermi 同码;/duty/** 走 duty:ledger:*;/open/v1/** 走设备凭据 + HMAC + 时间窗 + nonce。新增权限点 apt:catering:recipe:*、duty:ledger:confirm 必须登记 sys_menu.perms(docs/公寓端/02 §4.2) |
| 2 | 职责分离:消费终端 / 签到终端只能提交请求与查询,管理员负责核对与发布,apt.ledger.local 落库,platform-ledger 写主账;设备与前端零余额表写权限 |
| 3 | 二次确认:N-54 充值确认、N-57 食谱发布 / 撤回必须二次确认并写 duty_manual_action |
| 4 | 审计:选单确认、额度查询(异常时)、扣减、充值申请、核对、作废、发布、撤回、上报、对账十类动作全部记 duty_operation_log(操作人 / 设备、flowId / commandId、前后额度、结果) |
| 5 | 最小必要:N-49 / N-53 / N-56 不返回人脸媒体;食谱只返回 imageRef 引用;抓拍、录音、签到现场图不出公寓 |
| 6 | gyid 锁定:只从会话 / 设备凭据解析;所有 SQL 必带 gyid,唯一键以 gyid 打头 |
| 7 | 金额安全:一律字符串两位小数 / decimal(12,2),禁止浮点;服务端按 recipeVersion 重算金额,不信任终端合计 |
10. 决策点与验收
表 12-33 待决口径(Q-41~Q-45,承接 docs/公寓端/11 Q-40)
| 编号 | 事项 | 默认口径(未决前执行) | 拍板时点 |
|---|---|---|---|
| Q-41 | 补贴不足时是否允许现金补足 | 允许(本流程核心规则);两者合计仍不足则全额拒绝,不部分扣款 | 阶段二契约冻结前 |
| Q-42 | 线下充值在断网时是否受 offlineQuota 约束 |
是;缺省全 0 即断网不开放本地充值记账(沿用 Q-21) |
阶段三试点前 |
| Q-43 | 食谱主数据归属与 knye / xjye 语义复核 |
平台库 YKT_MEALS 补列 + 公寓只读下发;knye=补贴 / xjye=现金为设计映射,须按 backend/11 存量数据复核 |
阶段二契约冻结前 |
| Q-44 | 远程点菜启用条件 | 本期 features.remoteOrdering=false,仅留 N-58 入口与两表预留;后厨 / 配送 / 结算规则待 平台端/09 P-07 定稿 |
后续迭代 |
| Q-45 | 充值单笔上限、是否需要双人复核、内部人员透支 | 单笔 > 0 且 < 100(沿用 backend/04)、二次确认即可、不允许透支 |
阶段三试点前 |
表 12-34 验收要点
| # | 验收项 | 判据 |
|---|---|---|
| 1 | 消费流程 | 终端按 PUBLISHED 食谱选菜并显示金额;N-49 返回两条额度;扣减严格"补贴优先、现金补足";一笔消费只产生一条 flowId 且 allowanceAmount + cashAmount = abs(amount);CAS 前后值留痕 |
| 2 | 充值流程 | 扫码只识别公寓、无支付订单与回调;申请表不存金额;管理员二次确认后只增 xjye;channel=OFFLINE_CASH;request_state 沿用三值、终态不可回写 |
| 3 | 菜品管理 | 阶段性食谱按 recipe_id + version + period + 区间 发布;终端只看到当前有效版;选单确认前不落库;N-58 恒 40940 且 features.remoteOrdering=false |
| 4 | 提报与同步 | #10 LEDGER_OFFLINE 字段与表 12-26 一致;bizId=flowId、idempotencyKey 复用;归日以 serverReceivedAt;日对账通过才置 RECONCILED;差异 PF-5003 阻断调账 |
| 5 | 状态一致性 | 四条状态链(表 12-4)互不替代;ledger_state / request_state / offline_scope / publish_state 与 §4~§6 状态机、§9 异常表逐项对应;终态不回写 |
| 6 | 异常完整性 | 余额不足、CAS 冲突、断网超额度、重复提交、对账差异、食谱改价、远程点菜误调用、越权均有明确错误码与"不做什么" |
| 7 | 边界不破 | 跨端 20 编号、v2-draft、平台零回调、59 张表、不新增 bizType、apt.ledger.local / platform-ledger 单写入口不变 |
| 8 | 安全与审计 | 十类动作全部留痕;新增权限点前后端同码并登记 sys_menu.perms;gyid 不可由请求参数指定;金额禁止浮点、服务端重算 |