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

307 lines
15 KiB
SQL

-- 银行账户管理系统 - 数据库初始化脚本
-- 创建时间: 2026-01-05
-- =====================================================
-- 账户域表
-- =====================================================
-- 实体账户
CREATE TABLE IF NOT EXISTS physical_account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
account_no VARCHAR(32) UNIQUE NOT NULL COMMENT '银行账号',
bank_code VARCHAR(20) NOT NULL COMMENT '银行代码',
bank_name VARCHAR(100) COMMENT '银行名称',
consistency_mode ENUM('strong', 'eventual') DEFAULT 'eventual' COMMENT '一致性模式',
outbound_control ENUM('receive_only', 'online_bank', 'token') DEFAULT 'online_bank' COMMENT '出金管控模式',
status ENUM('active', 'frozen', 'closed') DEFAULT 'active' COMMENT '账户状态',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_bank_code (bank_code),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='实体账户表';
-- 虚拟子账户
CREATE TABLE IF NOT EXISTS virtual_sub_account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
physical_account_id BIGINT NOT NULL COMMENT '所属实体账户ID',
account_code VARCHAR(32) UNIQUE NOT NULL COMMENT '子账户编号',
account_type ENUM('settlement', 'management', 'temporary') NOT NULL COMMENT '账户类型',
valid_from DATETIME COMMENT '有效期开始',
valid_to DATETIME COMMENT '有效期结束',
status ENUM('active', 'frozen', 'closed') DEFAULT 'active' COMMENT '账户状态',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (physical_account_id) REFERENCES physical_account(id),
INDEX idx_physical_account (physical_account_id),
INDEX idx_account_type (account_type),
INDEX idx_status (status),
INDEX idx_valid_to (valid_to)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='虚拟子账户表';
-- 账户控制配置
CREATE TABLE IF NOT EXISTS account_control (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
physical_account_id BIGINT UNIQUE NOT NULL COMMENT '实体账户ID',
reconciliation_interval INT DEFAULT 60 COMMENT '对账频率(分钟)',
direct_connect_config JSON COMMENT '银企直连配置',
third_party_config JSON COMMENT '第三方支付配置',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (physical_account_id) REFERENCES physical_account(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账户控制配置表';
-- 子账户池
CREATE TABLE IF NOT EXISTS sub_account_pool (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
physical_account_id BIGINT NOT NULL COMMENT '所属实体账户ID',
name VARCHAR(100) NOT NULL COMMENT '池名称',
valid_from DATETIME NOT NULL COMMENT '有效期开始',
valid_to DATETIME NOT NULL COMMENT '有效期结束',
auto_close_rule JSON COMMENT '自动销户规则',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
FOREIGN KEY (physical_account_id) REFERENCES physical_account(id),
INDEX idx_physical_account (physical_account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='子账户池表';
-- =====================================================
-- 账务域表
-- =====================================================
-- 会计科目
CREATE TABLE IF NOT EXISTS accounting_subject (
code VARCHAR(20) PRIMARY KEY COMMENT '科目代码',
name VARCHAR(100) NOT NULL COMMENT '科目名称',
category ENUM('asset', 'liability', 'income', 'expense') NOT NULL COMMENT '科目类别',
direction_default TINYINT DEFAULT 1 COMMENT '默认增加方向: 1=借方增加, -1=贷方增加',
parent_code VARCHAR(20) COMMENT '父科目代码',
level INT DEFAULT 1 COMMENT '科目级别',
INDEX idx_category (category),
INDEX idx_parent_code (parent_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会计科目表';
-- 初始化会计科目
INSERT INTO accounting_subject (code, name, category, direction_default, parent_code, level) VALUES
('1001', '现金', 'asset', 1, NULL, 1),
('1002', '银行存款', 'asset', 1, NULL, 1),
('1003', '在途资金', 'asset', 1, NULL, 1),
('2001', '客户存款', 'liability', -1, NULL, 1),
('2002', '待清算款项', 'liability', -1, NULL, 1),
('3001', '手续费收入', 'income', -1, NULL, 1),
('4001', '利息支出', 'expense', 1, NULL, 1)
ON DUPLICATE KEY UPDATE name = VALUES(name);
-- 账户余额
CREATE TABLE IF NOT EXISTS account_balance (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
account_id BIGINT NOT NULL COMMENT '账户ID',
account_type ENUM('physical', 'virtual') NOT NULL COMMENT '账户类型',
system_balance DECIMAL(20,2) DEFAULT 0 COMMENT '系统余额',
bank_balance DECIMAL(20,2) DEFAULT 0 COMMENT '银行余额',
available_balance DECIMAL(20,2) DEFAULT 0 COMMENT '可支配余额',
frozen_amount DECIMAL(20,2) DEFAULT 0 COMMENT '冻结金额',
transit_amount DECIMAL(20,2) DEFAULT 0 COMMENT '在途金额',
version INT DEFAULT 0 COMMENT '乐观锁版本',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_account (account_id, account_type),
INDEX idx_account_type (account_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账户余额表';
-- 余额组成
CREATE TABLE IF NOT EXISTS balance_component (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
balance_id BIGINT NOT NULL COMMENT '余额ID',
subject_code VARCHAR(20) NOT NULL COMMENT '科目代码',
amount DECIMAL(20,2) DEFAULT 0 COMMENT '金额',
FOREIGN KEY (balance_id) REFERENCES account_balance(id),
INDEX idx_balance_id (balance_id),
INDEX idx_subject_code (subject_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='余额组成表';
-- 记账分录(凭证头)
CREATE TABLE IF NOT EXISTS ledger_entry (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
entry_no VARCHAR(32) UNIQUE NOT NULL COMMENT '分录编号',
txn_no VARCHAR(32) NOT NULL COMMENT '关联交易号',
post_date DATE NOT NULL COMMENT '记账日期',
post_time DATETIME NOT NULL COMMENT '记账时间',
description VARCHAR(200) COMMENT '摘要描述',
status ENUM('pending', 'posted', 'reversed') DEFAULT 'pending' COMMENT '状态',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
INDEX idx_txn_no (txn_no),
INDEX idx_post_date (post_date),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='记账分录表';
-- 分录明细(凭证行)
CREATE TABLE IF NOT EXISTS ledger_line (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
entry_id BIGINT NOT NULL COMMENT '分录ID',
account_id BIGINT NOT NULL COMMENT '账户ID',
account_type ENUM('physical', 'virtual') NOT NULL COMMENT '账户类型',
subject_code VARCHAR(20) NOT NULL COMMENT '科目代码',
direction ENUM('debit', 'credit') NOT NULL COMMENT '借贷方向',
amount DECIMAL(20,2) NOT NULL COMMENT '金额',
FOREIGN KEY (entry_id) REFERENCES ledger_entry(id),
INDEX idx_entry_id (entry_id),
INDEX idx_account (account_id, account_type),
INDEX idx_subject_code (subject_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分录明细表';
-- =====================================================
-- 交易域表
-- =====================================================
-- 系统交易
CREATE TABLE IF NOT EXISTS system_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
txn_no VARCHAR(32) UNIQUE NOT NULL COMMENT '交易号',
txn_type VARCHAR(20) NOT NULL COMMENT '交易类型',
from_account_id BIGINT COMMENT '转出账户ID',
to_account_id BIGINT COMMENT '转入账户ID',
amount DECIMAL(20,2) NOT NULL COMMENT '金额',
status ENUM('pending', 'processing', 'confirmed', 'failed', 'mismatch') DEFAULT 'pending' COMMENT '状态',
bank_ref_no VARCHAR(64) COMMENT '银行参考号',
remark VARCHAR(200) COMMENT '备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
confirmed_at DATETIME COMMENT '确认时间',
INDEX idx_txn_type (txn_type),
INDEX idx_from_account (from_account_id),
INDEX idx_to_account (to_account_id),
INDEX idx_status (status),
INDEX idx_bank_ref_no (bank_ref_no),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统交易表';
-- 银行交易
CREATE TABLE IF NOT EXISTS bank_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
bank_ref_no VARCHAR(64) UNIQUE NOT NULL COMMENT '银行参考号',
physical_account_id BIGINT NOT NULL COMMENT '实体账户ID',
txn_type VARCHAR(20) NOT NULL COMMENT '交易类型',
direction ENUM('inbound', 'outbound') NOT NULL COMMENT '交易方向',
amount DECIMAL(20,2) NOT NULL COMMENT '金额',
counterparty_name VARCHAR(100) COMMENT '对手方名称',
counterparty_account VARCHAR(32) COMMENT '对手方账号',
txn_time DATETIME NOT NULL COMMENT '交易时间',
sync_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '同步时间',
match_status ENUM('unmatched', 'matched', 'mismatch') DEFAULT 'unmatched' COMMENT '匹配状态',
matched_txn_no VARCHAR(32) COMMENT '匹配的系统交易号',
remark VARCHAR(200) COMMENT '摘要',
INDEX idx_physical_account (physical_account_id),
INDEX idx_txn_time (txn_time),
INDEX idx_match_status (match_status),
INDEX idx_matched_txn_no (matched_txn_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='银行交易表';
-- =====================================================
-- 对账域表
-- =====================================================
-- 对账批次
CREATE TABLE IF NOT EXISTS reconciliation_batch (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
batch_no VARCHAR(32) UNIQUE NOT NULL COMMENT '批次编号',
physical_account_id BIGINT NOT NULL COMMENT '实体账户ID',
recon_date DATE NOT NULL COMMENT '对账日期',
total_count INT DEFAULT 0 COMMENT '总记录数',
matched_count INT DEFAULT 0 COMMENT '匹配数',
mismatch_count INT DEFAULT 0 COMMENT '不匹配数',
status ENUM('processing', 'completed', 'need_review') DEFAULT 'processing' COMMENT '状态',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
completed_at DATETIME COMMENT '完成时间',
INDEX idx_physical_account (physical_account_id),
INDEX idx_recon_date (recon_date),
INDEX idx_status (status),
UNIQUE KEY uk_account_date (physical_account_id, recon_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对账批次表';
-- 对账明细
CREATE TABLE IF NOT EXISTS reconciliation_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
batch_id BIGINT NOT NULL COMMENT '批次ID',
system_txn_no VARCHAR(32) COMMENT '系统交易号',
bank_ref_no VARCHAR(64) COMMENT '银行参考号',
system_amount DECIMAL(20,2) COMMENT '系统金额',
bank_amount DECIMAL(20,2) COMMENT '银行金额',
diff_amount DECIMAL(20,2) DEFAULT 0 COMMENT '差异金额',
status ENUM('matched', 'system_unreached', 'bank_unreached', 'amount_mismatch', 'adjusted') NOT NULL COMMENT '状态',
remark VARCHAR(200) COMMENT '处理备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
FOREIGN KEY (batch_id) REFERENCES reconciliation_batch(id),
INDEX idx_batch_id (batch_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对账明细表';
-- 手工补录
CREATE TABLE IF NOT EXISTS manual_adjustment (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
adjustment_no VARCHAR(32) UNIQUE NOT NULL COMMENT '补录编号',
related_txn_no VARCHAR(32) COMMENT '关联交易号',
reconciliation_item_id BIGINT COMMENT '关联对账项ID',
adjustment_type ENUM('add', 'reverse', 'modify') NOT NULL COMMENT '补录类型',
account_id BIGINT NOT NULL COMMENT '账户ID',
amount DECIMAL(20,2) NOT NULL COMMENT '金额',
reason VARCHAR(500) NOT NULL COMMENT '原因说明',
operator VARCHAR(50) NOT NULL COMMENT '操作人',
approver VARCHAR(50) COMMENT '审批人',
status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending' COMMENT '状态',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
approved_at DATETIME COMMENT '审批时间',
INDEX idx_related_txn_no (related_txn_no),
INDEX idx_operator (operator),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='手工补录表';
-- =====================================================
-- 积分域表
-- =====================================================
-- 积分账户
CREATE TABLE IF NOT EXISTS points_account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
sub_account_id BIGINT NOT NULL COMMENT '关联子账户ID',
points_type ENUM('production', 'management', 'other') NOT NULL COMMENT '积分类型',
balance DECIMAL(20,2) DEFAULT 0 COMMENT '积分余额',
total_earned DECIMAL(20,2) DEFAULT 0 COMMENT '累计获得',
total_spent DECIMAL(20,2) DEFAULT 0 COMMENT '累计消费',
total_expired DECIMAL(20,2) DEFAULT 0 COMMENT '累计过期',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (sub_account_id) REFERENCES virtual_sub_account(id),
UNIQUE KEY uk_sub_account_type (sub_account_id, points_type),
INDEX idx_points_type (points_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分账户表';
-- 积分交易
CREATE TABLE IF NOT EXISTS points_transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
txn_no VARCHAR(32) UNIQUE NOT NULL COMMENT '交易编号',
points_account_id BIGINT NOT NULL COMMENT '积分账户ID',
txn_type ENUM('earn', 'spend', 'transfer', 'expire', 'adjust') NOT NULL COMMENT '交易类型',
amount DECIMAL(20,2) NOT NULL COMMENT '积分数量',
balance_before DECIMAL(20,2) NOT NULL COMMENT '交易前余额',
balance_after DECIMAL(20,2) NOT NULL COMMENT '交易后余额',
related_business_id VARCHAR(64) COMMENT '关联业务ID',
remark VARCHAR(200) COMMENT '备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
FOREIGN KEY (points_account_id) REFERENCES points_account(id),
INDEX idx_points_account (points_account_id),
INDEX idx_txn_type (txn_type),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分交易表';
-- 积分规则
CREATE TABLE IF NOT EXISTS points_rule (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL COMMENT '规则名称',
points_type ENUM('production', 'management', 'other') NOT NULL COMMENT '积分类型',
rule_type VARCHAR(20) NOT NULL COMMENT '规则类型',
config JSON NOT NULL COMMENT '规则配置',
enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
INDEX idx_points_type (points_type),
INDEX idx_enabled (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分规则表';