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