# 财务系统数据库设计文档

> **最后更新**: 2024-01-XX - 账户体系重构完成  
> **版本**: v2.0

## 概述

本文档描述了租房平台财务系统的数据库设计，包括账户体系、资金流转和数据一致性保障机制。

## 数据库表与 Entity 映射关系

| 数据库表 | Entity 文件 | 说明 | 状态 |
|---------|------------|------|------|
| `system_accounts` | `system-account.entity.ts` | 系统总账户表 | ✅ v2.0 新增 |
| `system_transactions` | `system-transaction.entity.ts` | 系统总账户交易流水表 | ✅ v2.0 新增 |
| `platform_accounts` | `platform-account.entity.ts` | 平台账户表 | ✅ v2.0 重构 |
| `platform_transactions` | `platform-transaction.entity.ts` | 平台交易流水表 | ✅ 已完成 |
| `merchant_accounts` | `merchant-account.entity.ts` | 商家账户表 | ✅ 已完成 |
| `merchant_transactions` | `merchant-transaction.entity.ts` | 商家交易流水表 | ✅ 已完成 |
| `user_accounts` | `user-account.entity.ts` | 用户账户表 | ✅ 已完成 |
| `user_transactions` | `user-transaction.entity.ts` | 用户交易流水表 | ✅ 已完成 |
| `user_withdrawals` | `user-withdrawal.entity.ts` | 用户提现表 | ✅ 已完成 |
| `merchant_withdrawals` | `merchant-withdrawal.entity.ts` | 商家提现表 | ✅ 已完成 |
| `settlements` | `settlement.entity.ts` | 结算单表 | ✅ 已完成 |
| `settlement_items` | `settlement-item.entity.ts` | 结算明细表 | ✅ 已完成 |

## 账户体系设计

### 四层账户结构

系统采用四层账户结构，确保资金流转清晰、职责明确：

#### 1. 系统总账户 (System Account) - v2.0 新增

**职责**: 记录平台内所有未提现的资金总额

**计算公式**:
```
系统总账户余额 = 用户实付总额 - 累计退款 - 累计提现
```

**字段说明**:
- `balance`: 当前余额
- `total_income`: 累计收入（用户实付总额）
- `total_refund`: 累计退款
- `total_withdrawn`: 累计提现

#### 2. 平台账户 (Platform Account) - v2.0 重构

**职责**: 记录平台净收益（服务费收入 - 邀请返现支出）

**计算公式**:
```
平台账户余额 = 累计服务费收入 - 累计邀请返现支出
```

**字段说明**:
- `balance`: 当前余额
- `total_income`: 累计收入（服务费收入）
- `total_expense`: 累计支出（邀请返现支出）

**v2.0 变更**:
- ❌ 删除字段: `total_order_income`, `total_service_fee`, `total_cashback`, `total_settlement`
- ✅ 字段含义变更: `total_income` 从"订单收入"改为"服务费收入"
- ✅ 字段含义变更: `total_expense` 从"所有支出"改为"邀请返现支出"

#### 3. 商家账户 (Merchant Account)

**职责**: 记录商家的订单收入和提现

**计算公式**:
```
商家账户余额 = 累计订单收入 - 累计提现
订单收入 = 订单金额 - 服务费
```

#### 4. 用户账户 (User Account)

**职责**: 记录用户的邀请返现收入和提现

**计算公式**:
```
用户账户余额 = 累计邀请返现 - 累计提现
```

### 资金守恒验证

系统通过以下公式验证资金守恒：

```
系统总账户余额 = 商家账户余额总和 + 用户账户余额总和 + 平台账户余额
```

这个公式确保平台内所有资金都有明确的归属，不会出现资金丢失或凭空产生的情况。

## 数据表结构详解

### system_accounts（系统总账户表）- v2.0 新增

```sql
CREATE TABLE IF NOT EXISTS `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='系统总账户表';
```

**关键点**:
- 记录平台内所有未提现的资金
- `balance` = `total_income` - `total_refund` - `total_withdrawn`
- 用于验证资金守恒

### system_transactions（系统总账户交易流水表）- v2.0 新增

```sql
CREATE TABLE IF NOT EXISTS `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='系统总账户交易流水表';
```

**交易类型**:
- `用户支付`: 用户支付订单（income）
- `订单退款`: 订单退款（expense）
- `用户提现`: 用户提现（expense）
- `商家提现`: 商家提现（expense）

### platform_accounts（平台账户表）- v2.0 重构

