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

319 lines
7.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 账户域实体定义
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
/// 账户状态
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AccountStatus {
/// 正常
Active,
/// 冻结
Frozen,
/// 已关闭
Closed,
}
impl Default for AccountStatus {
fn default() -> Self {
Self::Active
}
}
impl std::fmt::Display for AccountStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Active => write!(f, "active"),
Self::Frozen => write!(f, "frozen"),
Self::Closed => write!(f, "closed"),
}
}
}
impl std::str::FromStr for AccountStatus {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"active" => Ok(Self::Active),
"frozen" => Ok(Self::Frozen),
"closed" => Ok(Self::Closed),
_ => Err(format!("未知账户状态: {}", s)),
}
}
}
/// 一致性模式
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ConsistencyMode {
/// 强一致性 - 交易需等待银行确认
Strong,
/// 最终一致性 - 先记账后对账
Eventual,
}
impl Default for ConsistencyMode {
fn default() -> Self {
Self::Eventual
}
}
impl std::fmt::Display for ConsistencyMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Strong => write!(f, "strong"),
Self::Eventual => write!(f, "eventual"),
}
}
}
/// 出金管控模式
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OutboundControl {
/// 只收不付
ReceiveOnly,
/// 网银控制
OnlineBank,
/// 令牌控制
Token,
}
impl Default for OutboundControl {
fn default() -> Self {
Self::OnlineBank
}
}
impl std::fmt::Display for OutboundControl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ReceiveOnly => write!(f, "receive_only"),
Self::OnlineBank => write!(f, "online_bank"),
Self::Token => write!(f, "token"),
}
}
}
/// 账户类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AccountType {
/// 实体账户
Physical,
/// 虚拟账户
Virtual,
}
impl std::fmt::Display for AccountType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Physical => write!(f, "physical"),
Self::Virtual => write!(f, "virtual"),
}
}
}
/// 虚拟子账户类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SubAccountType {
/// 结算子账户
Settlement,
/// 管理子账户
Management,
/// 临时子账户
Temporary,
}
impl Default for SubAccountType {
fn default() -> Self {
Self::Settlement
}
}
impl std::fmt::Display for SubAccountType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Settlement => write!(f, "settlement"),
Self::Management => write!(f, "management"),
Self::Temporary => write!(f, "temporary"),
}
}
}
/// 实体账户
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhysicalAccount {
/// 账户ID
pub id: i64,
/// 银行账号
pub account_no: String,
/// 银行代码
pub bank_code: String,
/// 银行名称
pub bank_name: Option<String>,
/// 一致性模式
pub consistency_mode: ConsistencyMode,
/// 出金管控模式
pub outbound_control: OutboundControl,
/// 账户状态
pub status: AccountStatus,
/// 创建时间
pub created_at: DateTime<Utc>,
/// 更新时间
pub updated_at: DateTime<Utc>,
}
impl PhysicalAccount {
/// 检查账户是否可用
pub fn is_active(&self) -> bool {
self.status == AccountStatus::Active
}
/// 检查是否允许出金
pub fn can_outbound(&self) -> bool {
self.is_active() && self.outbound_control != OutboundControl::ReceiveOnly
}
}
/// 虚拟子账户
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VirtualSubAccount {
/// 子账户ID
pub id: i64,
/// 所属实体账户ID
pub physical_account_id: i64,
/// 子账户编号
pub account_code: String,
/// 子账户类型
pub account_type: SubAccountType,
/// 有效期开始
pub valid_from: Option<DateTime<Utc>>,
/// 有效期结束
pub valid_to: Option<DateTime<Utc>>,
/// 账户状态
pub status: AccountStatus,
/// 创建时间
pub created_at: DateTime<Utc>,
/// 更新时间
pub updated_at: DateTime<Utc>,
}
impl VirtualSubAccount {
/// 检查账户是否可用
pub fn is_active(&self) -> bool {
if self.status != AccountStatus::Active {
return false;
}
let now = Utc::now();
// 检查有效期
if let Some(valid_from) = self.valid_from {
if now < valid_from {
return false;
}
}
if let Some(valid_to) = self.valid_to {
if now > valid_to {
return false;
}
}
true
}
/// 检查是否为临时账户
pub fn is_temporary(&self) -> bool {
self.account_type == SubAccountType::Temporary
}
/// 检查临时账户是否过期
pub fn is_expired(&self) -> bool {
if !self.is_temporary() {
return false;
}
if let Some(valid_to) = self.valid_to {
return Utc::now() > valid_to;
}
false
}
}
/// 账户控制配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountControl {
/// 控制ID
pub id: i64,
/// 实体账户ID
pub physical_account_id: i64,
/// 对账频率(分钟)
pub reconciliation_interval: i32,
/// 银企直连配置JSON
pub direct_connect_config: Option<serde_json::Value>,
/// 第三方支付配置JSON
pub third_party_config: Option<serde_json::Value>,
/// 创建时间
pub created_at: DateTime<Utc>,
/// 更新时间
pub updated_at: DateTime<Utc>,
}
/// 临时账户池
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubAccountPool {
/// 池ID
pub id: i64,
/// 所属实体账户ID
pub physical_account_id: i64,
/// 池名称
pub name: String,
/// 有效期开始
pub valid_from: DateTime<Utc>,
/// 有效期结束
pub valid_to: DateTime<Utc>,
/// 自动销户规则JSON
pub auto_close_rule: Option<serde_json::Value>,
/// 创建时间
pub created_at: DateTime<Utc>,
}
/// 创建实体账户请求
#[derive(Debug, Clone, Deserialize)]
pub struct CreatePhysicalAccountRequest {
pub account_no: String,
pub bank_code: String,
pub bank_name: Option<String>,
pub consistency_mode: Option<ConsistencyMode>,
pub outbound_control: Option<OutboundControl>,
}
/// 创建虚拟子账户请求
#[derive(Debug, Clone, Deserialize)]
pub struct CreateVirtualSubAccountRequest {
pub physical_account_id: i64,
pub account_code: String,
pub account_type: SubAccountType,
pub valid_from: Option<DateTime<Utc>>,
pub valid_to: Option<DateTime<Utc>>,
}
/// 批量创建子账户请求
#[derive(Debug, Clone, Deserialize)]
pub struct BatchCreateSubAccountRequest {
pub physical_account_id: i64,
pub account_type: SubAccountType,
pub prefix: String,
pub count: usize,
pub valid_from: Option<DateTime<Utc>>,
pub valid_to: Option<DateTime<Utc>>,
}