tangweijie 1460187516 docs: 添加完整技术文档体系
- 系统架构文档 (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)
2026-01-05 18:12:37 +08:00

504 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 积分域 (Points Domain)
## 一、领域概述
积分域负责管理用户积分的完整生命周期,包括积分获取、消费、转移和过期处理。支持多种积分类型和灵活的积分规则配置。
## 二、核心实体
### 2.1 积分账户 (PointsAccount)
```rust
pub struct PointsAccount {
pub id: i64,
pub sub_account_id: i64, // 关联子账户ID
pub points_type: PointsType, // 积分类型
pub balance: Decimal, // 积分余额
pub total_earned: Decimal, // 累计获得
pub total_spent: Decimal, // 累计消费
pub total_expired: Decimal, // 累计过期
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
```
**核心方法**
```rust
impl PointsAccount {
// 检查是否有足够积分
pub fn has_sufficient_points(&self, amount: Decimal) -> bool {
self.balance >= amount
}
// 增加积分
pub fn add_points(&mut self, amount: Decimal) {
self.balance += amount;
self.total_earned += amount;
self.updated_at = Utc::now();
}
// 减少积分
pub fn subtract_points(&mut self, amount: Decimal) {
self.balance -= amount;
self.total_spent += amount;
self.updated_at = Utc::now();
}
// 过期积分
pub fn expire_points(&mut self, amount: Decimal) {
self.balance -= amount;
self.total_expired += amount;
self.updated_at = Utc::now();
}
}
```
### 2.2 积分交易 (PointsTransaction)
```rust
pub struct PointsTransaction {
pub id: i64,
pub txn_no: String, // 交易编号
pub points_account_id: i64, // 积分账户ID
pub txn_type: PointsTransactionType, // 交易类型
pub amount: Decimal, // 积分数量
pub balance_before: Decimal, // 交易前余额
pub balance_after: Decimal, // 交易后余额
pub related_business_id: Option<String>, // 关联业务ID
pub remark: Option<String>, // 备注
pub created_at: DateTime<Utc>,
}
```
### 2.3 积分规则 (PointsRule)
```rust
pub struct PointsRule {
pub id: i64,
pub name: String, // 规则名称
pub points_type: PointsType, // 积分类型
pub rule_type: String, // 规则类型
pub config: serde_json::Value, // 规则配置
pub enabled: bool, // 是否启用
pub created_at: DateTime<Utc>,
}
```
**规则配置示例**
```json
{
"earn_rule": {
"base_points": 100,
"multiplier": 1.5,
"max_daily": 1000,
"valid_days": 365
},
"spend_rule": {
"min_points": 100,
"exchange_rate": 0.01
}
}
```
## 三、枚举类型
### 3.1 积分类型 (PointsType)
```rust
pub enum PointsType {
Production, // 生产积分
Management, // 管理积分
Other, // 其他积分
}
```
| 类型 | 说明 | 获取方式 |
|------|------|----------|
| Production | 生产积分 | 完成生产任务 |
| Management | 管理积分 | 管理工作奖励 |
| Other | 其他积分 | 活动奖励等 |
### 3.2 积分交易类型 (PointsTransactionType)
```rust
pub enum PointsTransactionType {
Earn, // 获取
Spend, // 消费
Transfer, // 转移
Expire, // 过期
Adjust, // 调整
}
```
## 四、业务流程
### 4.1 积分获取流程
```mermaid
sequenceDiagram
participant Business
participant PointsService
participant PointsRepo
Business->>PointsService: 1. 发起积分获取
PointsService->>PointsService: 2. 校验规则
PointsService->>PointsRepo: 3. 获取积分账户
PointsService->>PointsService: 4. 计算积分
PointsService->>PointsRepo: 5. 更新余额
PointsService->>PointsRepo: 6. 记录交易
PointsService-->>Business: 7. 返回结果
```
### 4.2 积分消费流程
```mermaid
sequenceDiagram
participant User
participant PointsService
participant PointsRepo
User->>PointsService: 1. 发起积分消费
PointsService->>PointsRepo: 2. 获取积分账户
PointsService->>PointsService: 3. 检查余额
alt 余额充足
PointsService->>PointsRepo: 4a. 扣减余额
PointsService->>PointsRepo: 5a. 记录交易
PointsService-->>User: 6a. 消费成功
else 余额不足
PointsService-->>User: 4b. 返回余额不足
end
```
### 4.3 积分转移流程
```mermaid
sequenceDiagram
participant UserA
participant PointsService
participant PointsRepo
UserA->>PointsService: 1. 发起转移
PointsService->>PointsRepo: 2. 获取转出账户
PointsService->>PointsService: 3. 检查余额
PointsService->>PointsRepo: 4. 获取转入账户
PointsService->>PointsRepo: 5. 扣减转出账户
PointsService->>PointsRepo: 6. 增加转入账户
PointsService->>PointsRepo: 7. 记录两笔交易
PointsService-->>UserA: 8. 转移成功
```
## 五、领域服务
### 5.1 PointsService
```rust
impl PointsService {
// ========== 账户管理 ==========
// 获取积分账户
pub async fn get_accounts(&self, sub_account_id: i64) -> Result<Vec<PointsAccount>>;
// 创建积分账户
pub async fn create_account(&self, req: CreatePointsAccountRequest) -> Result<PointsAccount>;
// ========== 积分操作 ==========
// 获取积分
pub async fn earn_points(&self, req: PointsTransactionRequest) -> Result<PointsTransaction>;
// 消费积分
pub async fn spend_points(&self, req: PointsTransactionRequest) -> Result<PointsTransaction>;
// 转移积分
pub async fn transfer_points(&self, req: PointsTransferRequest) -> Result<(PointsTransaction, PointsTransaction)>;
// 调整积分
pub async fn adjust_points(&self, req: PointsTransactionRequest) -> Result<PointsTransaction>;
// ========== 过期处理 ==========
// 处理过期积分
pub async fn process_expired_points(&self) -> Result<i32>;
// ========== 查询 ==========
// 查询积分交易
pub async fn list_transactions(&self, query: PointsTransactionQuery) -> Result<Vec<PointsTransaction>>;
// 获取积分统计
pub async fn get_statistics(&self, account_id: i64) -> Result<PointsStatistics>;
}
```
### 5.2 积分获取实现
```rust
pub async fn earn_points(&self, req: PointsTransactionRequest) -> Result<PointsTransaction> {
// 1. 获取积分账户
let mut account = self.account_repo
.find_by_id(req.points_account_id)
.await?
.ok_or_else(|| AppError::NotFound("积分账户不存在".into()))?;
// 2. 校验规则(可选)
if let Some(rule) = self.get_earn_rule(&account.points_type).await? {
self.validate_earn_rule(&rule, req.amount)?;
}
// 3. 记录交易前余额
let balance_before = account.balance;
// 4. 增加积分
account.add_points(req.amount);
// 5. 更新账户
self.account_repo.update(&account).await?;
// 6. 创建交易记录
let txn = PointsTransaction {
id: 0,
txn_no: self.generate_txn_no(),
points_account_id: account.id,
txn_type: PointsTransactionType::Earn,
amount: req.amount,
balance_before,
balance_after: account.balance,
related_business_id: req.related_business_id,
remark: req.remark,
created_at: Utc::now(),
};
let txn_id = self.txn_repo.create(&txn).await?;
Ok(PointsTransaction { id: txn_id, ..txn })
}
```
### 5.3 积分转移实现
```rust
pub async fn transfer_points(&self, req: PointsTransferRequest) -> Result<(PointsTransaction, PointsTransaction)> {
// 1. 获取转出账户
let mut from_account = self.account_repo
.find_by_id(req.from_account_id)
.await?
.ok_or_else(|| AppError::NotFound("转出账户不存在".into()))?;
// 2. 检查余额
if !from_account.has_sufficient_points(req.amount) {
return Err(AppError::InsufficientBalance {
available: from_account.balance,
required: req.amount,
});
}
// 3. 获取转入账户
let mut to_account = self.account_repo
.find_by_id(req.to_account_id)
.await?
.ok_or_else(|| AppError::NotFound("转入账户不存在".into()))?;
// 4. 执行转移
let from_before = from_account.balance;
let to_before = to_account.balance;
from_account.subtract_points(req.amount);
to_account.add_points(req.amount);
// 5. 更新账户
self.account_repo.update(&from_account).await?;
self.account_repo.update(&to_account).await?;
// 6. 创建交易记录
let txn_no = self.generate_txn_no();
let from_txn = PointsTransaction {
id: 0,
txn_no: format!("{}-OUT", txn_no),
points_account_id: from_account.id,
txn_type: PointsTransactionType::Transfer,
amount: -req.amount,
balance_before: from_before,
balance_after: from_account.balance,
related_business_id: Some(format!("TRANSFER_TO_{}", to_account.id)),
remark: req.remark.clone(),
created_at: Utc::now(),
};
let to_txn = PointsTransaction {
id: 0,
txn_no: format!("{}-IN", txn_no),
points_account_id: to_account.id,
txn_type: PointsTransactionType::Transfer,
amount: req.amount,
balance_before: to_before,
balance_after: to_account.balance,
related_business_id: Some(format!("TRANSFER_FROM_{}", from_account.id)),
remark: req.remark,
created_at: Utc::now(),
};
let from_id = self.txn_repo.create(&from_txn).await?;
let to_id = self.txn_repo.create(&to_txn).await?;
Ok((
PointsTransaction { id: from_id, ..from_txn },
PointsTransaction { id: to_id, ..to_txn },
))
}
```
## 六、仓储接口
### 6.1 PointsAccountRepository
```rust
#[async_trait]
pub trait PointsAccountRepository: Send + Sync {
async fn create(&self, account: &PointsAccount) -> Result<i64>;
async fn find_by_id(&self, id: i64) -> Result<Option<PointsAccount>>;
async fn find_by_sub_account(&self, sub_account_id: i64) -> Result<Vec<PointsAccount>>;
async fn update(&self, account: &PointsAccount) -> Result<()>;
}
```
### 6.2 PointsTransactionRepository
```rust
#[async_trait]
pub trait PointsTransactionRepository: Send + Sync {
async fn create(&self, txn: &PointsTransaction) -> Result<i64>;
async fn find_by_account(&self, account_id: i64, query: &PointsTransactionQuery) -> Result<Vec<PointsTransaction>>;
async fn find_by_txn_no(&self, txn_no: &str) -> Result<Option<PointsTransaction>>;
}
```
## 七、API 接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /api/v1/points/accounts/:sub_account_id | 获取积分账户 |
| POST | /api/v1/points/earn | 获取积分 |
| POST | /api/v1/points/spend | 消费积分 |
| POST | /api/v1/points/transfer | 转移积分 |
| GET | /api/v1/points/transactions | 查询积分交易 |
## 八、数据库表结构
### 8.1 points_account 表
```sql
CREATE TABLE points_account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
sub_account_id BIGINT NOT NULL,
points_type VARCHAR(32) NOT NULL,
balance DECIMAL(20, 2) DEFAULT 0.00,
total_earned DECIMAL(20, 2) DEFAULT 0.00,
total_spent DECIMAL(20, 2) DEFAULT 0.00,
total_expired DECIMAL(20, 2) DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE INDEX idx_sub_account_type (sub_account_id, points_type),
INDEX idx_points_type (points_type)
);
```
### 8.2 points_transaction 表
```sql
CREATE TABLE points_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
txn_no VARCHAR(64) NOT NULL UNIQUE,
points_account_id BIGINT NOT NULL,
txn_type VARCHAR(32) NOT NULL,
amount DECIMAL(20, 2) NOT NULL,
balance_before DECIMAL(20, 2) NOT NULL,
balance_after DECIMAL(20, 2) NOT NULL,
related_business_id VARCHAR(128),
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (points_account_id) REFERENCES points_account(id),
INDEX idx_account (points_account_id),
INDEX idx_txn_type (txn_type),
INDEX idx_created_at (created_at)
);
```
### 8.3 points_rule 表
```sql
CREATE TABLE points_rule (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
points_type VARCHAR(32) NOT NULL,
rule_type VARCHAR(32) NOT NULL,
config JSON NOT NULL,
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_points_type (points_type),
INDEX idx_enabled (enabled)
);
```
## 九、积分规则
### 9.1 获取规则
| 参数 | 说明 | 示例 |
|------|------|------|
| base_points | 基础积分 | 100 |
| multiplier | 倍率 | 1.5 |
| max_daily | 每日上限 | 1000 |
| valid_days | 有效期(天) | 365 |
### 9.2 消费规则
| 参数 | 说明 | 示例 |
|------|------|------|
| min_points | 最低消费 | 100 |
| exchange_rate | 兑换比例 | 0.01 |
### 9.3 过期规则
| 参数 | 说明 | 示例 |
|------|------|------|
| expire_days | 过期天数 | 365 |
| notify_before | 提前通知天数 | 30 |
## 十、统计报表
### 10.1 账户统计
```rust
pub struct PointsStatistics {
pub account_id: i64,
pub balance: Decimal,
pub total_earned: Decimal,
pub total_spent: Decimal,
pub total_expired: Decimal,
pub earn_count: i64,
pub spend_count: i64,
pub transfer_in_count: i64,
pub transfer_out_count: i64,
}
```
### 10.2 周期统计
- 日统计:每日积分变动
- 月统计:月度积分汇总
- 年统计:年度积分报表
## 十一、注意事项
1. **积分精度**:使用 Decimal 类型保留2位小数
2. **并发控制**:积分操作需加锁,防止超发
3. **审计追踪**:所有积分变动必须有交易记录
4. **过期处理**:定期扫描处理过期积分