diff --git a/docs/design/2026-03-31-abnormal-account-bank-tag-design.md b/docs/design/2026-03-31-abnormal-account-bank-tag-design.md new file mode 100644 index 00000000..07b84cc9 --- /dev/null +++ b/docs/design/2026-03-31-abnormal-account-bank-tag-design.md @@ -0,0 +1,382 @@ +# 异常账户模型接入银行流水打标设计文档 + +**模块**: 银行流水打标 +**日期**: 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 中存在对应 `