VWCG-1121 充值赠送账户 MVP 技术方案

版本:V0.9(评审稿)
日期:2026-07-27
适用仓库:storeai_apiai_app
风险等级:高
结论状态:有条件可实施,7 项产品/范围决策确认后进入开发

1. 方案摘要

本需求不是单纯增加一个 gift_balance 字段。要完整满足“充值赠送、不可提现、全渠道消费、可配置扣款顺序、原路退款”,必须同时改造账户、充值、消费、退款和对账链路。

推荐方案:

  1. yoshop_user 增加赠送余额,并把现有现金余额内部拆为“可提现现金 + 活动本金”。
  2. 新建充值赠送规则表与赠送流水表,充值订单复用三金额字段并保存命中规则快照。
  3. 新建账户结算单作为消费/退款幂等锚点,统一 PC、消费设备和小程序扣款算法。
  4. ydy_meal_order 增加赠送支付、赠送退款、赠送剩余金额及扣款顺序快照。
  5. ydy_config 保存全局扣款顺序,暂定默认值为“餐补 → 现金 → 赠送”;只影响新订单。
  6. 跨账户库与业务库采用“账户侧幂等提交 + 业务侧重试同步 + 对账补偿”,不依赖伪跨库事务。
  7. 充值订单、消费订单及相关人员/部门/设备统计同步调整异步导出。

2. 关键冲突与开发前决策

编号 问题 暂定方案 是否阻断开发
D1 云效 PRD 默认“餐补→赠送→现金”,本轮输入默认“餐补→现金→赠送” 以本轮输入为暂定值:subsidy,cash,gift
D2 命中赠送活动的充值本金不可提现,但现有 balance 没有本金属性 新增 withdrawable_balanceactivity_principal_balance,且两者之和等于 balance
D3 部分退款如何恢复账户 建议按该订单实际扣款逆序恢复,确保每次退款可复算、可追溯
D4 旧充值套餐赠送与新赠送活动关系 同一订单只能命中一种赠送机制;建议新活动表成为统一入口,旧套餐迁移后停用
D5 PC/小程序之外的旧 H5 充值是否改造 本期默认不含旧 H5,但上线前必须确认是否仍有真实流量
D6 source=10 是否直接产生扣款 先确认主/聚合订单是否只是父单;父单不得重复扣款
D7 PRD 的 scanPay.vue 在当前 ai_app 分支不存在 确认目标分支或真实页面后再排期

3. 范围与非范围

3.1 本期范围

3.2 本期非范围

4. 系统边界与目标架构

flowchart LR subgraph Clients["客户端与设备"] PC["PC 管理后台"] WX["小程序 ai_app"] DEV["绑盘机/消费机/闸机/点餐机"] EXT["外部订单"] end subgraph Services["服务层"] STORE["store\nPC、设备、订单域"] AI["ai_api\n小程序、账户域"] SETTLE["统一 AccountSettlementService\n规划、扣款、退款、幂等"] RECON["对账与补偿任务"] end subgraph Databases["数据层"] YDY[("业务库 ydy_*\nmeal_order/config/refund")] YOSHOP[("账户库 yoshop_*\nuser/recharge/log/settlement")] end PC --> STORE DEV --> STORE EXT --> STORE WX --> AI STORE --> SETTLE AI --> SETTLE SETTLE --> YOSHOP STORE --> YDY AI --> YDY RECON --> YOSHOP RECON --> YDY

4.1 设计原则

5. 账户与金额模型

5.1 账户定义

账户 用户可见 可消费 可提现 有效期 说明
餐补 沿用现状 继续以有效补贴明细合计为事实值
个人现金 部分可提现 永久 展示值仍为 balance
可提现现金 可在提现页展示 永久 withdrawable_balance
活动本金 可在明细中标识 永久 命中赠送活动时的 pay_price
赠送余额 永久 gift_balance

5.2 核心不变量

actual_money = pay_price + gift_money
balance = withdrawable_balance + activity_principal_balance
usable_total = valid_subsidy + balance + gift_balance
meal_order.pay_price = subsidy + cash + gift
cash = withdrawable_cash_payment + activity_principal_payment
累计退款金额 <= 原订单各账户实际扣款金额

建议现金账户消费内部顺序为“活动本金 → 可提现现金”,避免用户先消耗可提现资金而留下不可提现本金。这个内部顺序不改变前台显示的“现金”一级账户顺序,但须保存内部拆分以支持提现与退款。

