- 实现账户管理改进设计文档中的所有核心功能 - 三科目余额管理 (个人余额、劳动报酬、冻结余额) - 交易状态机 (created → pending → bank_submitted → success/failed/timeout → reversed) - 三键幂等体系 (JZTxId/BankTxId/SourceKey) - 优先级扣款规则 (先个人后劳动) - 在途资金管理 (可用→在途→结转/回退) - 三账对账闭环 (总账 = 银行账 + 在途净额) - 补偿服务域 (超时检测、重试、死信队列) - 虚拟银行模拟器用于业务测试 - 完整的集成测试套件 (133 个测试全部通过) - Docker 容器化部署配置 - 前端 Vue3 + TypeScript 项目结构
180 lines
5.2 KiB
Rust
180 lines
5.2 KiB
Rust
//! 充值流程集成测试
|
|
|
|
use chrono::Utc;
|
|
use rust_decimal_macros::dec;
|
|
|
|
use rust_decimal::Decimal;
|
|
use rustjr::domain::ledger::entity::AccountBalance;
|
|
use rustjr::domain::account::AccountType;
|
|
use rustjr::infrastructure::bank_integration::{BankClient, mock_bank::MockBankClient};
|
|
|
|
// ==================== 测试辅助 ====================
|
|
|
|
fn create_balance(personal: rust_decimal::Decimal, labor: rust_decimal::Decimal) -> AccountBalance {
|
|
AccountBalance {
|
|
id: 1,
|
|
account_id: 1001,
|
|
account_type: AccountType::Virtual,
|
|
personal_balance: personal,
|
|
labor_balance: labor,
|
|
frozen_balance: Decimal::ZERO,
|
|
bank_balance: personal + labor,
|
|
transit_amount: Decimal::ZERO,
|
|
system_balance: personal + labor,
|
|
available_balance: personal + labor,
|
|
frozen_amount: Decimal::ZERO,
|
|
version: 1,
|
|
updated_at: Utc::now(),
|
|
}
|
|
}
|
|
|
|
// ==================== 个人余额充值测试 ====================
|
|
|
|
#[test]
|
|
fn test_personal_deposit() {
|
|
let mut balance = create_balance(dec!(1000.00), dec!(500.00));
|
|
|
|
// 模拟外部充值(家属汇款)
|
|
balance.add_personal(dec!(2000.00));
|
|
|
|
assert_eq!(balance.personal_balance, dec!(3000.00));
|
|
assert_eq!(balance.labor_balance, dec!(500.00));
|
|
assert_eq!(balance.bank_balance, dec!(3500.00));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_deposits() {
|
|
let mut balance = create_balance(dec!(0.00), dec!(0.00));
|
|
|
|
// 多次充值
|
|
balance.add_personal(dec!(100.00));
|
|
balance.add_personal(dec!(200.00));
|
|
balance.add_personal(dec!(300.00));
|
|
|
|
assert_eq!(balance.personal_balance, dec!(600.00));
|
|
assert_eq!(balance.bank_balance, dec!(600.00));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
}
|
|
|
|
// ==================== 劳动报酬发放测试 ====================
|
|
|
|
#[test]
|
|
fn test_labor_deposit() {
|
|
let mut balance = create_balance(dec!(1000.00), dec!(0.00));
|
|
|
|
// 劳动报酬发放
|
|
balance.add_labor(dec!(500.00));
|
|
|
|
assert_eq!(balance.personal_balance, dec!(1000.00));
|
|
assert_eq!(balance.labor_balance, dec!(500.00));
|
|
assert_eq!(balance.bank_balance, dec!(1500.00));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_mixed_deposits() {
|
|
let mut balance = create_balance(dec!(0.00), dec!(0.00));
|
|
|
|
// 先充值个人
|
|
balance.add_personal(dec!(1000.00));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
|
|
// 再发放劳动报酬
|
|
balance.add_labor(dec!(500.00));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
|
|
// 再充值个人
|
|
balance.add_personal(dec!(200.00));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
|
|
assert_eq!(balance.personal_balance, dec!(1200.00));
|
|
assert_eq!(balance.labor_balance, dec!(500.00));
|
|
assert_eq!(balance.bank_balance, dec!(1700.00));
|
|
}
|
|
|
|
// ==================== 外部入账模拟测试 ====================
|
|
|
|
#[tokio::test]
|
|
async fn test_external_deposit_via_mock_bank() {
|
|
let client = MockBankClient::new();
|
|
client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap();
|
|
|
|
// 模拟外部入账
|
|
let bank_ref = client.simulate_external_deposit(
|
|
"PRISON001",
|
|
"FAMILY001",
|
|
"家属张三",
|
|
dec!(2000.00),
|
|
Some("给罪犯XXX的充值".to_string()),
|
|
).unwrap();
|
|
|
|
assert!(bank_ref.starts_with("MOCK"));
|
|
|
|
// 验证余额
|
|
let balance = client.query_balance("PRISON001").await.unwrap();
|
|
assert_eq!(balance.balance, dec!(102000.00));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_external_deposit_in_statements() {
|
|
let client = MockBankClient::new();
|
|
client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap();
|
|
|
|
// 模拟外部入账
|
|
client.simulate_external_deposit(
|
|
"PRISON001",
|
|
"FAMILY001",
|
|
"家属张三",
|
|
dec!(2000.00),
|
|
Some("家属汇款".to_string()),
|
|
).unwrap();
|
|
|
|
// 查询流水
|
|
let today = Utc::now().date_naive();
|
|
let statements = client.query_statements("PRISON001", today, today).await.unwrap();
|
|
|
|
assert!(!statements.is_empty());
|
|
|
|
// 找到入账流水
|
|
let deposit = statements.iter().find(|s| s.direction == "in").unwrap();
|
|
assert_eq!(deposit.amount, dec!(2000.00));
|
|
}
|
|
|
|
// ==================== 边界条件测试 ====================
|
|
|
|
#[test]
|
|
fn test_zero_deposit() {
|
|
let mut balance = create_balance(dec!(1000.00), dec!(500.00));
|
|
let original_bank = balance.bank_balance;
|
|
|
|
balance.add_personal(dec!(0.00));
|
|
|
|
assert_eq!(balance.bank_balance, original_bank);
|
|
assert!(balance.validate_invariant().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_large_deposit() {
|
|
let mut balance = create_balance(dec!(0.00), dec!(0.00));
|
|
|
|
balance.add_personal(dec!(999999999.99));
|
|
|
|
assert_eq!(balance.personal_balance, dec!(999999999.99));
|
|
assert_eq!(balance.bank_balance, dec!(999999999.99));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_decimal_precision_deposit() {
|
|
let mut balance = create_balance(dec!(0.00), dec!(0.00));
|
|
|
|
balance.add_personal(dec!(0.01));
|
|
balance.add_personal(dec!(0.01));
|
|
balance.add_personal(dec!(0.01));
|
|
|
|
assert_eq!(balance.personal_balance, dec!(0.03));
|
|
assert!(balance.validate_invariant().is_ok());
|
|
}
|
|
|