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

10 KiB
Raw Permalink Blame History

结果总览员工结果表设计文档

模块: 初核项目详情 - 结果总览 日期: 2026-03-20 状态: 已确认

一、背景

当前结果总览页已接通的查询包括:

  • 风险仪表盘
  • 风险人员总览
  • 模型预警次数统计
  • 命中模型涉及人员

现状中,这些查询依赖 ccdi_bank_statement_tag_resultccdi_bank_statementccdi_base_staffccdi_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
  • modelNameshitTagList 展示基于 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 继续保持原始标签命中次数口径。

该方案满足“最短路径实现、口径一致、页面查询降本”的目标,后续可据此输出前后端实施计划并进入实现阶段。