结算方案-业务说明.md 7.9 KB

自助洗车 — 平台与商家资金结算方案(V2)

1. 背景

当前结算方案采用"70% 即时结算 + 30% 冻结缓冲池"模式,商家充值后立即可提 70%,剩余 30% 在用户消费时按比例解冻返还,并扣除 10% 平台软件服务费。

该方案存在以下问题:

  • 分账逻辑复杂,冻结/解冻/分比例三层耦合,代码晦涩难维护
  • 30% 的冻结比例一刀切,无法差异化管控风险
  • 用户不消费则冻结资金永远无法释放,商家体验差
  • 冻结池与消费强绑定,对账困难

2. 新方案核心设计

2.1 基本原则

  • 以结算周期为核心:每月 15 日结算上一个自然月
  • 消费即结算:充值部分不直接分账,用户消费后才进入结算范畴
  • 平台服务费按消费额收取:费率 10%
  • 统一规则:所有商家统一 T+N(N 为上月天数),不区分等级

2.2 结算公式

平台服务费基数 = 总充值 - 总退款 - 跨店支出 + 跨店收入
平台服务费     = 平台服务费基数 × 10%

结算金额 = 总充值
          - 总退款
          - 跨店支出
          + 跨店收入
          - 平台服务费

设计意图

  • 平台服务费对站点实际涉及的每笔资金收取(充值、退款、跨店收支均纳入基数)
  • 退款部分不收取平台服务费(基数中减去退款)
  • 跨店消费:充值方和消费方各自按实收金额承担平台费

跨店转账比例:70:30

  • 用户在 A 站充值、去 B 站消费 1,000 元时,A 站向 B 站转账 700 元(消费金额的 70%),A 站保留 300 元(30%)作为拉新奖励
  • 即跨店支出/收入以 70% 记账

关键规则

  • 充值金额作为待结算资金,在结算日统一计算
  • 退款在退款发生的所属周期内扣除(与原始消费是否同周期无关)

示例

5 月份,站点 A 累计用户充值 10,000 元。用户跨店在站点 B 消费了 1,000 元。

站点 A 站点 B
总充值 10,000 0
总退款 0 0
跨店支出 700(1,000 × 70%) 0
跨店收入 0 700(1,000 × 70%)
平台费基数 10,000 - 0 - 700 + 0 = 9,300 0 - 0 - 0 + 700 = 700
平台费 9,300 × 10% = 930 700 × 10% = 70
结算金额 10,000 - 0 - 700 + 0 - 930 = 8,370 0 - 0 - 0 + 700 - 70 = 630

校验:8,370 + 630 + 1,000(平台费) = 10,000 ✓

A 站留住 300 元拉新奖励,B 站到手 630 元,平台合计收费 1,000 元。

2.3 结算周期

项目 说明
结算日 每月 15 日
结算范围 上一个自然月(1日 00:00 — 月末 23:59:59)
执行方式 定时任务
失败处理 日志记录 + TODO 标记,后续完善重试机制

3. 账户模型

3.1 新模型

废弃旧模型中的冻结/解冻概念,字段简化如下:

字段 类型 说明
availableBalance int(分) 已结算金额,可提现
frozenWithdrawAmount int(分) 提现申请处理中
totalWithdrawnAmount int(分) 累计已提现

废弃字段balancefrozenAmount(旧冻结金额)

3.2 待结算资金

不再维护一个独立的"待结算余额"字段,而是通过以下方式动态计算:

待结算资金 = 累计充值 - 累计退款(用户) - 累计消费 + 累计结算

即:充值未消费的部分,自然滚入下一周期,随消费逐步结算

4. 业务流程

4.1 充值

用户充值 100 元
  → 记入 SplitRecord(type=充值, from=平台, to=站点A)
  → 站点账户不立即增加可提现金额
  → 100 元成为站点的"待结算资金"

4.2 消费