```sql
CREATE TABLE IF NOT EXISTS `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='平台账户表';
```

**v2.0 变更**:
- `total_income` 含义变更: 从"订单收入"改为"服务费收入"
- `total_expense` 含义变更: 从"所有支出"改为"邀请返现支出"
- 删除冗余字段: `total_order_income`, `total_service_fee`, `total_cashback`, `total_settlement`

### user_accounts（用户账户表）

```sql
CREATE TABLE `user_accounts` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT,
  `user_id` BIGINT UNSIGNED,                          -- 用户ID
  `balance` DECIMAL(12,2) DEFAULT 0.00,               -- 可用余额
  `frozen_balance` DECIMAL(12,2) DEFAULT 0.00,        -- 冻结余额（提现中）
  `total_income` DECIMAL(12,2) DEFAULT 0.00,          -- 累计收入（邀请返现）
  `total_expense` DECIMAL(12,2) DEFAULT 0.00,         -- 累计支出（提现）
  `version` INT UNSIGNED DEFAULT 0,                   -- 乐观锁版本号
  `status` ENUM('active','frozen','closed'),          -- 状态
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_id` (`user_id`)
);
```

**关键点**:
- 用户账户主要用于邀请返现
- `balance` = `total_income` - `total_expense`
- 提现时先冻结余额，打款后扣减

### merchant_accounts（商家账户表）

```sql
CREATE TABLE `merchant_accounts` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT,
  `merchant_id` BIGINT UNSIGNED,                      -- 商家ID
  `balance` DECIMAL(12,2) DEFAULT 0.00,               -- 可用余额
  `frozen_balance` DECIMAL(12,2) DEFAULT 0.00,        -- 冻结余额（提现中）
  `debt_amount` DECIMAL(12,2) DEFAULT 0.00,           -- 欠款金额（退款扣回）
  `total_income` DECIMAL(12,2) DEFAULT 0.00,          -- 累计收入（订单结算）
  `total_expense` DECIMAL(12,2) DEFAULT 0.00,         -- 累计支出（提现+退款扣回）
  `total_settlement` DECIMAL(12,2) DEFAULT 0.00,      -- 累计结算金额
  `total_withdraw` DECIMAL(12,2) DEFAULT 0.00,        -- 累计提现金额
  `last_settlement_at` DATETIME,                      -- 最后结算时间
  `pending_settlement` DECIMAL(12,2) DEFAULT 0.00,    -- 待结算金额
  `version` INT UNSIGNED DEFAULT 0,                   -- 乐观锁版本号
  `status` ENUM('active','frozen','closed'),          -- 状态
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_merchant_id` (`merchant_id`)
);
```

**关键点**:
- 商家账户记录订单结算收入
- 结算金额 = 订单金额 - 服务费
- 每周一凌晨2点自动执行周结算

## 资金流转流程

### 1. 订单支付流程 - v2.0 更新

```
用户支付订单
  ↓
系统总账户 +实付金额 (income)
  ↓
订单状态：pending_pay → pending_confirm
```

**涉及表**:
- `system_accounts`: balance += 实付金额, total_income += 实付金额
- `system_transactions`: 插入交易流水（income）

**代码位置**: `apps/server/src/modules/app/order/order.service.ts`

### 2. 订单结算流程 - v2.0 更新

```
订单完成（用户离店）
  ↓
商家账户 +（实付金额 - 服务费）
  ↓
平台账户 +服务费
  ↓
如果有邀请关系：
  用户账户（邀请人）+返现金额
  平台账户 -返现金额
```

**涉及表**:
- `merchant_accounts`: balance += 结算金额
- `merchant_transactions`: 插入交易流水
- `platform_accounts`: balance += 服务费, total_income += 服务费
- `platform_transactions`: 插入交易流水
- `user_accounts`: balance += 返现金额（如有邀请）
- `user_transactions`: 插入交易流水（如有邀请）
- `platform_accounts`: balance -= 返现金额, total_expense += 返现金额（如有邀请）
- `settlements`: 插入结算单记录
- `settlement_items`: 插入结算明细

**代码位置**: 
- `apps/server/src/modules/shared/finance/settlement.service.ts`
- `apps/server/src/modules/app/activity/activity.service.ts`

### 3. 订单退款流程 - v2.0 更新

```
用户申请退款
  ↓
调用第三方支付退款API
  ↓
系统总账户 +退款金额 (expense)
  ↓
订单状态：pending_confirm/pending_checkin → refunded
```

**涉及表**:
- `system_accounts`: balance -= 退款金额, total_refund += 退款金额
- `system_transactions`: 插入交易流水（expense）

**代码位置**: `apps/server/src/modules/shared/finance/refund.service.ts`

### 4. 提现流程 - v2.0 更新

#### 用户提现

```
用户申请提现
  ↓
用户账户：balance → frozen_balance
  ↓
管理员审核通过
  ↓
管理员确认打款
  ↓
