优化流水账号入库和明细筛选

This commit is contained in:
wjj
2026-07-10 17:33:16 +08:00
parent c999497693
commit 486a499af7
9 changed files with 120 additions and 18 deletions

View File

@@ -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;

View File

@@ -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());

View File

@@ -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(), "付款金额应该匹配");

View File

@@ -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"));

View File

@@ -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 错误。

View File

@@ -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` 未继续监听。

View File

@@ -35,11 +35,11 @@
/>
</el-select>
</el-form-item>
<el-form-item label="可疑规则">
<el-form-item label="预警类型">
<el-select
v-model="currentSuspiciousType"
class="suspicious-filter-select suspicious-type-select"
placeholder="全部可疑规则"
placeholder="全部预警类型"
@change="handleSuspiciousTypeSelectChange"
>
<el-option
@@ -369,7 +369,7 @@ import { getBankStatementDetail } from "@/api/ccdiProjectBankStatement";
import { buildFlowEvidenceFingerprint, buildFlowEvidenceSnapshot } from "@/utils/ccdiEvidence";
const SUSPICIOUS_TYPE_OPTIONS = [
{ value: "ALL", label: "全部可疑规则" },
{ value: "ALL", label: "全部预警类型" },
{ value: "MODEL_RULE", label: "员工规则命中" },
{ value: "EXTERNAL_PERSON", label: "外部人员预警" },
];
@@ -404,6 +404,23 @@ const normalizeRiskModelOptions = (rows) => {
});
};
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 = "";
}
},

View File

@@ -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));

View File

@@ -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=\"关联人\"",