用户消费 1,000 元(在站点B,充值站点为A)
  → 本站消费(A = B):
      记入 SplitRecord(type=消费, from=站点, to=站点),金额为消费全额
  → 跨店消费(A ≠ B):
      记入 SplitRecord(type=跨店支出, from=A, to=B, amount=700)  ← 消费金额 × 70%
      记入 SplitRecord(type=跨店收入, from=A, to=B, amount=700)  ← 同上
      A 站保留 300 元(30%)作为拉新奖励

4.3 退款

用户退款 15 元
  → 记入 SplitRecord(type=退款, from=站点, to=用户/平台)
  → 在当期结算时扣除

4.4 结算

每月 15 日 00:00 定时任务触发
  → 读取上期结算记录的 closing_pending_balance 作为本期期初
  → 汇总各站点上月:充值总额、退款总额、跨店收支
  → 平台服务费基数 = 总充值 - 总退款 - 跨店支出 + 跨店收入
  → 平台服务费 = 基数 × 10%
  → 结算金额 = 总充值 - 总退款 - 跨店支出 + 跨店收入 - 平台服务费
  → 期末待结算余额 = 期初 + 总充值 - 总退款 - 跨店支出 + 跨店收入 - 结算金额
  → 结算金额 > 0:增加站点 availableBalance,状态 = 已结算
  → 结算金额 ≤ 0:结算 0 元,状态 = 异常结算,等待下期资金为正
  → 生成 SettlementRecord(结算单)

4.5 提现

与旧方案基本一致,但可用余额来源变为 availableBalance

商家申请提现
  → 校验:提现金额 ≤ availableBalance
  → 扣减 availableBalance,增加 frozenWithdrawAmount
  → 生成 WithdrawnRecord(待审核)
  → 审核通过 → 扣减 frozenWithdrawAmount,增加 totalWithdrawnAmount
  → 打款确认 → 实际转账(后续对接微信企业付款)

5. 数据模型变更

5.1 新增表

t_settlement_record — 结算单

字段 类型 说明
id bigint 主键
station_id varchar 站点 ID
settlement_period varchar 结算周期(如 2026-05)
total_recharge int 本期总充值金额(分)
total_refund int 本期总退款金额(分)
total_cross_income int 跨店消费收入(分)
total_cross_expend int 跨店消费支出(分)
opening_pending_balance int 期初待结算余额(分)
closing_pending_balance int 期末待结算余额(分)
platform_fee_base int 平台服务费基数(总充值 - 总退款 - 跨店支出 + 跨店收入)
platform_fee int 平台服务费(基数 × 10%)(分)
settlement_amount int 实际结算金额(分)
status tinyint 0-待结算, 1-已结算, 2-异常结算
remark varchar 备注/失败原因
create_time datetime 创建时间
update_time datetime 更新时间

5.2 修改表

t_station_account — 站点账户

操作 字段
新增 available_balance int,已结算可提现金额
保留 frozen_withdraw_amount int,提现处理中
保留 total_withdrawn_amount int,累计已提现
废弃 balance
废弃 frozen_amount

t_split_record — 分账记录

操作 说明
废弃 type=3(解冻),不再需要解冻类型

t_station_fee_rate / t_platform_fee_rate — 费率表

操作 字段
废弃 frozen_ratio,不再需要冻结比例

6. 已确认事项

所有讨论项已确认,无待定事项。

# 事项 结论
1 结算金额为负 当期结算 0 元,标记结算记录为"异常结算",等待下期资金为正后正常结算
2 结算单余额追溯 记录期初待结算余额、期末待结算余额,便于观察资金趋势
3 定时任务分批 无需分批,站点量级不大,单事务直接结算即可

7. 迁移说明

当前处于开发环境,存量数据全部清理,不做迁移

清理范围:

  • t_station_account 全表
  • t_split_record 全表
  • t_withdrawn_record 全表
  • t_station_fee_rate / t_platform_fee_rate:费率重新配置

8. 版本记录

版本 日期 说明
V2-draft 2026-05-14 初稿,基于业务讨论整理