用户账户 -提现金额（从frozen_balance扣减）
系统总账户 +提现金额 (expense)
  ↓
提现状态：pending → approved → paid
```

#### 商家提现

```
商家申请提现
  ↓
商家账户：balance → frozen_balance
  ↓
管理员审核通过
  ↓
管理员确认打款
  ↓
商家账户 -提现金额（从frozen_balance扣减）
系统总账户 +提现金额 (expense)
  ↓
提现状态：pending → approved → paid
```

**涉及表**:
- `user_accounts` / `merchant_accounts`: frozen_balance → balance扣减
- `user_transactions` / `merchant_transactions`: 插入交易流水
- `system_accounts`: balance -= 提现金额, total_withdrawn += 提现金额
- `system_transactions`: 插入交易流水
- `user_withdrawals` / `merchant_withdrawals`: 更新提现状态

**代码位置**: `apps/server/src/modules/shared/finance/withdrawal.service.ts`

### platform_accounts（平台账户表）

```sql
CREATE TABLE `platform_accounts` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT,
  `account_name` VARCHAR(50),                         -- 账户名称
  `balance` DECIMAL(12,2) DEFAULT 0.00,               -- 可用余额
  `frozen_balance` DECIMAL(12,2) DEFAULT 0.00,        -- 冻结余额
  `total_income` DECIMAL(12,2) DEFAULT 0.00,          -- 累计收入（订单收入）
  `total_expense` DECIMAL(12,2) DEFAULT 0.00,         -- 累计支出（商家结算+退款）
  `total_service_fee` DECIMAL(12,2) DEFAULT 0.00,     -- 累计服务费收入
  `version` INT UNSIGNED DEFAULT 0,                   -- 乐观锁版本号
  `status` ENUM('active','frozen','closed'),          -- 状态
  PRIMARY KEY (`id`)
);
```

**关键点**:
- 账户按角色分离：用户、商家、平台各有独立的账户表
- 每个账户表都有 `version` 字段用于乐观锁，防止并发问题
- `balance` 和 `frozen_balance` 有 CHECK 约束，不能为负数

### user_transactions / merchant_transactions / platform_transactions（交易流水表）

交易流水表也按角色分离，结构类似：

```sql
CREATE TABLE `user_transactions` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT,
  `transaction_no` VARCHAR(32) UNIQUE,                -- 交易流水号（全局唯一）
  `user_id` BIGINT UNSIGNED,                          -- 用户ID
  `account_id` BIGINT UNSIGNED,                       -- 用户账户ID
  `direction` ENUM('income','expense'),               -- 方向
  `amount` DECIMAL(12,2),                             -- 金额
  `balance_before` DECIMAL(12,2),                     -- 交易前余额
  `balance_after` DECIMAL(12,2),                      -- 交易后余额
  `transaction_type` VARCHAR(50),                     -- 交易类型
  `business_type` VARCHAR(50),                        -- 业务类型
  `business_id` BIGINT UNSIGNED,                      -- 业务ID
  `business_no` VARCHAR(32),                          -- 业务单号
  `related_account_type` ENUM('platform','merchant'), -- 对方账户类型
  `related_account_id` BIGINT UNSIGNED,               -- 对方账户ID
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_transaction_no` (`transaction_no`)
);
```

**关键点**:
- 所有账户操作都记录详细的交易流水
- `transaction_no` 全局唯一，用于追踪资金流向
- 记录交易前后余额，便于对账和审计

### settlements（结算单表）

```sql
CREATE TABLE `settlements` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT,
  `settlement_no` VARCHAR(32) UNIQUE,                 -- 结算单号
  `merchant_id` BIGINT UNSIGNED,                      -- 商家ID
  `period_start` DATE,                                -- 周期开始
  `period_end` DATE,                                  -- 周期结束
  `order_count` INT UNSIGNED DEFAULT 0,               -- 订单数量
  `order_amount` DECIMAL(12,2) DEFAULT 0.00,          -- 订单总额
  `service_fee` DECIMAL(12,2) DEFAULT 0.00,           -- 服务费
  `settlement_amount` DECIMAL(12,2) DEFAULT 0.00,     -- 结算金额
  `status` ENUM('pending','settled','failed'),        -- 状态
  `settled_at` DATETIME,                              -- 结算时间
  PRIMARY KEY (`id`)
);
```

**关键点**:
- 每周一凌晨2点自动生成上周的结算单
- `settlement_amount` = `order_amount` - `service_fee`
- 结算时执行转账：给商家账户增加余额，给平台账户增加服务费

## 数据一致性保证

### 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: 'SYSTEM_MAIN' },
  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. CHECK 约束

