Compare commits
4 Commits
33ba21e36a
...
9791dab67e
| Author | SHA1 | Date | |
|---|---|---|---|
| 9791dab67e | |||
| 1d7692e9c3 | |||
| a4a33bdd35 | |||
| f2945e7b2d |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -62,6 +62,8 @@ __pycache__/
|
||||
|
||||
######################################################################
|
||||
# Excel Temporary Files
|
||||
~$*
|
||||
**/~$*
|
||||
doc/test-data/**/~$*
|
||||
|
||||
######################################################################
|
||||
@@ -77,4 +79,4 @@ output/
|
||||
|
||||
logs/
|
||||
|
||||
ruoyi-admin/src/main/resources/logback.xml
|
||||
.DS_Store
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
- 使用简体中文进行思考和对话
|
||||
- Git 提交说明使用中文
|
||||
- Git 提交前必须检查暂存区,仅允许包含本次任务相关文件
|
||||
- 若暂存区存在无关文件,必须先移出暂存或与用户确认,禁止顺带提交
|
||||
- 根据设计文档产出实施计划时,默认输出两份文档:
|
||||
- 后端实施计划放 `docs/plans/backend/`
|
||||
- 前端实施计划放 `docs/plans/frontend/`
|
||||
|
||||
BIN
assets/~$模型信息.xlsx
Normal file
BIN
assets/~$模型信息.xlsx
Normal file
Binary file not shown.
34
assets/模型信息.csv
Normal file
34
assets/模型信息.csv
Normal file
@@ -0,0 +1,34 @@
|
||||
序号,模型名称,核心异常点(展示在前端页面),业务口径,相关指标,指标英文名,风险筛查对象,技术口径,代码,限制阈值指标,可疑结果返回,风险等级
|
||||
1.1,大额交易,房车消费支出交易,备注或对交易对手是房产公司、二手房、车辆销售公司、物业公司等。,购买车房支出金额,prop_exp_amt,员工本人及亲属,关联员工及其亲属所有账户(ccdi_account_info 关联 ccdi_fmy_relation_person),在 ccdi_bank_statement 中筛选 amount_dr>0 且对手方/摘要含房产/车产关键词,,/,流水明细,一般
|
||||
1.2,大额交易,税务支出交易,有税务支出记录,税务支出金额,tax_exp_amt,员工本人及亲属,员工及其亲属账户中,筛选 amount_dr>0 且摘要含税务关键词,,/,流水明细,一般
|
||||
1.3,大额交易,大额单笔收入,同一交易对手(除本人、家庭成员外、本单位代发工资)单笔超过设置限额超过设置限额的资金流入;,大额流入金额(单笔),SINGLE_TRANSACTION_AMOUNT,员工本人,员工账户中,筛选 amount_cr>0,对手方名称不在该员工的家庭关系内,排除工资代发,按员工和对手方汇总金额,判断单笔是否超限,,大额流入金额,流水明细,一般
|
||||
新增,大额交易,累计收入超限,同一交易对手(除本人、家庭成员外、本单位代发工资)累计交易金额超过设置限额的资金流入;,累计流入金额(所有累计),CUMULATIVE_TRANSACTION_AMOUNT,员工本人,员工账户中,筛选 amount_cr>0,对手方名称不在该员工的家庭关系内,排除工资代发,按员工和对手方汇总金额,判断累计是否超限,,累计流入金额,个人、累积金额,一般
|
||||
1.4,大额交易,年流水交易额超限,年流水交易额超过设置限额,年交易金额,annual_turnover,员工本人,员工账户中,排除本人及亲属名称,统计一年内 amount_cr+amount_dr 总额,,年交易金额,个人、累积金额,一般
|
||||
1.5,大额交易,大额存现交易,大额存现,单笔超过设置限额;,大额存现金额(单笔),LARGE_CASH_DEPOSIT,员工本人,员工及其亲属账户中,筛选 现金存入,且单笔 amount_cr 超阈值,按员工汇总,,大额存现金额,流水明细,一般
|
||||
,大额交易,短时间多次存现,短时间多次存现,次数超过设置限额,单日存现总次数,FREQUENT_CASH_DEPOSIT,员工本人,员工及其亲属账户中,按日统计现金存入次数超阈值,,单日存现总次数,个人、日期、次数,一般
|
||||
1.6,大额交易,大额转账交易,大额转账单笔超过设置限额,大额转账金额(单笔),large_tfr_cnt,员工本人,员工及其亲属账户中,筛选单笔 amount_dr 超金额阈值的数据,,大额转账金额,流水明细,一般
|
||||
2.1,异常交易,与客户之间非正常资金往来,员工及关系人与客户及关系人之间有超过1000元以上的资金往来;客户指信贷类客户包括贷款户、担保人,中介库人员,包括中介注册的主体及主体关系人。,与特定客户交易总额,,员工本人及亲属、贷款客户、担保人、中介,,,/,流水明细,高风险
|
||||
2.2,异常交易,低收入亲属大额交易,关系人中没有收入或月收入低于3000元的人员,累计交易金额超过10万元。,低收入亲属的累计交易额,,员工亲属,关联员工及其亲属,筛选平均月收入<3000的亲属的账户,在流水中单笔或累计 >10 万元,按员工汇总,,/,个人、累积金额,一般
|
||||
3.1,疑似赌博,疑似赌博交易,多人(2人及以上)、多次(2次以上)、相近时间(同一天)内,有转账、微信转账、支付宝转账发生,且额度在可疑区间。金额区间可在排查设置页面进行设置,单日疑似赌博转账金额下限、单日疑似赌博转账金额上限,,员工本人,员工本人账户中,24h内统计向不同对手方转账(cash_type='转账')且金额在可疑区间,笔数≥2且对手方≥2,,单日频繁转账金额下限,个人、日期、累计金额,高风险
|
||||
3.2,疑似赌博,疑似敏感交易,备注或交易摘要、对手有“游戏、抖币、体彩、福彩”等字眼。,赌博关键词支出,,员工本人,员工本人账户中,筛选备注或交易摘要、对手方名称有“游戏、抖币、体彩、福彩”等字眼关键词,汇总金额,,/,流水明细,高风险
|
||||
4,可疑关系,特殊金额交易,除与配偶、子女外,发生特殊金额交易,如1314元、520元等具有特殊含义的金额。,特殊金额交易,,员工本人,员工本人账户中,筛选交易金额匹配特殊值(如1314),且对手方名称非配偶/子女,统计笔数,,/,流水明细,
|
||||
5.1,可疑兼职,疑似兼职,除本行工资收入外,每月有固定收入,固定收入金额自行设置。,月度非本行工资收入金额,MONTHLY_FIXED_INCOME,员工本人,员工本人账户中,筛选 amount_cr>0,除本行工资收入外,每月有固定收入,固定收入金额自行设置。,,非本行工资收入金额,个人、累积金额,
|
||||
5.2,可疑兼职,疑似兼职,每季或每年从固定交易对手转入金额,金额可设区间值,如5000-10000。,季度稳定收入金额,FIXED_COUNTERPARTY_TRANSFER,员工本人,员工本人账户中,按季/年统计同一对手方累计流入金额在设定区间内,,稳定季度收入金额,个人、累积金额,
|
||||
,可疑兼职,疑似兼职,转入资金摘要有“工资”、“分红”、“红利”、“利息(非银行结息)”等收入,/,,员工本人,,,/,流水明细,高风险
|
||||
6.1,可疑财产,购房交易与房产登记不匹配,员工及关系人有购房交易,但名下房产无新增登记;有新增登记购房,但无相关购房交易记录。,/,,员工本人及亲属,员工及其亲属账户中,存在购房支出,且在家庭负债表(ccdi_family_liability)中该员工名下无新增房产登记,则标记1,,/,流水明细,
|
||||
6.2,可疑财产,物业缴费与房产登记不匹配,员工及关系人有物业缴费记录,但名下房产无新增登记。,/,,员工本人及亲属,员工及其亲属账户中,存在物业费支出,且在家庭负债表中无新增房产登记,则标记1,,/,流水明细,
|
||||
6.3,可疑财产,大额纳税与资产登记不匹配,员工及关系人有5000元以上的纳税记录,但名下无房产车产新增登记。,/,,员工本人及亲属,员工及其亲属账户中,存在单笔纳税 >5000 元,且在家庭负债表中无新增房产/车产登记,则标记1,,/,流水明细,
|
||||
,可疑财产,收入资产不符,豪华房产价值超家庭年收入10倍,/,,员工本人及亲属,关联家庭负债表中的房产和家庭年收入,计算比值 >10,,/,流水明细,高风险
|
||||
7,可疑外汇交易,可疑外汇交易,单笔购汇金额超限,单笔购汇金额,forex_buy_amt,员工本人,员工本人账户中,筛选 cash_type = '购汇',单笔 amount_dr 超过阈值,按员工 id_card 汇总,,单笔购汇金额,流水明细,
|
||||
,可疑外汇交易,可疑外汇交易,单笔结汇金额超限,单笔结汇金额,forex_sell_amt,员工本人,员工本人账户中,筛选 cash_type = '结汇',单笔 amount_cr 超过阈值,按员工 id_card 汇总,,单笔结汇金额,流水明细,
|
||||
,可疑外汇交易,可疑外汇交易,单笔跨境汇款金额超限,跨境汇款金额,cross_border_amt,员工本人,员工本人账户中,筛选跨境汇款中,单笔 amount_dr 超过阈值,按员工 id_card 汇总,,跨境汇款金额,流水明细,
|
||||
8,可疑付息,可疑付息,客户经理管户的客户在智柜、柜面连续代交利息,且代交人数超过2人。,代交利息客户数,,员工本人,统计客户经理名下管户的客户,在智柜、柜面连续由他人代交贷款利息的客户数(去重),,/,个人,高风险
|
||||
9.1,可疑采购,可疑采购,单笔采购金额超过10万元。,单笔大额采购金额,,员工本人,关联采购表ccdi_purchase_transaction中员工经手的采购,筛选 amount_dr>10万 且对手方/摘要含采购关键词,记录单笔金额,,/,流水明细,
|
||||
9.2,可疑采购,可疑采购,单个供应商采购额占总采购额比例超过70%。,供应商集中度,,员工本人,关联采购表ccdi_purchase_transaction中,计算员工本人向同一对手方采购支出占总采购支出比例 >70%,,/,个人,
|
||||
10.1,异常行为,可疑银证大额转账,家庭老人/非关系人银证大额转账,银证转账大额金额,stock_tfr_large,员工本人,员工及其亲属账户中,筛选 cash_type='银证转账',非亲属,单笔金额超阈值,,银证转账大额金额,流水明细,
|
||||
10.2,异常行为,微信支付宝频繁提现,微信、支付宝单日提现次数超过设置次数,微信、支付宝单日提现次数,withdraw_cnt,员工本人,员工本人账户中,统计微信/支付宝提现单日次数超阈值,,微信、支付宝单日提现次数,个人,
|
||||
,异常行为,微信支付宝提现超额,微信、支付宝单日累计提现金额超过限额,微信、支付宝单日提现金额,withdraw_amt,员工本人,同上,统计单日累计提现金额超阈值,,微信、支付宝单日提现金额,个人,
|
||||
10.3,异常行为,工资快速转出,工资发放后24小时内转出超过80%的资金,工资后转出比例,,员工本人,员工本人账户中,工资入账后24h内转出金额/工资额 >80%,,/,个人,
|
||||
,异常行为,工资无使用记录,工资发放后除代扣项目外,连续30天犖奕魏蜗<EFBFBD>鸦蜃<EFBFBD>思锹肌<EFBFBD>,,,,,,/,个人,
|
||||
,异常行为,大额炒股,单次三方资管交易金额超过100万元。,大额炒股交易,,员工本人,员工本人账户中,筛选 三方资管,单笔 >100万,,/,流水明细,高风险
|
||||
,异常行为,疑似代理他人账户,,,,,同一员工操作不同客户账户的次数超阈值(多次代理他人账户交易,或登录员工手机操作他人丰收互联交易),,/,个人,
|
||||
|
BIN
assets/模型信息.xlsx
Normal file
BIN
assets/模型信息.xlsx
Normal file
Binary file not shown.
@@ -10,7 +10,7 @@
|
||||
- 规则执行入口 `CcdiBankTagServiceImpl`
|
||||
- 技术口径 SQL `CcdiBankTagAnalysisMapper.xml`
|
||||
|
||||
现阶段仅落地了“大额交易”模型下的 8 条规则。根据 [assets/模型信息.xlsx](../../../assets/模型信息.xlsx),其余模型规则尚未补齐到现有打标框架中,导致规则元数据、执行分发、XML SQL 坑位与参数配置之间不完整。
|
||||
现阶段仅落地了“大额交易”模型下的 8 条规则。根据 [assets/模型信息.xlsx](../../assets/模型信息.xlsx),其余模型规则尚未补齐到现有打标框架中,导致规则元数据、执行分发、XML SQL 坑位与参数配置之间不完整。
|
||||
|
||||
本次需求是基于现有银行流水模型打标能力,补齐尚未添加的模型规则,并自动填充必要字段。由于这些规则的正式 SQL 尚未准备好,本期先在 XML 中预留规则级 SQL 位置,并使用恒不命中的假 SQL 保证执行时不报错。
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
# Bank Tag Placeholder Models Backend 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.
|
||||
|
||||
**Goal:** 根据 `assets/模型信息.xlsx` 补齐现有银行流水打标缺失模型的后端规则骨架,让新增规则可以进入统一打标流程,并通过 XML 占位 SQL 保证执行不报错且不产生伪命中结果。
|
||||
|
||||
**Architecture:** 继续沿用现有 `ccdi_bank_tag_rule + CcdiBankTagServiceImpl + CcdiBankTagAnalysisMapper.xml` 的规则式架构,不引入动态 SQL。新增规则只补“规则定义 + 分发入口 + XML 独立占位查询”,真实业务 SQL 后续逐条替换;本期不改现有 8 条大额交易规则逻辑。
|
||||
|
||||
**Tech Stack:** Java 21, Spring Boot 3, MyBatis Plus, MySQL, Maven, JUnit 5, Mockito
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 补齐规则清单与初始化 SQL
|
||||
|
||||
**Files:**
|
||||
- Reference: `assets/模型信息.xlsx`
|
||||
- Reference: `docs/design/2026-03-18-bank-tag-model-placeholder-design.md`
|
||||
- Modify: `sql/2026-03-16-bank-tagging.sql`
|
||||
|
||||
- [ ] **Step 1: 盘点缺失规则**
|
||||
|
||||
对照 `assets/模型信息.xlsx` 与 `sql/2026-03-16-bank-tagging.sql`,列出当前未落库的 25 条规则,确认每条规则的:
|
||||
|
||||
- `model_code`
|
||||
- `model_name`
|
||||
- `rule_code`
|
||||
- `rule_name`
|
||||
- `indicator_code`
|
||||
- `result_type`
|
||||
- `risk_level`
|
||||
- `business_caliber`
|
||||
- `sort_order`
|
||||
|
||||
- [ ] **Step 2: 写失败前检查**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
rg -n "ABNORMAL_TRANSACTION|SUSPICIOUS_RELATION|SUSPICIOUS_PROPERTY|SUSPICIOUS_PURCHASE|SUSPICIOUS_INTEREST_PAYMENT" sql/2026-03-16-bank-tagging.sql
|
||||
```
|
||||
|
||||
预期:当前命中为空或明显不完整,说明缺失模型尚未写入初始化 SQL。
|
||||
|
||||
- [ ] **Step 3: 更新规则初始化数据**
|
||||
|
||||
在 `sql/2026-03-16-bank-tagging.sql` 中:
|
||||
|
||||
- 保留现有 8 条 `LARGE_TRANSACTION` 规则不变
|
||||
- 按设计稿补齐其余 25 条规则的 `INSERT INTO ccdi_bank_tag_rule`
|
||||
- `rule_code` 生成规则遵循设计稿:
|
||||
- 优先复用 Excel 已有英文指标名
|
||||
- 无英文指标名时使用 `<MODEL_CODE>_<两位序号>` 占位编码
|
||||
- `result_type` 按“流水明细/个人”等返回形式映射为 `STATEMENT` 或 `OBJECT`
|
||||
- `risk_level` 映射为 `HIGH`、`GENERAL` 或 `NULL`
|
||||
- `remark` 统一写明“占位规则,待补充真实SQL”
|
||||
|
||||
- [ ] **Step 4: 静态自检**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
sed -n '1,220p' sql/2026-03-16-bank-tagging.sql
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- 所有 10 个模型组均已出现
|
||||
- 现有 8 条规则仍在
|
||||
- 新增规则字段完整,没有手工遗漏列值
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add sql/2026-03-16-bank-tagging.sql
|
||||
git commit -m "补齐流水打标规则初始化数据"
|
||||
```
|
||||
|
||||
### Task 2: 为缺失规则补齐 Mapper 接口与 XML 占位 SQL
|
||||
|
||||
**Files:**
|
||||
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapper.java`
|
||||
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml`
|
||||
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.java`
|
||||
|
||||
- [ ] **Step 1: 写失败前测试**
|
||||
|
||||
先在 `CcdiBankTagAnalysisMapperXmlTest.java` 增加两个失败测试,示例结构如下:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void allPlaceholderRules_shouldExistInAnalysisMapperXml() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
assertTrue(xml.contains("selectAbnormalTransactionStatements"));
|
||||
assertTrue(xml.contains("selectSuspiciousPropertyObjects"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeholderRules_shouldUseEmptyResultSqlTemplate() throws Exception {
|
||||
String xml = readXml(RESOURCE);
|
||||
assertTrue(xml.contains("where 1 = 0"));
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试确认失败**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest#allPlaceholderRules_shouldExistInAnalysisMapperXml,CcdiBankTagAnalysisMapperXmlTest#placeholderRules_shouldUseEmptyResultSqlTemplate
|
||||
```
|
||||
|
||||
预期:FAIL,提示新规则方法或占位 SQL 尚未存在。
|
||||
|
||||
- [ ] **Step 3: 增加 Mapper 方法签名**
|
||||
|
||||
在 `CcdiBankTagAnalysisMapper.java` 中为 25 条缺失规则补方法签名,要求:
|
||||
|
||||
- `STATEMENT` 规则返回 `List<BankTagStatementHitVO>`
|
||||
- `OBJECT` 规则返回 `List<BankTagObjectHitVO>`
|
||||
- 方法命名与 `rule_code` 语义或占位编码一一对应
|
||||
- 暂不为占位规则增加阈值参数
|
||||
|
||||
- [ ] **Step 4: 增加 XML 占位 SQL**
|
||||
|
||||
在 `CcdiBankTagAnalysisMapper.xml` 中为每条缺失规则新增独立 `<select>`:
|
||||
|
||||
- `STATEMENT` 规则使用 `bankStatementId/groupId/logId/reasonDetail` 四列
|
||||
- `OBJECT` 规则使用 `objectType/objectKey/reasonDetail` 三列
|
||||
- 所有占位 SQL 都必须 `where 1 = 0`
|
||||
- 每条 SQL 单独保留坑位,不复用通用 `select`
|
||||
|
||||
参考占位结构:
|
||||
|
||||
```xml
|
||||
<select id="selectSuspiciousPurchaseStatements" resultMap="BankTagStatementHitResultMap">
|
||||
select
|
||||
bs.bank_statement_id AS bankStatementId,
|
||||
bs.group_id AS groupId,
|
||||
bs.batch_id AS logId,
|
||||
'占位SQL,待补充真实规则' AS reasonDetail
|
||||
from ccdi_bank_statement bs
|
||||
where 1 = 0
|
||||
</select>
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 重新运行 XML 测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest
|
||||
```
|
||||
|
||||
预期:PASS,XML 结构完整、方法名齐全、文档仍然是合法 MyBatis XML。
|
||||
|
||||
- [ ] **Step 6: 提交**
|
||||
|
||||
```bash
|
||||
git add 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/mapper/CcdiBankTagAnalysisMapperXmlTest.java
|
||||
git commit -m "补齐流水打标占位分析SQL"
|
||||
```
|
||||
|
||||
### Task 3: 补齐 Service 规则分发并保证空结果任务可成功结束
|
||||
|
||||
**Files:**
|
||||
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java`
|
||||
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java`
|
||||
|
||||
- [ ] **Step 1: 写失败前测试**
|
||||
|
||||
在 `CcdiBankTagServiceImplTest.java` 中增加两个代表性测试:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void rebuildProject_shouldDispatchPlaceholderStatementRuleAndFinishWithoutResults() {
|
||||
// 规则 resultType=STATEMENT
|
||||
// mock analysisMapper.selectXxx() 返回 List.of()
|
||||
// 断言 deleteByProjectAndModel 被调用
|
||||
// 断言 insertBatch 不会被调用
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildProject_shouldDispatchPlaceholderObjectRuleAndFinishWithoutResults() {
|
||||
// 规则 resultType=OBJECT
|
||||
// mock analysisMapper.selectXxx() 返回 List.of()
|
||||
// 断言任务状态最终更新为 SUCCESS
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试确认失败**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=CcdiBankTagServiceImplTest#rebuildProject_shouldDispatchPlaceholderStatementRuleAndFinishWithoutResults,CcdiBankTagServiceImplTest#rebuildProject_shouldDispatchPlaceholderObjectRuleAndFinishWithoutResults
|
||||
```
|
||||
|
||||
预期:FAIL,当前 `switch` 还未覆盖新增规则。
|
||||
|
||||
- [ ] **Step 3: 补齐规则分发**
|
||||
|
||||
在 `CcdiBankTagServiceImpl.java` 中:
|
||||
|
||||
- 在 `executeStatementRule` 中补齐所有新增 `STATEMENT` 规则的分发
|
||||
- 在 `executeObjectRule` 中补齐所有新增 `OBJECT` 规则的分发
|
||||
- 新增规则统一调用对应占位 Mapper 方法
|
||||
- 保持现有 8 条大额交易规则的调用方式不变
|
||||
|
||||
- [ ] **Step 4: 重新运行服务测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=CcdiBankTagServiceImplTest
|
||||
```
|
||||
|
||||
预期:PASS,新增占位规则不会报错,空结果任务仍能正常完成。
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java
|
||||
git commit -m "补齐流水打标占位规则分发"
|
||||
```
|
||||
|
||||
### Task 4: 固化占位规则的参数解析边界
|
||||
|
||||
**Files:**
|
||||
- Modify if needed: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java`
|
||||
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java`
|
||||
- Reference: `sql/ccdi_model_param.sql`
|
||||
- Reference: `sql/2026-03-16-update-ccdi-model-param-defaults.sql`
|
||||
|
||||
- [ ] **Step 1: 先写边界测试**
|
||||
|
||||
在 `BankTagRuleConfigResolverTest.java` 中增加一个回归测试,确认占位规则即便没有参数映射也能安全返回:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void resolve_shouldReturnEmptyThresholdsForPlaceholderRulesWithoutIndicatorCode() {
|
||||
// ruleCode 使用新增占位规则
|
||||
// indicatorCode 为空
|
||||
// 断言 thresholdValues 为空 map
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试确认当前边界**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=BankTagRuleConfigResolverTest#resolve_shouldReturnEmptyThresholdsForPlaceholderRulesWithoutIndicatorCode
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- 若当前实现已满足,测试直接 PASS
|
||||
- 若实现依赖了不安全的默认逻辑,则先 FAIL,再做最小修正
|
||||
|
||||
- [ ] **Step 3: 做最小实现**
|
||||
|
||||
遵循最小原则:
|
||||
|
||||
- 不为占位规则新增 `RULE_PARAM_MAPPING`
|
||||
- 不修改现有大额交易参数映射
|
||||
- 只有在测试暴露不安全行为时,才对 `BankTagRuleConfigResolver.java` 做最小修补
|
||||
- 本期不改 `sql/ccdi_model_param.sql` 和 `sql/2026-03-16-update-ccdi-model-param-defaults.sql`,避免在无真实 SQL 的前提下引入无效默认参数
|
||||
|
||||
- [ ] **Step 4: 运行解析器测试**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=BankTagRuleConfigResolverTest
|
||||
```
|
||||
|
||||
预期:PASS,现有阈值规则和新增占位规则都能安全解析。
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java
|
||||
git commit -m "明确占位规则参数解析边界"
|
||||
```
|
||||
|
||||
### Task 5: 做全量编译与交付前验证
|
||||
|
||||
**Files:**
|
||||
- Reference: `sql/2026-03-16-bank-tagging.sql`
|
||||
- Reference: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankTagAnalysisMapper.xml`
|
||||
- Optional Record: `docs/tests/records/bank-tag-placeholder-backend-test.md`
|
||||
|
||||
- [ ] **Step 1: 运行后端核心测试集**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn test -pl ccdi-project -am -Dtest=CcdiBankTagAnalysisMapperXmlTest,BankTagRuleConfigResolverTest,CcdiBankTagServiceImplTest
|
||||
```
|
||||
|
||||
预期:PASS。
|
||||
|
||||
- [ ] **Step 2: 运行模块编译**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
mvn -pl ccdi-project -am -DskipTests compile
|
||||
```
|
||||
|
||||
预期:BUILD SUCCESS。
|
||||
|
||||
- [ ] **Step 3: 如需落库验证,使用 UTF-8 执行脚本**
|
||||
|
||||
如需在本地库验证中文 SQL 脚本,必须运行:
|
||||
|
||||
```bash
|
||||
bin/mysql_utf8_exec.sh sql/2026-03-16-bank-tagging.sql
|
||||
```
|
||||
|
||||
预期:脚本执行成功,中文模型名与规则名不乱码。
|
||||
|
||||
- [ ] **Step 4: 做最终 diff 自检**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
git diff -- .gitignore sql/2026-03-16-bank-tagging.sql 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/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.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/BankTagRuleConfigResolverTest.java
|
||||
```
|
||||
|
||||
预期:diff 仅包含本次规则补齐与占位实现,没有顺手改出无关噪音。
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add .gitignore sql/2026-03-16-bank-tagging.sql 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/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiBankTagAnalysisMapperXmlTest.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/BankTagRuleConfigResolverTest.java
|
||||
git commit -m "补齐银行流水模型占位规则"
|
||||
```
|
||||
@@ -0,0 +1,149 @@
|
||||
# Bank Tag Placeholder Models Frontend 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.
|
||||
|
||||
**Goal:** 确认本次“银行流水模型占位补齐”不会引入前端改动需求,并为后端落地后的页面兼容性和回归验证提供明确执行步骤。
|
||||
|
||||
**Architecture:** 本次需求的主变更点在后端规则定义、XML 占位 SQL 和打标调度,不新增前端接口或页面。前端计划采用“先核查现有动态渲染能力,再做最小 smoke 验证”的策略;如果验证通过,则前端不做代码改动。
|
||||
|
||||
**Tech Stack:** Vue 2, Element UI, Axios, npm
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 核查本次需求是否影响现有前端代码
|
||||
|
||||
**Files:**
|
||||
- Reference: `ruoyi-ui/src/api/ccdi/modelParam.js`
|
||||
- Reference: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||
- Reference: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||
- Reference: `docs/design/2026-03-18-bank-tag-model-placeholder-design.md`
|
||||
|
||||
- [ ] **Step 1: 查看模型参数 API**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
sed -n '1,220p' ruoyi-ui/src/api/ccdi/modelParam.js
|
||||
```
|
||||
|
||||
预期:`listAllParams`、`saveAllParams` 已存在,本次无需新增接口方法。
|
||||
|
||||
- [ ] **Step 2: 查看全局参数页**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
sed -n '1,220p' ruoyi-ui/src/views/ccdi/modelParam/index.vue
|
||||
```
|
||||
|
||||
预期:页面按接口返回的 `modelGroups` 动态渲染,不写死模型数量或固定模型清单。
|
||||
|
||||
- [ ] **Step 3: 查看项目参数页**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
sed -n '1,220p' ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue
|
||||
```
|
||||
|
||||
预期:页面同样根据接口返回动态渲染,不依赖固定模型代码。
|
||||
|
||||
- [ ] **Step 4: 记录结论**
|
||||
|
||||
确认本次前端边界为:
|
||||
|
||||
- 不新增页面
|
||||
- 不新增按钮或交互
|
||||
- 不修改路由
|
||||
- 不修改 API 契约
|
||||
- 仅在后端新增模型参数分组无法被页面展示时才补前端代码
|
||||
|
||||
### Task 2: 做“无需前端改动”的验证
|
||||
|
||||
**Files:**
|
||||
- Reference: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||
- Reference: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||
- Optional Record: `docs/tests/records/bank-tag-placeholder-frontend-test.md`
|
||||
|
||||
- [ ] **Step 1: 做静态差异检查**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
git diff -- ruoyi-ui/src/api/ccdi/modelParam.js ruoyi-ui/src/views/ccdi/modelParam/index.vue ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue
|
||||
```
|
||||
|
||||
预期:在开始前无本次需求相关前端改动,作为“无代码变更”基线。
|
||||
|
||||
- [ ] **Step 2: 运行前端构建 smoke test**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
预期:构建成功,证明本次后端计划不依赖任何前端同步改造。
|
||||
|
||||
- [ ] **Step 3: 人工核查两个页面的动态能力**
|
||||
|
||||
重点确认以下行为已由现有页面支持:
|
||||
|
||||
- 接口返回多少模型卡片就展示多少模型卡片
|
||||
- 每个模型下的参数表格由 `params` 数组驱动
|
||||
- 模型代码新增不会导致页面白屏或字段读取异常
|
||||
|
||||
若以上三点都成立,则本次前端结论为“无需代码改动”。
|
||||
|
||||
- [ ] **Step 4: 只有验证失败时才进入最小修补**
|
||||
|
||||
如果发现页面写死了模型列表,再新增一个单独前端实施分支,最小修改以下文件:
|
||||
|
||||
- `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||
- `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||
|
||||
且只允许做动态渲染修补,不扩大范围到样式重做或交互重构。
|
||||
|
||||
### Task 3: 联调与交付说明
|
||||
|
||||
**Files:**
|
||||
- Reference: `docs/plans/backend/2026-03-18-bank-tag-model-placeholder-backend-implementation.md`
|
||||
- Optional Record: `docs/tests/records/bank-tag-placeholder-frontend-test.md`
|
||||
|
||||
- [ ] **Step 1: 等待后端完成并提供可用数据**
|
||||
|
||||
后端完成后,联调时重点看两类场景:
|
||||
|
||||
- 规则占位补齐后,前端没有新增入口需求
|
||||
- 如后端顺带补了新模型参数,模型参数页仍能正常展示
|
||||
|
||||
- [ ] **Step 2: 做最小联调验证**
|
||||
|
||||
建议验证:
|
||||
|
||||
1. 打开全局模型参数页
|
||||
2. 打开项目详情中的参数配置页
|
||||
3. 确认页面可以正常加载、保存按钮状态正常、无控制台报错
|
||||
|
||||
- [ ] **Step 3: 如开启前端本地服务,验证后关闭进程**
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
完成验证后必须关闭该进程,避免残留端口占用。
|
||||
|
||||
- [ ] **Step 4: 若最终无前端改动,提交测试记录而不是代码提交**
|
||||
|
||||
可选记录:
|
||||
|
||||
```bash
|
||||
git add docs/tests/records/bank-tag-placeholder-frontend-test.md
|
||||
git commit -m "补充流水模型占位前端联调记录"
|
||||
```
|
||||
|
||||
如果没有新增测试记录文件,则本次前端任务以“无代码改动,验证通过”结束,不额外提交。
|
||||
@@ -73,6 +73,12 @@
|
||||
<version>3.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -105,4 +111,4 @@
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
71
ruoyi-admin/src/main/resources/logback-spring.xml
Normal file
71
ruoyi-admin/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 直接从 application.yml 的 logging.file.path 读取日志目录 -->
|
||||
<springProperty scope="context" name="log.path" source="logging.file.path" defaultValue="/home/ruoyi/logs" />
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/sys-info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>INFO</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/sys-error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/sys-user.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="com.ruoyi" level="info" />
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
|
||||
<logger name="sys-user" level="info">
|
||||
<appender-ref ref="sys-user"/>
|
||||
</logger>
|
||||
</configuration>
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.ruoyi.config;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.logging.LogFile;
|
||||
import org.springframework.boot.logging.LoggingInitializationContext;
|
||||
import org.springframework.boot.logging.logback.LogbackLoggingSystem;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class LogbackConfigurationTest {
|
||||
|
||||
@Test
|
||||
void shouldUseLogPathFromApplicationConfiguration() {
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
environment.getPropertySources().addFirst(new MapPropertySource("test", Map.of(
|
||||
"logging.file.path", "/tmp/ccdi-logback-test"
|
||||
)));
|
||||
|
||||
LogbackLoggingSystem loggingSystem = new LogbackLoggingSystem(getClass().getClassLoader());
|
||||
try {
|
||||
loggingSystem.beforeInitialize();
|
||||
loggingSystem.initialize(
|
||||
new LoggingInitializationContext(environment),
|
||||
"classpath:logback-spring.xml",
|
||||
LogFile.get(environment)
|
||||
);
|
||||
|
||||
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
RollingFileAppender<?> infoAppender = (RollingFileAppender<?>) rootLogger.getAppender("file_info");
|
||||
RollingFileAppender<?> errorAppender = (RollingFileAppender<?>) rootLogger.getAppender("file_error");
|
||||
RollingFileAppender<?> userAppender = (RollingFileAppender<?>) context.getLogger("sys-user").getAppender("sys-user");
|
||||
|
||||
assertThat(infoAppender.getFile()).isEqualTo("/tmp/ccdi-logback-test/sys-info.log");
|
||||
assertThat(errorAppender.getFile()).isEqualTo("/tmp/ccdi-logback-test/sys-error.log");
|
||||
assertThat(userAppender.getFile()).isEqualTo("/tmp/ccdi-logback-test/sys-user.log");
|
||||
} finally {
|
||||
loggingSystem.cleanUp();
|
||||
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
4
ruoyi-admin/src/test/resources/logback-test.xml
Normal file
4
ruoyi-admin/src/test/resources/logback-test.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<root level="OFF" />
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user