6. 数据库设计

以下为逻辑设计,正式 DDL 应按项目数据库版本、索引长度和发布时间另行生成。

6.1 账户库 yoshop_*

yoshop_user

字段 类型 默认值 用途
gift_balance decimal(10,2) 0.00 赠送余额
withdrawable_balance decimal(10,2) 迁移确定 可提现现金
activity_principal_balance decimal(10,2) 0.00 活动本金

约束:

yoshop_recharge_gift_rule(新增)

<<<<<<< HEAD ======= >>>>>>> 5d05833ef476571c794ef3016336bc136526872c
字段组 建议字段
主键与租户 rule_idstore_id
展示 rule_name
匹配条件pay_methodmin_moneyplatformpay_methodmin_money
奖励 gift_money
状态 statusis_delete
审计 create_user_id/nameupdate_user_id/namecreate_timeupdate_time

匹配规则:

  1. 只匹配当前门店、启用且未删除的规则。
  2. pay_price >= min_money
  3. <<<<<<< HEAD
  4. 支付方式仅允许 cashalipay_offlinewechat_offlineonline_scan,规则不设置适用端。
  5. =======
  6. 平台、支付方式匹配;“全部”用明确枚举值表达。
  7. >>>>>>> 5d05833ef476571c794ef3016336bc136526872c
  8. 多条命中时依次取:min_money 最大、gift_money 最大、rule_id/create_time 最新。
  9. 同一充值订单最多命中一条规则。

推荐索引:

yoshop_recharge_order(复用并扩展)

继续复用:

建议增加快照字段:

<<<<<<< HEAD ======= >>>>>>> 5d05833ef476571c794ef3016336bc136526872c
字段 用途
gift_rule_id 命中规则 ID,未命中为 0
gift_rule_snapshot规则名、门槛、赠送金额、支付方式 JSON 快照规则名、门槛、赠送金额、平台、支付方式 JSON 快照
withdrawable_amount 本次进入可提现现金的金额
activity_principal_amount 本次进入活动本金的金额

规则:

yoshop_user_gift_log(新增)

字段组 建议字段
主键与租户 log_idstore_iduser_id
变动 scenemoneybalance_beforebalance_after
业务定位 biz_typebiz_order_idbiz_order_no
幂等 idempotency_key
审计 describeremarkoperator_id/namecreate_time

场景建议:

idempotency_key 建唯一索引,避免回调/消费/退款重复入账。

yoshop_account_settlement(新增)

用于跨库消费和退款的幂等、重试与对账,不取代现有账户流水。

字段组 建议字段
业务键 settlement_idbiz_typebiz_order_norefund_no
主体 user_idstore_idorder_source
金额 total_moneysubsidy_moneycash_moneygift_money
现金内部 withdrawable_moneyactivity_principal_money
快照 deduction_order_snapshotaccount_before_snapshotaccount_after_snapshot
状态 status:处理中/账户已提交/业务已同步/失败待处理
重试 retry_countlast_errornext_retry_time
时间 create_timeupdate_timecompleted_time

唯一键:

6.2 业务库 ydy_*

ydy_config

新增配置:

key: account_deduction_order
value: ["subsidy","cash","gift"]
desc: 消费账户扣款顺序

校验规则:

建议新增 ydy_account_config_log,记录配置键、变更前、变更后、操作人、时间、IP,满足账户设置审计要求。

ydy_meal_order

字段 用途
gift 本单赠送余额支付金额
gift_refund_amount 已退赠送金额
remain_gift 可退赠送金额
withdrawable_cash_payment 本单消耗的可提现现金
activity_principal_payment 本单消耗的活动本金
withdrawable_cash_refund 已恢复的可提现现金
activity_principal_refund 已恢复的活动本金
deduction_order_snapshot 本单实际使用的一级账户顺序
account_settlement_id 账户侧结算单 ID

兼容规则:

ydy_meal_order_refund

增加:

退款单保存本次实际恢复金额,不只保存申请金额。

7. 充值流程

