# 项目结果总览排除可疑改造设计

## 1. 背景

项目详情的结果总览当前直接消费银行流水打标结果与员工结果快照。用户在核查过程中如果确认某条预警不是可疑问题，只能在认知上忽略，系统没有持久化排除能力。刷新页面、重新进入项目或导出报告时，该预警仍会按有效命中展示。

仓库初始化 SQL 中已存在 `ccdi_project_risk_exclusion` 表，语义是“项目结果页排除可疑记录表”，字段能够承载项目、人员或流水、规则编码、排除类型与排除原因。本次改造使用该表完成排除记录持久化，不删除原始打标结果。

## 2. 目标

1. 用户可在项目分析详情的异常明细中对单条预警执行“排除可疑”。
2. 排除对象精确到单条规则命中，不影响同一人员或同一流水的其他规则。
3. 排除后当前页面立即刷新，风险人员、模型预警次数、命中人数、涉疑交易明细按有效命中重新展示。
4. 浏览器刷新或重新进入项目后，已排除预警仍不作为有效预警展示。
5. 本阶段不做“恢复预警”入口，保持最短闭环。

## 3. 非目标

1. 不删除 `ccdi_bank_statement_tag_result` 原始命中结果。
2. 不使用 `localStorage`、`sessionStorage` 或 Cookie 保存排除状态。
3. 不做批量排除、恢复排除、排除记录管理页。
4. 不改变打标规则执行逻辑；重新打标后仍生成原始命中，展示查询阶段通过排除表过滤。

## 4. 用户操作链路

### 4.1 流水明细型预警

示例：某笔流水同时命中“大额转账交易”和“疑似敏感交易”。

1. 用户进入项目详情 > 结果总览。
2. 点击风险人员或模型命中人员的“查看项目”。
3. 在项目分析详情中进入“异常明细”。
4. 在“流水异常明细”表格中，异常标签旁展示小号操作“排除可疑”。
5. 用户点击某个标签的“排除可疑”，填写排除原因并确认。
6. 系统只排除该笔流水上的该条规则标签。

排除键：

```text
project_id + bank_statement_id + rule_code + exclusion_type = STATEMENT
```

效果：

1. 被排除的标签不再显示。
2. 同一笔流水的其他标签继续显示。
3. 如果该笔流水没有剩余有效标签，则不再出现在异常流水明细中。
4. 相关模型预警次数减少。

### 4.2 对象型 / 人员型预警

示例：某人命中“年流水交易额超限”，该预警是按人员聚合生成，不对应单笔流水。

1. 用户进入项目详情 > 结果总览。
2. 点击该人员的“查看项目”。
3. 在项目分析详情中进入“异常明细”。
4. 在“对象异常明细”卡片右上角展示小号操作“排除可疑”，样式与“加入证据库”保持一致。
5. 用户点击“排除可疑”，填写排除原因并确认。
6. 系统只排除该人员命中的该条规则。

排除键：

```text
project_id + staff_id_card + rule_code + exclusion_type = OBJECT
```

效果：

1. 该对象型预警卡片不再显示。
2. 该人员其他规则命中继续保留。
3. 如果该人员没有剩余有效规则，则从风险人员列表中移除。
4. 如果该人员仍有其他有效规则，则人员仍展示，但模型数、规则标签和风险等级按剩余有效命中重新计算。

## 5. 数据设计

使用既有表 `ccdi_project_risk_exclusion`。

关键字段：

| 字段 | 用途 |
| --- | --- |
| `project_id` | 项目 ID |
| `staff_id_card` | 对象型预警对应人员证件号；外部人员也使用证件号 |
| `rule_code` | 被排除规则编码 |
| `exclusion_type` | `STATEMENT` 或 `OBJECT` |
| `bank_statement_id` | 流水型预警对应流水 ID |
| `exclude_reason` | 排除原因 |

唯一约束：

1. 流水型：`project_id + rule_code + exclusion_type + bank_statement_id`
2. 对象型：`project_id + staff_id_card + rule_code + exclusion_type`

如果目标环境缺少该表，实施时补充增量 SQL，使用 `utf8mb4` 与 `utf8mb4_general_ci`。

