# 积分域 (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, pub updated_at: DateTime, } ``` **核心方法**: ```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, // 关联业务ID pub remark: Option, // 备注 pub created_at: DateTime, } ``` ### 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, } ``` **规则配置示例**: ```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>; // 创建积分账户 pub async fn create_account(&self, req: CreatePointsAccountRequest) -> Result; // ========== 积分操作 ========== // 获取积分 pub async fn earn_points(&self, req: PointsTransactionRequest) -> Result; // 消费积分 pub async fn spend_points(&self, req: PointsTransactionRequest) -> Result; // 转移积分 pub async fn transfer_points(&self, req: PointsTransferRequest) -> Result<(PointsTransaction, PointsTransaction)>; // 调整积分 pub async fn adjust_points(&self, req: PointsTransactionRequest) -> Result; // ========== 过期处理 ========== // 处理过期积分 pub async fn process_expired_points(&self) -> Result; // ========== 查询 ========== // 查询积分交易 pub async fn list_transactions(&self, query: PointsTransactionQuery) -> Result>; // 获取积分统计 pub async fn get_statistics(&self, account_id: i64) -> Result; } ``` ### 5.2 积分获取实现 ```rust pub async fn earn_points(&self, req: PointsTransactionRequest) -> Result { // 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; async fn find_by_id(&self, id: i64) -> Result>; async fn find_by_sub_account(&self, sub_account_id: i64) -> Result>; 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; async fn find_by_account(&self, account_id: i64, query: &PointsTransactionQuery) -> Result>; async fn find_by_txn_no(&self, txn_no: &str) -> Result>; } ``` ## 七、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. **过期处理**:定期扫描处理过期积分