优化流水账号入库和明细筛选
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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(), "付款金额应该匹配");
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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 错误。
|
||||
@@ -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` 未继续监听。
|
||||
@@ -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 = "";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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));
|
||||
@@ -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=\"关联人\"",
|
||||
|
||||
Reference in New Issue
Block a user