From 486a499af706ae5315cf6e60de921be971b74d3f Mon Sep 17 00:00:00 2001 From: wjj <2069666735@qq.com> Date: Fri, 10 Jul 2026 17:33:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=81=E6=B0=B4=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=85=A5=E5=BA=93=E5=92=8C=E6=98=8E=E7=BB=86=E7=AD=9B?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/GetBankStatementResponse.java | 4 +-- .../domain/entity/CcdiBankStatement.java | 4 +-- .../domain/entity/CcdiBankStatementTest.java | 8 ++--- .../impl/CcdiFileUploadServiceImplTest.java | 4 +-- ...07-10-bank-statement-account-no-mapping.md | 18 ++++++++++ .../2026-07-10-risk-detail-filter-linkage.md | 30 +++++++++++++++++ .../components/detail/RiskDetailSection.vue | 33 ++++++++++++++++--- ...k-detail-suspicious-filter-linkage.test.js | 29 ++++++++++++++++ ...tail-suspicious-transaction-layout.test.js | 8 +++-- 9 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 docs/reports/implementation/2026-07-10-bank-statement-account-no-mapping.md create mode 100644 docs/reports/implementation/2026-07-10-risk-detail-filter-linkage.md create mode 100644 ruoyi-ui/tests/unit/risk-detail-suspicious-filter-linkage.test.js diff --git a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java index 724e6f1b..e5873bc3 100644 --- a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java +++ b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/GetBankStatementResponse.java @@ -51,7 +51,7 @@ public class GetBankStatementResponse { private String leName; /** 企业银行账号 */ - private String accountMaskNo; + private String accountNo; /** 账号日期ID */ private Integer accountingDateId; @@ -102,7 +102,7 @@ public class GetBankStatementResponse { private String customerName; /** 对手方账号 */ - private String customerAccountMaskNo; + private String customerAccountNo; /** 对手方银行 */ private String customerBank; diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java index 58086336..c7cacc6a 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatement.java @@ -194,8 +194,8 @@ public class CcdiBankStatement implements Serializable { BeanUtils.copyProperties(item, entity); // 4. 手动映射字段名不一致的情况 - entity.setLeAccountNo(item.getAccountMaskNo()); - entity.setCustomerAccountNo(item.getCustomerAccountMaskNo()); + entity.setLeAccountNo(item.getAccountNo()); + entity.setCustomerAccountNo(item.getCustomerAccountNo()); entity.setLeAccountName(item.getLeName()); entity.setAmountDr(item.getDrAmount()); entity.setAmountCr(item.getCrAmount()); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java index f94aa4cc..aedfe4a0 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/domain/entity/CcdiBankStatementTest.java @@ -23,12 +23,12 @@ class CcdiBankStatementTest { item.setLeId(100); item.setAccountId(200L); item.setLeName("测试企业"); - item.setAccountMaskNo("6222****1234"); + item.setAccountNo("6222023300001234"); item.setDrAmount(new BigDecimal("1000.00")); item.setCrAmount(new BigDecimal("500.00")); item.setBalanceAmount(new BigDecimal("5000.00")); item.setTrxDate("2026-03-04"); - item.setCustomerAccountMaskNo("6228****5678"); + item.setCustomerAccountNo("6228483300005678"); // 执行转换 CcdiBankStatement entity = CcdiBankStatement.fromResponse(item); @@ -41,8 +41,8 @@ class CcdiBankStatementTest { assertEquals("测试企业", entity.getLeAccountName(), "企业名称应该匹配"); // 验证手动映射的字段 - assertEquals("6222****1234", entity.getLeAccountNo(), "企业账号应该从 accountMaskNo 映射"); - assertEquals("6228****5678", entity.getCustomerAccountNo(), "对手方账号应该从 customerAccountMaskNo 映射"); + assertEquals("6222023300001234", entity.getLeAccountNo(), "企业账号应该从 accountNo 映射"); + assertEquals("6228483300005678", entity.getCustomerAccountNo(), "对手方账号应该从 customerAccountNo 映射"); // 验证金额字段 assertEquals(new BigDecimal("1000.00"), entity.getAmountDr(), "付款金额应该匹配"); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java index af63b6d5..4bf2da13 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java @@ -957,9 +957,9 @@ class CcdiFileUploadServiceImplTest { return response; } - private GetBankStatementResponse.BankStatementItem buildBankStatementItem(String accountMaskNo) { + private GetBankStatementResponse.BankStatementItem buildBankStatementItem(String accountNo) { GetBankStatementResponse.BankStatementItem item = new GetBankStatementResponse.BankStatementItem(); - item.setAccountMaskNo(accountMaskNo); + item.setAccountNo(accountNo); item.setAccountingDateId(20260310); item.setDrAmount(new BigDecimal("100.00")); item.setCrAmount(new BigDecimal("0.00")); diff --git a/docs/reports/implementation/2026-07-10-bank-statement-account-no-mapping.md b/docs/reports/implementation/2026-07-10-bank-statement-account-no-mapping.md new file mode 100644 index 00000000..52045c6e --- /dev/null +++ b/docs/reports/implementation/2026-07-10-bank-statement-account-no-mapping.md @@ -0,0 +1,18 @@ +# 流水账号字段映射实施记录 + +## 修改内容 + +- 在流水分析响应 DTO `GetBankStatementResponse.BankStatementItem` 中补充 `accountNo`、`customerAccountNo` 字段,并移除 `accountMaskNo`、`customerAccountMaskNo` 字段。 +- 将银行流水实体转换逻辑从 `accountMaskNo`、`customerAccountMaskNo` 改为直接读取 `accountNo`、`customerAccountNo`。 +- 同步调整相关单元测试数据和断言,避免继续按掩码账号字段验证。 + +## 影响范围 + +- 影响流水分析接口返回流水明细后写入 `ccdi_bank_statement.LE_ACCOUNT_NO`、`CUSTOMER_ACCOUNT_NO` 的字段来源。 +- 不改变数据库结构。 +- 不改变前端页面。 + +## 验证情况 + +- 已执行 `mvn -pl ccdi-project -am test "-Dtest=CcdiBankStatementTest,CcdiFileUploadServiceImplTest" "-Dsurefire.failIfNoSpecifiedTests=false"`。 +- 测试结果:39 个测试通过,0 失败,0 错误。 diff --git a/docs/reports/implementation/2026-07-10-risk-detail-filter-linkage.md b/docs/reports/implementation/2026-07-10-risk-detail-filter-linkage.md new file mode 100644 index 00000000..df79cbd9 --- /dev/null +++ b/docs/reports/implementation/2026-07-10-risk-detail-filter-linkage.md @@ -0,0 +1,30 @@ +# 涉疑交易明细筛选联动实施记录 + +## 修改内容 + +- 将涉疑交易明细第二个筛选框文案由“可疑规则”统一为“预警类型”。 +- 将默认选项文案由“全部可疑规则”调整为“全部预警类型”。 +- 在“全部预警类型”下,“命中模型”下拉同时展示员工模型与外部人员模型,并按模型编码去重。 +- 选择外部人员模型时不再强制切换“预警类型”,支持 `全部预警类型 + 外部人员可疑关系` 直接查询。 +- 保留明确类型约束:当预警类型切到“外部人员预警”时,仅保留外部人员模型;当预警类型切到“员工规则命中”时,清空已选择的外部人员模型。 + +## 影响范围 + +- 前端组件:`ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue` +- 前端静态单测: + - `ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js` + - `ruoyi-ui/tests/unit/risk-detail-suspicious-filter-linkage.test.js` + +本次未修改后端接口、SQL 或查询参数结构。查询仍沿用 `suspiciousType` 与 `modelCode` 两个参数组合。 + +## 验证情况 + +- Node 环境:`.nvmrc` 为 14.21.3;当前 PowerShell 下 `nvm use` 未自动读取版本,且本机已知 Node 14 安装缺少 npm,因此按项目约定使用已验证可用的 Node 22.22.3 / npm 10.9.8。 +- 已执行静态单测: + - `node tests/unit/risk-detail-suspicious-transaction-layout.test.js` + - `node tests/unit/risk-detail-suspicious-filter-linkage.test.js` + - `node tests/unit/project-overview-api.test.js` +- 已启动前端开发服务并编译成功:`npm run dev`,端口 `8088`。 +- 已使用系统 Chrome 通过 Playwright CLI 打开实际应用入口并截图:`output/playwright/risk-detail-filter-home-independent.png`。 +- 浏览器验证限制:当前后端 `62318` 未监听,实际应用入口停留在登录页并提示“系统接口500异常”,因此未进入涉疑交易明细业务页做端到端交互验证。 +- 测试后已关闭本次启动的前端开发进程,`8088` 未继续监听。 diff --git a/ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue b/ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue index dbd1870e..8c6239de 100644 --- a/ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue +++ b/ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue @@ -35,11 +35,11 @@ /> - + { }); }; +const mergeRiskModelOptions = (...optionGroups) => { + const seen = new Set(); + return optionGroups + .flat() + .filter((item) => { + if (!item || !item.value || seen.has(item.value)) { + return false; + } + seen.add(item.value); + return true; + }); +}; + +const isExternalModelCode = (value) => ( + typeof value === "string" && value.indexOf("EXTERNAL_") === 0 +); + const ABNORMAL_CUSTOMER_TRANSACTION_RULE_CODE = "ABNORMAL_CUSTOMER_TRANSACTION"; const ABNORMAL_CUSTOMER_TRANSACTION_RULE_NAME = "与客户之间非正常资金往来"; @@ -542,7 +559,13 @@ export default { return this.currentSuspiciousType === "EXTERNAL_PERSON"; }, displayRiskModelOptions() { - return this.isExternalSuspiciousType ? EXTERNAL_RISK_MODEL_OPTIONS : this.riskModelOptions; + if (this.isExternalSuspiciousType) { + return EXTERNAL_RISK_MODEL_OPTIONS; + } + if (this.currentSuspiciousType === "ALL") { + return mergeRiskModelOptions(this.riskModelOptions, EXTERNAL_RISK_MODEL_OPTIONS); + } + return this.riskModelOptions; }, allModelOptionLabel() { return this.isExternalSuspiciousType ? "全部外部模型" : "全部模型"; @@ -589,7 +612,7 @@ export default { } return; } - if (this.selectedModelCode && this.selectedModelCode.indexOf("EXTERNAL_") === 0) { + if (value === "MODEL_RULE" && isExternalModelCode(this.selectedModelCode)) { this.selectedModelCode = ""; } }, diff --git a/ruoyi-ui/tests/unit/risk-detail-suspicious-filter-linkage.test.js b/ruoyi-ui/tests/unit/risk-detail-suspicious-filter-linkage.test.js new file mode 100644 index 00000000..91b2b1c1 --- /dev/null +++ b/ruoyi-ui/tests/unit/risk-detail-suspicious-filter-linkage.test.js @@ -0,0 +1,29 @@ +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const source = fs.readFileSync( + path.resolve( + __dirname, + "../../src/views/ccdiProject/components/detail/RiskDetailSection.vue" + ), + "utf8" +); + +[ + 'const mergeRiskModelOptions = (...optionGroups) => {', + 'const isExternalModelCode = (value) => (', + 'if (this.currentSuspiciousType === "ALL") {', + 'return mergeRiskModelOptions(this.riskModelOptions, EXTERNAL_RISK_MODEL_OPTIONS);', + 'if (value === "MODEL_RULE" && isExternalModelCode(this.selectedModelCode)) {', + 'this.selectedModelCode = "";', +].forEach((token) => assert(source.includes(token), token)); + +[ + '@change="handleModelSelectChange"', + 'handleModelSelectChange(value) {', + 'this.currentSuspiciousType = "EXTERNAL_PERSON";', + 'label="可疑规则"', + 'placeholder="全部可疑规则"', + '{ value: "ALL", label: "全部可疑规则" }', +].forEach((token) => assert(!source.includes(token), token)); diff --git a/ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js b/ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js index 269ee8fa..b13c8525 100644 --- a/ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js +++ b/ruoyi-ui/tests/unit/risk-detail-suspicious-transaction-layout.test.js @@ -12,9 +12,10 @@ const source = fs.readFileSync( [ "涉疑交易明细", - "全部可疑人员类型", - "名单库命中", - "模型规则命中", + "预警类型", + "全部预警类型", + "员工规则命中", + "外部人员预警", "本方账户", "对方账户", "关联员工", @@ -29,6 +30,7 @@ const source = fs.readFileSync( ].forEach((token) => assert(source.includes(token), token)); [ + "label=\"可疑规则\"", "label=\"可疑人员\"", "prop=\"suspiciousPersonName\"", "label=\"关联人\"",