# 自助洗车 — 平台与商家资金结算方案(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 | 初稿,基于业务讨论整理 |