次数口径
继续按当前成功订单数统计,不按 cash/subsidy/gift 最终扣款拆分。
TECHNICAL DEVELOPMENT DESIGN · VWCG-1119
覆盖 store 消费机 HTTP API、MQTT,以及 ai_api 小程序订餐的双项目技术方案。
本版不再根据“本单最终扣了哪个账户”决定规则,而是在下单/支付前读取所选受限账户的可用余额。
继续按当前成功订单数统计,不按 cash/subsidy/gift 最终扣款拆分。
规则只决定是否拦截,不修改营销价格、扣款顺序,也不自动换账户。
消费机在 store;小程序订餐在 ai_api。只改一个仓库无法闭环。
依赖 VWCG-1121;迁移与全链路验证前保持置灰。
| 账户 | “有钱”定义 | 排除项 |
|---|---|---|
| cash个人 | 活动本金余额 + 可提现余额;旧数据未拆分时兼容回退 balance | 透支额度默认不算 |
| subsidy餐补 | 判断时点有效、未删除且大于 0 的补贴余额之和 | 未生效、过期、负数 |
| gift赠送 | gift_balance 大于 0 | 能力未开放时不可配置 |
| 入口 | 真实调用链 | 接入要求 |
|---|---|---|
| 消费机 HTTP API | Consume controller → orderPayAsync | 覆盖 Redis 快照与 DB 回退 |
| 消费机 MQTT | MqttServer → orderPayAsync | 与 HTTP 共享 Service,不复制规则 |
| 小程序点餐 | MealCheckout / DirectPay → MealPayment / PaySuccess | 提交早检 + 支付最终复检 |
source=4 的订餐订单,没有小程序提交入口;小程序必须在 ai_api 改造。两个项目使用相同的评估输入、输出和契约样例。输入至少包含租户、人员、智慧餐厅用户、场景、档口、餐次、消费时间、余额快照和幂等号。
新增 ydy_consume_limit_account(limit_id, account_type, store_id, create_time),唯一索引为 (limit_id, account_type)。所有未删除历史规则(包括启用和停用)幂等回填 cash + subsidy,不自动回填 gift。
新增/编辑传 accountTypes: ["cash","subsidy"];详情返回 account_types。必须为数组、去重后至少一项,并执行枚举白名单与赠送能力校验。
增加命中账户、脱敏余额快照和渠道,渠道取值 consume_api、consume_mqtt、mini_program。
staff_uuid → store user → ai_user_id 映射。Consume.php 抽出消费限制评估器,按“账户余额交集 → 范围 → 次数”执行。orderPayAsync() 的支付锁内判断。handleAsyncSnapshotPayment() 和 runAsyncFallback() → orderPay()。MealCheckout::submit():创建订单前早检,避免产生不可支付订单。meal/DirectPay::pay():创建订单和扣款前,在同一事务内最终检查。cashier/MealPayment / meal/PaySuccess:支付前防御复检,覆盖历史待支付订单和旁路调用。双检查的目的:提交时尽早反馈,支付时处理余额、规则和时间变化,避免提交后充值或规则调整造成绕过。
| 风险 | 隐形后果 | 防护 |
|---|---|---|
| 只改 HTTP 或 MQTT | 上报方式不同,限制结果不同 | 共享 Service + 双入口契约测试 |
| 300 秒旧快照 | 充值/扣款/补贴变化后误判 | 锁内刷新并复用同快照 |
| 只改 store | 小程序完全不判断 | ai_api 三个接入点同步改 |
| 透支算余额 | 实际 0 余额仍触发规则 | 默认排除透支 |
| 账户异常当 0 | 跨库故障时规则绕过 | 失败关闭、提示重试 |
| 只在提交检查 | 支付时余额变化后绕过 | 支付前最终复检 |
| 次数先查后写 | 并发两单同时通过最后额度 | 跨项目同一用户/场景锁 |
| 两个项目算法漂移 | 消费机与小程序行为不一致 | 共享版本化契约样例 |
| 0 元自动支付 | 范围/次数被旁路 | 自动支付前检查 |
| 只做前端默认 | API/任务链路仍把旧规则当空配置 | SQL、详情、前端、运行期四层兼容 |
consume_limit:{storeId}:{zhctUserId}:{scenario},覆盖最终复检到成功订单落库。两项目 Redis 不共享时必须采用 mysql_zhct 数据库锁/守卫行。个人拆分/旧值、有效与过期餐补、赠送开关、多选任一有钱、透支、查询失败。
同一载荷走 HTTP 和 MQTT,除 channel/trace_id 外结果一致。
submit、0 元自动支付、余额支付、directPay、历史待支付复检。
MQTT 与小程序并发争抢最后一次额度,只允许一单成功。
本需求不改变订单列表字段或导出口径,九类异步导出无需同步修改。
本文按“任一所选账户有可用余额即生效”设计。
本文默认透支额度不属于“账户有钱”。
确认 store 与 ai_api 是否共享 Redis;否则采用 mysql_zhct 数据库锁。
本文默认也执行范围与次数限制。
确认在线时间漂移窗口;离线补传默认仍不实时拦截。