实现结果总览排除可疑预警

This commit is contained in:
wjj
2026-07-09 14:49:02 +08:00
parent 9c02812675
commit 3f3e9268f2
22 changed files with 1549 additions and 32 deletions

View File

@@ -0,0 +1,264 @@
# 项目结果总览排除可疑后端实施计划
## 1. 目标
后端提供“排除可疑”持久化接口,并在结果总览所有预警查询、统计、导出链路中统一过滤已排除命中。排除粒度为单条规则命中,不删除原始打标结果。
## 2. 涉及范围
模块:
1. `ccdi-project`
2. `ruoyi-admin` 装配依赖无需调整
3. `sql/migration/`
重点文件:
1. `CcdiProjectOverviewController`
2. `ICcdiProjectOverviewService`
3. `CcdiProjectOverviewServiceImpl`
4. `CcdiProjectOverviewMapper`
5. `CcdiBankTagResultMapper`
6. 新增排除记录实体、DTO、Mapper
## 3. 数据库实施
### 3.1 增量脚本
新增 SQL
```text
sql/migration/2026-07-09-create-project-risk-exclusion.sql
```
内容使用 `CREATE TABLE IF NOT EXISTS ccdi_project_risk_exclusion`,字段与 `sql/ccdi_prod_init.sql` 保持一致,并显式声明:
```sql
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
```
### 3.2 表用途
`ccdi_project_risk_exclusion` 保存排除记录:
1. `STATEMENT`:单笔流水上的单个规则标签。
2. `OBJECT`:某个对象或人员上的单个规则预警。
## 4. 后端对象
### 4.1 新增 Entity
新增:
```text
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/entity/CcdiProjectRiskExclusion.java
```
字段对应表结构,实体类使用 Lombok `@Data`,不继承 `BaseEntity`
### 4.2 新增 DTO
新增:
```text
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectRiskExclusionSaveDTO.java
```
字段:
1. `projectId`
2. `staffIdCard`
3. `ruleCode`
4. `exclusionType`
5. `bankStatementId`
6. `excludeReason`
校验:
1. `projectId` 必填。
2. `ruleCode` 必填。
3. `exclusionType` 必填且只允许 `STATEMENT``OBJECT`
4. `excludeReason` 必填且最大 1000 字符。
5. `STATEMENT` 要求 `bankStatementId`
6. `OBJECT` 要求 `staffIdCard`
### 4.3 新增 Mapper
新增:
```text
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectRiskExclusionMapper.java
ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectRiskExclusionMapper.xml
```
方法:
1. `upsertExclusion`
2. `selectByProjectId`
3. `selectStatementExclusions`
4. `selectObjectExclusions`
`upsertExclusion` 使用唯一键实现幂等写入;重复排除时更新 `exclude_reason/update_by/update_time`
## 5. 接口实施
`CcdiProjectOverviewController` 新增:
```text
POST /ccdi/project/overview/risk-exclusions
```
返回:
```java
AjaxResult.success("排除成功")
```
权限:
1. `@PreAuthorize("@ss.hasPermi('ccdi:project:query')")`
2. 使用 `projectAccessService.assertCanRead(projectId)` 校验项目访问。
3. 使用项目状态或访问服务校验当前项目可操作;归档或只读项目返回错误。
## 6. Service 实施
`ICcdiProjectOverviewService` 增加:
```java
void excludeRisk(CcdiProjectRiskExclusionSaveDTO dto);
```
`CcdiProjectOverviewServiceImpl` 实现流程:
1. 校验 DTO。
2. 校验规则是否存在于当前项目有效命中中,避免写入无效排除记录。
3. 写入 `ccdi_project_risk_exclusion`
4. 触发当前项目结果总览员工快照重算。
5. 返回成功。
## 7. 查询过滤实施
### 7.1 统一过滤 SQL
`CcdiProjectOverviewMapper.xml` 中新增复用 SQL 片段:
```xml
not exists (
select 1
from ccdi_project_risk_exclusion ex
where ex.project_id = tr.project_id
and ex.rule_code = tr.rule_code
and (
(ex.exclusion_type = 'STATEMENT' and ex.bank_statement_id = tr.bank_statement_id)
or
(ex.exclusion_type = 'OBJECT' and ex.staff_id_card = resolved_staff_id_card)
)
)
```
实际实现时根据不同查询上下文替换 `resolved_staff_id_card`
### 7.2 员工风险基础 SQL
修改 `resolvedEmployeeRiskBaseSql`
1. 先解析 `staff_id_card`
2.`STATEMENT``OBJECT` 分别过滤。
3. 被排除命中不进入员工风险聚合。
### 7.3 风险模型卡片
修改模型统计查询:
1. `warning_count` 按未排除命中统计。
2. `people_count` 按未排除命中涉及人员去重。
### 7.4 风险模型命中人员
修改命中人员查询:
1. `hitTagList` 不包含已排除规则。
2. `modelNames` 按剩余命中规则组装。
3. 过滤后无有效规则的人员不返回。
### 7.5 人员项目分析详情
修改详情组装:
1. `BANK_STATEMENT` 记录的 `hitTags` 排除已排除标签。
2. 如果一条流水没有剩余 `hitTags`,不进入流水异常明细。
3. `OBJECT` 记录排除已排除对象规则。
### 7.6 涉疑交易明细与导出
修改涉疑交易查询:
1. 已排除流水标签不进入 `hitTags`
2. 过滤后无有效标签的流水不作为可疑流水展示。
3. Excel 导出和 PDF 报告复用同一过滤口径。
### 7.7 外部人员预警
外部人员使用证件号作为对象键:
1. `OBJECT` 类型按 `staff_id_card = cert_no` 过滤。
2. `STATEMENT` 类型按 `bank_statement_id` 过滤。
3. 外部人员列表、模型卡片、详情、报告口径一致。
## 8. 快照重算
如果当前已有员工结果快照重算逻辑,排除成功后复用该逻辑。
若没有可复用入口,新增内部方法:
```java
refreshOverviewEmployeeResult(Long projectId)
```
要求:
1. 使用过滤排除后的有效命中重算。
2. 删除或更新当前项目 `ccdi_project_overview_employee_result`
3. 确保列表、模型统计和详情口径一致。
## 9. 测试计划
### 9.1 后端编译
```bash
mvn -pl ccdi-project -am compile -DskipTests
```
### 9.2 单接口验证
验证接口:
```text
POST /ccdi/project/overview/risk-exclusions
```
场景:
1. 缺少原因返回错误。
2. `STATEMENT` 缺少 `bankStatementId` 返回错误。
3. `OBJECT` 缺少 `staffIdCard` 返回错误。
4. 重复排除幂等成功。
5. 归档项目或只读项目不允许排除。
### 9.3 数据口径验证
场景:
1. 一笔流水多个标签,排除一个后另一个仍存在。
2. 人员多个对象型预警,排除一个后人员仍存在。
3. 人员唯一预警被排除后,风险人员列表不再展示该人员。
4. 模型预警次数和命中人数减少。
5. PDF/Excel 不输出已排除预警。
## 10. 风险控制
1. 排除表只影响展示和统计,不影响原始命中结果。
2. 所有新增 SQL 使用 `utf8mb4_general_ci`
3. `rule_code` 保持全大写。
4. 不引入恢复接口,避免扩大范围。

