14 KiB
结果总览风险接口设计文档
日期: 2026-03-19 模块: 初核项目详情 - 结果总览 作者: Codex 状态: 已确认
一、概述
本文档用于沉淀结果总览页面中以下 3 个区块的后端设计方案:
- 风险仪表盘
- 风险人员总览
- 中高风险人员 TOP10
本次设计目标是为现有结果总览页面补齐真实后端接口,并把项目级高、中、低风险人数的统计口径收拢到流水标签打标链路中,保证项目列表、结果总览仪表盘、风险人员榜单三处口径一致。
二、设计范围
2.1 包含内容
- 新增结果总览 3 个独立后端查询接口
- 新增员工维度风险聚合查询
- 在项目流水标签打标完成后回写项目风险人数
- 输出对应后端实施计划与前端实施计划
2.2 不包含内容
- 不改造结果总览中的风险模型区、风险明细区
- 不新增设计需求之外的导出、弹窗、降级、补丁逻辑
- 不在查询接口中触发重新打标
- 不新增客户、中介等非员工维度榜单
三、当前上下文
3.1 前端现状
结果总览页面已经完成静态结构,相关文件如下:
ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vueruoyi-ui/src/views/ccdiProject/components/detail/OverviewStats.vueruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vueruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js
其中当前页面仍由本地 mock 数据驱动,三块目标区块的字段结构已经稳定:
- 风险仪表盘:
title/subtitle/stats - 风险人员总览:
overviewList - 中高风险人员 TOP10:
topRiskList
3.2 后端现状
当前仓库中还没有结果总览专用后端接口,但已有以下可复用数据基础:
ccdi_project- 已有
target_count - 已有
high_risk_count - 已有
medium_risk_count - 已有
low_risk_count
- 已有
ccdi_bank_statement_tag_result- 已沉淀项目维度的标签命中结果
- 含
project_id/model_code/rule_code/risk_level/bank_statement_id/object_type/object_key
ccdi_base_staff- 员工主数据,含
name/dept_id/id_card
- 员工主数据,含
ccdi_staff_fmy_relation- 员工亲属映射,含
person_id/relation_cert_no/status
- 员工亲属映射,含
sys_dept- 用于补齐所属部门名称
3.3 已确认业务口径
用户已确认以下口径:
- 接口粒度
- 采用 3 个独立接口,而非 1 个汇总接口
- 风险仪表盘口径
总人数 = ccdi_project.target_count高风险/中风险/低风险 = ccdi_project.high_risk_count / medium_risk_count / low_risk_count无风险人员 = 总人数 - 高风险 - 中风险 - 低风险
- 榜单统计对象
- 统计“员工本人 + 员工亲属”
- 若命中亲属,则归并到所属员工名下
- 风险人员总览中的
疑似违规数- 统计归并到该员工名下的打标明细数量
- 包含员工本人命中与亲属命中
- 同一规则多次命中按多条明细累计
- 员工风险等级口径
- 命中规则数
>= 5:高风险 - 命中规则数
2-4:中风险 - 命中规则数
= 1:低风险
- 命中规则数
- TOP10 排序
- 按员工风险等级优先级排序
- 同等级按命中模型数倒序
- 再按命中规则数倒序
- 最后按员工身份证号升序
- 项目表风险人数维护方式
- 在项目流水标签打标完成后回写高、中、低风险人数
四、方案对比与结论
4.1 方案一:新增结果总览专用控制器与 3 个独立接口
优点:
- 与前端 3 个区块一一对应
- 查询职责边界清晰
- 后续风险模型区、风险明细区继续扩展时可沿用同一入口
缺点:
- 需要新增一组 DTO/VO/Mapper 查询
4.2 方案二:把接口直接追加到 CcdiProjectController
优点:
- 表面改动点更少
缺点:
- 项目管理与结果总览查询混在同一控制器
- 后续结果总览继续扩展时边界会变乱
4.3 方案三:提供 1 个汇总接口
优点:
- 请求数更少
缺点:
- 与已确认的“3 个独立接口”目标不符
- 单个区块口径变化会牵动整个响应结构
4.4 最终结论
采用方案一:新增结果总览专用控制器,提供 3 个独立接口,并新增员工维度聚合查询与项目风险人数回写逻辑。
五、接口设计
5.1 控制器落点
建议新增:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java
接口统一挂载到:
/ccdi/project/overview
5.2 接口清单
接口一:查询风险仪表盘
- 方法:
GET - 路径:
/ccdi/project/overview/dashboard - 参数:
projectId - 返回:
AjaxResult.success(data)
返回结构示例:
{
"title": "风险仪表盘",
"subtitle": "风险仪表盘数据概览",
"stats": [
{ "key": "people", "label": "总人数", "value": 500 },
{ "key": "riskPeople", "label": "高风险", "value": 10 },
{ "key": "medium", "label": "中风险", "value": 20 },
{ "key": "low", "label": "低风险", "value": 38 },
{ "key": "count", "label": "无风险人员", "value": 432 }
]
}
接口二:查询风险人员总览
- 方法:
GET - 路径:
/ccdi/project/overview/risk-people - 参数:
projectId - 返回:
AjaxResult.success(data)
返回结构示例:
{
"overviewList": [
{
"name": "李四",
"idNo": "330000000000000001",
"department": "信息二部",
"riskCount": 5,
"riskPoint": "大额单笔收入、疑似兼职",
"actionLabel": "查看详情"
}
]
}
接口三:查询中高风险人员 TOP10
- 方法:
GET - 路径:
/ccdi/project/overview/top-risk-people - 参数:
projectId - 返回:
AjaxResult.success(data)
返回结构示例:
{
"topRiskList": [
{
"name": "张三",
"idNo": "330000000000000002",
"department": "信贷部",
"riskLevel": "高风险",
"riskLevelType": "danger",
"modelCount": 8,
"actionLabel": "查看详情"
}
]
}
六、数据模型设计
6.1 新增 VO
建议新增以下 VO,避免与现有 CcdiProjectVO 混用:
- 仪表盘 VO
CcdiProjectOverviewDashboardVOCcdiProjectOverviewStatVO
- 风险人员总览 VO
CcdiProjectRiskPeopleOverviewVOCcdiProjectRiskPeopleOverviewItemVO
- 中高风险人员 TOP10 VO
CcdiProjectTopRiskPeopleVOCcdiProjectTopRiskPeopleItemVO
- 内部聚合中间 VO
CcdiProjectEmployeeRiskAggregateVO
6.2 内部聚合字段
员工维度聚合中间结果至少包含:
staffIdCardstaffNamedeptIddeptNameruleCountmodelCounthitCounttopRuleCodetopRuleNameriskPointriskLevelCoderiskLevelNameriskLevelSort
其中:
ruleCount = count(distinct rule_code)modelCount = count(distinct model_code)hitCount = count(1),表示归并到员工名下的打标明细数riskLevelCode根据ruleCount映射为HIGH/MEDIUM/LOWriskLevelName映射为高风险/中风险/低风险riskLevelSort仅供 SQL 排序使用,HIGH=1、MEDIUM=2、LOW=3
七、员工维度归并设计
7.1 归并目标
所有榜单都统一归并到员工维度,页面不直接展示亲属维度行。
7.2 归并规则
标签结果转员工归属时,按以下优先级处理:
- 若
object_type = STAFF_ID_CARD且object_key命中员工身份证- 直接归到该员工
- 若
object_key为空,但该条结果关联到流水,且流水cret_no命中员工身份证- 归到该员工
- 若
object_key为空或为亲属证件号,且能通过ccdi_staff_fmy_relation.relation_cert_no关联到员工person_id- 归到该员工
- 若无法归并到员工
- 本次查询中直接丢弃,不进入结果总览榜单
7.3 归并实现原则
- 不改造现有标签结果表结构
- 不为本次需求新增缓存表或汇总表
- 统一通过查询层完成员工归并
八、风险等级与项目人数回写设计
8.1 员工风险等级计算
按员工命中的去重规则数计算:
ruleCount >= 5->HIGH/高风险ruleCount between 2 and 4->MEDIUM/中风险ruleCount = 1->LOW/低风险
8.2 项目表风险人数统计
在员工维度聚合结果基础上统计:
highRiskCount = 员工风险等级为 HIGH 的人数mediumRiskCount = 员工风险等级为 MEDIUM 的人数lowRiskCount = 员工风险等级为 LOW 的人数
8.3 回写时机
项目流水标签任务成功结束时执行回写,链路放在 CcdiBankTagServiceImpl.rebuildProject(...) 内:
- 删除旧标签结果
- 执行全部规则
- 批量写入新标签结果
- 重新按员工维度聚合高、中、低风险人数
- 更新
ccdi_project - 更新任务状态为成功
这样可保证查询接口读到的项目风险人数与最新标签结果一致。
8.4 不采用的方案
不采用“查询时实时扫标签结果并顺手更新项目表”的做法,因为:
- 查询副作用过重
- 口径维护分散
- 项目列表与结果总览可能出现时间差
九、SQL 设计
9.1 Mapper 落点
建议新增:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.javaccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml
9.2 查询职责拆分
Mapper 只负责以下查询:
- 查询项目仪表盘基础数据
- 查询员工维度风险总览列表
- 查询员工维度中高风险 TOP10
- 查询项目员工风险等级分布人数
9.3 员工聚合 SQL 结构
建议采用“公共子查询 + 外层聚合”的方式:
- 先构造标签结果到员工身份证的归并明细
- 再按员工身份证聚合规则数、模型数和核心异常点拼接结果
- 最后外层补部门名称、风险等级和排序字段
建议公共子查询输出字段:
project_idstaff_id_cardrule_coderule_namemodel_code
风险人员总览中的“疑似违规数”使用员工聚合后的 hitCount;风险等级、TOP10 和项目人数回写继续使用 ruleCount。
9.4 核心异常点拼接策略
风险人员总览.riskPoint 采用以下稳定选择策略:
- 先按员工 + 规则维度统计命中次数
- 按命中次数倒序
- 再按
rule_code升序 - 将排序后的全部
rule_name以、拼接成字符串
这样既能展示员工命中的多条核心异常点,也不依赖数据库非确定性行为,不会因为同条规则多次命中而随机波动。
9.5 TOP10 筛选规则
TOP10 查询仅保留:
ruleCount >= 2
即仅展示中风险和高风险员工。
十、服务层设计
10.1 Service 落点
建议新增:
ICcdiProjectOverviewServiceCcdiProjectOverviewServiceImpl
10.2 服务职责
服务层负责:
- 调用 Mapper 查询项目仪表盘基础数据
- 组装前端需要的
title/subtitle/stats - 调用员工聚合查询并映射到列表 VO
- 在打标完成后调用项目风险人数回写逻辑
- 处理空数据场景
10.3 空数据处理
- 仪表盘
- 若项目存在但风险人数为空,按
0返回
- 若项目存在但风险人数为空,按
- 风险人员总览
- 返回
overviewList: []
- 返回
- TOP10
- 返回
topRiskList: []
- 返回
十一、控制器与权限设计
控制器沿用现有项目模块风格:
- 使用
@RestController - 使用
@Tag - 使用
@Operation - 使用
@PreAuthorize("@ss.hasPermi('ccdi:project:query')") - 返回
AjaxResult.success(...)
本次不新增新的权限标识,直接复用项目查询权限。
十二、异常处理设计
12.1 查询接口
- 项目不存在:返回业务异常,提示“项目不存在”
projectId为空:走参数校验失败
12.2 回写链路
若项目风险人数回写失败:
- 视为本次打标任务失败
- 不允许出现“标签写入成功但项目人数未更新且任务仍成功”的状态
原因是本次需求要求三处口径一致,项目人数回写属于打标成功链路的一部分,而不是可选附加动作。
十三、测试设计
13.1 后端单元/集成测试重点
- 风险仪表盘
- 校验
无风险人员计算逻辑
- 校验
- 风险人员总览
- 校验员工本人命中可正常归并
- 校验亲属命中可正常归并到员工
- 校验
riskCount = 打标明细数 - 校验
riskPoint多规则拼接顺序稳定
- TOP10
- 校验仅返回中高风险
- 校验排序规则正确
- 打标回写
- 校验任务成功后项目表风险人数被更新
- 校验回写失败时任务整体失败
13.2 前端联调验证重点
- mock 替换为 3 个真实接口
- 字段命名与当前页面结构一致
- 空列表时页面能正确展示空表格
十四、风险与约束
14.1 已知约束
- 当前标签结果表未直接存员工归属字段,需要通过查询归并
- 部分标签结果可能来自
bank_statement_id关联的流水,而非直接对象命中
14.2 风险控制
- 统一通过员工聚合查询输出榜单,避免前后接口各自实现一套归并逻辑
- 项目风险人数只在打标成功后回写,避免查询阶段产生副作用
- 不引入额外缓存与汇总表,保持最短实现路径
十五、最终结论
本方案采用“结果总览专用接口 + 标签完成后回写项目风险人数”的最短路径实现:
- 结果总览新增 3 个独立接口
- 风险榜单统一按员工维度聚合
- 员工风险等级按命中规则条数区间计算
- 项目表高、中、低风险人数在打标完成后统一回写
- 项目列表、风险仪表盘、风险人员榜单三处口径保持一致