//! 提现流程集成测试 use chrono::Utc; use rust_decimal::Decimal; use rust_decimal_macros::dec; use rustjr::domain::ledger::entity::AccountBalance; use rustjr::domain::account::AccountType; use rustjr::infrastructure::bank_integration::mock_bank::{MockBankClient, FailureConfig}; use rustjr::infrastructure::bank_integration::{BankClient, BankTransferRequest}; // ==================== 测试辅助 ==================== fn create_balance(personal: Decimal, labor: 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(), } } // ==================== 正常提现流程测试 ==================== #[tokio::test] async fn test_withdrawal_full_flow_success() { // 1. 初始化系统余额 let mut balance = create_balance(dec!(5000.00), dec!(3000.00)); assert!(balance.validate_invariant().is_ok()); // 2. 初始化虚拟银行 let client = MockBankClient::new(); client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap(); client.create_account("FAMILY001", "家属账户", dec!(0.00)).unwrap(); let withdrawal_amount = dec!(2000.00); // 3. 按优先级扣款(先个人后劳动) let deduction = balance.deduct_with_priority(withdrawal_amount).unwrap(); assert_eq!(deduction.from_personal, dec!(2000.00)); assert_eq!(deduction.from_labor, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); // 4. 建立在途 balance.add_transit(withdrawal_amount); // 5. 提交银行 let request = BankTransferRequest { from_account: "PRISON001".to_string(), to_account: "FAMILY001".to_string(), to_account_name: "家属张三".to_string(), to_bank_code: "MOCK".to_string(), amount: withdrawal_amount, remark: Some("罪犯提现".to_string()), business_no: "WD001".to_string(), }; let response = client.transfer(request).await.unwrap(); assert!(response.success); // 6. 银行成功,结转在途 balance.settle_transit(withdrawal_amount).unwrap(); // 7. 验证最终状态 assert_eq!(balance.personal_balance, dec!(3000.00)); assert_eq!(balance.labor_balance, dec!(3000.00)); assert_eq!(balance.bank_balance, dec!(6000.00)); assert_eq!(balance.transit_amount, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); // 8. 验证银行余额 let prison_balance = client.query_balance("PRISON001").await.unwrap(); assert_eq!(prison_balance.balance, dec!(98000.00)); let family_balance = client.query_balance("FAMILY001").await.unwrap(); assert_eq!(family_balance.balance, dec!(2000.00)); } #[tokio::test] async fn test_withdrawal_from_both_balances() { // 测试跨越个人和劳动余额的提现 let mut balance = create_balance(dec!(1000.00), dec!(2000.00)); let withdrawal_amount = dec!(2500.00); let deduction = balance.deduct_with_priority(withdrawal_amount).unwrap(); // 应该先扣个人 1000,再扣劳动 1500 assert_eq!(deduction.from_personal, dec!(1000.00)); assert_eq!(deduction.from_labor, dec!(1500.00)); assert_eq!(balance.personal_balance, dec!(0.00)); assert_eq!(balance.labor_balance, dec!(500.00)); assert!(balance.validate_invariant().is_ok()); } // ==================== 提现失败回退测试 ==================== #[tokio::test] async fn test_withdrawal_bank_failure_rollback() { // 1. 初始化 let mut balance = create_balance(dec!(5000.00), dec!(3000.00)); let original_personal = balance.personal_balance; let original_labor = balance.labor_balance; let original_bank = balance.bank_balance; // 2. 配置银行强制失败 let client = MockBankClient::with_failure_config(FailureConfig::force_failure()); client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap(); let withdrawal_amount = dec!(2000.00); // 3. 扣款并建立在途 balance.deduct_with_priority(withdrawal_amount).unwrap(); balance.add_transit(withdrawal_amount); // 4. 提交银行(会失败) let request = BankTransferRequest { from_account: "PRISON001".to_string(), to_account: "FAMILY001".to_string(), to_account_name: "家属".to_string(), to_bank_code: "MOCK".to_string(), amount: withdrawal_amount, remark: None, business_no: "WD002".to_string(), }; let response = client.transfer(request).await.unwrap(); assert!(!response.success); // 5. 回退在途 balance.rollback_transit(withdrawal_amount); // 6. 验证余额恢复 assert_eq!(balance.personal_balance, original_personal); assert_eq!(balance.labor_balance, original_labor); assert_eq!(balance.bank_balance, original_bank); assert_eq!(balance.transit_amount, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } // ==================== 余额不足测试 ==================== #[test] fn test_withdrawal_insufficient_balance() { let mut balance = create_balance(dec!(1000.00), dec!(500.00)); let withdrawal_amount = dec!(2000.00); let result = balance.deduct_with_priority(withdrawal_amount); assert!(result.is_err()); // 余额不应变化 assert_eq!(balance.personal_balance, dec!(1000.00)); assert_eq!(balance.labor_balance, dec!(500.00)); } #[test] fn test_withdrawal_exact_balance() { let mut balance = create_balance(dec!(1000.00), dec!(500.00)); let withdrawal_amount = dec!(1500.00); // 刚好等于总余额 let result = balance.deduct_with_priority(withdrawal_amount); assert!(result.is_ok()); assert_eq!(balance.personal_balance, dec!(0.00)); assert_eq!(balance.labor_balance, dec!(0.00)); assert_eq!(balance.bank_balance, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } // ==================== 冻结余额不可提现测试 ==================== #[test] fn test_withdrawal_with_frozen_balance() { let mut balance = create_balance(dec!(1000.00), dec!(500.00)); // 冻结 800 balance.freeze(dec!(800.00)); assert_eq!(balance.frozen_balance, dec!(800.00)); assert_eq!(balance.available_balance(), dec!(700.00)); // 1000 - 800 + 500 // 尝试提现 1000(超过可用余额 700) let result = balance.deduct_with_priority(dec!(1000.00)); assert!(result.is_err()); // 提现 600(在可用范围内) let result = balance.deduct_with_priority(dec!(600.00)); assert!(result.is_ok()); assert!(balance.validate_invariant().is_ok()); } // ==================== 多笔提现测试 ==================== #[tokio::test] async fn test_multiple_withdrawals() { let mut balance = create_balance(dec!(10000.00), dec!(5000.00)); let client = MockBankClient::new(); client.create_account("PRISON001", "监狱账户", dec!(100000.00)).unwrap(); client.create_account("FAMILY001", "家属1", dec!(0.00)).unwrap(); client.create_account("FAMILY002", "家属2", dec!(0.00)).unwrap(); // 第一笔提现 balance.deduct_with_priority(dec!(3000.00)).unwrap(); balance.add_transit(dec!(3000.00)); let request1 = BankTransferRequest { from_account: "PRISON001".to_string(), to_account: "FAMILY001".to_string(), to_account_name: "家属1".to_string(), to_bank_code: "MOCK".to_string(), amount: dec!(3000.00), remark: None, business_no: "WD003".to_string(), }; let response1 = client.transfer(request1).await.unwrap(); assert!(response1.success); balance.settle_transit(dec!(3000.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); // 第二笔提现 balance.deduct_with_priority(dec!(2000.00)).unwrap(); balance.add_transit(dec!(2000.00)); let request2 = BankTransferRequest { from_account: "PRISON001".to_string(), to_account: "FAMILY002".to_string(), to_account_name: "家属2".to_string(), to_bank_code: "MOCK".to_string(), amount: dec!(2000.00), remark: None, business_no: "WD004".to_string(), }; let response2 = client.transfer(request2).await.unwrap(); assert!(response2.success); balance.settle_transit(dec!(2000.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); // 验证最终余额 assert_eq!(balance.bank_balance, dec!(10000.00)); // 15000 - 3000 - 2000 } // ==================== 小数精度测试 ==================== #[tokio::test] async fn test_withdrawal_decimal_precision() { let mut balance = create_balance(dec!(100.99), dec!(50.01)); let deduction = balance.deduct_with_priority(dec!(75.50)).unwrap(); assert_eq!(deduction.from_personal, dec!(75.50)); assert_eq!(deduction.from_labor, dec!(0.00)); assert_eq!(balance.personal_balance, dec!(25.49)); assert!(balance.validate_invariant().is_ok()); }