View File

@@ -0,0 +1,293 @@
# 项目结果总览排除可疑前端实施计划
## 1. 目标
在项目分析详情的异常明细中提供轻量“排除可疑”操作。用户确认排除后,页面重新请求后端数据,展示过滤后的风险人员、模型预警次数、命中人数与异常明细。
本阶段不做恢复入口,不做排除记录列表。
## 2. 涉及范围
前端模块:
```text
ruoyi-ui
```
重点文件:
1. `ruoyi-ui/src/api/ccdi/projectOverview.js`
2. `ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisAbnormalTab.vue`
3. `ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisDialog.vue`
4. `ruoyi-ui/src/views/ccdiProject/components/detail/ExternalPersonDetailDialog.vue`
5. `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
6. `ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue`
7. `ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue`
8. `ruoyi-ui/src/views/ccdiProject/components/detail/RiskDetailSection.vue`
## 3. API 封装
`projectOverview.js` 新增:
```js
export function excludeOverviewRisk(data) {
return request({
url: '/ccdi/project/overview/risk-exclusions',
method: 'post',
data
})
}
```
请求字段:
```js
{
projectId,
exclusionType,
ruleCode,
bankStatementId,
staffIdCard,
excludeReason
}
```
## 4. UI 设计
### 4.1 流水异常明细
位置:
```text
ProjectAnalysisAbnormalTab.vue > BANK_STATEMENT 表格 > 异常标签列
```
展示方式:
1. 每个异常标签保持 `el-tag`
2. 标签右侧增加小号文字按钮“排除可疑”。
3. 按钮与标签在同一行内,不增加大操作列。
交互:
1. 点击“排除可疑”。
2. 打开确认弹窗。
3. 用户填写排除原因。
4. 确认后调用后端接口。
提交参数:
```js
{
projectId,
exclusionType: 'STATEMENT',
ruleCode: tag.ruleCode,
bankStatementId: row.bankStatementId,
staffIdCard: resolvePersonIdCard(),
excludeReason
}
```
其中 `staffIdCard` 仅作为上下文传递,后端流水型以 `bankStatementId` 为主。
### 4.2 对象异常明细
位置:
```text
ProjectAnalysisAbnormalTab.vue > OBJECT 卡片右上角
```
展示方式:
1. 卡片右上角增加小号按钮“排除可疑”。
2. 样式仿照现有“加入证据库”按钮。
3. 不新增恢复按钮。
提交参数:
```js
{
projectId,
exclusionType: 'OBJECT',
ruleCode: item.ruleCode || item.modelCode,
staffIdCard: resolvePersonIdCard(),
excludeReason
}
```
实施时需要确保对象异常记录保留真实 `ruleCode`。如果当前对象卡片只带 `modelCode`,前后端需补齐 `ruleCode` 字段,不能用 `modelCode` 替代规则编码。
## 5. 确认弹窗
使用 Element UI 对话框或 `$prompt`
推荐文案:
流水型:
```text
确认将当前流水的“{规则名称}”标记为排除可疑吗?
该操作只影响当前流水上的这一个规则标签。
```
对象型:
```text
确认将“{人员姓名}”的“{规则名称}”标记为排除可疑吗?
该操作只影响当前人员的这一个规则预警。
```
输入框:
```text
请输入排除原因
```
校验:
1. 原因不能为空。
2. 原因长度不超过 1000。
## 6. 刷新策略
排除成功后不做前端本地假删除,统一通知父组件刷新。
事件链路:
1. `ProjectAnalysisAbnormalTab` 调用接口成功。
2. 向上 emit `risk-excluded`
3. `ProjectAnalysisDialog` 接收后重新加载当前人员详情,并继续向上 emit。
4. `PreliminaryCheck` 接收后重新加载结果总览数据。
5. 子组件因 props 更新重新加载风险人员、模型卡片、命中人员与涉疑交易。
外部人员详情:
1. `ExternalPersonDetailDialog` 接收 `risk-excluded` 后重新加载外部人员流水。
2. 同时通知 `PreliminaryCheck` 刷新总览数据。
需要刷新的数据:
1. 风险人员列表。
2. 外部人员预警列表。
3. 风险模型卡片。
4. 风险模型命中人员。
5. 涉疑交易明细。
6. 当前弹窗异常明细。
## 7. 页面状态
### 7.1 操作中
点击确认后按钮进入 loading 或禁用状态,避免重复提交。
### 7.2 成功
提示:
```text
排除成功
```
随后刷新页面数据。
### 7.3 失败
提示后端错误信息:
```text
排除失败,请稍后重试
```
不修改当前页面数据。
### 7.4 只读项目
如果 `canOperate=false`,不展示“排除可疑”按钮,或展示禁用态并提示当前项目仅可查看。
## 8. 数据要求
前端需要从后端获取或透传以下字段:
流水标签:
1. `ruleCode`
2. `ruleName`
3. `bankStatementId`
对象卡片:
1. `ruleCode`
2. `ruleName`
3. `modelCode`
4. `modelName`
5. `reasonDetail`
人员上下文:
1. `projectId`
2. `idNo``staffIdCard`
3. `name``staffName`
如果对象异常卡片缺少 `ruleCode`,应先补齐数据映射,再展示排除按钮。
## 9. 测试计划
### 9.1 Node 环境
前端命令执行前按仓库规则:
```bash
cd ruoyi-ui
nvm use
node -v
npm -v
where node
where npm
```
如果 Node 14.21.3 缺少 `npm.cmd`,切换:
```bash
nvm use 22.22.3
```
并在实施记录中说明实际版本。
### 9.2 构建验证
```bash
npm run build:prod
```
### 9.3 浏览器验证
完成页面开发后,使用 `browser-use` 打开真实业务页面验证,不打开 prototype。
验证路径:
1. 登录系统。
2. 进入项目详情。
3. 打开结果总览。
4. 打开某个风险人员项目分析详情。
5. 对流水标签执行“排除可疑”。
6. 确认弹窗填写原因。
7. 验证异常明细、模型统计、风险人员数据刷新。
8. 刷新浏览器,验证排除仍生效。
9. 对对象型“年流水交易额超限”执行同样验证。
### 9.4 关键用例
1. 单笔流水多标签,只排除其中一个标签。
2. 对象型单预警人员,排除后人员从风险列表消失。
3. 对象型多预警人员,排除后人员保留但标签和次数减少。
4. 只读项目不显示或禁用按钮。
5. 排除原因为空时不能提交。
## 10. 不做事项
1. 不做恢复入口。
2. 不做排除记录管理页。
3. 不使用浏览器本地存储。
4. 不新增批量排除。