# 异常账户模型接入银行流水打标设计文档

**模块**: 银行流水打标  
**日期**: 2026-03-31

## 一、背景

当前银行流水打标主链路已经具备以下基础能力：

- 规则元数据管理与启用控制
- `CcdiBankTagServiceImpl` 统一执行入口
- `CcdiBankTagAnalysisMapper.xml` 承载真实规则 SQL
- `ccdi_bank_statement_tag_result` 统一承载 `STATEMENT / OBJECT` 命中结果
- 项目风险总览按对象型结果聚合员工风险情况

根据 [异常账户.xlsx](/Users/wkc/Desktop/ccdi/ccdi/assets/异常账户.xlsx) 与 [员工账户.xlsx](/Users/wkc/Desktop/ccdi/ccdi/assets/员工账户.xlsx)，本次需要新增独立模型“异常账户”，并正式接入以下两条规则：

- `SUDDEN_ACCOUNT_CLOSURE`：突然销户
- `DORMANT_ACCOUNT_LARGE_ACTIVATION`：休眠账户大额启用

这两条规则均依赖新增账户信息表 `ccdi_account_info`，且风险筛查对象明确为“员工本人”。本次目标是在不改造现有打标架构的前提下，将两条规则纳入现有项目打标主链路，并补充能够稳定命中的测试数据与验证手段。

## 二、目标

本次设计目标如下：

1. 新增账户信息表 `ccdi_account_info`，支撑异常账户规则计算。
2. 新增独立模型 `ABNORMAL_ACCOUNT`，并接入 2 条对象型规则。
3. 将两条规则接入现有 `executeObjectRule(...)` 打标链路，不新增平行处理模块。
4. 补充最小可命中的测试数据 SQL，并覆盖正样本与反样本。
5. 保留 Java 自动化测试，同时在验证阶段使用 MySQL MCP 执行真实 SQL，确认命中结果符合业务口径。
6. 在设计确认后，分别产出后端与前端实施计划文档。

## 三、范围

### 3.1 本次范围

- 新增 `ccdi_account_info` 建表 SQL
- 新增模型 `ABNORMAL_ACCOUNT`
- 新增规则元数据 `SUDDEN_ACCOUNT_CLOSURE`、`DORMANT_ACCOUNT_LARGE_ACTIVATION`
- `CcdiBankTagServiceImpl` 新增对象型规则分发
- `CcdiBankTagAnalysisMapper.java/.xml` 新增 2 条对象型查询
- 新增测试数据 SQL
- 新增 Java 自动化测试
- 新增基于 MySQL MCP 的真实 SQL 验证步骤
- 新增设计文档、后端实施计划、前端实施计划

### 3.2 不在本次范围

- 不开发“异常账户人员信息”独立查询、分页、详情、导出真实数据链路
- 不改前端页面展示逻辑
- 不扩展到关系人或外部账户
- 不新增动态规则引擎、DSL 或兼容性补丁方案
- 不改造 `lsfx-mock-server`
- 不将固定阈值改造成项目可配置参数

## 四、现状分析

### 4.1 当前主链路

当前项目级银行流水打标流程为：

1. `CcdiBankTagServiceImpl.rebuildProject(...)` 加载启用规则。
2. 规则按 `rule_code` 分发到 `executeStatementRule(...)` 或 `executeObjectRule(...)`。
3. `CcdiBankTagAnalysisMapper.xml` 执行真实 SQL，返回流水型或对象型命中结果。
4. Service 将命中结果组装为 `CcdiBankTagResult` 并写入 `ccdi_bank_statement_tag_result`。
5. 项目结果总览再按对象维度聚合风险人数和命中规则快照。

### 4.2 当前缺口

当前仓库中“异常账户人员信息”仍为占位展示，且主打标规则中尚无“异常账户”模型与对应规则编码。也就是说，本次缺口主要是：

- 缺少账户信息基础表
- 缺少异常账户模型与规则元数据
- 缺少两条规则的对象型 SQL
- 缺少最小可命中的测试样本与真实 SQL 验证

## 五、方案对比

### 5.1 方案一：最小闭环接入现有对象型打标链路

做法：

- 新增独立模型 `ABNORMAL_ACCOUNT`
- 两条规则均按 `OBJECT` 结果类型落到员工维度
- 通过 `CcdiBankTagAnalysisMapper.xml` 计算命中结果
- 结果继续写入 `ccdi_bank_statement_tag_result`

优点：

- 改动最小
- 完全复用现有打标主链路
- 能直接进入现有员工风险总览聚合

缺点：

- 本轮不打通“异常账户人员信息”独立详情链路

### 5.2 方案二：在方案一基础上同时打通异常账户独立结果链路

优点：

- 风险详情中的“异常账户人员信息”可展示真实数据