sequenceDiagram participant C as PC/小程序 participant API as 充值服务 participant PAY as 支付渠道 participant DB as 账户库 <<<<<<< HEAD C->>API: 充值预览(pay_price, pay_method) ======= C->>API: 充值预览(pay_price, platform, pay_method) >>>>>>> 5d05833ef476571c794ef3016336bc136526872c API->>DB: 匹配启用规则 DB-->>API: 最优规则 API-->>C: pay/gift/actual 预览 C->>API: 创建充值订单 API->>DB: 服务端重新匹配并保存规则快照 API->>PAY: 发起支付 PAY-->>API: 支付成功回调 API->>DB: 锁定订单并校验未处理 API->>DB: 现金本金入账、赠送入账、分别写流水 API->>DB: 条件更新订单为已支付 API-->>PAY: 成功

7.1 服务端规则

7.2 三金额与流水

示例:支付 100 元,赠送 20 元。

项目 变动
pay_price 100.00
gift_money 20.00
actual_money 120.00
balance +100.00
activity_principal_balance +100.00
gift_balance +20.00
现金流水 +100.00,标记活动本金
赠送流水 +20.00,场景为充值赠送

8. 消费结算流程

8.1 统一算法

输入:

输出:

伪代码:

remaining = payable
for account in configured_order:
    used[account] = min(available(account), remaining)
    remaining -= used[account]
if remaining > 0:
    fail("账户余额不足")

if used.cash > 0:
    used.activity_principal = min(activity_principal_balance, used.cash)
    used.withdrawable = used.cash - used.activity_principal

8.2 跨库一致性

stateDiagram-v2 [*] --> Processing: 创建/锁定幂等结算单 Processing --> AccountCommitted: 账户库扣款与流水提交 AccountCommitted --> BusinessSynced: 业务订单写入拆分和结算ID AccountCommitted --> PendingSync: 业务库写入失败 PendingSync --> BusinessSynced: 重试成功 PendingSync --> ManualReview: 超过重试阈值 Processing --> Failed: 余额不足或业务校验失败

规则:

8.3 所有下单入口接入

来源 当前入口 改造要求
1/2/3/6/7 store/application/api/service/Consume.php 同步与队列路径调用同一结算服务,删除重复算法
4 storeai_apimeal/PaySuccess.php 统一调用结算服务;只保留渠道编排
4 的直接支付 ai_api/app/api/service/meal/DirectPay.php 移除独立 deductBalance() 规则
5 外部订单入口 纳入相同幂等键、余额检查与拆账
10 主/聚合订单 确认是否父单;父单不得与子单重复扣款

设备协议新增 gift_paymentremain_gift 等字段应保持可选,旧设备继续读取既有字段。

9. 退款与提现

9.1 消费退款

9.2 充值退款/提现

9.3 历史现金迁移

建议初始迁移:

withdrawable_balance = balance
activity_principal_balance = 0
gift_balance = 0

该方案保持历史现金可提现能力,不倒推历史活动属性。若产品要求按 365 天或旧活动记录重建可提现金额,必须另立数据治理任务并做差异报表,本期不能凭现有余额字段可靠推导。

10. API 与页面改造

10.1 PC 后台

模块 改造
人员管理-机构人员 列表返回并展示 gift_balance;总余额口径明确为餐补+现金+赠送
账户设置-赠送活动 规则列表、新增、编辑、启停、删除、命中预览
账户设置-扣款顺序 三账户拖拽/排序、保存、默认值、操作日志
充值订单 展示/筛选/统计 pay_pricegift_moneyactual_money 及命中规则
消费订单 展示现金、餐补、赠送及退款拆分
流水 增加赠送流水页或在统一流水中增加账户类型筛选

权限要求:

10.2 小程序 ai_app / ai_api

页面/接口 改造
用户资产接口 返回现金、可提现现金、活动本金、餐补、赠送及总可用余额
余额页 展示赠送余额和不可提现说明
充值页 输入金额后调用预览;展示支付、赠送、到账三金额
充值记录 展示三金额和命中活动
收银台 展示预计/实际账户拆分
订单详情 展示现金、餐补、赠送支付与退款
流水页 支持赠送账户流水与账户类型筛选

创建订单和支付成功响应建议增加:

{
  "pay_price": "100.00",
  "gift_money": "20.00",
  "actual_money": "120.00",
  "account_payment": {
    "subsidy": "10.00",
    "cash": "20.00",
    "gift": "30.00"
  },
  "deduction_order": ["subsidy", "cash", "gift"]
}

金额字段统一使用两位小数字符串,避免前端浮点误差。

11. 统计与异步导出

该列表页关联异步导出功能,需同步确认/修改异步导出逻辑。

