diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java index 3b708fc1..5743cd83 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiAbnormalAccountRuleSqlMetadataTest.java @@ -27,4 +27,21 @@ class CcdiAbnormalAccountRuleSqlMetadataTest { () -> assertTrue(sql.contains("'OBJECT'")) ); } + + @Test + void abnormalAccountMetadataSql_shouldContainAccountInfoTableDefinition() throws IOException { + Path path = Path.of("..", "sql", "migration", + "2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql"); + + assertTrue(Files.exists(path), "异常账户模型迁移脚本应存在"); + + String sql = Files.readString(path, StandardCharsets.UTF_8).toLowerCase(); + assertAll( + () -> 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`")) + ); + } } diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java index 22239681..66300f3c 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/sql/CcdiBankTagRuleSqlMetadataTest.java @@ -30,6 +30,23 @@ class CcdiBankTagRuleSqlMetadataTest { assertPhase2Metadata(migrationSql); } + @Test + void abnormalAccountMetadataSql_shouldContainBusinessCaliberAndRuleRemark() throws IOException { + String migrationSql = readProjectFile("sql", "migration", + "2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql"); + + assertAll( + () -> assertTrue(migrationSql.contains("员工本人账户已销户,且销户日前30天内仍存在交易记录。"), + "SUDDEN_ACCOUNT_CLOSURE 应使用设计文档中的业务口径"), + () -> assertTrue(migrationSql.contains("员工本人账户开户后长期未使用,首次启用后出现大额资金流动。"), + "DORMANT_ACCOUNT_LARGE_ACTIVATION 应使用设计文档中的业务口径"), + () -> assertTrue(migrationSql.contains("真实规则:识别员工本人账户销户前30天内仍有交易的员工对象"), + "SUDDEN_ACCOUNT_CLOSURE 应同步真实规则说明"), + () -> assertTrue(migrationSql.contains("真实规则:识别长期休眠后首次启用即出现大额资金流动的员工对象"), + "DORMANT_ACCOUNT_LARGE_ACTIVATION 应同步真实规则说明") + ); + } + private void assertPhase1Metadata(String sqlContent) { assertAll( () -> assertTrue(sqlContent.contains("'FOREX_BUY_AMT'") diff --git a/sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql b/sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql index bae497e4..9b12d2ef 100644 --- a/sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql +++ b/sql/migration/2026-03-31-create-ccdi-account-info-and-abnormal-account-rules.sql @@ -1,5 +1,36 @@ START TRANSACTION; +CREATE TABLE IF NOT EXISTS `ccdi_account_info` ( + `account_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `account_no` VARCHAR(240) NOT NULL COMMENT '账号', + `account_type` VARCHAR(64) DEFAULT NULL COMMENT '账户类型', + `account_name` VARCHAR(128) DEFAULT NULL COMMENT '账户名称', + `owner_type` VARCHAR(32) NOT NULL COMMENT '归属人类型', + `owner_id` VARCHAR(64) NOT NULL COMMENT '归属人标识', + `bank` VARCHAR(128) DEFAULT NULL COMMENT '开户行', + `bank_code` VARCHAR(64) DEFAULT NULL COMMENT '开户行编码', + `currency` VARCHAR(32) DEFAULT NULL COMMENT '币种', + `is_self_account` TINYINT DEFAULT 1 COMMENT '是否本人账户', + `monthly_avg_trans_count` DECIMAL(18, 2) DEFAULT NULL COMMENT '月均交易笔数', + `monthly_avg_trans_amount` DECIMAL(18, 2) DEFAULT NULL COMMENT '月均交易金额', + `trans_freq_type` VARCHAR(32) DEFAULT NULL COMMENT '交易频率类型', + `dr_max_single_amount` DECIMAL(18, 2) DEFAULT NULL COMMENT '最大单笔支出金额', + `cr_max_single_amount` DECIMAL(18, 2) DEFAULT NULL COMMENT '最大单笔收入金额', + `dr_max_daily_amount` DECIMAL(18, 2) DEFAULT NULL COMMENT '最大单日支出金额', + `cr_max_daily_amount` DECIMAL(18, 2) DEFAULT NULL COMMENT '最大单日收入金额', + `trans_risk_level` VARCHAR(32) DEFAULT NULL COMMENT '交易风险等级', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '账户状态:1正常,2已销户', + `effective_date` DATE DEFAULT NULL COMMENT '开户日期', + `invalid_date` DATE DEFAULT NULL COMMENT '销户日期', + `create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建者', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新者', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`account_id`), + KEY `idx_ccdi_account_info_owner` (`owner_type`, `owner_id`), + KEY `idx_ccdi_account_info_account_no` (`account_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工账户信息表'; + INSERT INTO ccdi_bank_tag_rule ( model_code, model_name, @@ -16,10 +47,10 @@ INSERT INTO ccdi_bank_tag_rule ( ) VALUES ('ABNORMAL_ACCOUNT', '异常账户', 'SUDDEN_ACCOUNT_CLOSURE', '突然销户', NULL, 'OBJECT', 'HIGH', '员工本人账户已销户,且销户日前30天内仍存在交易记录。', 1, 10, 'system', - '异常账户模型规则骨架,后续补充建表与完整业务口径'), + '真实规则:识别员工本人账户销户前30天内仍有交易的员工对象'), ('ABNORMAL_ACCOUNT', '异常账户', 'DORMANT_ACCOUNT_LARGE_ACTIVATION', '休眠账户大额启用', NULL, 'OBJECT', 'HIGH', '员工本人账户开户后长期未使用,首次启用后出现大额资金流动。', 1, 20, 'system', - '异常账户模型规则骨架,后续补充建表与完整业务口径') + '真实规则:识别长期休眠后首次启用即出现大额资金流动的员工对象') ON DUPLICATE KEY UPDATE model_code = VALUES(model_code), model_name = VALUES(model_name),