缺点：

- 改动范围明显扩大
- 超出本次需求
- 不符合最短路径实现要求

### 5.3 方案三：仅补 SQL 验证，不接入主系统打标链路

优点：

- 开发最省

缺点：

- 无法满足“正式接入主系统打标链路”的需求

### 5.4 结论

采用方案一：

- 新增独立模型 `ABNORMAL_ACCOUNT`
- 两条规则均按对象型规则接入现有打标链路
- 结果沉淀到现有结果表
- 后续如需开发异常账户独立查询能力，再以此为基础扩展

## 六、总体设计

### 6.1 模型与规则设计

本次新增如下模型与规则：

- 模型编码：`ABNORMAL_ACCOUNT`
- 模型名称：`异常账户`
- 规则一：`SUDDEN_ACCOUNT_CLOSURE` / `突然销户`
- 规则二：`DORMANT_ACCOUNT_LARGE_ACTIVATION` / `休眠账户大额启用`

两条规则统一定义为：

- `result_type = OBJECT`
- `object_type = STAFF_ID_CARD`
- `object_key = 员工身份证号`

### 6.2 结果落库

两条规则命中后继续写入现有结果表 `ccdi_bank_statement_tag_result`，不新增单独结果表。

结果字段约束如下：

- `model_code = ABNORMAL_ACCOUNT`
- `rule_code` 使用全大写风格
- `result_type = OBJECT`
- `bank_statement_id = null`
- `object_type = STAFF_ID_CARD`
- `object_key = 员工身份证号`
- `reason_detail` 存储账户号、异常日期与统计快照

### 6.3 数据流

数据流保持为：

1. 项目级打标入口加载启用规则。
2. 当规则编码为 `SUDDEN_ACCOUNT_CLOSURE` 或 `DORMANT_ACCOUNT_LARGE_ACTIVATION` 时，进入 `executeObjectRule(...)`。
3. Mapper SQL 在项目范围内将 `ccdi_bank_statement` 与 `ccdi_account_info`、`ccdi_base_staff` 关联。
4. SQL 返回员工身份证号维度的对象型命中结果。
5. Service 将命中结果写入 `ccdi_bank_statement_tag_result`。
6. 员工风险聚合继续从该结果表汇总，无需新建平行链路。

## 七、表结构设计

### 7.1 新增表 `ccdi_account_info`

以 [员工账户.xlsx](/Users/wkc/Desktop/ccdi/ccdi/assets/员工账户.xlsx) 为准，新增表 `ccdi_account_info`，核心字段如下：

- `account_id`
- `account_no`
- `account_type`
- `account_name`
- `owner_type`
- `owner_id`
- `bank`
- `bank_code`
- `currency`
- `is_self_account`
- `monthly_avg_trans_count`
- `monthly_avg_trans_amount`
- `trans_freq_type`
- `dr_max_single_amount`
- `cr_max_single_amount`
- `dr_max_daily_amount`
- `cr_max_daily_amount`
- `trans_risk_level`
- `status`
- `effective_date`
- `invalid_date`
- `created_by`
- `updated_by`
- `create_time`
- `update_time`

### 7.2 关联约束

本次规则只识别员工本人账户，关联口径固定为：

- `ccdi_account_info.owner_type = 'EMPLOYEE'`
- `ccdi_account_info.owner_id = ccdi_base_staff.id_card`
- `ccdi_account_info.account_no = ccdi_bank_statement.LE_ACCOUNT_NO`

说明：

- 仓库中当前未见单独的账号加解密或标准化链路，因此本次设计要求建表脚本、测试数据与流水数据直接使用一致账号值
- 本次不将关系人账户纳入规则范围

## 八、规则 SQL 口径

### 8.1 `SUDDEN_ACCOUNT_CLOSURE`

业务口径：

- 员工本人账户已销户
- 销户日前 30 天内仍存在交易记录

SQL 设计约束：

- 仅统计项目内流水
- 统计窗口限定为 `[invalid_date - 30天, invalid_date)`
- 按“员工身份证号 + 账号”粒度聚合，再映射回员工对象

命中条件：

- `status = 2`
- `invalid_date is not null`
- 窗口内存在至少 1 笔交易

返回结果：

- `objectType = STAFF_ID_CARD`
- `objectKey = 员工身份证号`

`reasonDetail` 结构：

- `账户{account_no}于{invalid_date}销户，销户前30天内最后交易日{last_tx_date}，累计交易金额{window_total_amount}元，单笔最大金额{window_max_single_amount}元`

### 8.2 `DORMANT_ACCOUNT_LARGE_ACTIVATION`

业务口径：

- 员工本人账户状态正常
- 开户后长期未使用
- 首次启用后出现大额资金流动

SQL 设计约束：

