tangweijie d7f81893c5 Initial commit: 完整的 Rust 账户管理系统
- 实现账户管理改进设计文档中的所有核心功能
- 三科目余额管理 (个人余额、劳动报酬、冻结余额)
- 交易状态机 (created → pending → bank_submitted → success/failed/timeout → reversed)
- 三键幂等体系 (JZTxId/BankTxId/SourceKey)
- 优先级扣款规则 (先个人后劳动)
- 在途资金管理 (可用→在途→结转/回退)
- 三账对账闭环 (总账 = 银行账 + 在途净额)
- 补偿服务域 (超时检测、重试、死信队列)
- 虚拟银行模拟器用于业务测试
- 完整的集成测试套件 (133 个测试全部通过)
- Docker 容器化部署配置
- 前端 Vue3 + TypeScript 项目结构
2026-01-05 17:56:01 +08:00

204 lines
5.4 KiB
Rust

//! 虚拟银行测试设置
//!
//! 提供虚拟银行的初始化和配置辅助函数
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use rustjr::infrastructure::bank_integration::{
BankClient,
mock_bank::{FailureConfig, MockBankClient, MockBankTestEnv},
};
use super::fixtures::{
gen_inmate_account_no, FAMILY_ACCOUNT, FAMILY_ACCOUNT_NAME,
PRISON_MAIN_ACCOUNT, PRISON_MAIN_ACCOUNT_NAME,
};
// ==================== 虚拟银行环境 ====================
/// 标准测试银行环境
pub struct TestBankEnv {
/// 虚拟银行客户端
pub client: MockBankClient,
/// 监狱主账户号
pub prison_account: String,
/// 家属账户号
pub family_account: String,
}
impl TestBankEnv {
/// 创建标准测试环境
///
/// 包含:
/// - 监狱主账户:初始余额 1,000,000
/// - 家属账户:初始余额 100,000
pub fn new() -> Self {
let client = MockBankClient::new();
// 创建监狱主账户
client
.create_account(
PRISON_MAIN_ACCOUNT,
PRISON_MAIN_ACCOUNT_NAME,
dec!(1000000.00),
)
.expect("创建监狱账户失败");
// 创建家属账户
client
.create_account(FAMILY_ACCOUNT, FAMILY_ACCOUNT_NAME, dec!(100000.00))
.expect("创建家属账户失败");
Self {
client,
prison_account: PRISON_MAIN_ACCOUNT.to_string(),
family_account: FAMILY_ACCOUNT.to_string(),
}
}
/// 创建罪犯子账户
pub fn create_inmate_account(
&self,
inmate_id: i64,
initial_balance: Decimal,
) -> String {
let account_no = gen_inmate_account_no(inmate_id);
let account_name = format!("罪犯{}账户", inmate_id);
self.client
.create_account(&account_no, &account_name, initial_balance)
.expect("创建罪犯账户失败");
account_no
}
/// 启用故障注入
pub fn enable_failures(&self) {
self.client
.set_failure_config(FailureConfig::for_testing());
}
/// 禁用故障注入
pub fn disable_failures(&self) {
self.client.set_failure_enabled(false);
}
/// 配置强制超时
pub fn force_timeout(&self) {
self.client.set_failure_config(FailureConfig::force_timeout());
}
/// 配置强制失败
pub fn force_failure(&self) {
self.client.set_failure_config(FailureConfig::force_failure());
}
/// 重置环境
pub fn reset(&self) {
self.client.reset();
// 重新创建账户
self.client
.create_account(
PRISON_MAIN_ACCOUNT,
PRISON_MAIN_ACCOUNT_NAME,
dec!(1000000.00),
)
.expect("重建监狱账户失败");
self.client
.create_account(FAMILY_ACCOUNT, FAMILY_ACCOUNT_NAME, dec!(100000.00))
.expect("重建家属账户失败");
}
}
impl Default for TestBankEnv {
fn default() -> Self {
Self::new()
}
}
// ==================== 快捷创建函数 ====================
/// 创建标准测试银行环境
pub fn setup_test_bank() -> TestBankEnv {
TestBankEnv::new()
}
/// 创建带故障注入的测试银行环境
pub fn setup_test_bank_with_failures() -> TestBankEnv {
let env = TestBankEnv::new();
env.enable_failures();
env
}
/// 创建强制超时的测试银行环境
pub fn setup_test_bank_force_timeout() -> TestBankEnv {
let env = TestBankEnv::new();
env.force_timeout();
env
}
/// 创建强制失败的测试银行环境
pub fn setup_test_bank_force_failure() -> TestBankEnv {
let env = TestBankEnv::new();
env.force_failure();
env
}
// ==================== 验证辅助函数 ====================
/// 验证银行余额
pub async fn assert_bank_balance(
client: &MockBankClient,
account_no: &str,
expected: Decimal,
) {
let balance = client.query_balance(account_no).await.expect("查询余额失败");
assert_eq!(
balance.balance, expected,
"账户 {} 余额不符: 期望 {}, 实际 {}",
account_no, expected, balance.balance
);
}
/// 验证银行可用余额
pub async fn assert_available_balance(
client: &MockBankClient,
account_no: &str,
expected: Decimal,
) {
let balance = client.query_balance(account_no).await.expect("查询余额失败");
assert_eq!(
balance.available_balance, expected,
"账户 {} 可用余额不符: 期望 {}, 实际 {}",
account_no, expected, balance.available_balance
);
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_setup_env() {
let env = setup_test_bank();
let prison_balance = env.client.query_balance(&env.prison_account).await.unwrap();
assert_eq!(prison_balance.balance, dec!(1000000.00));
let family_balance = env.client.query_balance(&env.family_account).await.unwrap();
assert_eq!(family_balance.balance, dec!(100000.00));
}
#[tokio::test]
async fn test_create_inmate_account() {
let env = setup_test_bank();
let inmate_account = env.create_inmate_account(1001, dec!(5000.00));
let balance = env.client.query_balance(&inmate_account).await.unwrap();
assert_eq!(balance.balance, dec!(5000.00));
}
}