对账域 (Reconciliation Domain)
一、领域概述
对账域负责确保系统账务与银行账务的一致性,包括交易匹配、差异处理、手工补录、三账校验等功能。
二、核心概念
2.1 三账对账闭环
┌─────────────────────────────────────────────────────────┐
│ 三账校验公式 │
│ │
│ 总账余额 = 银行账余额 + 在途净额 │
│ │
│ ledger_total = bank_balance + transit_net │
└─────────────────────────────────────────────────────────┘
银行账: 银行实际余额(通过银行接口查询)
在途账: 已扣未确认的资金(transit_amount)
总账: 系统记账余额(personal + labor + frozen)
2.2 对账流程
graph TD
A[获取银行流水] --> B[获取系统交易]
B --> C[交易匹配]
C --> D{匹配结果}
D -->|匹配| E[标记已匹配]
D -->|系统未达| F[创建系统未达项]
D -->|银行未达| G[创建银行未达项]
D -->|金额不匹配| H[创建差异项]
E --> I[三账校验]
F --> I
G --> I
H --> I
I --> J{是否平衡}
J -->|是| K[对账完成]
J -->|否| L[需要审核]
三、核心实体
3.1 对账批次 (ReconciliationBatch)
pub struct ReconciliationBatch {
pub id: i64,
pub batch_no: String, // 批次编号
pub physical_account_id: i64, // 实体账户ID
pub recon_date: NaiveDate, // 对账日期
pub total_count: i32, // 总记录数
pub matched_count: i32, // 匹配数
pub mismatch_count: i32, // 不匹配数
pub status: ReconciliationStatus, // 状态
pub created_at: DateTime<Utc>, // 创建时间
pub completed_at: Option<DateTime<Utc>>, // 完成时间
// 三账对账结果
pub bank_total: Option<Decimal>, // 银行账汇总
pub transit_net: Option<Decimal>, // 在途净额
pub ledger_total: Option<Decimal>, // 总账汇总
pub three_account_balanced: Option<bool>, // 三账是否平衡
}
核心方法:
impl ReconciliationBatch {
// 计算匹配率
pub fn match_rate(&self) -> f64 {
if self.total_count == 0 {
return 100.0;
}
(self.matched_count as f64 / self.total_count as f64) * 100.0
}
// 是否全部匹配
pub fn is_all_matched(&self) -> bool {
self.mismatch_count == 0
}
// 是否已完成三账对账
pub fn has_three_account_result(&self) -> bool {
self.three_account_balanced.is_some()
}
// 三账差异金额
pub fn three_account_difference(&self) -> Option<Decimal> {
match (self.ledger_total, self.bank_total, self.transit_net) {
(Some(ledger), Some(bank), Some(transit)) => {
Some(ledger - (bank + transit))
}
_ => None,
}
}
}
3.2 对账明细项 (ReconciliationItem)
pub struct ReconciliationItem {
pub id: i64,
pub batch_id: i64, // 批次ID
pub system_txn_no: Option<String>, // 系统交易号
pub bank_ref_no: Option<String>, // 银行参考号
pub system_amount: Option<Decimal>, // 系统金额
pub bank_amount: Option<Decimal>, // 银行金额
pub diff_amount: Decimal, // 差异金额
pub status: ReconciliationItemStatus, // 状态
pub remark: Option<String>, // 处理备注
pub created_at: DateTime<Utc>,
}
核心方法:
impl ReconciliationItem {
// 检查是否需要手工处理
pub fn needs_manual_handling(&self) -> bool {
matches!(
self.status,
ReconciliationItemStatus::SystemUnreached
| ReconciliationItemStatus::BankUnreached
| ReconciliationItemStatus::AmountMismatch
)
}
}
3.3 手工补录 (ManualAdjustment)
pub struct ManualAdjustment {
pub id: i64,
pub adjustment_no: String, // 补录编号
pub related_txn_no: Option<String>, // 关联交易号
pub reconciliation_item_id: Option<i64>,// 关联对账项ID
pub adjustment_type: AdjustmentType, // 补录类型
pub account_id: i64, // 账户ID
pub amount: Decimal, // 金额
pub reason: String, // 原因说明
pub operator: String, // 操作人
pub approver: Option<String>, // 审批人
pub status: AdjustmentStatus, // 状态
pub created_at: DateTime<Utc>,
pub approved_at: Option<DateTime<Utc>>, // 审批时间
}
四、枚举类型
4.1 对账批次状态 (ReconciliationStatus)
pub enum ReconciliationStatus {
Processing, // 处理中
Completed, // 已完成
NeedReview, // 需要审核
}
4.2 对账项状态 (ReconciliationItemStatus)
pub enum ReconciliationItemStatus {
Matched, // 已匹配
SystemUnreached, // 系统未达(系统有银行无)
BankUnreached, // 银行未达(银行有系统无)
AmountMismatch, // 金额不匹配
Adjusted, // 已调整
}
| 状态 |
说明 |
处理方式 |
| Matched |
系统与银行完全匹配 |
无需处理 |
| SystemUnreached |
系统有记录但银行无 |
核实后补录或冲销 |
| BankUnreached |
银行有记录但系统无 |
核实后补录 |
| AmountMismatch |
金额不一致 |
核实后调整 |
| Adjusted |
已手工调整 |
已处理 |
4.3 补录类型 (AdjustmentType)
pub enum AdjustmentType {
Add, // 补录(新增)
Reverse, // 冲销
Modify, // 修改
}
4.4 补录状态 (AdjustmentStatus)
pub enum AdjustmentStatus {
Pending, // 待审批
Approved, // 已审批
Rejected, // 已拒绝
}
五、对账匹配规则
5.1 匹配策略
- 精确匹配:银行交易号 = 系统银行参考号
- 金额匹配:金额完全一致
- 时间匹配:交易时间在允许范围内
- 方向匹配:入账/出账方向一致
5.2 匹配优先级
1. 按银行交易号精确匹配
2. 按金额 + 时间 + 方向模糊匹配
3. 按 SourceKey 匹配(入账)
4. 无法匹配则标记为未达
5.3 差异处理
| 差异类型 |
处理方式 |
| 金额差异 < 1分 |
自动调平 |
| 金额差异 < 100元 |
人工审核 |
| 金额差异 >= 100元 |
升级处理 |
六、三账校验
6.1 校验结果
pub struct ThreeAccountResult {
pub bank_balance: Decimal, // 银行账余额
pub transit_net: Decimal, // 在途净额
pub ledger_total: Decimal, // 总账余额
pub is_balanced: bool, // 是否平衡
pub difference: Decimal, // 差异金额
}
6.2 校验公式
期望总账 = 银行余额 + 在途净额
差异 = 实际总账 - 期望总账
平衡 = (差异 == 0)
6.3 不平衡原因分析
| 原因 |
说明 |
处理 |
| 银行延迟 |
银行入账延迟 |
等待银行同步 |
| 系统错误 |
记账错误 |
查找并修正 |
| 在途异常 |
在途状态不正确 |
核实在途交易 |
| 并发问题 |
对账期间有新交易 |
锁定后重新对账 |
七、领域服务
7.1 ReconciliationService
impl ReconciliationService {
// ========== 对账批次管理 ==========
// 执行对账
pub async fn run_reconciliation(&self, req: CreateReconciliationBatchRequest) -> Result<ReconciliationBatch>;
// 获取对账批次
pub async fn get_batch(&self, id: i64) -> Result<ReconciliationBatch>;
// 获取对账明细
pub async fn get_batch_items(&self, batch_id: i64) -> Result<Vec<ReconciliationItem>>;
// ========== 三账校验 ==========
// 执行三账校验
pub async fn verify_three_accounts(&self, physical_account_id: i64) -> Result<ThreeAccountVerificationResult>;
// 更新批次三账结果
pub async fn update_three_account_result(&self, batch_id: i64, result: &ThreeAccountResult) -> Result<()>;
// ========== 手工补录 ==========
// 创建手工补录
pub async fn create_adjustment(&self, req: CreateManualAdjustmentRequest) -> Result<ManualAdjustment>;
// 审批补录
pub async fn approve_adjustment(&self, id: i64, approver: &str) -> Result<()>;
// 拒绝补录
pub async fn reject_adjustment(&self, id: i64, approver: &str, reason: &str) -> Result<()>;
// 获取待审批补录
pub async fn list_pending_adjustments(&self) -> Result<Vec<ManualAdjustment>>;
// ========== 统计查询 ==========
// 获取对账统计
pub async fn get_stats(&self, batch_id: i64) -> Result<ReconciliationStats>;
}
7.2 三账校验实现
pub async fn verify_three_accounts(&self, physical_account_id: i64) -> Result<ThreeAccountVerificationResult> {
// 1. 获取银行余额
let bank_balance = self.bank_client.query_balance(physical_account_id).await?;
// 2. 计算在途净额
let transit_net = self.ledger_service.get_transit_total(physical_account_id).await?;
// 3. 计算总账余额
let ledger_total = self.ledger_service.get_ledger_total(physical_account_id).await?;
// 4. 校验
let expected_total = bank_balance + transit_net;
let difference = ledger_total - expected_total;
let is_balanced = difference.abs() < Decimal::new(1, 2); // 允许1分误差
Ok(ThreeAccountVerificationResult {
physical_account_id,
bank_balance,
transit_net,
ledger_total,
expected_total,
difference,
is_balanced,
verified_at: Utc::now(),
})
}
八、API 接口
| 方法 |
路径 |
说明 |
| POST |
/api/v1/reconciliation/run |
执行对账 |
| GET |
/api/v1/reconciliation/batches/:id |
获取对账批次 |
| GET |
/api/v1/reconciliation/batches/:id/items |
获取对账明细 |
| GET |
/api/v1/reconciliation/three-account/:id |
三账校验 |
| POST |
/api/v1/reconciliation/adjustments |
创建手工补录 |
| POST |
/api/v1/reconciliation/adjustments/:id/approve |
审批补录 |
| POST |
/api/v1/reconciliation/adjustments/:id/reject |
拒绝补录 |
| GET |
/api/v1/reconciliation/adjustments/pending |
获取待审批补录 |
九、数据库表结构
9.1 reconciliation_batch 表
CREATE TABLE reconciliation_batch (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
batch_no VARCHAR(64) NOT NULL UNIQUE,
physical_account_id BIGINT NOT NULL,
recon_date DATE NOT NULL,
total_count INT DEFAULT 0,
matched_count INT DEFAULT 0,
mismatch_count INT DEFAULT 0,
status VARCHAR(32) DEFAULT 'processing',
bank_total DECIMAL(20, 2),
transit_net DECIMAL(20, 2),
ledger_total DECIMAL(20, 2),
three_account_balanced BOOLEAN,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
INDEX idx_physical_account (physical_account_id),
INDEX idx_recon_date (recon_date),
INDEX idx_status (status)
);
9.2 reconciliation_item 表
CREATE TABLE reconciliation_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
batch_id BIGINT NOT NULL,
system_txn_no VARCHAR(64),
bank_ref_no VARCHAR(64),
system_amount DECIMAL(20, 2),
bank_amount DECIMAL(20, 2),
diff_amount DECIMAL(20, 2) DEFAULT 0.00,
status VARCHAR(32) NOT NULL,
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (batch_id) REFERENCES reconciliation_batch(id),
INDEX idx_batch (batch_id),
INDEX idx_status (status)
);
9.3 manual_adjustment 表
CREATE TABLE manual_adjustment (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
adjustment_no VARCHAR(64) NOT NULL UNIQUE,
related_txn_no VARCHAR(64),
reconciliation_item_id BIGINT,
adjustment_type VARCHAR(32) NOT NULL,
account_id BIGINT NOT NULL,
amount DECIMAL(20, 2) NOT NULL,
reason TEXT NOT NULL,
operator VARCHAR(64) NOT NULL,
approver VARCHAR(64),
status VARCHAR(32) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
approved_at TIMESTAMP NULL,
INDEX idx_status (status),
INDEX idx_operator (operator)
);
十、对账时机
10.1 自动对账
- 每日定时对账(T+1)
- 配置对账频率(按账户)
- 批量处理
10.2 手动对账
10.3 实时对账
十一、告警机制
| 告警级别 |
触发条件 |
处理方式 |
| 警告 |
匹配率 < 99% |
记录日志 |
| 严重 |
三账不平衡 |
通知运营 |
| 紧急 |
大额差异 |
升级处理 |