关联列表 必须调整的口径
消费订单 增加赠送支付、赠送退款、赠送净额
人员消费明细 个人现金、餐补、赠送分列并校验合计
部门消费明细 汇总增加赠送支付与退款
设备消费明细 按设备/来源汇总增加赠送
充值统计 支付金额、赠送金额、实际到账分别统计,不能继续只汇总 actual_money

菜品销量、餐厅经营汇总、档口营收结算是否受影响,取决于其收入口径是否只看订单实付总额。若只看订单总额且不展示账户来源,可不改金额口径,但仍需回归证明。

12. 安全、并发与审计

13. 兼容、上线与回滚

13.1 上线顺序

  1. 在影子环境执行 DDL 并验证表锁、耗时、索引和回滚脚本。
  2. 先部署后端兼容代码,新增字段读取为空时按 0/默认顺序处理。
  3. 执行用户现金属性迁移并产出迁移前后余额对账报告。
  4. 部署 PC 与小程序,保持赠送规则全部停用。
  5. 对内部测试门店启用一条小额规则,覆盖充值、消费、退款、导出。
  6. 观察对账无差异后逐门店启用。

13.2 监控

13.3 回滚

14. 测试覆盖矩阵

测试域 必测场景 自动化层级
规则匹配 门槛前/等于/超过;平台/方式;禁用;多规则排序;规则修改后订单快照不变 单元 + 接口
充值 无赠送/有赠送;三金额;现金/赠送双流水;重复回调;并发回调;回调异常不返回假成功 服务 + 集成
现金属性 普通本金可提现;活动本金不可提现;现金内部优先级;余额不变量 单元 + 集成
扣款顺序 6 种排列;单账户/多账户;临界余额;余额不足;非法配置回退 参数化单元
订单来源 1、2、3、4、5、6、7 逐一覆盖;10 依据决策 集成 + 契约
幂等并发 同一订单重复消费、设备重试、队列重放、并发请求 集成
跨库补偿 账户提交后业务库失败;重试不重复扣款;对账识别并修复 集成 + 故障注入
退款 全额、部分、重复、超额;配置变更后退款;现金内部属性原路恢复 服务 + 集成
历史兼容 老用户、老充值单、老消费单、空快照、赠送字段为 0 回归
权限 无菜单权限、无功能权限、跨门店数据、操作日志 接口 + 手工
导出统计 页面合计与导出合计一致;支付/赠送/到账不混淆 接口 + 文件比对
小程序/PC 金额预览、确认页、订单详情、流水、错误/加载/空状态 前端 + 手工
设备 新旧协议字段兼容、超时重试、处理中状态 联调 + 手工

建议新增专门的 GiftAccount 测试组,并回归既有:

15. 验收映射

需求点 技术验收证据
人员列表赠送余额 API 返回值、页面截图、租户数据权限测试
赠送活动 规则 CRUD、命中边界测试、操作日志
PC+小程序充值 三金额订单、双流水、重复回调测试
默认扣款顺序 配置值、6 种顺序参数化测试、订单快照
所有下单 来源矩阵测试及各入口统一服务调用证据
消费流水 现金/餐补/赠送流水和结算单一致性
原路退款 全额/部分退款账户前后快照
不可提现 活动本金与赠送提现拦截
导出 页面、接口、异步导出三方合计比对
跨库一致性 故障注入、重试与对账报告

16. 推荐实施切片

  1. 数据结构、迁移脚本、账户不变量与对账基线。
  2. 赠送规则和 PC 账户设置权限/审计。
  3. PC、小程序充值三金额与双流水。
  4. 统一账户结算服务和消费幂等结算单。
  5. 按来源逐个接入所有下单入口。
  6. 消费退款、提现资格和跨库补偿。
  7. PC/小程序页面、统计、异步导出与全链路验收。

每个切片都应先补回归测试,再合并到下一切片;资金链路不建议一次性大爆炸发布。

17. 评审结论

方案可以作为研发拆分基线,但不能在 D1~D7 未确认前直接编码。尤其是“默认扣款顺序”“活动本金不可提现模型”“部分退款规则”三项会直接决定数据库字段和历史兼容策略。

文档评审通过后,应为各实施切片创建独立研发任务,并分别补充 DDL、API 契约、测试用例和发布证据。

<<<<<<< HEAD ======= >>>>>>> 5d05833ef476571c794ef3016336bc136526872c