# 结果总览员工结果表设计文档 **模块**: 初核项目详情 - 结果总览 **日期**: 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` 继续保持原始标签命中次数口径。 该方案满足“最短路径实现、口径一致、页面查询降本”的目标,后续可据此输出前后端实施计划并进入实现阶段。