## 6. 后端设计

### 6.1 新增接口

新增“排除可疑”接口：

```text
POST /ccdi/project/overview/risk-exclusions
```

请求字段：

| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `projectId` | 是 | 项目 ID |
| `exclusionType` | 是 | `STATEMENT` 或 `OBJECT` |
| `ruleCode` | 是 | 规则编码 |
| `bankStatementId` | 流水型必填 | 流水 ID |
| `staffIdCard` | 对象型必填 | 人员证件号 |
| `excludeReason` | 是 | 排除原因 |

校验规则：

1. `exclusionType=STATEMENT` 时必须传 `bankStatementId`。
2. `exclusionType=OBJECT` 时必须传 `staffIdCard`。
3. `excludeReason` 不能为空，长度不超过 1000。
4. 先校验用户对项目有读写权限；归档或只读项目不允许排除。
5. 重复排除视为幂等成功，更新原因和更新时间。

### 6.2 查询过滤

所有结果总览相关查询应统一过滤排除表。

过滤规则：

1. 读取 `ccdi_bank_statement_tag_result` 时左关联排除表。
2. 流水型命中用 `project_id + rule_code + bank_statement_id + STATEMENT` 匹配。
3. 对象型命中用 `project_id + rule_code + staff_id_card/object_key + OBJECT` 匹配。
4. 匹配到排除记录的命中不进入后续聚合、列表、标签组装、导出。

重点影响范围：

1. 风险人员列表。
2. 风险模型卡片。
3. 风险模型命中人员。
4. 人员项目分析详情。
5. 涉疑交易明细。
6. 一键 PDF 报告与 Excel 导出。
7. 外部人员预警及外部人员详情。

### 6.3 员工结果快照

当前风险人员列表读取 `ccdi_project_overview_employee_result` 快照。为了让“排除可疑”后统计即时变化，实施时应在排除成功后触发当前项目员工结果快照重算，或将列表查询切回有效命中实时聚合。

本次推荐：

1. 排除接口写入排除表后，同步触发当前项目结果总览员工快照重算。
2. 重算逻辑只使用未排除命中。
3. 页面刷新后读取更新后的快照。

理由：保留现有列表分页性能与页面结构，改动集中，不引入双口径。

## 7. 前端设计

### 7.1 操作入口

流水异常明细：

1. 异常标签展示为标签 + 小号文字按钮。
2. 按钮文案为“排除可疑”。
3. 按钮只作用于当前标签，不作用于整行。

对象异常明细：

1. 卡片右上角增加小号按钮“排除可疑”。
2. 样式仿照“加入证据库”，保持轻量。
3. 不新增大按钮、不改变卡片主视觉。

### 7.2 确认弹窗

使用 Element UI 弹窗或对话框，要求用户填写排除原因。

确认文案需要明确影响范围：

1. 流水型：仅排除当前流水的当前规则标签。
2. 对象型：仅排除当前人员的当前规则预警。

### 7.3 刷新策略

排除成功后，前端通知父组件刷新：

1. 重新加载项目分析详情。
2. 重新加载风险人员列表。
3. 重新加载风险模型卡片与命中人员。
4. 重新加载涉疑交易明细。

不做局部假刷新，避免页面统计与后端状态不一致。

## 8. 风险与约束

1. 如果只过滤详情、不刷新快照，风险人员列表和模型统计会不一致；必须统一刷新统计口径。
2. 对象型预警没有 `bank_statement_id`，必须用人员证件号和规则编码定位。
3. 外部人员对象型预警同样需要按证件号处理，不能只适配员工。
4. 排除记录不删除原始命中，因此重新打标不会覆盖排除结果。

## 9. 验证场景

1. 流水一条多标签，排除其中一个标签后另一个标签仍显示。
2. 排除流水标签后模型预警次数减少。
3. 排除对象型“年流水交易额超限”后对象卡片消失。
4. 人员仅剩一条对象型预警时，排除后人员不再出现在风险人员列表。
5. 人员有多条预警时，排除一条后人员仍保留，标签和统计减少。
6. 刷新浏览器后排除结果仍生效。
7. 导出报告不包含已排除预警。
8. 归档或只读项目不允许排除。

