- 实现账户管理改进设计文档中的所有核心功能 - 三科目余额管理 (个人余额、劳动报酬、冻结余额) - 交易状态机 (created → pending → bank_submitted → success/failed/timeout → reversed) - 三键幂等体系 (JZTxId/BankTxId/SourceKey) - 优先级扣款规则 (先个人后劳动) - 在途资金管理 (可用→在途→结转/回退) - 三账对账闭环 (总账 = 银行账 + 在途净额) - 补偿服务域 (超时检测、重试、死信队列) - 虚拟银行模拟器用于业务测试 - 完整的集成测试套件 (133 个测试全部通过) - Docker 容器化部署配置 - 前端 Vue3 + TypeScript 项目结构
189 lines
5.0 KiB
Rust
189 lines
5.0 KiB
Rust
//! 账务服务单元测试
|
|
//!
|
|
//! 测试 LedgerService 的核心功能
|
|
|
|
// 由于 LedgerService 依赖仓储,这里主要测试账务逻辑
|
|
// 实际的服务测试在集成测试中进行
|
|
|
|
use rust_decimal_macros::dec;
|
|
|
|
use rustjr::domain::ledger::entity::ThreeAccountResult;
|
|
|
|
// ==================== 三账校验结果测试 ====================
|
|
|
|
#[test]
|
|
fn test_three_account_balanced() {
|
|
let result = ThreeAccountResult {
|
|
bank_balance: dec!(100000.00),
|
|
transit_net: dec!(5000.00),
|
|
ledger_total: dec!(95000.00),
|
|
is_balanced: true,
|
|
difference: dec!(0.00),
|
|
};
|
|
|
|
assert!(result.is_balanced);
|
|
assert_eq!(result.difference, dec!(0.00));
|
|
// 验证: ledger + transit = bank
|
|
assert_eq!(
|
|
result.ledger_total + result.transit_net,
|
|
result.bank_balance
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_three_account_short() {
|
|
// 短款:银行 < 总账 + 在途
|
|
let result = ThreeAccountResult {
|
|
bank_balance: dec!(100000.00),
|
|
transit_net: dec!(5000.00),
|
|
ledger_total: dec!(100000.00),
|
|
is_balanced: false,
|
|
difference: dec!(-5000.00), // 银行少 5000
|
|
};
|
|
|
|
assert!(!result.is_balanced);
|
|
assert!(result.difference < dec!(0.00));
|
|
}
|
|
|
|
#[test]
|
|
fn test_three_account_long() {
|
|
// 长款:银行 > 总账 + 在途
|
|
let result = ThreeAccountResult {
|
|
bank_balance: dec!(110000.00),
|
|
transit_net: dec!(5000.00),
|
|
ledger_total: dec!(100000.00),
|
|
is_balanced: false,
|
|
difference: dec!(5000.00), // 银行多 5000
|
|
};
|
|
|
|
assert!(!result.is_balanced);
|
|
assert!(result.difference > dec!(0.00));
|
|
}
|
|
|
|
// ==================== 扣款优先级逻辑测试 ====================
|
|
|
|
#[test]
|
|
fn test_deduction_priority_logic() {
|
|
// 测试扣款优先级计算逻辑
|
|
|
|
let personal = dec!(300.00);
|
|
let labor = dec!(500.00);
|
|
let to_deduct = dec!(600.00);
|
|
|
|
// 先扣个人
|
|
let from_personal = personal.min(to_deduct);
|
|
let remaining = to_deduct - from_personal;
|
|
|
|
// 再扣劳动
|
|
let from_labor = labor.min(remaining);
|
|
|
|
assert_eq!(from_personal, dec!(300.00));
|
|
assert_eq!(from_labor, dec!(300.00));
|
|
assert_eq!(from_personal + from_labor, to_deduct);
|
|
}
|
|
|
|
#[test]
|
|
fn test_deduction_personal_sufficient() {
|
|
let personal = dec!(1000.00);
|
|
let labor = dec!(500.00);
|
|
let to_deduct = dec!(800.00);
|
|
|
|
let from_personal = personal.min(to_deduct);
|
|
let remaining = to_deduct - from_personal;
|
|
let from_labor = labor.min(remaining);
|
|
|
|
assert_eq!(from_personal, dec!(800.00));
|
|
assert_eq!(from_labor, dec!(0.00));
|
|
}
|
|
|
|
#[test]
|
|
fn test_deduction_total_insufficient() {
|
|
let personal = dec!(300.00);
|
|
let labor = dec!(200.00);
|
|
let to_deduct = dec!(600.00);
|
|
|
|
let available = personal + labor;
|
|
|
|
// 余额不足
|
|
assert!(available < to_deduct);
|
|
}
|
|
|
|
// ==================== 在途计算逻辑测试 ====================
|
|
|
|
#[test]
|
|
fn test_transit_flow_calculation() {
|
|
// 模拟在途流转计算
|
|
|
|
let initial_personal = dec!(1000.00);
|
|
let initial_bank = dec!(1000.00);
|
|
let transit_amount = dec!(300.00);
|
|
|
|
// 1. 扣款(可用 -> 在途)
|
|
let after_deduct_personal = initial_personal - transit_amount;
|
|
let after_deduct_bank = initial_bank; // 银行余额不变
|
|
let in_transit = transit_amount;
|
|
|
|
assert_eq!(after_deduct_personal, dec!(700.00));
|
|
assert_eq!(in_transit, dec!(300.00));
|
|
|
|
// 2. 结转(银行确认扣款)
|
|
let final_bank = after_deduct_bank - transit_amount;
|
|
let final_transit = in_transit - transit_amount;
|
|
|
|
assert_eq!(final_bank, dec!(700.00));
|
|
assert_eq!(final_transit, dec!(0.00));
|
|
}
|
|
|
|
#[test]
|
|
fn test_transit_rollback_calculation() {
|
|
// 模拟在途回退计算
|
|
|
|
let after_deduct_personal = dec!(700.00);
|
|
let initial_bank = dec!(1000.00);
|
|
let in_transit = dec!(300.00);
|
|
|
|
// 回退(银行失败)
|
|
let final_personal = after_deduct_personal + in_transit;
|
|
let final_bank = initial_bank; // 银行余额不变
|
|
let final_transit = dec!(0.00);
|
|
|
|
assert_eq!(final_personal, dec!(1000.00));
|
|
assert_eq!(final_bank, dec!(1000.00));
|
|
assert_eq!(final_transit, dec!(0.00));
|
|
}
|
|
|
|
// ==================== 冻结逻辑测试 ====================
|
|
|
|
#[test]
|
|
fn test_freeze_from_available() {
|
|
// 冻结只能从可用余额中冻结
|
|
let personal = dec!(1000.00);
|
|
let labor = dec!(500.00);
|
|
let available = personal + labor;
|
|
let to_freeze = dec!(800.00);
|
|
|
|
assert!(available >= to_freeze);
|
|
|
|
// 冻结后
|
|
let new_personal = personal - to_freeze;
|
|
let new_frozen = to_freeze;
|
|
|
|
assert_eq!(new_personal, dec!(200.00));
|
|
assert_eq!(new_frozen, dec!(800.00));
|
|
}
|
|
|
|
#[test]
|
|
fn test_frozen_cannot_be_deducted() {
|
|
// 冻结金额不能被扣款
|
|
let personal = dec!(200.00);
|
|
let labor = dec!(500.00);
|
|
let frozen = dec!(800.00);
|
|
|
|
let available = personal + labor; // 不含冻结
|
|
let to_deduct = dec!(1000.00);
|
|
|
|
// 余额不足(虽然总额够,但可用余额不够)
|
|
assert!(available < to_deduct);
|
|
}
|
|
|