- 系统架构文档 (architecture/README.md) - 6个领域文档: - 账户域 (01-account.md) - 账务域 (02-ledger.md) - 交易域 (03-transaction.md) - 对账域 (04-reconciliation.md) - 补偿域 (05-compensation.md) - 积分域 (06-points.md) - API 参考文档 (api/README.md) - 前后端对接清单 (integration/frontend-backend.md)
489 lines
13 KiB
Markdown
489 lines
13 KiB
Markdown
# 账务域 (Ledger Domain)
|
||
|
||
## 一、领域概述
|
||
|
||
账务域是系统的核心财务引擎,负责余额管理、复式记账、三科目约束校验。该域实现了银行级别的资金安全保障机制。
|
||
|
||
## 二、核心概念:三科目余额模型
|
||
|
||
### 2.1 模型定义
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ 银行余额 (bank_balance) │
|
||
│ = 1000.00 │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ 个人余额 │ 劳动报酬 │ 冻结余额 │
|
||
│ personal │ labor │ frozen │
|
||
│ = 500.00 │ = 300.00 │ = 200.00 │
|
||
│ (可用) │ (可用) │ (不可用) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
|
||
不变量约束: personal + labor + frozen = bank_balance
|
||
```
|
||
|
||
### 2.2 余额类型说明
|
||
|
||
| 余额类型 | 字段 | 说明 | 可用性 |
|
||
|----------|------|------|--------|
|
||
| 个人余额 | personal_balance | 个人可支配资金 | 可用 |
|
||
| 劳动报酬 | labor_balance | 劳动所得报酬 | 可用 |
|
||
| 冻结余额 | frozen_balance | 被冻结的资金 | 不可用 |
|
||
| 银行余额 | bank_balance | 银行账面余额 | 对照 |
|
||
| 在途金额 | transit_amount | 已扣未确认 | 过渡 |
|
||
|
||
### 2.3 可用余额计算
|
||
|
||
```rust
|
||
// 总可用余额 = 个人 + 劳动
|
||
fn total_available(&self) -> Decimal {
|
||
self.personal_balance + self.labor_balance
|
||
}
|
||
|
||
// 可支配余额 = 总可用 - 在途
|
||
fn calculate_available(&self) -> Decimal {
|
||
self.total_available() - self.transit_amount
|
||
}
|
||
```
|
||
|
||
## 三、核心实体
|
||
|
||
### 3.1 账户余额 (AccountBalance)
|
||
|
||
```rust
|
||
pub struct AccountBalance {
|
||
pub id: i64,
|
||
pub account_id: i64,
|
||
pub account_type: AccountType,
|
||
|
||
// 三科目余额
|
||
pub personal_balance: Decimal, // 个人余额
|
||
pub labor_balance: Decimal, // 劳动报酬
|
||
pub frozen_balance: Decimal, // 冻结余额
|
||
|
||
// 银行对照
|
||
pub bank_balance: Decimal, // 银行余额
|
||
|
||
// 在途管理
|
||
pub transit_amount: Decimal, // 在途金额
|
||
|
||
// 版本控制
|
||
pub version: i32, // 乐观锁版本
|
||
pub updated_at: DateTime<Utc>,
|
||
}
|
||
```
|
||
|
||
### 3.2 会计科目 (AccountingSubject)
|
||
|
||
```rust
|
||
pub struct AccountingSubject {
|
||
pub code: String, // 科目代码
|
||
pub name: String, // 科目名称
|
||
pub category: SubjectCategory, // 科目类别
|
||
pub direction_default: i8, // 默认增加方向
|
||
pub parent_code: Option<String>,// 父科目代码
|
||
pub level: i32, // 科目级别
|
||
}
|
||
```
|
||
|
||
**预定义科目**:
|
||
|
||
| 代码 | 名称 | 类别 |
|
||
|------|------|------|
|
||
| 1002 | 银行存款 | 资产 |
|
||
| 1003 | 在途资金 | 资产 |
|
||
| 2001 | 客户存款 | 负债 |
|
||
| 2002 | 待清算款项 | 负债 |
|
||
| 3001 | 手续费收入 | 收入 |
|
||
| 4001 | 利息支出 | 支出 |
|
||
|
||
### 3.3 记账分录 (LedgerEntry)
|
||
|
||
```rust
|
||
pub struct LedgerEntry {
|
||
pub id: i64,
|
||
pub entry_no: String, // 分录编号
|
||
pub txn_no: String, // 关联交易号
|
||
pub post_date: NaiveDate, // 记账日期
|
||
pub post_time: DateTime<Utc>, // 记账时间
|
||
pub description: Option<String>,// 摘要描述
|
||
pub status: EntryStatus, // 状态
|
||
pub created_at: DateTime<Utc>,
|
||
}
|
||
```
|
||
|
||
### 3.4 分录明细 (LedgerLine)
|
||
|
||
```rust
|
||
pub struct LedgerLine {
|
||
pub id: i64,
|
||
pub entry_id: i64,
|
||
pub account_id: i64,
|
||
pub account_type: AccountType,
|
||
pub subject_code: String, // 科目代码
|
||
pub direction: Direction, // 借贷方向
|
||
pub amount: Decimal, // 金额
|
||
}
|
||
```
|
||
|
||
## 四、枚举类型
|
||
|
||
### 4.1 借贷方向 (Direction)
|
||
|
||
```rust
|
||
pub enum Direction {
|
||
Debit, // 借方
|
||
Credit, // 贷方
|
||
}
|
||
```
|
||
|
||
### 4.2 科目类别 (SubjectCategory)
|
||
|
||
```rust
|
||
pub enum SubjectCategory {
|
||
Asset, // 资产类 - 借方增加
|
||
Liability, // 负债类 - 贷方增加
|
||
Income, // 收入类 - 贷方增加
|
||
Expense, // 支出类 - 借方增加
|
||
}
|
||
```
|
||
|
||
### 4.3 分录状态 (EntryStatus)
|
||
|
||
```rust
|
||
pub enum EntryStatus {
|
||
Pending, // 待确认
|
||
Posted, // 已过账
|
||
Reversed, // 已冲销
|
||
}
|
||
```
|
||
|
||
## 五、核心业务逻辑
|
||
|
||
### 5.1 优先级扣款
|
||
|
||
出金时按优先级从余额中扣减:**先个人,后劳动**。
|
||
|
||
```rust
|
||
pub fn deduct_with_priority(&mut self, amount: Decimal) -> Result<DeductionResult> {
|
||
let available = self.available_balance();
|
||
if available < amount {
|
||
return Err(AppError::InsufficientBalance { available, required: amount });
|
||
}
|
||
|
||
let mut remaining = amount;
|
||
|
||
// 1. 先扣个人余额
|
||
let from_personal = remaining.min(self.personal_balance);
|
||
self.personal_balance -= from_personal;
|
||
remaining -= from_personal;
|
||
|
||
// 2. 再扣劳动报酬
|
||
let from_labor = remaining.min(self.labor_balance);
|
||
self.labor_balance -= from_labor;
|
||
|
||
// 3. 同步银行余额
|
||
self.bank_balance -= amount;
|
||
|
||
Ok(DeductionResult { from_personal, from_labor, total: amount })
|
||
}
|
||
```
|
||
|
||
**扣款示例**:
|
||
|
||
| 场景 | 扣款金额 | 个人余额 | 劳动余额 | 扣款来源 |
|
||
|------|----------|----------|----------|----------|
|
||
| 1 | 300 | 500 | 200 | 个人300 |
|
||
| 2 | 600 | 500 | 200 | 个人500 + 劳动100 |
|
||
| 3 | 800 | 500 | 200 | 失败(余额不足) |
|
||
|
||
### 5.2 冻结/解冻
|
||
|
||
冻结操作将可用余额转移到冻结余额,按优先级从个人和劳动中扣减。
|
||
|
||
```rust
|
||
// 冻结
|
||
pub fn freeze(&mut self, amount: Decimal) {
|
||
let mut remaining = amount;
|
||
|
||
// 先从个人扣
|
||
let from_personal = remaining.min(self.personal_balance);
|
||
self.personal_balance -= from_personal;
|
||
remaining -= from_personal;
|
||
|
||
// 再从劳动扣
|
||
if remaining > Decimal::ZERO {
|
||
let from_labor = remaining.min(self.labor_balance);
|
||
self.labor_balance -= from_labor;
|
||
}
|
||
|
||
// 增加冻结余额
|
||
self.frozen_balance += amount;
|
||
}
|
||
|
||
// 解冻(默认返回到个人余额)
|
||
pub fn unfreeze(&mut self, amount: Decimal) {
|
||
let unfreeze_amount = amount.min(self.frozen_balance);
|
||
self.frozen_balance -= unfreeze_amount;
|
||
self.personal_balance += unfreeze_amount;
|
||
}
|
||
```
|
||
|
||
### 5.3 在途管理
|
||
|
||
在途金额表示已从可用余额扣减但尚未得到银行确认的资金。
|
||
|
||
```rust
|
||
// 建立在途(出金时)
|
||
pub fn add_transit(&mut self, amount: Decimal) {
|
||
self.transit_amount += amount;
|
||
}
|
||
|
||
// 结转在途(银行确认成功)
|
||
pub fn settle_transit(&mut self, amount: Decimal) -> Result<()> {
|
||
if self.transit_amount < amount {
|
||
return Err(AppError::BusinessRule("在途金额不足".into()));
|
||
}
|
||
self.transit_amount -= amount;
|
||
Ok(())
|
||
}
|
||
|
||
// 回退在途(银行失败)
|
||
pub fn rollback_transit(&mut self, amount: Decimal) {
|
||
let rollback = amount.min(self.transit_amount);
|
||
self.transit_amount -= rollback;
|
||
self.personal_balance += rollback; // 返回到个人余额
|
||
self.bank_balance += rollback; // 恢复银行余额
|
||
}
|
||
```
|
||
|
||
### 5.4 不变量校验
|
||
|
||
确保三科目之和等于银行余额。
|
||
|
||
```rust
|
||
pub fn validate_invariant(&self) -> Result<()> {
|
||
let sum = self.personal_balance + self.labor_balance + self.frozen_balance;
|
||
if sum == self.bank_balance {
|
||
Ok(())
|
||
} else {
|
||
Err(AppError::InvariantViolation {
|
||
account_id: self.account_id,
|
||
expected: self.bank_balance,
|
||
actual: sum,
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
## 六、领域服务
|
||
|
||
### 6.1 LedgerService
|
||
|
||
```rust
|
||
impl LedgerService {
|
||
// ========== 余额操作 ==========
|
||
|
||
// 获取账户余额
|
||
pub async fn get_balance(&self, account_id: i64, account_type: AccountType) -> Result<AccountBalance>;
|
||
|
||
// 冻结金额
|
||
pub async fn freeze_amount(&self, account_id: i64, account_type: AccountType, amount: Decimal) -> Result<()>;
|
||
|
||
// 解冻金额
|
||
pub async fn unfreeze_amount(&self, account_id: i64, account_type: AccountType, amount: Decimal) -> Result<()>;
|
||
|
||
// 优先级扣款
|
||
pub async fn deduct_with_priority(&self, account_id: i64, account_type: AccountType, amount: Decimal) -> Result<DeductionResult>;
|
||
|
||
// 建立在途
|
||
pub async fn add_transit(&self, account_id: i64, account_type: AccountType, amount: Decimal) -> Result<()>;
|
||
|
||
// 结转在途
|
||
pub async fn settle_transit(&self, account_id: i64, account_type: AccountType, amount: Decimal) -> Result<()>;
|
||
|
||
// 回退在途
|
||
pub async fn rollback_transit(&self, account_id: i64, account_type: AccountType, amount: Decimal) -> Result<()>;
|
||
|
||
// ========== 记账操作 ==========
|
||
|
||
// 创建分录
|
||
pub async fn create_entry(&self, req: CreateEntryRequest) -> Result<LedgerEntry>;
|
||
|
||
// 获取账户分录
|
||
pub async fn get_account_entries(&self, account_id: i64, account_type: AccountType) -> Result<Vec<LedgerEntry>>;
|
||
|
||
// ========== 科目操作 ==========
|
||
|
||
// 获取所有科目
|
||
pub async fn list_subjects(&self) -> Result<Vec<AccountingSubject>>;
|
||
|
||
// 初始化预定义科目
|
||
pub async fn initialize_subjects(&self) -> Result<()>;
|
||
|
||
// ========== 校验操作 ==========
|
||
|
||
// 校验不变量
|
||
pub async fn validate_invariant(&self, account_id: i64, account_type: AccountType) -> Result<()>;
|
||
}
|
||
```
|
||
|
||
## 七、复式记账
|
||
|
||
### 7.1 记账原则
|
||
|
||
1. **借贷必相等**:每笔分录的借方金额总和必须等于贷方金额总和
|
||
2. **有借必有贷**:每笔分录至少包含一个借方和一个贷方
|
||
3. **科目对应**:资产/费用借增贷减,负债/收入贷增借减
|
||
|
||
### 7.2 记账示例
|
||
|
||
**存款入账**:
|
||
```
|
||
借: 银行存款 1002 1000.00
|
||
贷: 客户存款 2001 1000.00
|
||
```
|
||
|
||
**转账出金**:
|
||
```
|
||
借: 客户存款 2001 500.00
|
||
贷: 银行存款 1002 500.00
|
||
```
|
||
|
||
**收取手续费**:
|
||
```
|
||
借: 客户存款 2001 10.00
|
||
贷: 手续费收入 3001 10.00
|
||
```
|
||
|
||
### 7.3 分录验证
|
||
|
||
```rust
|
||
impl CreateEntryRequest {
|
||
pub fn validate_balance(&self) -> Result<(), (Decimal, Decimal)> {
|
||
let mut total_debit = Decimal::ZERO;
|
||
let mut total_credit = Decimal::ZERO;
|
||
|
||
for line in &self.lines {
|
||
match line.direction {
|
||
Direction::Debit => total_debit += line.amount,
|
||
Direction::Credit => total_credit += line.amount,
|
||
}
|
||
}
|
||
|
||
if total_debit == total_credit {
|
||
Ok(())
|
||
} else {
|
||
Err((total_debit, total_credit))
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 八、API 接口
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | /api/v1/ledger/subjects | 获取会计科目列表 |
|
||
| GET | /api/v1/ledger/entries/:id | 获取分录详情 |
|
||
| GET | /api/v1/ledger/accounts/:id/entries | 获取账户分录列表 |
|
||
|
||
## 九、数据库表结构
|
||
|
||
### 9.1 account_balance 表
|
||
|
||
```sql
|
||
CREATE TABLE account_balance (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
account_id BIGINT NOT NULL,
|
||
account_type VARCHAR(32) NOT NULL,
|
||
|
||
-- 三科目余额
|
||
personal_balance DECIMAL(20, 2) DEFAULT 0.00,
|
||
labor_balance DECIMAL(20, 2) DEFAULT 0.00,
|
||
frozen_balance DECIMAL(20, 2) DEFAULT 0.00,
|
||
|
||
-- 银行对照
|
||
bank_balance DECIMAL(20, 2) DEFAULT 0.00,
|
||
|
||
-- 在途管理
|
||
transit_amount DECIMAL(20, 2) DEFAULT 0.00,
|
||
|
||
-- 兼容字段
|
||
system_balance DECIMAL(20, 2) DEFAULT 0.00,
|
||
available_balance DECIMAL(20, 2) DEFAULT 0.00,
|
||
frozen_amount DECIMAL(20, 2) DEFAULT 0.00,
|
||
|
||
version INT DEFAULT 0,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
UNIQUE INDEX idx_account (account_id, account_type)
|
||
);
|
||
```
|
||
|
||
### 9.2 ledger_entry 表
|
||
|
||
```sql
|
||
CREATE TABLE ledger_entry (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
entry_no VARCHAR(64) NOT NULL UNIQUE,
|
||
txn_no VARCHAR(64) NOT NULL,
|
||
post_date DATE NOT NULL,
|
||
post_time TIMESTAMP NOT NULL,
|
||
description TEXT,
|
||
status VARCHAR(32) DEFAULT 'pending',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_txn_no (txn_no),
|
||
INDEX idx_post_date (post_date)
|
||
);
|
||
```
|
||
|
||
### 9.3 ledger_line 表
|
||
|
||
```sql
|
||
CREATE TABLE ledger_line (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
entry_id BIGINT NOT NULL,
|
||
account_id BIGINT NOT NULL,
|
||
account_type VARCHAR(32) NOT NULL,
|
||
subject_code VARCHAR(32) NOT NULL,
|
||
direction VARCHAR(16) NOT NULL,
|
||
amount DECIMAL(20, 2) NOT NULL,
|
||
|
||
FOREIGN KEY (entry_id) REFERENCES ledger_entry(id),
|
||
INDEX idx_account (account_id, account_type),
|
||
INDEX idx_subject (subject_code)
|
||
);
|
||
```
|
||
|
||
## 十、三账校验
|
||
|
||
### 10.1 校验公式
|
||
|
||
```
|
||
总账余额 = 银行账余额 + 在途净额
|
||
|
||
即: ledger_total = bank_balance + transit_net
|
||
```
|
||
|
||
### 10.2 校验结果
|
||
|
||
```rust
|
||
pub struct ThreeAccountResult {
|
||
pub bank_balance: Decimal, // 银行账余额
|
||
pub transit_net: Decimal, // 在途净额
|
||
pub ledger_total: Decimal, // 总账余额
|
||
pub is_balanced: bool, // 是否平衡
|
||
pub difference: Decimal, // 差异金额
|
||
}
|
||
```
|
||
|
||
### 10.3 不平衡处理
|
||
|
||
1. **记录差异**:生成差异报告
|
||
2. **人工审核**:提交人工对账
|
||
3. **自动调整**:小额差异自动调平
|
||
4. **告警通知**:大额差异触发告警
|
||
|