- 仅统计项目内流水
- 以该账户在项目内的首次流水日期作为“启用时间”
- “沉睡时长”按开户日期到首次交易日期计算

命中条件：

- `status = 1`
- `effective_date is not null`
- `first_tx_date >= effective_date + 6个月`
- 且满足以下任一：
  - 启用后累计交易总额 `>= 500000`
  - 启用后单笔最大交易金额 `>= 100000`

返回结果：

- `objectType = STAFF_ID_CARD`
- `objectKey = 员工身份证号`

`reasonDetail` 结构：

- `账户{account_no}开户于{effective_date}，首次交易日期{first_tx_date}，沉睡时长{dormant_months}个月，启用后累计交易金额{total_amount}元，单笔最大金额{max_single_amount}元`

### 8.3 公共规则约束

- 仅识别员工本人账户，不识别关系人和外部账户
- 仅按项目内流水计算，不跨项目拼接历史流水
- 累计金额使用 `amount_dr + amount_cr`
- 单笔最大金额使用 `greatest(amount_dr, amount_cr)`
- 同一员工多个账户分别判断，允许同一规则写入多条结果，避免强行合并后丢失账户级快照

## 九、测试数据设计

### 9.1 测试数据组织原则

新增一份独立增量 SQL，放在 `sql/migration/`，仅构造本次规则所需最小样本。

### 9.2 样本设计

建议最少包含以下样本：

- 员工 A：命中 `SUDDEN_ACCOUNT_CLOSURE`
  - 账户已销户
  - 销户前 30 天内有 2 到 3 笔项目流水
- 员工 B：命中 `DORMANT_ACCOUNT_LARGE_ACTIVATION`
  - 开户日期早于首次交易至少 6 个月
  - 启用后累计金额超过 50 万
- 员工 C：休眠不足 6 个月，不命中
- 员工 D：已销户，但销户前 30 天无流水，不命中

### 9.3 数据一致性要求

- `ccdi_account_info.account_no` 与 `ccdi_bank_statement.LE_ACCOUNT_NO` 必须一致
- `owner_id` 与员工身份证号一致
- 正样本与反样本必须处于同一项目验证口径下，避免跨项目误差

## 十、测试与验证设计

### 10.1 Java 自动化测试

保留两层自动化测试：

1. Service 分发测试
   - 新规则能进入 `executeObjectRule(...)`
2. Mapper / SQL 结构测试
   - 新 Mapper 方法存在
   - XML 中存在对应 `<select>`
   - 规则元数据和模型编码无拼写错误
3. 结果聚合测试
   - 新规则写入后，员工风险总览可正常聚合

### 10.2 MySQL MCP 真实 SQL 验证

本次新增一层真实 SQL 验证，要求在测试阶段直接通过 MySQL MCP 执行规则 SQL，确认结果符合口径。

验证要求：

- 使用项目数据库连接信息
- 不手写 `mysql -e`
- 直接执行对象型规则对应 SQL
- 校验命中员工身份证号是否与测试样本一致
- 校验反样本不会被查出
- 校验 `reason_detail` 中异常日期、累计金额、单笔最大金额等关键快照是否符合预期

### 10.3 测试结束清理

若验证阶段启动了本地前后端、Mock 服务或其他辅助进程，测试结束后需主动关闭，避免残留占用端口。

## 十一、实施边界

### 11.1 后端实施内容

- 新增建表与规则元数据 SQL
- 新增 Mapper 方法和 XML SQL
- 新增 Service 分发
- 新增测试数据 SQL
- 新增自动化测试
- 执行 MySQL MCP SQL 验证

### 11.2 前端实施内容

本轮前端原则上不改代码，但仍需产出一份前端实施计划，明确说明：

- 现有页面继续复用项目总览对象聚合结果
- 本轮不开发异常账户独立列表与详情
- 前端无需新增接口或交互

## 十二、验收标准

验收标准如下：

1. `ccdi_account_info` 建表脚本存在且字段与 Excel 一致。
2. 模型 `ABNORMAL_ACCOUNT` 与两条规则元数据已落库，编码统一全大写。
3. `CcdiBankTagServiceImpl` 已接入两条规则对象型执行分支。
4. 规则命中结果成功写入 `ccdi_bank_statement_tag_result`。
5. 员工风险总览聚合后可看到新增模型与规则命中。
6. 测试数据中的正样本可命中，反样本不命中。
7. MySQL MCP 真实 SQL 验证结果与业务口径一致。

## 十三、后续文档规划

设计确认后，继续补充以下文档：

- `docs/plans/backend/` 下的后端实施计划
- `docs/plans/frontend/` 下的前端实施计划
- `docs/reports/implementation/` 下的后端实施记录
- `docs/reports/implementation/` 下的前端实施记录
