# 自助洗车 — 平台与商家资金结算方案（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（分） | 累计已提现 |

**废弃字段**：`balance`、`frozenAmount`（旧冻结金额）

### 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）：
      以订单实收总额（充值余额 + 赠款余额）的 70% 为跨店转账基数
      记入 SplitRecord（type=跨店支出, from=A, to=B, amount=700）  ← 实收总额 × 70%
      记入 SplitRecord（type=跨店收入, from=A, to=B, amount=700）  ← 同上
      A 站保留 300 元（30%）作为拉新奖励
      赠款为归属站点自行发起的营销成本，参与跨店分账，由归属站承担
```

### 4.2.1 充值配置分组化（V2.2）

充值配置采用「分组 + 配置项」两级模型：

- **`t_recharge_config_group`**：配置分组，`station_id` 关联站点（NULL = 平台默认组），每站一个分组
- **`t_recharge_config`**：配置项，`group_id` 关联分组，单条记录表示一个充值金额档位

查询逻辑：先查站点专属分组 → 回退默认分组 → 返回组内配置项列表。充值回调（wxNotify）通过「归属站 + 金额」查找配置项，确保同金额不同组时赠款金额正确。新站点创建时自动创建专属分组并复制默认分组的配置项。

### 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.2 | 2026-05-26 | 充值配置分组化（group + item 两级模型） |
| V2.1 | 2026-05-26 | 跨店分账基数调整为充值余额+赠款余额；充值配置站点化（`RechargeConfig.stationId`）+ 默认配置回退 |
| V2-draft | 2026-05-14 | 初稿，基于业务讨论整理 |
