# 交易域 (Transaction Domain) ## 一、领域概述 交易域负责处理系统内的所有资金流转,包括转账、充值、提现等操作。该域实现了完整的交易状态机和三键幂等体系,确保交易的安全性和一致性。 ## 二、核心概念 ### 2.1 三键幂等体系 | 键名 | 字段 | 说明 | 唯一性 | |------|------|------|--------| | JZTxId | txn_no | 狱政交易号 | 系统全局唯一 | | BankTxId | bank_ref_no | 银行交易号 | 银行返回,用于对账 | | SourceKey | source_key | 来源幂等键 | 外部入账去重 | **SourceKey 格式**: ``` {银行流水号}_{金额}_{记账日}_{对方户名归一化} ``` ### 2.2 交易状态机 ``` ┌──────────────────────────────────────┐ │ │ ▼ │ ┌─────────┐ ┌─────────┐ ┌──────────────┐ ┌───────┴───┐ │ Created │───►│ Pending │───►│ BankSubmitted│───►│ Success │ └─────────┘ └─────────┘ └──────────────┘ └───────────┘ │ │ │ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ Failed │ │ Timeout │──────►│ Reversed │ └─────────┘ └─────────┘ └──────────┘ ``` **状态说明**: | 状态 | 说明 | 后续操作 | |------|------|----------| | Created | 已创建(初始状态) | 继续处理或取消 | | Pending | 待处理(已建立在途) | 提交银行 | | BankSubmitted | 已提交银行 | 等待回执 | | Success | 成功(银行确认) | 结转在途 | | Failed | 失败(银行拒绝) | 回退在途 | | Timeout | 超时(无回执) | 等待对账或重试 | | Reversed | 已冲正 | 终态 | ## 三、核心实体 ### 3.1 系统交易 (SystemTransaction) ```rust pub struct SystemTransaction { pub id: i64, pub txn_no: String, // 狱政交易号 (JZTxId) pub txn_type: TransactionType, // 交易类型 pub from_account_id: Option, // 转出账户ID pub to_account_id: Option, // 转入账户ID pub amount: Decimal, // 金额 pub status: TransactionStatus, // 状态 pub bank_ref_no: Option, // 银行交易号 (BankTxId) pub source_key: Option, // 来源幂等键 (SourceKey) pub remark: Option, // 备注 pub created_at: DateTime, // 创建时间 pub confirmed_at: Option>, // 确认时间 pub submitted_at: Option>, // 提交银行时间 pub version: i32, // 乐观锁版本 } ``` **核心方法**: ```rust impl SystemTransaction { // 检查是否可以提交到银行 pub fn can_submit(&self) -> bool { matches!(self.status, TransactionStatus::Pending | TransactionStatus::Created) } // 检查是否需要对账 pub fn needs_reconciliation(&self) -> bool { matches!(self.status, TransactionStatus::BankSubmitted | TransactionStatus::Timeout | TransactionStatus::Processing ) } // 检查是否为终态 pub fn is_terminal(&self) -> bool { self.status.is_terminal() } // 检查是否超时 pub fn is_timeout(&self, timeout_seconds: i64) -> bool { if self.status != TransactionStatus::BankSubmitted { return false; } if let Some(submitted_at) = self.submitted_at { let elapsed = Utc::now().signed_duration_since(submitted_at); return elapsed.num_seconds() > timeout_seconds; } false } // 尝试状态转移 pub fn try_transition(&mut self, target: TransactionStatus) -> Result<(), String> { if self.status.can_transition_to(target) { self.status = target; self.version += 1; Ok(()) } else { Err(format!("无效的状态转移: {:?} -> {:?}", self.status, target)) } } } ``` ### 3.2 银行交易 (BankTransaction) 从银行同步的交易流水。 ```rust pub struct BankTransaction { pub id: i64, pub bank_ref_no: String, // 银行参考号 pub physical_account_id: i64, // 实体账户ID pub txn_type: String, // 交易类型 pub direction: TransactionDirection, // 交易方向 pub amount: Decimal, // 金额 pub counterparty_name: Option,// 对手方名称 pub counterparty_account: Option,// 对手方账号 pub txn_time: DateTime, // 交易时间 pub sync_time: DateTime, // 同步时间 pub match_status: MatchStatus, // 匹配状态 pub matched_txn_no: Option, // 匹配的系统交易号 pub remark: Option, // 摘要 } ``` ## 四、枚举类型 ### 4.1 交易状态 (TransactionStatus) ```rust pub enum TransactionStatus { Created, // 已创建 Pending, // 待处理 BankSubmitted, // 已提交银行 Success, // 成功 Failed, // 失败 Timeout, // 超时 Reversed, // 已冲正 // 兼容旧状态 Processing, // 处理中 → BankSubmitted Confirmed, // 已确认 → Success Mismatch, // 不匹配 } ``` **状态转移规则**: ```rust impl TransactionStatus { pub fn can_transition_to(&self, target: Self) -> bool { match (self, target) { // Created -> Pending (Self::Created, Self::Pending) => true, // Pending -> BankSubmitted | Failed (Self::Pending, Self::BankSubmitted) => true, (Self::Pending, Self::Failed) => true, // BankSubmitted -> Success | Failed | Timeout (Self::BankSubmitted, Self::Success) => true, (Self::BankSubmitted, Self::Failed) => true, (Self::BankSubmitted, Self::Timeout) => true, // Timeout -> Success | Failed (对账确认) (Self::Timeout, Self::Success) => true, (Self::Timeout, Self::Failed) => true, // Success -> Reversed (冲正) (Self::Success, Self::Reversed) => true, _ => false, } } } ``` ### 4.2 交易类型 (TransactionType) ```rust pub enum TransactionType { Transfer, // 转账 Deposit, // 充值 Withdrawal, // 提现 Fee, // 手续费 Interest, // 利息 Adjustment, // 调整 Other(String), // 其他 } ``` ### 4.3 交易方向 (TransactionDirection) ```rust pub enum TransactionDirection { Inbound, // 入账 Outbound, // 出账 } ``` ### 4.4 匹配状态 (MatchStatus) ```rust pub enum MatchStatus { Unmatched, // 未匹配 Matched, // 已匹配 Mismatch, // 不匹配 } ``` ## 五、交易处理流程 ### 5.1 出金流程 ```mermaid sequenceDiagram participant Client participant TxnService participant LedgerService participant BankClient participant CompService Client->>TxnService: 1. 发起转账 TxnService->>TxnService: 2. 创建交易 (Created) TxnService->>LedgerService: 3. 优先级扣款 LedgerService-->>TxnService: 扣款结果 TxnService->>LedgerService: 4. 建立在途 TxnService->>TxnService: 5. 更新状态 (Pending) TxnService->>BankClient: 6. 提交银行 TxnService->>TxnService: 7. 更新状态 (BankSubmitted) alt 银行成功 BankClient-->>TxnService: 成功回执 TxnService->>LedgerService: 8a. 结转在途 TxnService->>TxnService: 9a. 更新状态 (Success) else 银行失败 BankClient-->>TxnService: 失败回执 TxnService->>LedgerService: 8b. 回退在途 TxnService->>TxnService: 9b. 更新状态 (Failed) else 超时 Note over TxnService: 等待超时 TxnService->>CompService: 8c. 创建补偿任务 TxnService->>TxnService: 9c. 更新状态 (Timeout) end TxnService-->>Client: 返回结果 ``` ### 5.2 入金流程 ```mermaid sequenceDiagram participant Bank participant TxnService participant LedgerService Bank->>TxnService: 1. 银行入账通知 TxnService->>TxnService: 2. 检查 SourceKey 幂等 alt 重复入账 TxnService-->>Bank: 返回已处理 else 新入账 TxnService->>TxnService: 3. 创建交易 (Success) TxnService->>LedgerService: 4. 增加个人余额 TxnService->>LedgerService: 5. 同步银行余额 TxnService-->>Bank: 处理成功 end ``` ### 5.3 冲正流程 ```mermaid sequenceDiagram participant Operator participant TxnService participant LedgerService Operator->>TxnService: 1. 发起冲正 TxnService->>TxnService: 2. 检查是否可冲正 alt 可以冲正 TxnService->>LedgerService: 3. 反向记账 TxnService->>TxnService: 4. 更新状态 (Reversed) TxnService-->>Operator: 冲正成功 else 不可冲正 TxnService-->>Operator: 冲正失败 end ``` ## 六、领域服务 ### 6.1 TransactionService ```rust impl TransactionService { // ========== 交易创建 ========== // 创建转账交易 pub async fn transfer(&self, req: TransferRequest) -> Result; // 创建充值交易 pub async fn deposit(&self, req: DepositRequest) -> Result; // 创建提现交易 pub async fn withdraw(&self, req: WithdrawRequest) -> Result; // ========== 交易查询 ========== // 获取交易详情 pub async fn get_transaction(&self, id: i64) -> Result; // 根据交易号查询 pub async fn get_by_txn_no(&self, txn_no: &str) -> Result; // 查询交易列表 pub async fn list_transactions(&self, query: TransactionQuery) -> Result>; // ========== 状态管理 ========== // 提交到银行 pub async fn submit_to_bank(&self, id: i64) -> Result; // 确认交易 pub async fn confirm_transaction(&self, id: i64, bank_ref_no: &str) -> Result<()>; // 标记失败 pub async fn mark_failed(&self, id: i64, reason: &str) -> Result<()>; // 标记超时 pub async fn mark_timeout(&self, id: i64) -> Result<()>; // 冲正交易 pub async fn reverse_transaction(&self, id: i64) -> Result<()>; // ========== 幂等检查 ========== // 检查 SourceKey 是否存在 pub async fn check_source_key(&self, source_key: &str) -> Result>; } ``` ## 七、仓储接口 ### 7.1 SystemTransactionRepository ```rust #[async_trait] pub trait SystemTransactionRepository: Send + Sync { async fn create(&self, req: &CreateSystemTransactionRequest) -> Result; async fn find_by_id(&self, id: i64) -> Result>; async fn find_by_txn_no(&self, txn_no: &str) -> Result>; async fn find_by_bank_ref_no(&self, bank_ref_no: &str) -> Result>; async fn find_by_source_key(&self, source_key: &str) -> Result>; async fn find_by_status(&self, status: TransactionStatus) -> Result>; async fn find_pending(&self) -> Result>; async fn find_needs_reconciliation(&self) -> Result>; async fn query(&self, query: &TransactionQuery) -> Result>; async fn update_status(&self, id: i64, status: TransactionStatus) -> Result<()>; async fn set_bank_ref_no(&self, id: i64, bank_ref_no: &str) -> Result<()>; async fn set_submitted_at(&self, id: i64) -> Result<()>; async fn confirm(&self, id: i64, bank_ref_no: &str) -> Result<()>; } ``` ### 7.2 BankTransactionRepository ```rust #[async_trait] pub trait BankTransactionRepository: Send + Sync { async fn create(&self, txn: &BankTransaction) -> Result; async fn find_by_bank_ref_no(&self, bank_ref_no: &str) -> Result>; async fn find_unmatched(&self) -> Result>; async fn update_match_status(&self, id: i64, status: MatchStatus, matched_txn_no: Option<&str>) -> Result<()>; } ``` ## 八、API 接口 | 方法 | 路径 | 说明 | |------|------|------| | POST | /api/v1/transactions/transfer | 发起转账 | | POST | /api/v1/transactions/deposit | 发起充值 | | POST | /api/v1/transactions/withdraw | 发起提现 | | GET | /api/v1/transactions/:id | 获取交易详情 | | GET | /api/v1/transactions | 获取交易列表 | ## 九、数据库表结构 ### 9.1 system_transaction 表 ```sql CREATE TABLE system_transaction ( id BIGINT PRIMARY KEY AUTO_INCREMENT, txn_no VARCHAR(64) NOT NULL UNIQUE, txn_type VARCHAR(32) NOT NULL, from_account_id BIGINT, to_account_id BIGINT, amount DECIMAL(20, 2) NOT NULL, status VARCHAR(32) DEFAULT 'created', bank_ref_no VARCHAR(64), source_key VARCHAR(256), remark TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, confirmed_at TIMESTAMP NULL, submitted_at TIMESTAMP NULL, version INT DEFAULT 0, INDEX idx_status (status), INDEX idx_from_account (from_account_id), INDEX idx_to_account (to_account_id), INDEX idx_bank_ref_no (bank_ref_no), INDEX idx_source_key (source_key), INDEX idx_created_at (created_at) ); ``` ### 9.2 bank_transaction 表 ```sql CREATE TABLE bank_transaction ( id BIGINT PRIMARY KEY AUTO_INCREMENT, bank_ref_no VARCHAR(64) NOT NULL UNIQUE, physical_account_id BIGINT NOT NULL, txn_type VARCHAR(32) NOT NULL, direction VARCHAR(16) NOT NULL, amount DECIMAL(20, 2) NOT NULL, counterparty_name VARCHAR(128), counterparty_account VARCHAR(64), txn_time TIMESTAMP NOT NULL, sync_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, match_status VARCHAR(32) DEFAULT 'unmatched', matched_txn_no VARCHAR(64), remark TEXT, INDEX idx_physical_account (physical_account_id), INDEX idx_match_status (match_status), INDEX idx_txn_time (txn_time) ); ``` ## 十、并发控制 ### 10.1 乐观锁 使用版本号防止并发更新冲突: ```rust // 更新时检查版本 UPDATE system_transaction SET status = ?, version = version + 1 WHERE id = ? AND version = ? ``` ### 10.2 幂等性保证 1. **txn_no 唯一**:系统交易号全局唯一 2. **source_key 去重**:外部入账通过 SourceKey 去重 3. **状态机约束**:只允许合法的状态转移 ## 十一、错误处理 | 错误类型 | 说明 | 处理方式 | |----------|------|----------| | InsufficientBalance | 余额不足 | 拒绝交易 | | InvalidStateTransition | 非法状态转移 | 返回错误 | | DuplicateTransaction | 重复交易 | 返回已有交易 | | BankTimeout | 银行超时 | 创建补偿任务 | | OptimisticLockFailed | 乐观锁冲突 | 重试 |