```sql
CONSTRAINT `chk_balance` CHECK (`balance` >= 0)
CONSTRAINT `chk_frozen_balance` CHECK (`frozen_balance` >= 0)
```

确保余额不会为负数。

### 7. 触发器

- 用户注册时自动创建账户
- 商家审核通过时自动创建账户

## 数据库初始化

### 1. 迁移脚本位置

`database/migrations/001_init_schema.sql` 包含完整的财务系统表结构

### 2. 初始化步骤

```bash
# 1. 执行数据库迁移脚本（会清空所有数据）
mysql -u root -p < database/migrations/001_init_schema.sql

# 2. 初始化系统总账户
mysql -u root -p
USE your_database_name;
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. 初始化平台账户
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');
```

### 3. 自动创建的数据

执行迁移脚本后会自动创建：

1. **触发器**
   - `trg_user_create_account`: 用户注册时自动创建用户账户
   - `trg_merchant_create_account`: 商家审核通过时自动创建商家账户

## 索引说明

### 关键索引

1. **system_accounts 表**
   - `uk_account_name`: 唯一索引，确保账户名称唯一

2. **system_transactions 表**
   - `uk_transaction_no`: 唯一索引，防止重复交易
   - `idx_account_id`: 按账户查询流水
   - `idx_business`: 按业务类型和业务ID查询
   - `idx_created_at`: 按时间范围查询

3. **platform_accounts 表**
   - `uk_account_name`: 唯一索引，确保账户名称唯一

4. **user_accounts / merchant_accounts 表**
   - `uk_user_id` / `uk_merchant_id`: 唯一索引，确保每个用户/商家只有一个账户
   - `idx_status`: 按状态查询

5. **transactions 表**
   - `uk_transaction_no`: 唯一索引，防止重复交易
   - `idx_account_id`: 按账户查询流水
   - `idx_business`: 按业务类型和业务ID查询
   - `idx_created_at`: 按时间范围查询

6. **settlements 表**
   - `uk_settlement_no`: 唯一索引
   - `idx_merchant_id`: 按商家查询
   - `idx_period`: 按周期查询

7. **withdrawals 表**
   - `uk_withdraw_no`: 唯一索引
   - `idx_status`: 按状态查询（待审核、已打款等）

## 版本更新记录

### v2.0 (2024-01-XX) - 账户体系重构

**重大变更**:
1. ✅ 新增 `system_accounts` 表 - 记录系统总资金
2. ✅ 新增 `system_transactions` 表 - 系统总账户交易流水
3. ✅ 重构 `platform_accounts` 表 - 只记录平台净收益
   - 字段含义变更: `total_income` 改为"服务费收入"
   - 字段含义变更: `total_expense` 改为"邀请返现支出"
   - 删除冗余字段: `total_order_income`, `total_service_fee`, `total_cashback`, `total_settlement`
4. ✅ 修改订单支付逻辑 - 更新系统总账户
5. ✅ 修改订单结算逻辑 - 同时更新商家账户和平台账户
6. ✅ 修改邀请返现逻辑 - 从平台账户扣减
7. ✅ 修改退款逻辑 - 更新系统总账户
8. ✅ 修改提现逻辑 - 更新系统总账户

**资金守恒验证**:
```
系统总账户余额 = 商家账户余额总和 + 用户账户余额总和 + 平台账户余额
```

**升级步骤**:
1. 备份现有数据库
2. 执行新的数据库迁移脚本
3. 初始化系统总账户和平台账户
4. 更新后端代码
5. 重启服务
6. 验证资金守恒

### v1.0 (2024-04-24) - 初始版本

**功能**:
- 用户账户、商家账户、平台账户
- 交易流水记录
- 周结算功能
- 提现功能
- 日对账功能

## 相关文档

- [财务系统完整设计文档](../finance-system.md) - 包含业务流程、核心服务、前端展示等完整内容
- [项目需求文档](../requirements/项目需求文档.md) - 了解项目功能和业务逻辑
- [开发总结](../DEVELOPMENT_SUMMARY.md) - 查看项目完成情况

## 注意事项

1. **执行迁移脚本**: 确保按顺序执行 `001_init_schema.sql`，会自动创建所有表和触发器。

2. **初始化账户**: 迁移脚本执行后需要手动初始化系统总账户和平台账户。

3. **定时任务**: 需要在 NestJS 中启用 `@nestjs/schedule` 模块，定时任务才会执行。

4. **资金安全**: 所有账户操作都使用事务、悲观锁和乐观锁保证数据一致性。

5. **金额精度**: 所有金额计算使用 `Number()` 和 `toFixed(2)` 确保精度。

---

**维护团队**: 开发团队  
**最后更新**: 2024-01-XX
