//! 对账集成测试 use chrono::Utc; use rust_decimal::Decimal; use rust_decimal_macros::dec; use rustjr::domain::ledger::entity::{AccountBalance, ThreeAccountResult}; use rustjr::domain::account::AccountType; use rustjr::infrastructure::bank_integration::mock_bank::MockBankClient; use rustjr::infrastructure::bank_integration::BankClient; // ==================== 测试辅助 ==================== fn create_balance(personal: Decimal, labor: Decimal, frozen: Decimal, transit: Decimal) -> AccountBalance { AccountBalance { id: 1, account_id: 1001, account_type: AccountType::Virtual, personal_balance: personal, labor_balance: labor, frozen_balance: frozen, bank_balance: personal + labor + frozen, transit_amount: transit, system_balance: personal + labor, available_balance: personal + labor, frozen_amount: frozen, version: 1, updated_at: Utc::now(), } } /// 执行三账校验 fn verify_three_accounts(balance: &AccountBalance, bank_balance: Decimal) -> ThreeAccountResult { let ledger_total = balance.personal_balance + balance.labor_balance + balance.frozen_balance; let transit_net = balance.transit_amount; // 公式:总账 + 在途 = 银行 let expected_bank = ledger_total + transit_net; let difference = bank_balance - expected_bank; let is_balanced = difference.abs() < dec!(0.01); // 允许 1 分钱误差 ThreeAccountResult { bank_balance, transit_net, ledger_total, is_balanced, difference, } } // ==================== 三账平衡测试 ==================== #[test] fn test_three_account_balanced_no_transit() { // 没有在途时的三账平衡 let balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00), dec!(0.00)); let bank = balance.bank_balance; // 8000 let result = verify_three_accounts(&balance, bank); assert!(result.is_balanced); assert_eq!(result.difference, dec!(0.00)); assert_eq!(result.ledger_total, dec!(8000.00)); assert_eq!(result.transit_net, dec!(0.00)); } #[test] fn test_three_account_balanced_with_transit() { // 有在途时的三账平衡 // 假设:扣款 1000 进入在途,银行尚未确认 let mut balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00), dec!(0.00)); balance.deduct_with_priority(dec!(1000.00)).unwrap(); balance.add_transit(dec!(1000.00)); // 此时: // 总账 = 4000 + 3000 + 0 = 7000 // 在途 = 1000 // 银行余额应该还是 8000(因为银行还没确认) // 但由于我们的 deduct_with_priority 同时减少了 bank_balance // 在实际系统中,bank_balance 应该是从银行同步的 // 这里我们手动设置正确的银行余额来测试 let actual_bank = dec!(8000.00); // 银行还没扣 let result = verify_three_accounts(&balance, actual_bank); assert!(result.is_balanced); // 7000 + 1000 = 8000 } #[test] fn test_three_account_balanced_with_frozen() { let balance = create_balance(dec!(5000.00), dec!(2000.00), dec!(1000.00), dec!(0.00)); let bank = balance.bank_balance; // 8000 let result = verify_three_accounts(&balance, bank); assert!(result.is_balanced); assert_eq!(result.ledger_total, dec!(8000.00)); // 5000 + 2000 + 1000 } // ==================== 三账不平衡测试 ==================== #[test] fn test_three_account_short() { // 短款:银行少于预期 let balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00), dec!(0.00)); let bank = dec!(7500.00); // 银行少了 500 let result = verify_three_accounts(&balance, bank); assert!(!result.is_balanced); assert_eq!(result.difference, dec!(-500.00)); // 银行少 500 } #[test] fn test_three_account_long() { // 长款:银行多于预期 let balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00), dec!(0.00)); let bank = dec!(8500.00); // 银行多了 500 let result = verify_three_accounts(&balance, bank); assert!(!result.is_balanced); assert_eq!(result.difference, dec!(500.00)); // 银行多 500 } #[test] fn test_three_account_transit_mismatch() { // 在途不匹配 let mut balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00), dec!(0.00)); balance.deduct_with_priority(dec!(1000.00)).unwrap(); balance.add_transit(dec!(1000.00)); // 银行已经扣款(不应该这么快) let bank = dec!(7000.00); // 银行已扣款 let result = verify_three_accounts(&balance, bank); // 总账 7000 + 在途 1000 = 8000,但银行只有 7000 assert!(!result.is_balanced); assert_eq!(result.difference, dec!(-1000.00)); } // ==================== 对账流水比对测试 ==================== #[tokio::test] async fn test_reconciliation_with_bank_statements() { let client = MockBankClient::new(); client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap(); client.create_account("EXT001", "外部账户", dec!(50000.00)).unwrap(); // 模拟几笔交易 use rustjr::infrastructure::bank_integration::BankTransferRequest; // 出账 let request = BankTransferRequest { from_account: "PRISON001".to_string(), to_account: "EXT001".to_string(), to_account_name: "外部账户".to_string(), to_bank_code: "MOCK".to_string(), amount: dec!(5000.00), remark: Some("提现".to_string()), business_no: "TXN001".to_string(), }; client.transfer(request).await.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_eq!(statements.len(), 2); // 找出入账 let inbound = statements.iter().filter(|s| s.direction == "in").collect::>(); assert_eq!(inbound.len(), 1); assert_eq!(inbound[0].amount, dec!(2000.00)); // 找出账 let outbound = statements.iter().filter(|s| s.direction == "out").collect::>(); assert_eq!(outbound.len(), 1); assert_eq!(outbound[0].amount, dec!(5000.00)); // 验证最终余额 let balance = client.query_balance("PRISON001").await.unwrap(); assert_eq!(balance.balance, dec!(97000.00)); // 100000 - 5000 + 2000 } // ==================== 外部入账识别测试 ==================== #[tokio::test] async fn test_external_deposit_recognition() { let client = MockBankClient::new(); client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap(); // 系统不知道的外部入账 let bank_ref = client.simulate_external_deposit( "PRISON001", "UNKNOWN_FAMILY", "未知家属", dec!(3000.00), Some("不明来源充值".to_string()), ).unwrap(); // 查询流水会发现这笔入账 let today = Utc::now().date_naive(); let statements = client.query_statements("PRISON001", today, today).await.unwrap(); let external = statements.iter() .find(|s| s.bank_ref_no == bank_ref) .unwrap(); assert_eq!(external.direction, "in"); assert_eq!(external.amount, dec!(3000.00)); // 在对账时,这笔交易应该标记为 "外部入账待确认" // 需要人工或自动匹配到对应的罪犯账户 } // ==================== 超时交易对账测试 ==================== #[test] fn test_timeout_transaction_reconciliation() { // 模拟超时交易场景 let mut balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00), dec!(0.00)); // 扣款并建立在途 balance.deduct_with_priority(dec!(1000.00)).unwrap(); balance.add_transit(dec!(1000.00)); // 此时: // 系统认为:银行余额 7000,在途 1000 // 如果银行实际已成功:银行余额 7000(正确) // 如果银行实际未成功:银行余额 8000(在途应回退) // 场景1:银行实际已成功 let actual_bank_success = dec!(7000.00); let result = verify_three_accounts(&balance, actual_bank_success); // 7000 + 1000 = 8000,但银行只有 7000 assert!(!result.is_balanced); // 这说明需要结转在途 // 场景2:银行实际未成功 let actual_bank_failed = dec!(8000.00); let result = verify_three_accounts(&balance, actual_bank_failed); // 7000 + 1000 = 8000,银行也是 8000 assert!(result.is_balanced); // 但这意味着银行没扣款,我们需要回退在途 } // ==================== 重复交易检测测试 ==================== #[tokio::test] async fn test_duplicate_transaction_detection() { let client = MockBankClient::new(); client.create_account("FROM001", "转出账户", dec!(10000.00)).unwrap(); client.create_account("TO001", "转入账户", dec!(0.00)).unwrap(); use rustjr::infrastructure::bank_integration::BankTransferRequest; let request = BankTransferRequest { from_account: "FROM001".to_string(), to_account: "TO001".to_string(), to_account_name: "转入账户".to_string(), to_bank_code: "MOCK".to_string(), amount: dec!(1000.00), remark: None, business_no: "TXN_DUP_001".to_string(), }; // 第一次转账 let response1 = client.transfer(request.clone()).await.unwrap(); assert!(response1.success); // 用相同的 business_no 查询,应该能找到 let status = client.query_transaction_status("TXN_DUP_001").await.unwrap(); assert!(status.success); assert_eq!(status.bank_ref_no, response1.bank_ref_no); } // ==================== 三账校验公式验证 ==================== #[test] fn test_three_account_formula() { // 验证公式:总账余额 = 银行余额 + 在途净额 // 即:personal + labor + frozen = bank - transit_out + transit_in // 简化为:total = bank (当没有在途时) struct TestCase { personal: Decimal, labor: Decimal, frozen: Decimal, transit: Decimal, bank: Decimal, expected_balanced: bool, } let cases = vec![ // 平衡:无在途 TestCase { personal: dec!(5000.00), labor: dec!(3000.00), frozen: dec!(0.00), transit: dec!(0.00), bank: dec!(8000.00), expected_balanced: true, }, // 平衡:有在途(银行未确认) TestCase { personal: dec!(4000.00), // 扣了1000 labor: dec!(3000.00), frozen: dec!(0.00), transit: dec!(1000.00), // 在途1000 bank: dec!(8000.00), // 银行还没扣 expected_balanced: true, }, // 不平衡:银行已扣但在途未清 TestCase { personal: dec!(4000.00), labor: dec!(3000.00), frozen: dec!(0.00), transit: dec!(1000.00), bank: dec!(7000.00), // 银行已扣 expected_balanced: false, }, // 平衡:有冻结 TestCase { personal: dec!(4000.00), labor: dec!(3000.00), frozen: dec!(1000.00), transit: dec!(0.00), bank: dec!(8000.00), expected_balanced: true, }, ]; for (i, case) in cases.iter().enumerate() { let balance = create_balance(case.personal, case.labor, case.frozen, case.transit); let result = verify_three_accounts(&balance, case.bank); assert_eq!( result.is_balanced, case.expected_balanced, "Case {} failed: expected balanced={}, got balanced={}", i, case.expected_balanced, result.is_balanced ); } }