Files
ccdi/docs/plans/fullstack/2026-04-09-account-library-design.md

438 lines
17 KiB
Markdown
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.
# 账户库管理设计方案
## 1. 背景与目标
当前系统在“信息维护”下已具备员工信息维护、员工亲属关系维护等基础档案能力,但尚未形成独立的“账户库管理”。现有员工表 `ccdi_base_staff` 与员工亲属关系表 `ccdi_staff_fmy_relation` 中均不承载完整账户信息,无法满足后续账户台账维护、手工补录、导入和账户分析展示的需要。
本次设计目标如下:
- 在“信息维护”菜单下新增“账户库管理”
- 支持维护员工本人账户与员工亲属账户
- 账户静态档案与分析结果分表存储
- 页面风格沿用现有维护页交互方式
- 第一版仅考虑当前有效账户分析结果,不保留多版本分析历史
本次明确不做以下事项:
- 不扩展朋友、同事、司机、秘书等非亲属关系
- 不修改“员工亲属关系维护”现有业务命名
- 不增加额外脱敏展示字段
- 不展开后端实现设计
## 2. 设计原则
- 主表只描述“谁持有了什么账户”
- 分析表只描述“该账户的交易画像和风险等级”
- 员工本人账户依附员工表
- 亲属账户依附员工亲属关系表
- 页面录入时允许在一个弹窗中同时维护主表和分析表信息
- 第一版以人工维护为主,分析字段允许手工录入或后续导入覆盖
## 3. 数据模型
### 3.1 关系说明
- 员工主表:`ccdi_base_staff`
- 员工亲属关系表:`ccdi_staff_fmy_relation`
- 账户主表:`ccdi_account_info`
- 账户分析表:`ccdi_account_analysis`
关系约束如下:
- 一个员工可有多个本人账户
- 一个员工亲属关系记录可有多个账户
- 一个账户仅归属于一个员工或一个亲属关系记录
- 一个账户在第一版仅对应一条当前分析记录
### 3.2 归属规则
-`is_self_account = 1` 时:
- `staff_id` 必填
- `relation_id` 为空
-`is_self_account = 0` 时:
- `staff_id` 必填
- `relation_id` 必填
- `relation_id` 对应的亲属记录应属于该 `staff_id`
## 4. 表结构设计
### 4.1 账户主表 `ccdi_account_info`
用途:存储账户静态档案、开户信息、归属关系和生效状态。
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| id | BIGINT | 是 | 自增 | 主键 |
| staff_id | BIGINT | 是 | - | 员工ID对应 `ccdi_base_staff.staff_id` |
| relation_id | BIGINT | 否 | NULL | 亲属关系ID对应 `ccdi_staff_fmy_relation.id` |
| account_no | VARCHAR(200) | 是 | - | 账户号码,按业务要求加密存储 |
| account_type | VARCHAR(30) | 是 | - | `BANK/SECURITIES/PAYMENT/OTHER` |
| account_name | VARCHAR(100) | 是 | - | 账户名称 |
| open_bank | VARCHAR(100) | 是 | - | 开户银行、证券公司或支付平台 |
| bank_code | VARCHAR(20) | 否 | NULL | 金融机构代码 |
| currency | CHAR(3) | 是 | `CNY` | 币种ISO 4217 标准 |
| is_self_account | BOOLEAN | 是 | TRUE | 是否本人账户 |
| status | INT | 是 | 1 | 状态1-有效0-无效 |
| effective_date | DATE | 是 | 当前日期 | 生效日期 |
| invalid_date | DATE | 否 | NULL | 失效日期 |
| data_source | VARCHAR(30) | 是 | `MANUAL` | 数据来源MANUAL、IMPORT、SYNC |
| remark | VARCHAR(500) | 否 | NULL | 备注 |
| create_by | VARCHAR(64) | 是 | - | 创建人 |
| create_time | DATETIME | 是 | 当前时间 | 创建时间 |
| update_by | VARCHAR(64) | 否 | NULL | 更新人 |
| update_time | DATETIME | 否 | 当前时间 | 更新时间 |
说明:
- `relation_id` 为空表示员工本人账户
- `relation_id` 不为空表示员工亲属账户
- 本次不增加账户号码 hash 字段与脱敏展示字段
### 4.2 账户分析表 `ccdi_account_analysis`
用途:存储账户交易画像、金额特征和风险等级。
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| id | BIGINT | 是 | 自增 | 主键 |
| account_id | BIGINT | 是 | - | 账户ID对应 `ccdi_account_info.id` |
| analysis_months | INT | 是 | 6 | 统计月数 |
| stat_start_date | DATE | 否 | NULL | 统计开始日期 |
| stat_end_date | DATE | 否 | NULL | 统计结束日期 |
| avg_month_txn_count | INT | 否 | 0 | 月均交易笔数 |
| avg_month_txn_amount | DECIMAL(18,2) | 否 | 0.00 | 月均交易金额 |
| txn_frequency_level | VARCHAR(20) | 否 | `MEDIUM` | 交易频率LOW、MEDIUM、HIGH |
| debit_single_max_amount | DECIMAL(18,2) | 否 | NULL | 借方单笔交易最高额 |
| credit_single_max_amount | DECIMAL(18,2) | 否 | NULL | 贷方单笔交易最高额 |
| debit_daily_max_amount | DECIMAL(18,2) | 否 | NULL | 借方日累计交易最高额 |
| credit_daily_max_amount | DECIMAL(18,2) | 否 | NULL | 贷方日累计交易最高额 |
| txn_risk_level | VARCHAR(10) | 否 | `MEDIUM` | 交易风险等级 |
| remark | VARCHAR(500) | 否 | NULL | 分析备注 |
| create_by | VARCHAR(64) | 是 | - | 创建人 |
| create_time | DATETIME | 是 | 当前时间 | 创建时间 |
| update_by | VARCHAR(64) | 否 | NULL | 更新人 |
| update_time | DATETIME | 否 | 当前时间 | 更新时间 |
说明:
- 第一版以“一户一条当前分析记录”为目标
- 后续如果需要保留历史分析版本,可新增 `is_latest``analysis_batch_no` 或改造唯一约束
## 5. 索引与约束建议
### 5.1 `ccdi_account_info`
- 主键:`PRIMARY KEY (id)`
- 索引:`idx_staff_id (staff_id)`
- 索引:`idx_relation_id (relation_id)`
- 索引:`idx_account_type (account_type)`
- 索引:`idx_open_bank (open_bank)`
- 索引:`idx_is_self_account (is_self_account)`
- 索引:`idx_status (status)`
### 5.2 `ccdi_account_analysis`
- 主键:`PRIMARY KEY (id)`
- 唯一索引:`uk_account_id (account_id)`
- 索引:`idx_txn_risk_level (txn_risk_level)`
### 5.3 校验规则
- `invalid_date` 不得早于 `effective_date`
- `analysis_months` 应大于 0
- 金额字段不得为负数
- 若账户为本人账户,则不允许选择亲属
- 若账户为亲属账户,则必须选定亲属关系记录
## 6. DDL 草案
```sql
CREATE TABLE IF NOT EXISTS `ccdi_account_info` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`staff_id` BIGINT(20) NOT NULL COMMENT '员工ID',
`relation_id` BIGINT(20) DEFAULT NULL COMMENT '员工亲属关系ID员工本人账户为空',
`account_no` VARCHAR(200) NOT NULL COMMENT '账户号码(加密存储)',
`account_type` VARCHAR(30) NOT NULL COMMENT '账户类型BANK/SECURITIES/PAYMENT/OTHER',
`account_name` VARCHAR(100) NOT NULL COMMENT '账户名称',
`open_bank` VARCHAR(100) NOT NULL COMMENT '开户银行/证券公司/支付平台',
`bank_code` VARCHAR(20) DEFAULT NULL COMMENT '金融机构代码',
`currency` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种',
`is_self_account` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否本人账户1-是 0-否',
`status` INT(11) NOT NULL DEFAULT 1 COMMENT '状态1-有效 0-无效',
`effective_date` DATE NOT NULL COMMENT '生效日期',
`invalid_date` DATE DEFAULT NULL COMMENT '失效日期',
`data_source` VARCHAR(30) NOT NULL DEFAULT 'MANUAL' COMMENT '数据来源',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
`create_by` VARCHAR(64) NOT NULL COMMENT '创建人',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_staff_id` (`staff_id`),
KEY `idx_relation_id` (`relation_id`),
KEY `idx_account_type` (`account_type`),
KEY `idx_open_bank` (`open_bank`),
KEY `idx_is_self_account` (`is_self_account`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账户主表';
```
```sql
CREATE TABLE IF NOT EXISTS `ccdi_account_analysis` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`account_id` BIGINT(20) NOT NULL COMMENT '账户ID',
`analysis_months` INT(11) NOT NULL DEFAULT 6 COMMENT '统计月数',
`stat_start_date` DATE DEFAULT NULL COMMENT '统计开始日期',
`stat_end_date` DATE DEFAULT NULL COMMENT '统计结束日期',
`avg_month_txn_count` INT(11) DEFAULT 0 COMMENT '月均交易笔数',
`avg_month_txn_amount` DECIMAL(18,2) DEFAULT 0.00 COMMENT '月均交易金额',
`txn_frequency_level` VARCHAR(20) DEFAULT 'MEDIUM' COMMENT '交易频率等级',
`debit_single_max_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '借方单笔最高额',
`credit_single_max_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '贷方单笔最高额',
`debit_daily_max_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '借方日累计最高额',
`credit_daily_max_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '贷方日累计最高额',
`txn_risk_level` VARCHAR(10) DEFAULT 'MEDIUM' COMMENT '交易风险等级',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '分析备注',
`create_by` VARCHAR(64) NOT NULL COMMENT '创建人',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_account_id` (`account_id`),
KEY `idx_txn_risk_level` (`txn_risk_level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账户分析表';
```
## 7. 页面原型设计
### 7.1 菜单位置
新增菜单:
- 父级:信息维护
- 名称:账户库管理
- 建议顺序:放在“员工亲属关系维护”后、“招聘信息管理”前
### 7.2 列表页
#### 查询区
- 员工姓名
- 归属类型:本人 / 亲属
- 关系类型
- 关系人姓名
- 账户类型
- 开户机构
- 风险等级
- 状态
#### 操作区
- 新增
- 导入
- 导出
#### 表格字段
- 员工姓名
- 归属类型
- 关系类型
- 关系人姓名
- 账户号码
- 账户名称
- 账户类型
- 开户机构
- 币种
- 是否本人账户
- 月均交易笔数
- 月均交易金额
- 交易频率等级
- 交易风险等级
- 生效日期
- 状态
- 创建时间
- 操作
#### 列表页低保真线框
```text
+----------------------------------------------------------------------------------+
| 账户库管理 |
+----------------------------------------------------------------------------------+
| 员工姓名 [________] 归属类型 [本人/亲属] 关系类型 [____] 关系人姓名 [________] |
| 账户类型 [____] 开户机构 [________] 风险等级 [____] 状态 [有效/无效] |
| [搜索] [重置] |
+----------------------------------------------------------------------------------+
| [新增] [导入] [导出] |
+----------------------------------------------------------------------------------+
| 员工姓名 | 归属类型 | 关系类型 | 关系人姓名 | 账号 | 账户名称 | 账户类型 | ... |
|----------------------------------------------------------------------------------|
| 张三 | 本人 | - | - | 6222 | 工资卡 | BANK | ... |
| 张三 | 亲属 | 配偶 | 李四 | 6217 | 储蓄卡 | BANK | ... |
| ... |
+----------------------------------------------------------------------------------+
| 分页 |
+----------------------------------------------------------------------------------+
```
### 7.3 新增/编辑弹窗
#### 一、归属信息
- 归属类型
- 员工选择
- 关系人选择
- 关系类型
- 是否本人账户
交互规则:
- 归属类型为“本人”时,关系人选择和关系类型隐藏
- 归属类型为“亲属”时,先选员工,再加载该员工亲属列表
- 是否本人账户根据归属类型自动带出,只读显示
#### 二、账户信息
- 账户号码
- 账户类型
- 账户名称
- 开户机构
- 银行代码
- 币种
- 生效日期
- 失效日期
- 状态
- 备注
#### 三、分析信息
- 统计月数
- 统计开始日期
- 统计结束日期
- 月均交易笔数
- 月均交易金额
- 交易频率等级
- 借方单笔最高额
- 贷方单笔最高额
- 借方日累计最高额
- 贷方日累计最高额
- 交易风险等级
- 分析备注
#### 弹窗低保真线框
```text
+--------------------------------------------------------------+
| 新增账户 [X] |
+--------------------------------------------------------------+
| 一、归属信息 |
| 归属类型 [本人/亲属] |
| 员工 [请选择员工____________________] |
| 关系人 [请选择关系人__________________] |
| 关系类型 [自动带出______________________] |
| 是否本人 [是/否,只读] |
+--------------------------------------------------------------+
| 二、账户信息 |
| 账户号码 [____________________________] |
| 账户类型 [BANK/SECURITIES/PAYMENT/OTHER] |
| 账户名称 [____________________________] |
| 开户机构 [____________________________] |
| 银行代码 [________________] 币种 [CNY] |
| 生效日期 [yyyy-MM-dd] 失效日期 [yyyy-MM-dd] |
| 状态 [有效/无效] |
| 备注 [____________________________________________] |
+--------------------------------------------------------------+
| 三、分析信息 |
| 统计月数 [6] |
| 开始日期 [yyyy-MM-dd] 结束日期 [yyyy-MM-dd] |
| 月均笔数 [____] 月均金额 [__________] |
| 频率等级 [LOW/MEDIUM/HIGH] 风险等级 [LOW/MEDIUM/HIGH] |
| 借方单笔最高 [________] 贷方单笔最高 [________] |
| 借方日累计最高 [________] 贷方日累计最高 [________] |
| 分析备注 [____________________________________________] |
+--------------------------------------------------------------+
| [取消] [确定] |
+--------------------------------------------------------------+
```
### 7.4 详情弹窗
详情页建议与新增/编辑弹窗结构一致,全部字段只读展示,避免维护两套信息布局。
## 8. 导入模板建议
建议导入模板字段如下:
- 员工柜员号
- 员工姓名
- 归属类型
- 关系人姓名
- 关系类型
- 账户号码
- 账户类型
- 账户名称
- 开户机构
- 银行代码
- 币种
- 生效日期
- 失效日期
- 统计月数
- 月均交易笔数
- 月均交易金额
- 交易频率等级
- 借方单笔最高额
- 贷方单笔最高额
- 借方日累计最高额
- 贷方日累计最高额
- 交易风险等级
- 备注
导入校验建议:
- 本人账户时,不允许填写关系人姓名
- 亲属账户时,关系人姓名不能为空
- 若员工不存在,则导入失败
- 若亲属不存在,则导入失败
## 9. 菜单 SQL 草案
```sql
SET @parent_menu_id = (
SELECT menu_id
FROM sys_menu
WHERE menu_name = '信息维护' AND parent_id = 0
LIMIT 1
);
INSERT INTO sys_menu (
menu_name, parent_id, order_num, path, component, is_frame, is_cache,
menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark
) VALUES (
'账户库管理', @parent_menu_id, 5, 'accountInfo', 'ccdiAccountInfo/index', 1, 0,
'C', '0', '0', 'ccdi:accountInfo:list', 'money', 'admin', NOW(), '', NULL, '账户库管理菜单'
);
SET @menu_id = LAST_INSERT_ID();
INSERT INTO sys_menu (
menu_name, parent_id, order_num, path, component, is_frame, is_cache,
menu_type, visible, status, perms, icon, create_by, create_time, remark
) VALUES
('账户查询', @menu_id, 1, '', '', 1, 0, 'F', '0', '0', 'ccdi:accountInfo:query', '#', 'admin', NOW(), ''),
('账户新增', @menu_id, 2, '', '', 1, 0, 'F', '0', '0', 'ccdi:accountInfo:add', '#', 'admin', NOW(), ''),
('账户修改', @menu_id, 3, '', '', 1, 0, 'F', '0', '0', 'ccdi:accountInfo:edit', '#', 'admin', NOW(), ''),
('账户删除', @menu_id, 4, '', '', 1, 0, 'F', '0', '0', 'ccdi:accountInfo:remove', '#', 'admin', NOW(), ''),
('账户导入', @menu_id, 5, '', '', 1, 0, 'F', '0', '0', 'ccdi:accountInfo:import', '#', 'admin', NOW(), ''),
('账户导出', @menu_id, 6, '', '', 1, 0, 'F', '0', '0', 'ccdi:accountInfo:export', '#', 'admin', NOW(), '');
```
## 10. 后续落地建议
如果本方案确认,可按以下顺序实施:
1. 先建两张表并补菜单 SQL
2. 再补前端列表页与弹窗原型
3. 最后对接后端接口与导入导出
当前方案适合作为第一版基础模型,后续若需要接入自动打标或保留历史分析结果,可在 `ccdi_account_analysis` 上继续扩展,而不破坏 `ccdi_account_info` 主档结构。