1101 lines
28 KiB
Markdown
1101 lines
28 KiB
Markdown
# 财务系统设计文档
|
||
|
||
> **版本**:v3.0(合并版)
|
||
> **最后更新**:2024-01-XX
|
||
> **状态**:✅ 已实现并优化
|
||
|
||
---
|
||
|
||
## 📋 目录
|
||
|
||
1. [概述](#概述)
|
||
2. [账户体系](#账户体系)
|
||
3. [资金流转](#资金流转)
|
||
4. [结算周期](#结算周期)
|
||
5. [服务费计算](#服务费计算)
|
||
6. [核心服务](#核心服务)
|
||
7. [数据库表结构](#数据库表结构)
|
||
8. [前端展示](#前端展示)
|
||
9. [API接口](#api接口)
|
||
10. [扩展方案](#扩展方案)
|
||
11. [安全保障](#安全保障)
|
||
12. [数据一致性保证](#数据一致性保证)
|
||
13. [测试验证](#测试验证)
|
||
14. [部署说明](#部署说明)
|
||
15. [常见问题](#常见问题)
|
||
16. [更新日志](#更新日志)
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
本文档描述了租房平台的财务系统架构,包括账户体系、资金流转、结算逻辑和安全保障机制。
|
||
|
||
**核心特性**:
|
||
- ✅ 四层账户体系(系统总账户、平台账户、商家账户、用户账户)
|
||
- ✅ 资金守恒验证机制
|
||
- ✅ 订单支付、结算、退款、提现完整流程
|
||
- ✅ 自动周结算(每周一凌晨2点)
|
||
- ✅ 邀请返现机制
|
||
- ✅ 多重安全保障(事务、悲观锁、乐观锁、复式记账)
|
||
- ✅ 完整的交易流水记录
|
||
|
||
---
|
||
|
||
## 账户体系
|
||
|
||
### 账户名称常量说明
|
||
|
||
为了提高代码可维护性和国际化支持,系统账户名称采用英文常量存储,前端显示时映射为中文。
|
||
|
||
**账户名称映射表:**
|
||
|
||
| 英文常量 (数据库存储) | 中文显示名称 | 说明 |
|
||
|---------------------|------------|------|
|
||
| `SYSTEM_MAIN` | 系统总账户 | 记录整个系统的资金流入流出,用于资金守恒验证 |
|
||
| `PLATFORM_MAIN` | 平台主账户 | 平台的主要收益账户,记录服务费收入和邀请返现支出 |
|
||
| `PLATFORM_BACKUP` | 平台备用账户 | 平台的备用账户(可选) |
|
||
|
||
**后端使用:**
|
||
```typescript
|
||
// 导入常量
|
||
import { ACCOUNT_NAMES } from '@/constants/account.constant';
|
||
|
||
// 使用常量查询
|
||
const systemAccount = await this.systemAccountRepo.findOne({
|
||
where: { account_name: ACCOUNT_NAMES.SYSTEM_MAIN }
|
||
});
|
||
|
||
const platformAccount = await this.platformAccountRepo.findOne({
|
||
where: { account_name: ACCOUNT_NAMES.PLATFORM_MAIN }
|
||
});
|
||
```
|
||
|
||
**前端使用:**
|
||
```typescript
|
||
// 导入映射工具
|
||
import { getAccountDisplayName } from '@/utils/accountNameMap';
|
||
|
||
// 将英文常量转换为中文显示
|
||
const displayName = getAccountDisplayName('SYSTEM_MAIN'); // 返回:系统总账户
|
||
const displayName2 = getAccountDisplayName('PLATFORM_MAIN'); // 返回:平台主账户
|
||
```
|
||
|
||
### 四层账户结构
|
||
|
||
系统采用四层账户结构,确保资金流转清晰、职责明确:
|
||
|
||
#### 1. 系统总账户 (System Account)
|
||
|
||
**职责**:记录平台内所有未提现的资金总额
|
||
|
||
**计算公式**:
|
||
```
|
||
系统总账户余额 = 用户实付总额 - 累计退款 - 累计提现
|
||
```
|
||
|
||
**字段说明**:
|
||
- `balance`: 当前余额
|
||
- `total_income`: 累计收入(用户实付总额)
|
||
- `total_refund`: 累计退款
|
||
- `total_withdrawn`: 累计提现
|
||
|
||
**数据表**:`system_accounts`
|
||
|
||
#### 2. 平台账户 (Platform Account)
|
||
|
||
**职责**:记录平台净收益(服务费收入 - 邀请返现支出)
|
||
|
||
**计算公式**:
|
||
```
|
||
平台账户余额 = 累计服务费收入 - 累计邀请返现支出
|
||
```
|
||
|
||
**字段说明**:
|
||
- `balance`: 当前余额
|
||
- `total_income`: 累计收入(服务费收入)
|
||
- `total_expense`: 累计支出(邀请返现支出)
|
||
|
||
**数据表**:`platform_accounts`
|
||
|
||
#### 3. 商家账户 (Merchant Account)
|
||
|
||
**职责**:记录商家的订单收入和提现
|
||
|
||
**计算公式**:
|
||
```
|
||
商家账户余额 = 累计订单收入 - 累计提现
|
||
订单收入 = 订单金额 - 服务费
|
||
```
|
||
|
||
**字段说明**:
|
||
- `balance`: 可用余额
|
||
- `frozen_balance`: 冻结余额(提现申请中)
|
||
- `total_income`: 累计收入
|
||
- `total_expense`: 累计支出(提现)
|
||
|
||
**数据表**:`merchant_accounts`
|
||
|
||
#### 4. 用户账户 (User Account)
|
||
|
||
**职责**:记录用户的邀请返现收入和提现
|
||
|
||
**计算公式**:
|
||
```
|
||
用户账户余额 = 累计邀请返现 - 累计提现
|
||
```
|
||
|
||
**字段说明**:
|
||
- `balance`: 可用余额
|
||
- `frozen_balance`: 冻结余额(提现申请中)
|
||
- `total_income`: 累计收入(邀请返现)
|
||
- `total_expense`: 累计支出(提现)
|
||
|
||
**数据表**:`user_accounts`
|
||
|
||
### 资金守恒验证
|
||
|
||
系统通过以下公式验证资金守恒:
|
||
|
||
```
|
||
系统总账户余额 = 商家账户余额总和 + 用户账户余额总和 + 平台账户余额
|
||
```
|
||
|
||
这个公式确保平台内所有资金都有明确的归属,不会出现资金丢失或凭空产生的情况。
|
||
|
||
## 资金流转
|
||
|
||
### 1. 订单支付流程
|
||
|
||
```
|
||
用户支付订单
|
||
↓
|
||
系统总账户 +实付金额
|
||
↓
|
||
订单状态:pending_pay → pending_confirm
|
||
```
|
||
|
||
**涉及文件**:
|
||
- `apps/server/src/modules/app/order/order.service.ts` (payByOrderNo, pay 方法)
|
||
- `apps/server/src/modules/shared/finance/account.service.ts` (addSystemIncome 方法)
|
||
|
||
**关键代码**:
|
||
```typescript
|
||
// 记录系统总账户收入(用户实付金额)
|
||
await this.accountService.addSystemIncome(
|
||
order.payAmount,
|
||
transactionNo,
|
||
'order_payment',
|
||
order.id,
|
||
order.orderNo,
|
||
`用户支付订单:${order.orderNo}`,
|
||
);
|
||
```
|
||
|
||
### 2. 订单结算流程
|
||
|
||
```
|
||
订单完成(用户离店)
|
||
↓
|
||
商家账户 +(实付金额 - 服务费)
|
||
↓
|
||
平台账户 +服务费
|
||
↓
|
||
如果有邀请关系:
|
||
用户账户(邀请人)+返现金额
|
||
平台账户 -返现金额
|
||
```
|
||
|
||
**涉及文件**:
|
||
- `apps/server/src/modules/shared/finance/settlement.service.ts` (createSettlement 方法)
|
||
- `apps/server/src/modules/app/activity/activity.service.ts` (handleOrderCompleted 方法)
|
||
|
||
**关键代码**:
|
||
```typescript
|
||
// 给商家账户增加余额
|
||
await this.accountService.addMerchantBalance(
|
||
merchantId,
|
||
settlementAmount, // 订单金额 - 服务费
|
||
transactionNo,
|
||
'settlement',
|
||
settlement.id,
|
||
settlementNo,
|
||
`商家周结算:${periodStart} ~ ${periodEnd}`
|
||
);
|
||
|
||
// 给平台账户增加服务费收入
|
||
await this.accountService.addPlatformBalance(
|
||
serviceFee,
|
||
transactionNo,
|
||
'settlement',
|
||
settlement.id,
|
||
settlementNo,
|
||
`商家 ${merchantId} 周结算服务费:${periodStart} ~ ${periodEnd}`
|
||
);
|
||
|
||
// 邀请返现(如果有)
|
||
await this.accountService.addUserBalance(
|
||
invitation.inviterId,
|
||
amount,
|
||
transactionNo,
|
||
'invite_cashback',
|
||
cashback.id,
|
||
order.orderNo,
|
||
`邀请返现 - 订单${order.orderNo}第${orderIndex}单`,
|
||
);
|
||
|
||
await this.accountService.deductPlatformCashback(
|
||
amount,
|
||
transactionNo,
|
||
'invite_cashback',
|
||
cashback.id,
|
||
order.orderNo,
|
||
`邀请返现支出 - 用户${invitation.inviterId}订单${order.orderNo}`,
|
||
);
|
||
```
|
||
|
||
### 3. 订单退款流程
|
||
|
||
```
|
||
用户申请退款
|
||
↓
|
||
调用第三方支付退款API
|
||
↓
|
||
系统总账户 +退款金额
|
||
↓
|
||
订单状态:pending_confirm/pending_checkin → refunded
|
||
```
|
||
|
||
**涉及文件**:
|
||
- `apps/server/src/modules/shared/finance/refund.service.ts` (processRefund 方法)
|
||
|
||
**关键代码**:
|
||
```typescript
|
||
// 记录系统总账户退款
|
||
await this.accountService.addSystemRefund(
|
||
order.payAmount,
|
||
transactionNo,
|
||
'order_refund',
|
||
order.id,
|
||
order.orderNo,
|
||
`订单退款 - ${order.orderNo}`,
|
||
);
|
||
```
|
||
|
||
### 4. 提现流程
|
||
|
||
#### 用户提现
|
||
|
||
```
|
||
用户申请提现
|
||
↓
|
||
用户账户:balance → frozen_balance
|
||
↓
|
||
管理员审核通过
|
||
↓
|
||
管理员确认打款
|
||
↓
|
||
用户账户 -提现金额(从frozen_balance扣减)
|
||
系统总账户 +提现金额
|
||
↓
|
||
提现状态:pending → approved → paid
|
||
```
|
||
|
||
#### 商家提现
|
||
|
||
```
|
||
商家申请提现
|
||
↓
|
||
商家账户:balance → frozen_balance
|
||
↓
|
||
管理员审核通过
|
||
↓
|
||
管理员确认打款
|
||
↓
|
||
商家账户 -提现金额(从frozen_balance扣减)
|
||
系统总账户 +提现金额
|
||
↓
|
||
提现状态:pending → approved → paid
|
||
```
|
||
|
||
**涉及文件**:
|
||
- `apps/server/src/modules/shared/finance/withdrawal.service.ts` (payUserWithdrawal, payMerchantWithdrawal 方法)
|
||
|
||
**关键代码**:
|
||
```typescript
|
||
// 扣减用户账户余额
|
||
await this.accountService.deductUserBalance(
|
||
withdrawal.userId,
|
||
Number(withdrawal.actualAmount),
|
||
transactionNo,
|
||
'withdraw',
|
||
withdrawal.id,
|
||
withdrawal.withdrawNo,
|
||
`用户提现 - ${withdrawal.paymentChannel}`
|
||
);
|
||
|
||
// 记录系统总账户提现
|
||
await this.accountService.addSystemWithdrawal(
|
||
Number(withdrawal.actualAmount),
|
||
transactionNo,
|
||
'user_withdraw',
|
||
withdrawal.id,
|
||
withdrawal.withdrawNo,
|
||
`用户 ${withdrawal.userId} 提现 - ${withdrawal.paymentChannel}`,
|
||
);
|
||
```
|
||
|
||
## 结算周期
|
||
|
||
### 周结算机制
|
||
|
||
**执行时间**:每周一凌晨 2:00
|
||
**结算周期**:上周一 00:00:00 ~ 上周日 23:59:59
|
||
**结算对象**:状态为 `completed` 的订单
|
||
**判断依据**:订单的 `checkout_at`(离店时间)字段
|
||
|
||
### 定时任务配置
|
||
|
||
**文件位置**:`apps/server/src/schedule/settlement.schedule.ts`
|
||
|
||
```typescript
|
||
@Cron('0 2 * * 1') // 每周一凌晨2点
|
||
async handleWeeklySettlement() {
|
||
await this.settlementService.handleWeeklySettlement();
|
||
}
|
||
```
|
||
|
||
### 结算逻辑
|
||
|
||
1. **查询上周已完成订单**
|
||
```typescript
|
||
const orders = await this.orderRepo
|
||
.createQueryBuilder('o')
|
||
.where('o.status = :status', { status: 'completed' })
|
||
.andWhere('o.checkout_at BETWEEN :start AND :end', {
|
||
start: `${lastWeekStart} 00:00:00`,
|
||
end: `${lastWeekEnd} 23:59:59`
|
||
})
|
||
.getMany();
|
||
```
|
||
|
||
2. **按商家分组统计**
|
||
- 订单数量:`orderCount`
|
||
- 订单总额:`orderAmount`
|
||
- 服务费总额:`serviceFee`
|
||
- 结算金额:`settlementAmount = orderAmount - serviceFee`
|
||
|
||
3. **生成结算单**
|
||
- 创建 `Settlement` 记录
|
||
- 创建 `SettlementItem` 记录
|
||
|
||
4. **执行资金转账**
|
||
- 商家账户增加
|
||
- 平台账户增加(服务费)
|
||
- 复式记账
|
||
|
||
### 防止重复结算
|
||
|
||
```typescript
|
||
// 检查该周期是否已经结算过
|
||
const existingSettlements = await this.settlementRepo.count({
|
||
where: {
|
||
periodStart: lastWeekStart,
|
||
periodEnd: lastWeekEnd
|
||
}
|
||
});
|
||
|
||
if (existingSettlements > 0) {
|
||
throw new Error(`该周期 ${lastWeekStart} ~ ${lastWeekEnd} 已经结算过,无法重复结算`);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 服务费计算
|
||
|
||
### 计算公式
|
||
|
||
```typescript
|
||
// 用户实付金额
|
||
payAmount = totalAmount - couponDiscount
|
||
|
||
// 服务费
|
||
serviceFee = Math.round(payAmount * serviceFeeRate * 100) / 100
|
||
|
||
// 商家实收金额
|
||
merchantIncome = payAmount - serviceFee
|
||
```
|
||
|
||
### 服务费率配置
|
||
|
||
**默认值**:5% (0.05)
|
||
**配置位置**:`platform_configs` 表
|
||
**配置键**:`service_fee_rate`
|
||
|
||
### 示例计算
|
||
|
||
```
|
||
房费总额:1000元
|
||
优惠券抵扣:100元
|
||
服务费率:5%
|
||
|
||
用户实付:1000 - 100 = 900元
|
||
服务费:900 × 0.05 = 45元
|
||
商家实收:900 - 45 = 855元
|
||
平台收入:45元
|
||
```
|
||
|
||
---
|
||
|
||
## 核心服务
|
||
|
||
### AccountService
|
||
|
||
账户操作核心服务,提供所有账户的增减操作。
|
||
|
||
**位置**:`apps/server/src/modules/shared/finance/account.service.ts`
|
||
|
||
**主要方法**:
|
||
|
||
#### 系统总账户操作
|
||
- `getSystemAccount()` - 获取系统总账户
|
||
- `addSystemIncome(amount, ...)` - 记录用户实付收入
|
||
- `addSystemRefund(amount, ...)` - 记录退款
|
||
- `addSystemWithdrawal(amount, ...)` - 记录提现
|
||
|
||
#### 平台账户操作
|
||
- `addPlatformBalance(serviceFee, ...)` - 记录服务费收入
|
||
- `deductPlatformCashback(amount, ...)` - 记录邀请返现支出
|
||
|
||
**核心代码示例**:
|
||
|
||
```typescript
|
||
// 平台账户增加余额(服务费收入)
|
||
async addPlatformBalance(
|
||
amount: number,
|
||
transactionNo: string,
|
||
businessType: string,
|
||
businessId: number,
|
||
businessNo: string,
|
||
remark: string,
|
||
): Promise<void> {
|
||
const queryRunner = this.dataSource.createQueryRunner();
|
||
await queryRunner.connect();
|
||
await queryRunner.startTransaction();
|
||
|
||
try {
|
||
// 使用悲观锁查询账户
|
||
const account = await queryRunner.manager.findOne(PlatformAccount, {
|
||
where: { account_name: '主账户' },
|
||
lock: { mode: 'pessimistic_write' },
|
||
});
|
||
|
||
// 金额精度控制
|
||
const amountNum = Number(amount);
|
||
const balanceBefore = Number(account.balance);
|
||
const balanceAfter = parseFloat((balanceBefore + amountNum).toFixed(2));
|
||
|
||
// 更新账户
|
||
account.balance = balanceAfter;
|
||
account.total_income = parseFloat((Number(account.total_income) + amountNum).toFixed(2));
|
||
account.version += 1;
|
||
|
||
await queryRunner.manager.save(account);
|
||
|
||
// 记录交易流水
|
||
const transaction = queryRunner.manager.create(PlatformTransaction, {
|
||
transactionNo,
|
||
accountId: account.id,
|
||
direction: 'income',
|
||
amount: amountNum,
|
||
balanceBefore,
|
||
balanceAfter,
|
||
transactionType: 'service_fee',
|
||
businessType,
|
||
businessId,
|
||
businessNo,
|
||
remark,
|
||
});
|
||
|
||
await queryRunner.manager.save(transaction);
|
||
await queryRunner.commitTransaction();
|
||
} catch (error) {
|
||
await queryRunner.rollbackTransaction();
|
||
throw error;
|
||
} finally {
|
||
await queryRunner.release();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 商家账户操作
|
||
- `addMerchantBalance(merchantId, amount, ...)` - 增加商家余额
|
||
- `deductMerchantBalance(merchantId, amount, ...)` - 扣减商家余额
|
||
- `freezeMerchantBalance(merchantId, amount)` - 冻结商家余额
|
||
- `unfreezeMerchantBalance(merchantId, amount)` - 解冻商家余额
|
||
|
||
#### 用户账户操作
|
||
- `addUserBalance(userId, amount, ...)` - 增加用户余额
|
||
- `deductUserBalance(userId, amount, ...)` - 扣减用户余额
|
||
- `freezeUserBalance(userId, amount)` - 冻结用户余额
|
||
- `unfreezeUserBalance(userId, amount)` - 解冻用户余额
|
||
|
||
**安全机制**:
|
||
- 使用数据库事务确保操作原子性
|
||
- 使用悲观锁(`pessimistic_write`)防止并发修改
|
||
- 使用乐观锁(`version` 字段)检测并发冲突
|
||
- 所有金额计算使用 `Number()` 和 `toFixed(2)` 确保精度
|
||
|
||
### SettlementService
|
||
|
||
订单结算服务,负责商家的周结算。
|
||
|
||
**位置**:`apps/server/src/modules/shared/finance/settlement.service.ts`
|
||
|
||
**主要功能**:
|
||
- 每周一凌晨2点自动执行周结算
|
||
- 统计上周已完成订单
|
||
- 按商家分组计算结算金额
|
||
- 更新商家账户和平台账户
|
||
|
||
### RefundService
|
||
|
||
退款服务,负责订单退款处理。
|
||
|
||
**位置**:`apps/server/src/modules/shared/finance/refund.service.ts`
|
||
|
||
**主要功能**:
|
||
- 调用第三方支付退款API(微信支付、支付宝)
|
||
- 更新订单状态
|
||
- 记录系统总账户退款
|
||
|
||
### WithdrawalService
|
||
|
||
提现服务,负责用户和商家的提现申请、审核、打款。
|
||
|
||
**位置**:`apps/server/src/modules/shared/finance/withdrawal.service.ts`
|
||
|
||
**主要功能**:
|
||
- 提现申请(冻结余额)
|
||
- 提现审核(通过/拒绝)
|
||
- 提现打款(扣减余额,记录系统总账户提现)
|
||
|
||
### ReportService
|
||
|
||
财务报表服务,提供财务数据统计。
|
||
|
||
**位置**:`apps/server/src/modules/shared/finance/report.service.ts`
|
||
|
||
**主要功能**:
|
||
- 财务总览(系统总账户、平台账户、商家账户、用户账户统计)
|
||
- 财务趋势(按日统计收入支出)
|
||
- 资金守恒验证
|
||
|
||
## 数据库表结构
|
||
|
||
### system_accounts(系统总账户表)
|
||
|
||
```sql
|
||
CREATE TABLE `system_accounts` (
|
||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
`account_name` VARCHAR(50) NOT NULL COMMENT '账户名称',
|
||
`balance` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '可用余额',
|
||
`total_income` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '累计收入(用户实付总额)',
|
||
`total_refund` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '累计退款',
|
||
`total_withdrawn` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '累计提现',
|
||
`version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_account_name` (`account_name`)
|
||
) COMMENT='系统总账户表';
|
||
```
|
||
|
||
### system_transactions(系统总账户交易流水表)
|
||
|
||
```sql
|
||
CREATE TABLE `system_transactions` (
|
||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
`transaction_no` VARCHAR(64) NOT NULL COMMENT '交易流水号',
|
||
`account_id` BIGINT UNSIGNED NOT NULL COMMENT '账户ID',
|
||
`direction` ENUM('income','expense') NOT NULL COMMENT '交易方向',
|
||
`amount` DECIMAL(12,2) NOT NULL COMMENT '交易金额',
|
||
`balance_before` DECIMAL(12,2) NOT NULL COMMENT '交易前余额',
|
||
`balance_after` DECIMAL(12,2) NOT NULL COMMENT '交易后余额',
|
||
`transaction_type` VARCHAR(50) NOT NULL COMMENT '交易类型',
|
||
`business_type` VARCHAR(50) NOT NULL COMMENT '业务类型',
|
||
`business_id` BIGINT UNSIGNED NOT NULL COMMENT '业务ID',
|
||
`business_no` VARCHAR(64) NOT NULL COMMENT '业务单号',
|
||
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_transaction_no` (`transaction_no`),
|
||
KEY `idx_account_id` (`account_id`),
|
||
KEY `idx_business` (`business_type`, `business_id`),
|
||
KEY `idx_created_at` (`created_at`)
|
||
) COMMENT='系统总账户交易流水表';
|
||
```
|
||
|
||
### platform_accounts(平台账户表)
|
||
|
||
```sql
|
||
CREATE TABLE `platform_accounts` (
|
||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
`account_name` VARCHAR(50) NOT NULL COMMENT '账户名称',
|
||
`balance` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '可用余额',
|
||
`total_income` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '累计收入(服务费收入)',
|
||
`total_expense` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '累计支出(邀请返现支出)',
|
||
`version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_account_name` (`account_name`)
|
||
) COMMENT='平台账户表';
|
||
```
|
||
|
||
## 前端展示
|
||
|
||
### 1. 财务总览页面
|
||
|
||
**位置**:`apps/platform-admin/src/pages/finance/Dashboard.tsx`
|
||
|
||
**展示内容**:
|
||
|
||
1. **系统总账户金额**(突出显示)
|
||
- 计算公式:用户实付 - 退款 - 提现
|
||
- 资金守恒验证:= 商家账户余额 + 用户账户余额 + 平台净收益
|
||
|
||
2. **平台净收益**
|
||
- 服务费收入 - 邀请返现支出
|
||
|
||
3. **用户总余额**
|
||
- 所有用户账户余额总和
|
||
- 用户账户数量
|
||
|
||
4. **商家总余额**
|
||
- 所有商家账户余额总和
|
||
- 商家账户数量
|
||
|
||
5. **今日统计**
|
||
- 今日订单数
|
||
- 今日收入
|
||
- 今日支出
|
||
- 今日净收益
|
||
|
||
### 2. 平台钱包页面
|
||
|
||
**位置**:`apps/platform-admin/src/pages/finance/PlatformWallet.tsx`
|
||
|
||
**功能特性**:
|
||
- 钱包余额、可提现金额、冻结金额、服务费收入展示
|
||
- 账户总收入、总支出、其他收入统计
|
||
- 钱包详情查看
|
||
- 申请提现功能(集成在页面内)
|
||
|
||
### 3. 平台交易记录
|
||
|
||
**位置**:`apps/platform-admin/src/pages/finance/PlatformTransactions.tsx`
|
||
|
||
**功能特性**:
|
||
- 交易流水查询(按流水号、方向、类型、时间筛选)
|
||
- 交易详情展示
|
||
- 分页展示
|
||
|
||
### 4. 结算管理
|
||
|
||
**位置**:`apps/platform-admin/src/pages/finance/Settlements.tsx`
|
||
|
||
**功能特性**:
|
||
- 结算单列表查询
|
||
- 结算单详情查看
|
||
- 预览周结算数据
|
||
- 手动执行周结算
|
||
|
||
### 5. 商家提现审核
|
||
|
||
**位置**:`apps/platform-admin/src/pages/finance/Withdrawals.tsx`
|
||
|
||
**功能特性**:
|
||
- 商家提现申请列表
|
||
- 审核通过/拒绝
|
||
- 确认打款
|
||
|
||
---
|
||
|
||
## API接口
|
||
|
||
### 平台钱包接口
|
||
|
||
#### 1. 查询平台账户
|
||
|
||
**接口**:`GET /api/admin/finance/platform-accounts`
|
||
**权限**:平台管理员
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"account_name": "主账户",
|
||
"balance": 40.00,
|
||
"frozen_balance": 0.00,
|
||
"total_income": 1000.00,
|
||
"total_expense": 960.00,
|
||
"status": "active"
|
||
}
|
||
```
|
||
|
||
#### 2. 平台申请提现
|
||
|
||
**接口**:`POST /api/admin/finance/platform-withdrawals`
|
||
**权限**:平台管理员
|
||
|
||
**请求参数**:
|
||
```json
|
||
{
|
||
"amount": 30.00,
|
||
"bankName": "中国工商银行",
|
||
"bankAccount": "6222021234567890123",
|
||
"accountName": "某某科技有限公司",
|
||
"remark": "提现备注"
|
||
}
|
||
```
|
||
|
||
### 结算管理接口
|
||
|
||
#### 3. 预览周结算
|
||
|
||
**接口**:`GET /api/admin/finance/settlements/preview-weekly`
|
||
**权限**:平台管理员
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"periodStart": "2026-05-13",
|
||
"periodEnd": "2026-05-19",
|
||
"totalOrders": 45,
|
||
"totalAmount": 35000.00,
|
||
"totalServiceFee": 1750.00,
|
||
"totalSettlementAmount": 33250.00,
|
||
"merchants": [...]
|
||
}
|
||
```
|
||
|
||
#### 4. 执行周结算
|
||
|
||
**接口**:`POST /api/admin/finance/settlements/execute-weekly`
|
||
**权限**:平台管理员
|
||
|
||
---
|
||
|
||
## 扩展方案
|
||
|
||
### 未来增加其他收入
|
||
|
||
#### 1. 数据库增加字段
|
||
|
||
```sql
|
||
ALTER TABLE platform_accounts
|
||
ADD COLUMN total_ad_revenue DECIMAL(12,2) DEFAULT 0 COMMENT '累计广告收入',
|
||
ADD COLUMN total_membership_fee DECIMAL(12,2) DEFAULT 0 COMMENT '累计会员费收入',
|
||
ADD COLUMN total_other_income DECIMAL(12,2) DEFAULT 0 COMMENT '累计其他收入';
|
||
```
|
||
|
||
#### 2. 后端增加方法
|
||
|
||
```typescript
|
||
// 广告收入
|
||
async addPlatformAdRevenue(amount: number, ...): Promise<void> {
|
||
account.balance += amount;
|
||
account.total_income += amount;
|
||
account.total_ad_revenue += amount;
|
||
}
|
||
|
||
// 会员费收入
|
||
async addPlatformMembershipFee(amount: number, ...): Promise<void> {
|
||
account.balance += amount;
|
||
account.total_income += amount;
|
||
account.total_membership_fee += amount;
|
||
}
|
||
```
|
||
|
||
#### 3. 前端增加展示
|
||
|
||
```typescript
|
||
<Statistic
|
||
title="广告收入"
|
||
value={account.total_ad_revenue}
|
||
precision={2}
|
||
suffix="元"
|
||
/>
|
||
|
||
<Statistic
|
||
title="会员费收入"
|
||
value={account.total_membership_fee}
|
||
precision={2}
|
||
suffix="元"
|
||
/>
|
||
```
|
||
|
||
#### 4. 调整可提现计算
|
||
|
||
```typescript
|
||
// 方案1:继续使用各项收入累加
|
||
const withdrawableAmount = (
|
||
total_service_fee +
|
||
total_ad_revenue +
|
||
total_membership_fee
|
||
) - frozen_balance;
|
||
|
||
// 方案2:直接使用钱包余额(推荐)
|
||
const withdrawableAmount = balance - frozen_balance;
|
||
```
|
||
|
||
**推荐方案2**,因为:
|
||
- `balance` 已经包含了所有收入和支出
|
||
- 不需要每次增加新收入类型都修改代码
|
||
- 更简洁、更易维护
|
||
|
||
---
|
||
|
||
## 前端展示
|
||
|
||
## 安全保障
|
||
|
||
### 1. 数据库事务
|
||
|
||
所有涉及账户操作的方法都使用数据库事务,确保操作的原子性:
|
||
|
||
```typescript
|
||
const queryRunner = this.dataSource.createQueryRunner();
|
||
await queryRunner.connect();
|
||
await queryRunner.startTransaction();
|
||
|
||
try {
|
||
// 账户操作
|
||
await queryRunner.commitTransaction();
|
||
} catch (error) {
|
||
await queryRunner.rollbackTransaction();
|
||
throw error;
|
||
} finally {
|
||
await queryRunner.release();
|
||
}
|
||
```
|
||
|
||
### 2. 悲观锁
|
||
|
||
在查询账户时使用悲观锁,防止并发修改:
|
||
|
||
```typescript
|
||
const account = await queryRunner.manager.findOne(SystemAccount, {
|
||
where: { account_name: '系统总账户' },
|
||
lock: { mode: 'pessimistic_write' },
|
||
});
|
||
```
|
||
|
||
### 3. 乐观锁
|
||
|
||
使用 `version` 字段实现乐观锁,检测并发冲突:
|
||
|
||
```typescript
|
||
account.version += 1;
|
||
await queryRunner.manager.save(account);
|
||
```
|
||
|
||
### 4. 金额精度控制
|
||
|
||
所有金额计算都使用 `Number()` 转换和 `toFixed(2)` 保留两位小数:
|
||
|
||
```typescript
|
||
const amountNum = Number(amount);
|
||
const balanceBefore = Number(account.balance);
|
||
const balanceAfter = parseFloat((balanceBefore + amountNum).toFixed(2));
|
||
```
|
||
|
||
### 5. 余额校验
|
||
|
||
在扣减余额前检查余额是否足够:
|
||
|
||
```typescript
|
||
if (balanceBefore < amount) {
|
||
throw new BadRequestException('账户余额不足');
|
||
}
|
||
```
|
||
|
||
### 6. 交易流水
|
||
|
||
所有账户操作都记录详细的交易流水,包括:
|
||
- 交易流水号(唯一)
|
||
- 交易前后余额
|
||
- 交易类型和业务类型
|
||
- 关联的业务ID和业务单号
|
||
- 备注信息
|
||
|
||
## 测试验证
|
||
|
||
### 资金守恒验证
|
||
|
||
定期执行以下查询验证资金守恒:
|
||
|
||
```sql
|
||
-- 查询系统总账户余额
|
||
SELECT balance FROM system_accounts WHERE account_name = '系统总账户';
|
||
|
||
-- 查询商家账户余额总和
|
||
SELECT SUM(balance) FROM merchant_accounts;
|
||
|
||
-- 查询用户账户余额总和
|
||
SELECT SUM(balance) FROM user_accounts;
|
||
|
||
-- 查询平台账户余额
|
||
SELECT balance FROM platform_accounts WHERE account_name = '主账户';
|
||
|
||
-- 验证:系统总账户余额 = 商家账户余额 + 用户账户余额 + 平台账户余额
|
||
```
|
||
|
||
### 完整流程测试
|
||
|
||
1. **订单支付测试**
|
||
- 创建订单并支付
|
||
- 验证系统总账户余额增加
|
||
|
||
2. **订单结算测试**
|
||
- 订单完成后触发结算
|
||
- 验证商家账户余额增加
|
||
- 验证平台账户余额增加(服务费)
|
||
- 如果有邀请关系,验证用户账户余额增加(返现)
|
||
|
||
3. **订单退款测试**
|
||
- 申请退款
|
||
- 验证系统总账户退款金额增加
|
||
|
||
4. **提现测试**
|
||
- 申请提现
|
||
- 审核通过
|
||
- 确认打款
|
||
- 验证账户余额扣减
|
||
- 验证系统总账户提现金额增加
|
||
|
||
5. **资金守恒测试**
|
||
- 执行多个订单流程
|
||
- 验证资金守恒公式始终成立
|
||
|
||
## 部署说明
|
||
|
||
### 数据库初始化
|
||
|
||
1. 执行数据库迁移脚本:
|
||
```bash
|
||
# 重新初始化数据库(会清空所有数据)
|
||
mysql -u root -p < database/migrations/001_init_schema.sql
|
||
```
|
||
|
||
2. 初始化系统总账户:
|
||
```sql
|
||
INSERT INTO system_accounts (account_name, balance, total_income, total_refund, total_withdrawn, status)
|
||
VALUES ('SYSTEM_MAIN', 0.00, 0.00, 0.00, 0.00, 'active');
|
||
```
|
||
|
||
3. 初始化平台账户:
|
||
```sql
|
||
INSERT INTO platform_accounts (account_name, balance, frozen_balance, total_income, total_expense, status)
|
||
VALUES ('PLATFORM_MAIN', 0.00, 0.00, 0.00, 0.00, 'active');
|
||
```
|
||
|
||
或者直接执行种子数据脚本:
|
||
```bash
|
||
mysql -u root -p rent_platform < database/seeds/001_init_data.sql
|
||
```
|
||
|
||
### 服务启动
|
||
|
||
```bash
|
||
# 安装依赖
|
||
pnpm install
|
||
|
||
# 启动后端服务
|
||
cd apps/server
|
||
pnpm run start:dev
|
||
|
||
# 启动平台管理后台
|
||
cd apps/platform-admin
|
||
pnpm run dev
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### Q1: 为什么要分系统总账户和平台账户?
|
||
|
||
A: 系统总账户记录的是平台内所有未提现的资金总额,用于验证资金守恒。平台账户记录的是平台的净收益(服务费 - 邀请返现),用于计算平台的实际盈利。两者职责不同,分开管理更清晰。
|
||
|
||
### Q2: 订单支付时为什么只更新系统总账户,不更新商家账户?
|
||
|
||
A: 订单支付时资金进入平台,但还未分配给商家。只有订单完成(用户离店)后才进行结算,将资金分配给商家。这样可以防止用户退款时出现商家已收款的情况。
|
||
|
||
### Q3: 邀请返现为什么要从平台账户扣减?
|
||
|
||
A: 邀请返现是平台的营销成本,应该从平台的收益中扣除。平台的净收益 = 服务费收入 - 邀请返现支出。
|
||
|
||
### Q4: 如何确保资金安全?
|
||
|
||
A: 系统采用多重安全机制:
|
||
- 数据库事务确保操作原子性
|
||
- 悲观锁和乐观锁防止并发问题
|
||
- 详细的交易流水记录
|
||
- 资金守恒验证
|
||
- 余额校验
|
||
|
||
### Q5: 如果发现账户余额不对怎么办?
|
||
|
||
A:
|
||
1. 查看交易流水,定位问题操作
|
||
2. 检查是否有并发操作导致的问题
|
||
3. 验证资金守恒公式是否成立
|
||
4. 如果是数据错误,需要手动调整并记录原因
|
||
|
||
## 更新日志
|
||
|
||
### 2024-01-XX - 账户体系重构
|
||
|
||
**重大变更**:
|
||
- 新增系统总账户,记录平台内所有未提现的资金
|
||
- 修改平台账户职责,只记录平台净收益
|
||
- 修改订单支付逻辑,更新系统总账户
|
||
- 修改订单结算逻辑,同时更新商家账户和平台账户
|
||
- 修改邀请返现逻辑,从平台账户扣减
|
||
- 修改退款逻辑,更新系统总账户
|
||
- 修改提现逻辑,更新系统总账户
|
||
- 修改财务报表,使用新的账户数据
|
||
- 修改前端页面,展示新的账户结构
|
||
|
||
**影响范围**:
|
||
- 数据库表结构变更,需要重新初始化数据库
|
||
- 所有涉及账户操作的代码都需要更新
|
||
- 前端页面需要更新
|
||
|
||
**升级步骤**:
|
||
1. 备份现有数据库
|
||
2. 执行新的数据库迁移脚本
|
||
3. 更新后端代码
|
||
4. 更新前端代码
|
||
5. 重启服务
|
||
6. 验证资金守恒
|
||
|
||
## 联系方式
|
||
|
||
如有问题,请联系开发团队。
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
|
||
- [财务数据库设计](../database/finance-database.md)
|
||
- [邀请码系统设计](./invite-system.md)
|
||
- [项目文档索引](../README.md)
|
||
|
||
---
|
||
|
||
**版本**:v3.0(合并版)
|
||
**最后更新**:2024-01-XX
|
||
**维护团队**:开发团队
|