修复第二期流水规则元数据

This commit is contained in:
wkc
2026-03-20 16:31:58 +08:00
parent 085e2e7e35
commit a0e8057d70
4 changed files with 145 additions and 0 deletions

View File

@@ -21,6 +21,15 @@ class CcdiBankTagRuleSqlMetadataTest {
assertPhase1Metadata(migrationSql);
}
@Test
void phase2MetadataSql_shouldAlignInitAndMigrationScripts() throws IOException {
String initSql = readProjectFile("sql", "2026-03-16-bank-tagging.sql");
String migrationSql = readProjectFile("sql", "migration", "2026-03-20-sync-bank-tag-phase2-rule-metadata.sql");
assertPhase2Metadata(initSql);
assertPhase2Metadata(migrationSql);
}
private void assertPhase1Metadata(String sqlContent) {
assertAll(
() -> assertTrue(sqlContent.contains("'FOREX_BUY_AMT'")
@@ -41,6 +50,37 @@ class CcdiBankTagRuleSqlMetadataTest {
);
}
private void assertPhase2Metadata(String sqlContent) {
assertAll(
() -> assertTrue(sqlContent.contains("真实规则识别低收入关系人累计交易超10万元的员工对象"),
"应同步 LOW_INCOME_RELATIVE_LARGE_TRANSACTION 的真实规则说明"),
() -> assertTrue(sqlContent.contains("真实规则:识别同日多对手方且金额落在可疑区间的疑似赌博对象"),
"应同步 MULTI_PARTY_GAMBLING_TRANSFER 的真实规则说明"),
() -> assertTrue(sqlContent.contains("'MONTHLY_FIXED_INCOME'")
&& sqlContent.contains("真实规则识别近12个月持续出现稳定月度非工资收入的员工对象"),
"应同步 MONTHLY_FIXED_INCOME 的指标编码和真实规则说明"),
() -> assertTrue(sqlContent.contains("'FIXED_COUNTERPARTY_TRANSFER', '疑似兼职', NULL, 'OBJECT'")
&& sqlContent.contains("真实规则:识别固定交易对手季度转入金额落在设定区间的员工对象"),
"FIXED_COUNTERPARTY_TRANSFER 应清空旧 indicator_code 并同步真实规则说明"),
() -> assertTrue(sqlContent.contains("真实规则:识别购房支出但当前房产登记口径缺失的流水"),
"应同步 HOUSE_REGISTRATION_MISMATCH 的真实规则说明"),
() -> assertTrue(sqlContent.contains("真实规则:识别物业缴费但当前房产登记口径缺失的流水"),
"应同步 PROPERTY_FEE_REGISTRATION_MISMATCH 的真实规则说明"),
() -> assertTrue(sqlContent.contains("员工及关系人有5000元以上的纳税记录但当前资产登记口径下无房产登记。"),
"TAX_ASSET_REGISTRATION_MISMATCH 应使用当前资产登记口径表述"),
() -> assertTrue(sqlContent.contains("真实规则:识别大额纳税但当前房产登记口径缺失的流水"),
"应同步 TAX_ASSET_REGISTRATION_MISMATCH 的真实规则说明"),
() -> assertTrue(sqlContent.contains("真实规则识别单个供应商采购额占比超过70%的员工对象"),
"应同步 SUPPLIER_CONCENTRATION 的真实规则说明"),
() -> assertTrue(sqlContent.contains("工资发放后24小时内转出超过80%的资金。")
&& sqlContent.contains("真实规则识别工资入账24小时内快速转出的员工对象"),
"应同步 SALARY_QUICK_TRANSFER 的业务口径和真实规则说明"),
() -> assertTrue(sqlContent.contains("工资发放后除代扣项目外连续30天无消费或转账支出记录。")
&& sqlContent.contains("真实规则识别工资入账后30天内无消费或转账支出的员工对象"),
"应同步 SALARY_UNUSED 的业务口径修复和真实规则说明")
);
}
private String readProjectFile(String... parts) throws IOException {
Path path = Path.of("..", parts);
return Files.readString(path, StandardCharsets.UTF_8);

View File

@@ -0,0 +1,31 @@
# 第二期银行流水规则元数据修复实施记录
## 问题背景
- 2026-03-20 校验发现第二期规则已完成后端真实实现,但当前数据库中的第二期 10 条规则元数据仍停留在占位状态。
- 直接查询 `ccdi_bank_tag_rule` 可见:
- 10 条第二期规则 `remark` 仍为“占位规则待补充真实SQL”
- `FIXED_COUNTERPARTY_TRANSFER.indicator_code` 仍为旧值 `FIXED_COUNTERPARTY_TRANSFER`
- `SALARY_UNUSED.business_caliber` 仍为乱码
- `TAX_ASSET_REGISTRATION_MISMATCH.business_caliber` 仍为旧口径
## 根因分析
- 第二期真实规则落地时已更新初始化脚本 [`sql/2026-03-16-bank-tagging.sql`](/Users/wkc/Desktop/ccdi/ccdi/.worktrees/bank-tag-real-rule-phase2-backend/sql/2026-03-16-bank-tagging.sql),但没有同步补一份增量迁移脚本。
- 当前仓库的 SQL 元数据测试此前只覆盖第一期,没有覆盖第二期,所以“只改初始化脚本、遗漏增量脚本”的问题没有被自动拦截。
- 已执行过旧增量脚本、但未重建规则表初始化数据的环境,会继续保留第二期占位元数据。
## 本次修改
- 扩展 SQL 元数据测试 [`CcdiBankTagRuleSqlMetadataTest.java`](/Users/wkc/Desktop/ccdi/ccdi/.worktrees/bank-tag-real-rule-phase2-backend/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java)
- 保留第一期元数据校验
- 新增第二期初始化脚本与迁移脚本一致性校验
- 约束第二期 10 条规则的 `indicator_code``business_caliber``remark` 必须与真实规则实现对齐
- 新增增量脚本 [`2026-03-20-sync-bank-tag-phase2-rule-metadata.sql`](/Users/wkc/Desktop/ccdi/ccdi/.worktrees/bank-tag-real-rule-phase2-backend/sql/migration/2026-03-20-sync-bank-tag-phase2-rule-metadata.sql)
- 使用 `INSERT ... ON DUPLICATE KEY UPDATE` 同步第二期 10 条规则元数据
- 清空 `FIXED_COUNTERPARTY_TRANSFER` 的旧 `indicator_code`
- 修复 `SALARY_UNUSED` 乱码与 `TAX_ASSET_REGISTRATION_MISMATCH` 业务口径
- 同步 10 条规则的真实规则 `remark`
- 使用 `bin/mysql_utf8_exec.sh` 将第二期元数据修复脚本落到当前验证数据库
## 实施结果
- 第二期规则元数据已与真实后端实现对齐。
- 新增测试可在仓库层拦住“第二期初始化脚本已改、迁移脚本漏补”的回归。
- 当前数据库中的第二期规则不再继续保留占位 `remark`、旧 `indicator_code` 和乱码业务口径。

View File

@@ -0,0 +1,33 @@
# 第二期银行流水规则元数据修复验证记录
## 执行命令
```bash
mvn test -pl ccdi-project -Dtest=CcdiBankTagRuleSqlMetadataTest
bin/mysql_utf8_exec.sh sql/migration/2026-03-20-sync-bank-tag-phase2-rule-metadata.sql
python3 - <<'PY'
# 查询 ccdi_bank_tag_rule 第二期 10 条规则的 indicator_code、business_caliber、remark
PY
mvn test -pl ccdi-project -Dtest=CcdiBankTagRuleSqlMetadataTest,CcdiBankTagAnalysisMapperXmlTest,BankTagRuleConfigResolverTest,CcdiBankTagServiceImplTest,CcdiBankTagServiceRiskCountRefreshTest,CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest
```
## 执行时间
- 2026-03-20 16:29 执行 `mvn test -pl ccdi-project -Dtest=CcdiBankTagRuleSqlMetadataTest` 红灯验证,确认第二期迁移脚本缺失,测试报 `NoSuchFileException`
- 2026-03-20 16:30 重新执行 `mvn test -pl ccdi-project -Dtest=CcdiBankTagRuleSqlMetadataTest`,结果 `BUILD SUCCESS``Tests run: 2, Failures: 0, Errors: 0, Skipped: 0`
- 2026-03-20 16:30 执行 `bin/mysql_utf8_exec.sh sql/migration/2026-03-20-sync-bank-tag-phase2-rule-metadata.sql`,脚本落库成功,无报错、无乱码输出。
- 2026-03-20 16:30 查询 `ccdi_bank_tag_rule` 第二期 10 条规则元数据,确认数据库已与真实规则状态对齐。
- 2026-03-20 16:30 执行最终回归命令 `mvn test -pl ccdi-project -Dtest=CcdiBankTagRuleSqlMetadataTest,CcdiBankTagAnalysisMapperXmlTest,BankTagRuleConfigResolverTest,CcdiBankTagServiceImplTest,CcdiBankTagServiceRiskCountRefreshTest,CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest`,结果 `BUILD SUCCESS``Tests run: 49, Failures: 0, Errors: 0, Skipped: 0`,完成时间 `2026-03-20T16:30:53+08:00`
## 结果摘要
- 已补第二期元数据迁移脚本与 SQL 校验测试。
- 修复后已确认:
- 第二期 10 条规则 `remark` 均已变为真实规则说明
- `FIXED_COUNTERPARTY_TRANSFER.indicator_code` 已变为 `NULL`
- `SALARY_UNUSED.business_caliber` 乱码已修复为“工资发放后除代扣项目外连续30天无消费或转账支出记录。”
- `TAX_ASSET_REGISTRATION_MISMATCH.business_caliber` 已更新为“员工及关系人有5000元以上的纳税记录但当前资产登记口径下无房产登记。”
- `MONTHLY_FIXED_INCOME.indicator_code` 继续保持 `MONTHLY_FIXED_INCOME`
- 测试日志中的 `threshold missing``refresh failed` 为既有异常路径断言产生的预期日志,不代表最终回归失败。
## 结论
- 第二期规则元数据修复已完成,仓库脚本与当前数据库均已对齐到真实规则状态。
- 当前验证仅执行 Maven 单元测试与数据库只读复核,未启动额外前后端进程,因此无需执行进程清理。

View File

@@ -0,0 +1,41 @@
START TRANSACTION;
INSERT INTO ccdi_bank_tag_rule (
model_code,
model_name,
rule_code,
rule_name,
indicator_code,
result_type,
risk_level,
business_caliber,
enabled,
sort_order,
create_by,
remark
) VALUES
('ABNORMAL_TRANSACTION', '异常交易', 'LOW_INCOME_RELATIVE_LARGE_TRANSACTION', '低收入亲属大额交易', NULL, 'OBJECT', 'GENERAL', '关系人中没有收入或月收入低于3000元的人员累计交易金额超过10万元。', 1, 20, 'system', '真实规则识别低收入关系人累计交易超10万元的员工对象'),
('SUSPICIOUS_GAMBLING', '疑似赌博', 'MULTI_PARTY_GAMBLING_TRANSFER', '疑似赌博交易', NULL, 'OBJECT', 'HIGH', '多人2人及以上、多次2次以上、相近时间同一天有转账、微信转账、支付宝转账发生且额度在可疑区间。金额区间可在排查设置页面进行设置', 1, 10, 'system', '真实规则:识别同日多对手方且金额落在可疑区间的疑似赌博对象'),
('SUSPICIOUS_PART_TIME', '可疑兼职', 'MONTHLY_FIXED_INCOME', '疑似兼职', 'MONTHLY_FIXED_INCOME', 'OBJECT', NULL, '除本行工资收入外,每月有固定收入,固定收入金额自行设置。', 1, 10, 'system', '真实规则识别近12个月持续出现稳定月度非工资收入的员工对象'),
('SUSPICIOUS_PART_TIME', '可疑兼职', 'FIXED_COUNTERPARTY_TRANSFER', '疑似兼职', NULL, 'OBJECT', NULL, '每季或每年从固定交易对手转入金额金额可设区间值如5000-10000。', 1, 20, 'system', '真实规则:识别固定交易对手季度转入金额落在设定区间的员工对象'),
('SUSPICIOUS_PROPERTY', '可疑财产', 'HOUSE_REGISTRATION_MISMATCH', '购房交易与房产登记不匹配', NULL, 'STATEMENT', NULL, '员工及关系人有购房交易,但名下房产无新增登记;有新增登记购房,但无相关购房交易记录。', 1, 10, 'system', '真实规则:识别购房支出但当前房产登记口径缺失的流水'),
('SUSPICIOUS_PROPERTY', '可疑财产', 'PROPERTY_FEE_REGISTRATION_MISMATCH', '物业缴费与房产登记不匹配', NULL, 'STATEMENT', NULL, '员工及关系人有物业缴费记录,但名下房产无新增登记。', 1, 20, 'system', '真实规则:识别物业缴费但当前房产登记口径缺失的流水'),
('SUSPICIOUS_PROPERTY', '可疑财产', 'TAX_ASSET_REGISTRATION_MISMATCH', '大额纳税与资产登记不匹配', NULL, 'STATEMENT', NULL, '员工及关系人有5000元以上的纳税记录但当前资产登记口径下无房产登记。', 1, 30, 'system', '真实规则:识别大额纳税但当前房产登记口径缺失的流水'),
('SUSPICIOUS_PURCHASE', '可疑采购', 'SUPPLIER_CONCENTRATION', '可疑采购', NULL, 'OBJECT', NULL, '单个供应商采购额占总采购额比例超过70%。', 1, 20, 'system', '真实规则识别单个供应商采购额占比超过70%的员工对象'),
('ABNORMAL_BEHAVIOR', '异常行为', 'SALARY_QUICK_TRANSFER', '工资快速转出', NULL, 'OBJECT', NULL, '工资发放后24小时内转出超过80%的资金。', 1, 40, 'system', '真实规则识别工资入账24小时内快速转出的员工对象'),
('ABNORMAL_BEHAVIOR', '异常行为', 'SALARY_UNUSED', '工资无使用记录', NULL, 'OBJECT', NULL, '工资发放后除代扣项目外连续30天无消费或转账支出记录。', 1, 50, 'system', '真实规则识别工资入账后30天内无消费或转账支出的员工对象')
ON DUPLICATE KEY UPDATE
model_code = VALUES(model_code),
model_name = VALUES(model_name),
rule_name = VALUES(rule_name),
indicator_code = VALUES(indicator_code),
result_type = VALUES(result_type),
risk_level = VALUES(risk_level),
business_caliber = VALUES(business_caliber),
enabled = VALUES(enabled),
sort_order = VALUES(sort_order),
update_by = 'system',
update_time = NOW(),
remark = VALUES(remark);
COMMIT;