- 系统架构文档 (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)
356 lines
11 KiB
Markdown
356 lines
11 KiB
Markdown
# 账户域 (Account Domain)
|
||
|
||
## 一、领域概述
|
||
|
||
账户域负责管理银行账户的完整生命周期,包括实体账户(对应真实银行账户)和虚拟子账户(在实体账户下创建的逻辑账户)。
|
||
|
||
## 二、核心实体
|
||
|
||
### 2.1 实体账户 (PhysicalAccount)
|
||
|
||
实体账户对应真实的银行账户,是资金管理的基础单元。
|
||
|
||
```rust
|
||
pub struct PhysicalAccount {
|
||
pub id: i64, // 账户ID
|
||
pub account_no: String, // 银行账号
|
||
pub bank_code: String, // 银行代码
|
||
pub bank_name: Option<String>, // 银行名称
|
||
pub consistency_mode: ConsistencyMode, // 一致性模式
|
||
pub outbound_control: OutboundControl, // 出金管控模式
|
||
pub status: AccountStatus, // 账户状态
|
||
pub created_at: DateTime<Utc>, // 创建时间
|
||
pub updated_at: DateTime<Utc>, // 更新时间
|
||
}
|
||
```
|
||
|
||
**核心方法**:
|
||
- `is_active()` - 检查账户是否可用
|
||
- `can_outbound()` - 检查是否允许出金
|
||
|
||
### 2.2 虚拟子账户 (VirtualSubAccount)
|
||
|
||
虚拟子账户是在实体账户下创建的逻辑账户,用于资金隔离和管理。
|
||
|
||
```rust
|
||
pub struct VirtualSubAccount {
|
||
pub id: i64, // 子账户ID
|
||
pub physical_account_id: i64, // 所属实体账户ID
|
||
pub account_code: String, // 子账户编号
|
||
pub account_type: SubAccountType, // 子账户类型
|
||
pub valid_from: Option<DateTime<Utc>>, // 有效期开始
|
||
pub valid_to: Option<DateTime<Utc>>, // 有效期结束
|
||
pub status: AccountStatus, // 账户状态
|
||
pub created_at: DateTime<Utc>, // 创建时间
|
||
pub updated_at: DateTime<Utc>, // 更新时间
|
||
}
|
||
```
|
||
|
||
**核心方法**:
|
||
- `is_active()` - 检查账户是否可用(考虑有效期)
|
||
- `is_temporary()` - 是否为临时账户
|
||
- `is_expired()` - 临时账户是否过期
|
||
|
||
### 2.3 账户控制 (AccountControl)
|
||
|
||
账户控制配置,用于设置对账频率和银行对接配置。
|
||
|
||
```rust
|
||
pub struct AccountControl {
|
||
pub id: i64,
|
||
pub physical_account_id: i64,
|
||
pub reconciliation_interval: i32, // 对账频率(分钟)
|
||
pub direct_connect_config: Option<Value>, // 银企直连配置
|
||
pub third_party_config: Option<Value>, // 第三方支付配置
|
||
}
|
||
```
|
||
|
||
### 2.4 临时账户池 (SubAccountPool)
|
||
|
||
管理临时子账户的池,支持批量创建和自动销户。
|
||
|
||
```rust
|
||
pub struct SubAccountPool {
|
||
pub id: i64,
|
||
pub physical_account_id: i64,
|
||
pub name: String,
|
||
pub valid_from: DateTime<Utc>,
|
||
pub valid_to: DateTime<Utc>,
|
||
pub auto_close_rule: Option<Value>, // 自动销户规则
|
||
}
|
||
```
|
||
|
||
## 三、枚举类型
|
||
|
||
### 3.1 账户状态 (AccountStatus)
|
||
|
||
```rust
|
||
pub enum AccountStatus {
|
||
Active, // 正常
|
||
Frozen, // 冻结
|
||
Closed, // 已关闭
|
||
}
|
||
```
|
||
|
||
**状态转移规则**:
|
||
```
|
||
Active ──► Frozen ──► Active
|
||
│ │
|
||
└──────► Closed ◄────┘
|
||
```
|
||
|
||
### 3.2 一致性模式 (ConsistencyMode)
|
||
|
||
```rust
|
||
pub enum ConsistencyMode {
|
||
Strong, // 强一致性 - 交易需等待银行确认
|
||
Eventual, // 最终一致性 - 先记账后对账
|
||
}
|
||
```
|
||
|
||
| 模式 | 特点 | 适用场景 |
|
||
|------|------|----------|
|
||
| Strong | 实时确认,响应慢 | 大额转账 |
|
||
| Eventual | 异步确认,响应快 | 小额高频 |
|
||
|
||
### 3.3 出金管控模式 (OutboundControl)
|
||
|
||
```rust
|
||
pub enum OutboundControl {
|
||
ReceiveOnly, // 只收不付
|
||
OnlineBank, // 网银控制
|
||
Token, // 令牌控制
|
||
}
|
||
```
|
||
|
||
| 模式 | 说明 | 使用场景 |
|
||
|------|------|----------|
|
||
| ReceiveOnly | 禁止出金 | 归集账户 |
|
||
| OnlineBank | 网银审批出金 | 常规账户 |
|
||
| Token | 令牌验证出金 | 高安全账户 |
|
||
|
||
### 3.4 账户类型 (AccountType)
|
||
|
||
```rust
|
||
pub enum AccountType {
|
||
Physical, // 实体账户
|
||
Virtual, // 虚拟账户
|
||
}
|
||
```
|
||
|
||
### 3.5 子账户类型 (SubAccountType)
|
||
|
||
```rust
|
||
pub enum SubAccountType {
|
||
Settlement, // 结算子账户
|
||
Management, // 管理子账户
|
||
Temporary, // 临时子账户
|
||
}
|
||
```
|
||
|
||
| 类型 | 说明 | 特点 |
|
||
|------|------|------|
|
||
| Settlement | 结算账户 | 长期使用,用于日常结算 |
|
||
| Management | 管理账户 | 内部管理,资金归集 |
|
||
| Temporary | 临时账户 | 有有效期,自动销户 |
|
||
|
||
## 四、领域服务
|
||
|
||
### 4.1 AccountService
|
||
|
||
账户域的核心服务,提供账户管理功能。
|
||
|
||
```rust
|
||
impl AccountService {
|
||
// 创建实体账户
|
||
pub async fn create_physical_account(&self, req: CreatePhysicalAccountRequest) -> Result<PhysicalAccount>;
|
||
|
||
// 获取实体账户
|
||
pub async fn get_physical_account(&self, id: i64) -> Result<PhysicalAccount>;
|
||
|
||
// 获取实体账户列表(分页)
|
||
pub async fn list_physical_accounts_paginated(
|
||
&self,
|
||
status: Option<AccountStatus>,
|
||
keyword: Option<String>,
|
||
page: i32,
|
||
page_size: i32
|
||
) -> Result<(Vec<PhysicalAccount>, i64)>;
|
||
|
||
// 冻结实体账户
|
||
pub async fn freeze_physical_account(&self, id: i64) -> Result<()>;
|
||
|
||
// 解冻实体账户
|
||
pub async fn unfreeze_physical_account(&self, id: i64) -> Result<()>;
|
||
|
||
// 创建虚拟子账户
|
||
pub async fn create_sub_account(&self, req: CreateVirtualSubAccountRequest) -> Result<VirtualSubAccount>;
|
||
|
||
// 批量创建子账户
|
||
pub async fn batch_create_sub_accounts(&self, req: BatchCreateSubAccountRequest) -> Result<Vec<VirtualSubAccount>>;
|
||
|
||
// 关闭子账户
|
||
pub async fn close_sub_account(&self, id: i64) -> Result<()>;
|
||
|
||
// 获取实体账户下的子账户列表
|
||
pub async fn list_sub_accounts(&self, physical_account_id: i64) -> Result<Vec<VirtualSubAccount>>;
|
||
}
|
||
```
|
||
|
||
## 五、仓储接口
|
||
|
||
### 5.1 PhysicalAccountRepository
|
||
|
||
```rust
|
||
#[async_trait]
|
||
pub trait PhysicalAccountRepository: Send + Sync {
|
||
async fn create(&self, account: &PhysicalAccount) -> Result<i64>;
|
||
async fn find_by_id(&self, id: i64) -> Result<Option<PhysicalAccount>>;
|
||
async fn find_by_account_no(&self, account_no: &str) -> Result<Option<PhysicalAccount>>;
|
||
async fn find_all(&self) -> Result<Vec<PhysicalAccount>>;
|
||
async fn find_paginated(&self, status: Option<AccountStatus>, keyword: Option<String>, offset: i64, limit: i64) -> Result<Vec<PhysicalAccount>>;
|
||
async fn count(&self, status: Option<AccountStatus>, keyword: Option<String>) -> Result<i64>;
|
||
async fn update_status(&self, id: i64, status: AccountStatus) -> Result<()>;
|
||
}
|
||
```
|
||
|
||
### 5.2 VirtualSubAccountRepository
|
||
|
||
```rust
|
||
#[async_trait]
|
||
pub trait VirtualSubAccountRepository: Send + Sync {
|
||
async fn create(&self, account: &VirtualSubAccount) -> Result<i64>;
|
||
async fn find_by_id(&self, id: i64) -> Result<Option<VirtualSubAccount>>;
|
||
async fn find_by_physical_account(&self, physical_account_id: i64) -> Result<Vec<VirtualSubAccount>>;
|
||
async fn find_expired_temporary(&self) -> Result<Vec<VirtualSubAccount>>;
|
||
async fn update_status(&self, id: i64, status: AccountStatus) -> Result<()>;
|
||
async fn batch_create(&self, accounts: &[VirtualSubAccount]) -> Result<Vec<i64>>;
|
||
}
|
||
```
|
||
|
||
## 六、业务规则
|
||
|
||
### 6.1 账户创建规则
|
||
|
||
1. 银行账号必须唯一
|
||
2. 实体账户必须关联有效的银行代码
|
||
3. 子账户编号在同一实体账户下必须唯一
|
||
4. 临时账户必须设置有效期
|
||
|
||
### 6.2 账户状态规则
|
||
|
||
1. 只有 `Active` 状态的账户可以进行交易
|
||
2. `Frozen` 状态只影响出金,入金正常
|
||
3. `Closed` 状态不可进行任何交易
|
||
4. 账户关闭前必须余额为零
|
||
|
||
### 6.3 出金管控规则
|
||
|
||
1. `ReceiveOnly` 模式禁止任何出金操作
|
||
2. `OnlineBank` 模式需要通过网银审批
|
||
3. `Token` 模式需要提供有效令牌
|
||
|
||
## 七、API 接口
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | /api/v1/physical-accounts | 创建实体账户 |
|
||
| GET | /api/v1/physical-accounts | 获取实体账户列表 |
|
||
| GET | /api/v1/physical-accounts/:id | 获取实体账户详情 |
|
||
| POST | /api/v1/physical-accounts/:id/freeze | 冻结实体账户资金 |
|
||
| POST | /api/v1/physical-accounts/:id/unfreeze | 解冻实体账户资金 |
|
||
| POST | /api/v1/sub-accounts | 创建虚拟子账户 |
|
||
| GET | /api/v1/sub-accounts/:id | 获取子账户详情 |
|
||
| GET | /api/v1/sub-accounts/:id/balance | 获取子账户余额 |
|
||
| POST | /api/v1/sub-accounts/:id/freeze | 冻结子账户资金 |
|
||
| POST | /api/v1/sub-accounts/:id/unfreeze | 解冻子账户资金 |
|
||
| POST | /api/v1/sub-accounts/:id/close | 关闭子账户 |
|
||
| GET | /api/v1/physical-accounts/:id/sub-accounts | 获取实体账户下的子账户列表 |
|
||
|
||
## 八、数据库表结构
|
||
|
||
### 8.1 physical_account 表
|
||
|
||
```sql
|
||
CREATE TABLE physical_account (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
account_no VARCHAR(64) NOT NULL UNIQUE,
|
||
account_name VARCHAR(128),
|
||
bank_code VARCHAR(32) NOT NULL,
|
||
bank_name VARCHAR(128),
|
||
consistency_mode VARCHAR(32) DEFAULT 'eventual',
|
||
outbound_control VARCHAR(32) DEFAULT 'online_bank',
|
||
status VARCHAR(32) DEFAULT 'active',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_bank_code (bank_code),
|
||
INDEX idx_status (status)
|
||
);
|
||
```
|
||
|
||
### 8.2 virtual_sub_account 表
|
||
|
||
```sql
|
||
CREATE TABLE virtual_sub_account (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
physical_account_id BIGINT NOT NULL,
|
||
account_code VARCHAR(64) NOT NULL,
|
||
account_type VARCHAR(32) DEFAULT 'settlement',
|
||
valid_from TIMESTAMP NULL,
|
||
valid_to TIMESTAMP NULL,
|
||
status VARCHAR(32) DEFAULT 'active',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (physical_account_id) REFERENCES physical_account(id),
|
||
UNIQUE INDEX idx_account_code (physical_account_id, account_code),
|
||
INDEX idx_status (status),
|
||
INDEX idx_valid_to (valid_to)
|
||
);
|
||
```
|
||
|
||
## 九、使用示例
|
||
|
||
### 9.1 创建实体账户
|
||
|
||
```rust
|
||
let req = CreatePhysicalAccountRequest {
|
||
account_no: "6222021234567890123".to_string(),
|
||
bank_code: "ICBC".to_string(),
|
||
bank_name: Some("中国工商银行".to_string()),
|
||
consistency_mode: Some(ConsistencyMode::Eventual),
|
||
outbound_control: Some(OutboundControl::OnlineBank),
|
||
};
|
||
|
||
let account = account_service.create_physical_account(req).await?;
|
||
```
|
||
|
||
### 9.2 创建子账户
|
||
|
||
```rust
|
||
let req = CreateVirtualSubAccountRequest {
|
||
physical_account_id: 1,
|
||
account_code: "SUB001".to_string(),
|
||
account_type: SubAccountType::Settlement,
|
||
valid_from: None,
|
||
valid_to: None,
|
||
};
|
||
|
||
let sub_account = account_service.create_sub_account(req).await?;
|
||
```
|
||
|
||
### 9.3 批量创建临时账户
|
||
|
||
```rust
|
||
let req = BatchCreateSubAccountRequest {
|
||
physical_account_id: 1,
|
||
account_type: SubAccountType::Temporary,
|
||
prefix: "TEMP".to_string(),
|
||
count: 100,
|
||
valid_from: Some(Utc::now()),
|
||
valid_to: Some(Utc::now() + Duration::days(30)),
|
||
};
|
||
|
||
let accounts = account_service.batch_create_sub_accounts(req).await?;
|
||
```
|
||
|