补充异常账户模型实施计划

This commit is contained in:
wkc
2026-03-31 16:18:20 +08:00
parent 3741ef5fe4
commit f4a72a6110
3 changed files with 683 additions and 0 deletions

View File

@@ -0,0 +1,494 @@
# 异常账户模型接入银行流水打标后端 Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
>
> 仓库约束:当前仓库明确禁止开启 subagent执行时统一使用 `superpowers:executing-plans`。
**Goal:** 在后端正式接入异常账户模型,新增 `ccdi_account_info`、两条对象型打标规则、最小可命中测试数据,并通过 Java 测试和 MySQL MCP SQL 校验确认命中口径正确。
**Architecture:** 复用现有 `CcdiBankTagServiceImpl -> CcdiBankTagAnalysisMapper.xml -> ccdi_bank_statement_tag_result` 主链路,不新增并行模块。两条规则统一作为 `OBJECT` 规则落到员工身份证号维度,`reason_detail` 保留账户级异常快照;测试层同时保留 Java 自动化测试与 MySQL MCP 真实 SQL 验证。
**Tech Stack:** Java 21, Spring Boot 3, MyBatis XML, MyBatis Plus, MySQL, JUnit 5, Mockito, MySQL MCP
---
## File Map
**Create:**
- `sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql`
- 建表 `ccdi_account_info`,补模型与规则元数据
- `sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql`
- 插入最小命中与反样本测试数据
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java`
- 校验模型、规则编码、结果类型与业务口径文本
- `docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md`
- 记录后端实施与验证结果
**Modify:**
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java`
- 新增两个对象型规则分发分支
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java`
- 新增两个 Mapper 方法签名
- `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml`
- 新增两条对象型真实 SQL
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java`
- 覆盖新规则分发与结果入库
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewEmployeeResultBuilderTest.java`
- 校验新增规则能进入员工风险聚合快照
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java`
- 补模型/规则元数据断言
**No Change Expected:**
- `ruoyi-ui/`
- 本轮前端不新增接口和页面改动
- `lsfx-mock-server/`
- 本轮不扩展 Mock 样本
## Task 1: 锁定模型、规则与落库契约
**Files:**
- Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java`
- [ ] **Step 1: 先写 SQL 元数据测试**
新增 `CcdiAbnormalAccountRuleSqlMetadataTest`,至少覆盖:
```java
assertTrue(sql.contains("ABNORMAL_ACCOUNT"));
assertTrue(sql.contains("SUDDEN_ACCOUNT_CLOSURE"));
assertTrue(sql.contains("DORMANT_ACCOUNT_LARGE_ACTIVATION"));
assertTrue(sql.contains("'OBJECT'"));
```
- [ ] **Step 2: 再写 Service 分发失败测试**
`CcdiBankTagServiceImplTest` 中新增两个断言,约束新规则走对象型 Mapper
```java
verify(analysisMapper).selectSuddenAccountClosureObjects(40L);
verify(analysisMapper).selectDormantAccountLargeActivationObjects(40L);
```
- [ ] **Step 3: 运行定向测试,确认失败点正确**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiAbnormalAccountRuleSqlMetadataTest,CcdiBankTagServiceImplTest test
```
Expected:
- FAIL提示缺少规则元数据脚本内容或缺少 Mapper / Service 分发
- [ ] **Step 4: 最小化补充规则元数据和方法签名**
按以下顺序补代码:
1. 新增规则元数据脚本文件骨架
2.`CcdiBankTagAnalysisMapper.java` 增加两个方法:
```java
List<BankTagObjectHitVO> selectSuddenAccountClosureObjects(@Param("projectId") Long projectId);
List<BankTagObjectHitVO> selectDormantAccountLargeActivationObjects(@Param("projectId") Long projectId);
```
3.`CcdiBankTagServiceImpl.java` 中新增两个 `case`
- [ ] **Step 5: 重新运行定向测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiAbnormalAccountRuleSqlMetadataTest,CcdiBankTagServiceImplTest test
```
Expected:
- PASS 或仅剩 SQL 实现相关失败
- [ ] **Step 6: 提交本任务**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java \
sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql
git commit -m "补充异常账户模型规则骨架"
```
## Task 2: 落地 `ccdi_account_info` 建表与规则元数据脚本
**Files:**
- Create: `sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java`
- [ ] **Step 1: 先补脚本结构测试**
为建表 SQL 增加断言,至少包含:
```java
assertTrue(sql.contains("create table if not exists `ccdi_account_info`"));
assertTrue(sql.contains("`account_no`"));
assertTrue(sql.contains("`owner_type`"));
assertTrue(sql.contains("`effective_date`"));
assertTrue(sql.contains("`invalid_date`"));
```
- [ ] **Step 2: 运行测试确认失败**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiAbnormalAccountRuleSqlMetadataTest,CcdiBankTagRuleSqlMetadataTest test
```
Expected:
- FAIL提示脚本字段或规则元数据缺失
- [ ] **Step 3: 写最小建表与元数据脚本**
`2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql` 中按以下顺序补内容:
1. `CREATE TABLE IF NOT EXISTS ccdi_account_info`
2. 插入模型 `ABNORMAL_ACCOUNT`
3. 插入两条规则元数据
4. 业务口径文本与设计文档保持一致
- [ ] **Step 4: 重新运行元数据测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiAbnormalAccountRuleSqlMetadataTest,CcdiBankTagRuleSqlMetadataTest test
```
Expected:
- PASS
- [ ] **Step 5: 提交本任务**
```bash
git add sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java
git commit -m "补充异常账户模型建表和规则元数据"
```
## Task 3: 先写两条规则 SQL 的失败测试
**Files:**
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewEmployeeResultBuilderTest.java`
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml`
- [ ] **Step 1: 写对象型结果断言**
`CcdiBankTagServiceImplTest` 中新增两个测试,断言对象型结果内容:
```java
verify(resultMapper).insertBatch(argThat(results -> results.stream().anyMatch(item ->
"ABNORMAL_ACCOUNT".equals(item.getModelCode())
&& "SUDDEN_ACCOUNT_CLOSURE".equals(item.getRuleCode())
&& "OBJECT".equals(item.getResultType())
&& "STAFF_ID_CARD".equals(item.getObjectType())
)));
```
- [ ] **Step 2: 写聚合层断言**
`CcdiProjectOverviewEmployeeResultBuilderTest` 中补断言,确保新增规则能进入 `hitRulesJson`
```java
assertTrue(result.getHitRulesJson().contains("SUDDEN_ACCOUNT_CLOSURE"));
assertTrue(result.getHitRulesJson().contains("DORMANT_ACCOUNT_LARGE_ACTIVATION"));
```
- [ ] **Step 3: 运行测试确认失败**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiBankTagServiceImplTest,CcdiProjectOverviewEmployeeResultBuilderTest test
```
Expected:
- FAIL提示 SQL 尚未返回结果或规则未写入正确字段
- [ ] **Step 4: 暂不修实现,先保存失败断言**
保持测试失败状态,进入下一任务补最小 SQL 实现。
## Task 4: 实现 `SUDDEN_ACCOUNT_CLOSURE` 最小闭环
**Files:**
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java`
- [ ] **Step 1: 在 XML 中新增查询骨架**
新增 `selectSuddenAccountClosureObjects`,结果列固定为:
```sql
select
'STAFF_ID_CARD' as objectType,
staff.id_card as objectKey,
concat(...) as reasonDetail
```
- [ ] **Step 2: 按设计补完整过滤条件**
SQL 需要覆盖:
- `owner_type = 'EMPLOYEE'`
- `status = 2`
- `invalid_date is not null`
- 统计窗口 `[invalid_date - 30天, invalid_date)`
- 按员工身份证号 + 账号聚合
- [ ] **Step 3: 运行定向测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiBankTagServiceImplTest test
```
Expected:
- 至少 `SUDDEN_ACCOUNT_CLOSURE` 相关断言 PASS
- [ ] **Step 4: 提交本任务**
```bash
git add ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java
git commit -m "实现突然销户打标规则"
```
## Task 5: 实现 `DORMANT_ACCOUNT_LARGE_ACTIVATION` 最小闭环
**Files:**
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java`
- [ ] **Step 1: 在 XML 中新增查询骨架**
新增 `selectDormantAccountLargeActivationObjects`,结果列同样返回:
```sql
'STAFF_ID_CARD' as objectType,
staff.id_card as objectKey,
concat(...) as reasonDetail
```
- [ ] **Step 2: 补完整命中条件**
SQL 需要覆盖:
- `owner_type = 'EMPLOYEE'`
- `status = 1`
- `effective_date is not null`
- `first_tx_date >= effective_date + 6个月`
- `total_amount >= 500000 or max_single_amount >= 100000`
- [ ] **Step 3: 运行定向测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiBankTagServiceImplTest,CcdiProjectOverviewEmployeeResultBuilderTest test
```
Expected:
- PASS
- [ ] **Step 4: 提交本任务**
```bash
git add ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewEmployeeResultBuilderTest.java
git commit -m "实现休眠账户大额启用打标规则"
```
## Task 6: 补最小测试数据 SQL
**Files:**
- Create: `sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql`
- Modify: `docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md`
- [ ] **Step 1: 先写测试数据注释骨架**
在 SQL 文件中先划分 4 组样本块:
- 员工 A命中 `SUDDEN_ACCOUNT_CLOSURE`
- 员工 B命中 `DORMANT_ACCOUNT_LARGE_ACTIVATION`
- 员工 C休眠不足 6 个月,不命中
- 员工 D销户前 30 天无流水,不命中
- [ ] **Step 2: 写最小测试数据**
按顺序补数据:
1. 员工基础数据
2. 项目内流水数据
3. `ccdi_account_info` 账户数据
4. 必要的清理 SQL
- [ ] **Step 3: 记录导入命令**
在实施记录中先预填导入方式:
```bash
bin/mysql_utf8_exec.sh sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql
bin/mysql_utf8_exec.sh sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql
```
- [ ] **Step 4: 提交本任务**
```bash
git add sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql \
docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md
git commit -m "补充异常账户规则测试数据"
```
## Task 7: 用 MySQL MCP 执行真实 SQL 校验口径
**Files:**
- Modify: `docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md`
- [ ] **Step 1: 导入建表和测试数据脚本**
Run:
```bash
bin/mysql_utf8_exec.sh sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql
bin/mysql_utf8_exec.sh sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql
```
Expected:
- PASS脚本执行成功且无乱码
- [ ] **Step 2: 使用 MySQL MCP 执行 `SUDDEN_ACCOUNT_CLOSURE` 对应 SQL**
要求:
- 直接执行 Mapper 中的真实查询等价 SQL
- 校验仅返回员工 A
- 校验 `reasonDetail` 中包含销户日期、最后交易日、累计金额和单笔最大金额
- [ ] **Step 3: 使用 MySQL MCP 执行 `DORMANT_ACCOUNT_LARGE_ACTIVATION` 对应 SQL**
要求:
- 直接执行 Mapper 中的真实查询等价 SQL
- 校验仅返回员工 B
- 校验员工 C 未命中
- 校验 `reasonDetail` 中包含开户日期、首次交易日期、累计金额或单笔最大金额快照
- [ ] **Step 4: 将 SQL 验证结果写入实施记录**
记录:
- 实际执行 SQL 摘要
- 命中对象
- 未命中对象
- 与业务口径的对照结论
- [ ] **Step 5: 提交本任务**
```bash
git add docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md
git commit -m "补充异常账户规则SQL校验记录"
```
## Task 8: 跑完整后端验证并收尾
**Files:**
- Modify: `docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md`
- [ ] **Step 1: 运行后端定向测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiAbnormalAccountRuleSqlMetadataTest,CcdiBankTagRuleSqlMetadataTest,CcdiBankTagServiceImplTest,CcdiProjectOverviewEmployeeResultBuilderTest test
```
Expected:
- PASS
- [ ] **Step 2: 如需联调打标主链路,启动后端并验证后主动关闭**
Run:
```bash
mvn -pl ruoyi-admin -am package -DskipTests
cd ruoyi-admin/target && java -jar ruoyi-admin.jar
```
验证完成后关闭进程。
- [ ] **Step 3: 完善实施记录**
记录:
- 最终改动文件
- 测试结果
- MySQL MCP 校验结论
- 若启动过进程,明确已关闭
- [ ] **Step 4: 最终提交**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java \
ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewEmployeeResultBuilderTest.java \
sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql \
sql/migration/2026-03-31-add-abnormal-account-rule-test-data.sql \
docs/reports/implementation/2026-03-31-abnormal-account-bank-tag-backend-implementation.md
git commit -m "完成异常账户模型后端接入"
```
## Final Verification
- [ ] 运行:
```bash
mvn -pl ccdi-project -Dtest=CcdiAbnormalAccountRuleSqlMetadataTest,CcdiBankTagRuleSqlMetadataTest,CcdiBankTagServiceImplTest,CcdiProjectOverviewEmployeeResultBuilderTest test
```
- [ ] 使用 MySQL MCP 执行两条规则真实 SQL确认正样本命中、反样本不命中
- [ ] 确认结果写入 `ccdi_bank_statement_tag_result`
- [ ] 确认新增编码全为大写
- [ ] 如启动过后端进程,验证结束后主动关闭