|
@@ -0,0 +1,228 @@
|
|
|
|
|
+# 自助洗车 — 平台独立账户设计说明
|
|
|
|
|
+
|
|
|
|
|
+## 1. 背景与动机
|
|
|
|
|
+
|
|
|
|
|
+### 1.1 旧方案的问题
|
|
|
|
|
+
|
|
|
|
|
+V2 结算方案落地后,平台账户以"虚拟站点"的形式存在:
|
|
|
|
|
+
|
|
|
|
|
+- `t_station_account` 中 `station_id = "0"` 的行即为平台账户
|
|
|
|
|
+- `StationAccount` 类中定义了两个常量:`PLATFORM_ACCOUNT_ID = 0`、`PLATFORM_STATION_ID = "0"`
|
|
|
|
|
+- 月结时对各站点计算的 `platformFee`(10% 服务费)仅保存在 `SettlementRecord.platform_fee` 字段中,不产生任何平台收入流水
|
|
|
|
|
+- 平台累计收入无法从数据库直接查询,只能遍历所有结算记录求和得出
|
|
|
|
|
+
|
|
|
|
|
+**核心矛盾**:平台和站点在业务上是两个不同的主体,但在代码和数据模型上被强行捏合在一起。平台不是站点,不应和站点共用一张表、一套逻辑。
|
|
|
|
|
+
|
|
|
|
|
+### 1.2 改造成因
|
|
|
|
|
+
|
|
|
|
|
+随着 V2 结算方案对模型的简化(废弃冻结/解冻、废弃 70% 即时结算),旧方案中平台作为"虚拟站点"参与分账的必要性已经消失。新方案下平台只需做一件事:**在每次站点结算时,记录应收取的软件服务费收入**。
|
|
|
|
|
+
|
|
|
|
|
+这是一个独立的、只与平台自身相关的能力,理应由独立的模型承载。
|
|
|
|
|
+
|
|
|
|
|
+## 2. 设计目标
|
|
|
|
|
+
|
|
|
|
|
+1. **平台账户独立**:平台拥有自己的账户实体和数据表,与站点账户彻底解耦
|
|
|
|
|
+2. **收入显式记录**:每次结算产生的平台服务费,生成一条可追溯的收入记录
|
|
|
|
|
+3. **审计友好**:`结算单 → 平台收入记录 → 平台账户余额` 形成完整的资金链路
|
|
|
|
|
+4. **对现有流程侵入最小**:结算公式、分账逻辑、提现流程均不变
|
|
|
|
|
+
|
|
|
|
|
+## 3. 数据模型
|
|
|
|
|
+
|
|
|
|
|
+### 3.1 平台账户 — `t_platform_account`
|
|
|
|
|
+
|
|
|
|
|
+单行表,整个平台只有一个账户。字段设计对标 `t_station_account`,预留在途提现相关字段供后续迭代。
|
|
|
|
|
+
|
|
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `id` | bigint | 主键 |
|
|
|
|
|
+| `total_revenue` | int | 累计服务费收入(分),只增不减 |
|
|
|
|
|
+| `available_balance` | int | 可用余额(分),当前已结算可动用的服务费收入 |
|
|
|
|
|
+| `withdrawn_frozen_amount` | int | 提现冻结金额(分),预留 |
|
|
|
|
|
+| `withdrawn_amount` | int | 已提现金额(分),预留 |
|
|
|
|
|
+| `version` | int | 乐观锁版本号,防并发写 |
|
|
|
|
|
+| `create_time` | datetime | 创建时间 |
|
|
|
|
|
+| `update_time` | datetime | 更新时间 |
|
|
|
|
|
+
|
|
|
|
|
+**初始化策略**:SQL 脚本中用 `INSERT ... WHERE NOT EXISTS` 在首次部署时自动创建一行全零记录;Java 层 `getPlatformAccount()` 也做了懒初始化兜底。
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 平台收入记录 — `t_platform_revenue_record`
|
|
|
|
|
+
|
|
|
|
|
+每次正常结算产生一条,与结算单一一对应(`settlement_record_id`),可追溯每笔平台费的来源站点和所属结算周期。
|
|
|
|
|
+
|
|
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `id` | bigint | 主键 |
|
|
|
|
|
+| `settlement_record_id` | bigint | 关联的结算单 ID |
|
|
|
|
|
+| `station_id` | varchar | 费用来源站点 |
|
|
|
|
|
+| `settlement_period` | varchar | 结算周期(如 `2026-05`) |
|
|
|
|
|
+| `amount` | int | 收入金额(分) |
|
|
|
|
|
+| `create_time` | datetime | 创建时间 |
|
|
|
|
|
+
|
|
|
|
|
+索引:
|
|
|
|
|
+- `idx_settlement_record_id` — 按结算单反查
|
|
|
|
|
+- `idx_station_id` — 按来源站点汇总
|
|
|
|
|
+- `idx_settlement_period` — 按周期汇总
|
|
|
|
|
+
|
|
|
|
|
+### 3.3 与结算单的关系
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+t_settlement_record(结算单)
|
|
|
|
|
+ │
|
|
|
|
|
+ │ settlement_record_id ──────┐
|
|
|
|
|
+ │ platform_fee │
|
|
|
|
|
+ │ │
|
|
|
|
|
+ ▼ ▼
|
|
|
|
|
+t_platform_revenue_record(平台收入记录)
|
|
|
|
|
+ │ settlement_record_id ← 外键关联
|
|
|
|
|
+ │ station_id ← 费用来源
|
|
|
|
|
+ │ settlement_period ← 结算周期
|
|
|
|
|
+ │ amount ← 对应 platform_fee
|
|
|
|
|
+ │
|
|
|
|
|
+ ▼
|
|
|
|
|
+t_platform_account(平台账户)
|
|
|
|
|
+ │ total_revenue += platform_fee
|
|
|
|
|
+ │ available_balance += platform_fee
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 4. 业务流程
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 结算时平台收入记录
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+每月 15 日定时任务 / 手动触发结算
|
|
|
|
|
+ │
|
|
|
|
|
+ ├── 汇总各站点上月 SplitRecord(充值、退款、跨店收支)
|
|
|
|
|
+ ├── 计算平台费基数、平台费、结算金额(公式不变)
|
|
|
|
|
+ │
|
|
|
|
|
+ ├── 正常结算(available > 0):
|
|
|
|
|
+ │ ├── 更新站点 availableBalance
|
|
|
|
|
+ │ ├── 保存 SettlementRecord
|
|
|
|
|
+ │ ├── [新增] 创建 PlatformRevenueRecord(platformFee > 0 时)
|
|
|
|
|
+ │ └── [新增] 更新 PlatformAccount.total_revenue += platformFee
|
|
|
|
|
+ │ 更新 PlatformAccount.available_balance += platformFee
|
|
|
|
|
+ │
|
|
|
|
|
+ └── 异常结算(available ≤ 0):
|
|
|
|
|
+ ├── 保存 SettlementRecord(status=异常结算)
|
|
|
|
|
+ └── 不产生平台收入(当期无实际结算金额)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 不变的部分
|
|
|
|
|
+
|
|
|
|
|
+- 充值、消费、退款、跨店消费的分账记录(`t_split_record`)生成逻辑完全不变
|
|
|
|
|
+- 结算公式不变:`平台费基数 = 总充值 - 总退款 - 跨店支出 + 跨店收入`
|
|
|
|
|
+- 10% 费率不变(`PLATFORM_FEE_RATE = 0.1`)
|
|
|
|
|
+- 站点账户的提现流程不变
|
|
|
|
|
+
|
|
|
|
|
+## 5. 关键设计决策
|
|
|
|
|
+
|
|
|
|
|
+### 5.1 为什么不用 `SplitRecord.TYPE_PLATFORM` 记录平台收入
|
|
|
|
|
+
|
|
|
|
|
+`SplitRecord`(`t_split_record`)的语义是"分账流水"——记录资金在站点之间的流转关系。平台收入并非"站点间分账",而是"站点向平台缴纳的服务费",属于不同业务域。混入同一张表会导致:
|
|
|
|
|
+
|
|
|
|
|
+- 查询分账记录时需要额外过滤掉平台记录
|
|
|
|
|
+- 分账汇总逻辑需要感知平台概念(这正是本次要消除的问题)
|
|
|
|
|
+- `fromStationId` / `toStationId` 字段对平台记录没有实际意义
|
|
|
|
|
+
|
|
|
|
|
+因此选择了独立的 `t_platform_revenue_record`。`SplitRecord.TYPE_PLATFORM = 0` 保留但标记 `@Deprecated`,防止未来有人误用。
|
|
|
|
|
+
|
|
|
|
|
+### 5.2 为什么异常结算不产生平台收入
|
|
|
|
|
+
|
|
|
|
|
+异常结算意味着站点当期结算金额为负(`available ≤ 0`),平台费虽然计算出来了但并未实际收取——站点没有收到可提现金额,平台也不应确认收入。负金额结转到下期(`closingPendingBalance`),待下期资金回正后一并结算。
|
|
|
|
|
+
|
|
|
|
|
+### 5.3 为什么平台账户使用乐观锁
|
|
|
|
|
+
|
|
|
|
|
+`t_platform_account` 是单行热点数据——每次站点结算成功都会更新。虽然当前结算流程是单事务串行(一个站点接一个站点),但预留乐观锁(`@Version`)可以在未来引入并行结算或手动运维操作时避免数据写覆盖。
|
|
|
|
|
+
|
|
|
|
|
+### 5.4 为什么预留提现字段
|
|
|
|
|
+
|
|
|
|
|
+平台账户当前是纯记账模型(只记录累计收入和可用余额),但平台未来可能需要将可用余额提现到企业银行账户。预留 `withdrawn_frozen_amount`、`withdrawn_amount` 字段使数据结构与站点账户保持一致,未来实现平台提现时无需改表。
|
|
|
|
|
+
|
|
|
|
|
+## 6. API 接口
|
|
|
|
|
+
|
|
|
|
|
+两个新端点均挂载在 `FinanceController` 下,复用现有权限体系。
|
|
|
|
|
+
|
|
|
|
|
+| 方法 | 路径 | 说明 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| POST | `/finance/platformAccount` | 查询平台账户信息(单行) |
|
|
|
|
|
+| POST | `/finance/platformRevenueRecords` | 分页查询平台收入记录,支持 `pageNum`/`pageSize` |
|
|
|
|
|
+
|
|
|
|
|
+响应示例:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+// POST /finance/platformAccount
|
|
|
|
|
+{
|
|
|
|
|
+ "code": 200,
|
|
|
|
|
+ "data": {
|
|
|
|
|
+ "id": 1,
|
|
|
|
|
+ "totalRevenue": 150000,
|
|
|
|
|
+ "availableBalance": 150000,
|
|
|
|
|
+ "withdrawnFrozenAmount": 0,
|
|
|
|
|
+ "withdrawnAmount": 0,
|
|
|
|
|
+ "version": 15
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// POST /finance/platformRevenueRecords
|
|
|
|
|
+{
|
|
|
|
|
+ "code": 200,
|
|
|
|
|
+ "data": {
|
|
|
|
|
+ "total": 15,
|
|
|
|
|
+ "records": [
|
|
|
|
|
+ {
|
|
|
|
|
+ "id": 1,
|
|
|
|
|
+ "settlementRecordId": 42,
|
|
|
|
|
+ "stationId": "10001",
|
|
|
|
|
+ "settlementPeriod": "2026-05",
|
|
|
|
|
+ "amount": 930
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 7. 数据对账
|
|
|
|
|
+
|
|
|
|
|
+平台总收入应与结算单中的平台费汇总一致:
|
|
|
|
|
+
|
|
|
|
|
+```sql
|
|
|
|
|
+-- 平台账户累计收入
|
|
|
|
|
+SELECT total_revenue FROM t_platform_account WHERE id = 1;
|
|
|
|
|
+
|
|
|
|
|
+-- 结算单平台费合计(应等于上述值)
|
|
|
|
|
+SELECT SUM(platform_fee) FROM t_settlement_record WHERE status = 1;
|
|
|
|
|
+
|
|
|
|
|
+-- 平台收入记录合计(应等于上述值)
|
|
|
|
|
+SELECT SUM(amount) FROM t_platform_revenue_record;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+三条查询结果应始终相等。如有偏差,说明存在直接操作数据库的旁路行为。
|
|
|
|
|
+
|
|
|
|
|
+## 8. 注意事项
|
|
|
|
|
+
|
|
|
|
|
+1. **`StationAccount.PLATFORM_ACCOUNT_ID` 和 `PLATFORM_STATION_ID` 已删除**。任何引用这些常量的旧代码需要同步移除。
|
|
|
|
|
+2. **`t_station_account` 中 `station_id = '0'` 的行应在部署时手动清除**(SQL 脚本中有注释掉的 DELETE 语句,按需执行)。
|
|
|
|
|
+3. **`SplitRecord.TYPE_PLATFORM = 0` 已废弃**,禁止在新增代码中使用。
|
|
|
|
|
+4. **平台账户不支持直接写入**,所有收入必须通过结算流程产生(`platformAccountService.addRevenue()` 仅在 `SettlementServiceImpl` 中调用)。
|
|
|
|
|
+5. **当前不支持平台提现**,`withdrawn_frozen_amount` 和 `withdrawn_amount` 字段仅供后续迭代使用。
|
|
|
|
|
+
|
|
|
|
|
+## 9. 文件清单
|
|
|
|
|
+
|
|
|
|
|
+| 文件 | 说明 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `car-wash-entity/.../entity/PlatformAccount.java` | 平台账户实体 |
|
|
|
|
|
+| `car-wash-entity/.../entity/PlatformRevenueRecord.java` | 平台收入记录实体 |
|
|
|
|
|
+| `car-wash-mapper/.../mapper/PlatformAccountMapper.java` | 平台账户 Mapper |
|
|
|
|
|
+| `car-wash-mapper/.../mapper/PlatformRevenueRecordMapper.java` | 平台收入记录 Mapper |
|
|
|
|
|
+| `car-wash-service/.../service/PlatformAccountService.java` | 平台账户服务接口 |
|
|
|
|
|
+| `car-wash-service/.../service/PlatformRevenueRecordService.java` | 平台收入记录服务接口 |
|
|
|
|
|
+| `car-wash-service/.../service/impl/PlatformAccountServiceImpl.java` | 平台账户服务实现 |
|
|
|
|
|
+| `car-wash-service/.../service/impl/PlatformRevenueRecordServiceImpl.java` | 平台收入记录服务实现 |
|
|
|
|
|
+| `car-wash-service/.../service/impl/SettlementServiceImpl.java` | 结算服务(核心改动点) |
|
|
|
|
|
+| `car-wash-admin/.../controller/FinanceController.java` | 财务接口(新增两个端点) |
|
|
|
|
|
+| `car-wash-entity/.../resources/sql/v2_settlement.sql` | V2 结算 SQL(新增两张表) |
|
|
|
|
|
+
|
|
|
|
|
+## 10. 版本记录
|
|
|
|
|
+
|
|
|
|
|
+| 版本 | 日期 | 说明 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| V1 | 2026-05-18 | 初稿,平台独立账户功能落地 |
|