Files
ccdi/docs/design/2026-03-20-results-overview-employee-result-table-design.md

348 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 结果总览员工结果表设计文档
**模块**: 初核项目详情 - 结果总览
**日期**: 2026-03-20
**状态**: 已确认
## 一、背景
当前结果总览页已接通的查询包括:
- 风险仪表盘
- 风险人员总览
- 模型预警次数统计
- 命中模型涉及人员
现状中,这些查询依赖 `ccdi_bank_statement_tag_result``ccdi_bank_statement``ccdi_base_staff``ccdi_staff_fmy_relation` 的运行时连表归并与聚合。页面访问时会重复执行重 SQL导致响应时间偏长尤其是模型区和风险人员总览区块。
本轮目标是新增一张结果总览结果表,把“项目内每个员工的模型触发情况”预先沉淀下来,再基于该表编写页面查询 SQL避免结果总览页面运行时直接连回源表。
## 二、目标
本次设计目标如下:
1. 新增一张结果总览员工结果表,记录项目内每个员工的结果快照。
2. 员工亲属的流水异常命中需归并计入该员工本人。
3. 页面查询只依赖项目表和结果总览员工结果表,不再运行时连 `标签结果表 + 流水表 + 员工表 + 亲属表`
4. 命中结果写库成功后,在同一事务内按项目整块重算结果总览员工结果表。
5. 保持当前页面统计口径不变,尤其是模型卡片 `warningCount` 仍定义为原始标签命中次数。
## 三、范围
### 3.1 本次范围
- 设计结果总览员工结果表结构
- 设计员工本人和亲属命中的归并口径
- 设计命中结果写库后的同步重算链路
- 设计结果总览 4 类查询从结果表取数的方案
- 后续输出前后端实施计划与实施记录
### 3.2 不在本次范围
- 不覆盖结果总览风险明细区块
- 不引入异步刷新、缓存或消息队列
- 不做增量更新,只做按项目整块重算
- 不新增多张平行结果表
- 不保留中高风险 TOP10 区块及其专用查询设计
## 四、当前口径约束
本次设计确认以下业务口径:
1. 结果表粒度为 `项目 + 员工`,一行代表一个项目内一个归并后的员工结果。
2. 员工亲属的流水异常命中,必须归并到对应员工本人名下。
3. 命中结果写库成功后,结果表需要在同一事务内同步重算完成。
4. 重算方式采用“按项目整块重算当前已接通区块所需数据”。
5. 模型卡片中的 `warningCount` 定义保持现状,为原始标签命中次数,而不是员工人数或规则去重数。
## 五、方案对比
### 5.1 方案 A直接存页面结果快照
做法:
- 按项目保存仪表盘、风险人员总览、模型卡片、模型人员列表等页面结果快照
问题:
- `命中模型涉及人员` 存在 `ANY / ALL / keyword / deptId / pageNum` 等动态筛选
- 一旦直接存页面结果,筛选和分页要么退化为内存处理,要么继续依赖复杂 JSON 查询
- 无法满足“先保存员工触发情况,再根据结果表写 SQL”的要求
### 5.2 方案 B单表保存项目内每个员工的结果快照
做法:
- 新增一张结果总览员工结果表
-`project_id + staff_id_card` 为唯一口径
- 每行保存员工基础信息、风险统计、模型命中快照、规则命中快照
- 页面查询全部基于该表聚合或筛选
优点:
- 符合“先存员工触发情况,再写页面 SQL”的要求
- 页面查询可以收敛为单表查询
- 兼容 `ANY / ALL`、关键字、部门、分页等动态筛选
- 与当前统计口径兼容,不需要改业务定义
缺点:
- 命中结果写库后需要按项目整块重算该表
- 表中会存在部分汇总冗余字段和 JSON 快照字段
### 5.3 方案 C多张专用结果表
做法:
- 仪表盘、风险人员、模型卡片、模型人员分别建独立结果表
问题:
- 不符合本轮“创建一张结果总览结果表”的目标
- 数据维护面更大DDL 和重算逻辑更分散
### 5.4 推荐方案
采用方案 B新增一张“结果总览员工结果表”以员工为核心粒度预聚合再由页面查询基于该表出数。
## 六、结果表设计
建议表名:
- `ccdi_project_overview_employee_result`
建议主键与唯一性约束:
- 主键:`id`
- 唯一键:`uk_project_staff (project_id, staff_id_card)`
建议字段如下。
### 6.1 基础标识字段
- `project_id`
- `staff_id_card`
- `staff_id`
- `staff_name`
- `staff_code`
- `dept_id`
- `dept_name`
### 6.2 风险统计字段
- `rule_count`
- 该员工命中的规则数,按 `rule_code` 去重
- `model_count`
- 该员工命中的模型数,按 `model_code` 去重
- `hit_count`
- 该员工归并后的原始标签命中总次数
- `risk_level_code`
- 依据现有规则推导 `HIGH / MEDIUM / LOW`
- `top_rule_code`
- `top_rule_name`
- `risk_point`
- 按命中次数降序、规则编码升序拼接规则名
### 6.3 模型与规则快照字段
- `model_codes_csv`
- 员工命中的全部模型编码,逗号分隔,统一按编码排序
- `model_names_json`
- 员工命中的模型名称数组
- `hit_rules_json`
- 员工命中的规则数组,元素建议至少包含:
- `ruleCode`
- `ruleName`
- `riskLevel`
- `modelCode`
- `modelName`
- `hitCount`
- `model_hit_summary_json`
- 员工按模型汇总的数组,元素建议至少包含:
- `modelCode`
- `modelName`
- `warningCount`
- `ruleCount`
- `hitRuleList`
其中:
- `warningCount` 表示该员工在该模型下的原始标签命中次数
- `hitRuleList` 用于页面“异常标签”展示
### 6.4 审计字段
- `create_by`
- `create_time`
- `update_by`
- `update_time`
## 七、员工归并与重算链路
### 7.1 归并口径
员工结果重算时,继续沿用当前结果总览链路中的归并规则:
1. 员工本人命中,归并到本人。
2. 当命中对象为空或为亲属对象时,通过流水身份证号或亲属身份证号映射到员工本人。
3. 无法归并到员工本人的命中记录,不进入结果总览员工结果表。
本轮不调整归并逻辑,只把这一步从页面查询阶段前移到结果表重算阶段。
### 7.2 重算触发时机
触发点放在命中结果写库成功之后、事务提交之前。
触发顺序固定为:
1. 写入当前项目标签结果
2. 删除当前项目历史员工结果
3. 基于当前项目全量标签结果,按员工归并口径重算员工结果
4. 批量插入新的员工结果
5. 同事务刷新项目表中的高/中/低风险人数
6. 提交事务
### 7.3 重算方式
本次采用按项目整块重算,不做增量更新。
原因:
- 需求已明确接受按项目整块重算
- 这样实现路径最短,且避免增量口径漂移
- 同事务内先删后插更容易保证一致性
## 八、页面查询改造方案
改造后,结果总览页查询只依赖:
- `ccdi_project`
- `ccdi_project_overview_employee_result`
### 8.1 风险仪表盘
来源:
- `ccdi_project.target_count`
- 员工结果表按 `risk_level_code` 的人数统计
查询方式:
- 高风险人数:结果表中 `risk_level_code = 'HIGH'`
- 中风险人数:结果表中 `risk_level_code = 'MEDIUM'`
- 低风险人数:结果表中 `risk_level_code = 'LOW'`
- 无风险人数:`target_count - high - medium - low`
### 8.2 风险人员总览
来源:
- 员工结果表单表查询
返回字段:
- `staff_name`
- `staff_id_card`
- `dept_name`
- `hit_count`
- `risk_level_code`
- `model_count`
- `risk_point`
排序延续现有口径:
- 风险等级
- 模型数
- 规则数
- 员工身份证号
### 8.3 模型预警次数统计
来源:
- 员工结果表中的 `model_hit_summary_json`
查询逻辑:
1. 先按项目过滤员工结果表
2. 展开每位员工的模型汇总
3.`modelCode` 聚合
统计口径:
- `warningCount` = 各员工该模型 `warningCount` 求和
- `peopleCount` = 命中该模型的员工人数
排序口径:
- `warningCount desc`
- `model_code asc`
### 8.4 命中模型涉及人员
来源:
- 员工结果表中的 `model_codes_csv`
- `model_names_json`
- `hit_rules_json`
- `model_hit_summary_json`
筛选规则:
- `keyword`:匹配员工姓名或工号
- `deptId`:精确匹配部门
- `modelCodes`
- `ANY`:命中任一所选模型即可
- `ALL`:必须同时命中全部所选模型
实现建议:
- `ANY / ALL` 筛选优先基于 `model_codes_csv`
- `modelNames``hitTagList` 展示基于 JSON 快照组装
这样可以把:
- 结构化筛选
- 模型条件判断
- 分页排序
都控制在结果表层完成,避免回源连表。
## 九、索引建议
为保证页面查询稳定,建议增加如下索引:
- 唯一键:`(project_id, staff_id_card)`
- 普通索引:`(project_id, risk_level_code)`
- 普通索引:`(project_id, dept_id)`
- 普通索引:`(project_id, staff_name)`
- 普通索引:`(project_id, staff_code)`
本轮先不扩展模型专用索引,优先依赖整项目重算后的结果表降本。如果后续 `model_codes_csv` 筛选成为瓶颈,再评估是否引入模型筛选辅助列。
## 十、测试与验收口径
本次设计的验收以“结果表口径与当前业务口径一致”为准,重点验证:
1. 同一项目下,结果表员工数应等于当前可归并出的员工数。
2. 员工亲属的流水异常命中必须归并到员工本人。
3. 风险仪表盘的高/中/低人数与结果表按等级统计一致。
4. 风险人员总览展示字段与结果表字段映射一致。
5. 模型卡片 `warningCount` 必须等于原始标签命中次数汇总。
6. 模型卡片 `peopleCount` 必须等于命中该模型的员工人数。
7. 命中模型涉及人员在 `ANY / ALL / keyword / deptId` 条件下的结果,必须与旧逻辑一致。
## 十一、结论
本次设计采用“单张结果总览员工结果表”作为结果总览页的唯一预聚合载体。
核心结论如下:
1. 结果表粒度为 `项目 + 员工`
2. 员工亲属命中统一归并到员工本人。
3. 命中结果写库成功后,同一事务内按项目整块重算结果表。
4. 页面查询不再直接运行重连表聚合 SQL而是基于结果表聚合或筛选。
5. 模型卡片 `warningCount` 继续保持原始标签命中次数口径。
该方案满足“最短路径实现、口径一致、页面查询降本”的目标,后续可据此输出前后端实施计划并进入实现阶段。