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

15 KiB
Raw Permalink Blame History

交易域 (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)

pub struct SystemTransaction {
    pub id: i64,
    pub txn_no: String,                      // 狱政交易号 (JZTxId)
    pub txn_type: TransactionType,           // 交易类型
    pub from_account_id: Option<i64>,        // 转出账户ID
    pub to_account_id: Option<i64>,          // 转入账户ID
    pub amount: Decimal,                     // 金额
    pub status: TransactionStatus,           // 状态
    pub bank_ref_no: Option<String>,         // 银行交易号 (BankTxId)
    pub source_key: Option<String>,          // 来源幂等键 (SourceKey)
    pub remark: Option<String>,              // 备注
    pub created_at: DateTime<Utc>,           // 创建时间
    pub confirmed_at: Option<DateTime<Utc>>, // 确认时间
    pub submitted_at: Option<DateTime<Utc>>, // 提交银行时间
    pub version: i32,                        // 乐观锁版本
}

核心方法

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)

从银行同步的交易流水。

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<String>,// 对手方名称
    pub counterparty_account: Option<String>,// 对手方账号
    pub txn_time: DateTime<Utc>,          // 交易时间
    pub sync_time: DateTime<Utc>,         // 同步时间
    pub match_status: MatchStatus,        // 匹配状态
    pub matched_txn_no: Option<String>,   // 匹配的系统交易号
    pub remark: Option<String>,           // 摘要
}

四、枚举类型

4.1 交易状态 (TransactionStatus)

pub enum TransactionStatus {
    Created,       // 已创建
    Pending,       // 待处理
    BankSubmitted, // 已提交银行
    Success,       // 成功
    Failed,        // 失败
    Timeout,       // 超时
    Reversed,      // 已冲正
    // 兼容旧状态
    Processing,    // 处理中 → BankSubmitted
    Confirmed,     // 已确认 → Success
    Mismatch,      // 不匹配
}

状态转移规则

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)

pub enum TransactionType {
    Transfer,    // 转账
    Deposit,     // 充值
    Withdrawal,  // 提现
    Fee,         // 手续费
    Interest,    // 利息
    Adjustment,  // 调整
    Other(String), // 其他
}

4.3 交易方向 (TransactionDirection)

pub enum TransactionDirection {
    Inbound,  // 入账
    Outbound, // 出账
}

4.4 匹配状态 (MatchStatus)

pub enum MatchStatus {
    Unmatched, // 未匹配
    Matched,   // 已匹配
    Mismatch,  // 不匹配
}

五、交易处理流程

5.1 出金流程

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 入金流程

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 冲正流程

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

impl TransactionService {
    // ========== 交易创建 ==========
    
    // 创建转账交易
    pub async fn transfer(&self, req: TransferRequest) -> Result<SystemTransaction>;
    
    // 创建充值交易
    pub async fn deposit(&self, req: DepositRequest) -> Result<SystemTransaction>;
    
    // 创建提现交易
    pub async fn withdraw(&self, req: WithdrawRequest) -> Result<SystemTransaction>;
    
    // ========== 交易查询 ==========
    
    // 获取交易详情
    pub async fn get_transaction(&self, id: i64) -> Result<SystemTransaction>;
    
    // 根据交易号查询
    pub async fn get_by_txn_no(&self, txn_no: &str) -> Result<SystemTransaction>;
    
    // 查询交易列表
    pub async fn list_transactions(&self, query: TransactionQuery) -> Result<Vec<SystemTransaction>>;
    
    // ========== 状态管理 ==========
    
    // 提交到银行
    pub async fn submit_to_bank(&self, id: i64) -> Result<String>;
    
    // 确认交易
    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<Option<SystemTransaction>>;
}

七、仓储接口

7.1 SystemTransactionRepository

#[async_trait]
pub trait SystemTransactionRepository: Send + Sync {
    async fn create(&self, req: &CreateSystemTransactionRequest) -> Result<SystemTransaction>;
    async fn find_by_id(&self, id: i64) -> Result<Option<SystemTransaction>>;
    async fn find_by_txn_no(&self, txn_no: &str) -> Result<Option<SystemTransaction>>;
    async fn find_by_bank_ref_no(&self, bank_ref_no: &str) -> Result<Option<SystemTransaction>>;
    async fn find_by_source_key(&self, source_key: &str) -> Result<Option<SystemTransaction>>;
    async fn find_by_status(&self, status: TransactionStatus) -> Result<Vec<SystemTransaction>>;
    async fn find_pending(&self) -> Result<Vec<SystemTransaction>>;
    async fn find_needs_reconciliation(&self) -> Result<Vec<SystemTransaction>>;
    async fn query(&self, query: &TransactionQuery) -> Result<Vec<SystemTransaction>>;
    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

#[async_trait]
pub trait BankTransactionRepository: Send + Sync {
    async fn create(&self, txn: &BankTransaction) -> Result<i64>;
    async fn find_by_bank_ref_no(&self, bank_ref_no: &str) -> Result<Option<BankTransaction>>;
    async fn find_unmatched(&self) -> Result<Vec<BankTransaction>>;
    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 表

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 表

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 乐观锁

使用版本号防止并发更新冲突:

// 更新时检查版本
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 乐观锁冲突 重试