# 项目详情风险总览员工列表分页设计文档

**模块**: 项目详情 - 结果总览 - 风险总览员工列表
**日期**: 2026-03-29

## 一、背景

当前项目详情页 `结果总览 -> 风险总览` 中的员工列表直接消费 `GET /ccdi/project/overview/risk-people` 返回的全量 `overviewList`。页面没有分页能力，项目内风险员工较多时会导致：

1. 单屏信息过长，浏览成本高。
2. 前端一次性渲染全量列表，交互体验不稳定。
3. 接口返回风格与当前结果总览域内其他分页接口不一致。

本轮需求要求为该员工列表增加分页，并固定为每页 5 条。同时，需求明确要求改造现有接口，不采用前端本地切片或新增补丁接口的方式。

## 二、目标

本次设计目标如下：

1. 将 `GET /ccdi/project/overview/risk-people` 改造成真实分页接口。
2. 风险总览员工列表固定每页展示 5 条。
3. 前端翻页时仅刷新员工列表，不影响结果总览内其他区块。
4. 分页接口返回结构对齐项目现有分页风格，统一为 `rows + total + pageNum + pageSize`。

## 三、范围

### 3.1 本次范围

- 改造 `risk-people` 接口入参与返回结构
- 为后端风险人员查询增加数据库分页
- 调整项目详情结果总览首屏数据装配
- 在 `RiskPeopleSection.vue` 增加分页条与翻页请求
- 补充本次设计文档、设计记录，以及后续前后端实施计划入口

### 3.2 不在本次范围

- 不修改风险仪表盘接口
- 不修改风险模型卡片与模型命中人员查询
- 不修改风险明细区块
- 不新增筛选条件、搜索条件或排序条件
- 不修改风险等级、命中模型数、核心异常点的业务口径
- 不删除现有 `GET /ccdi/project/overview/top-risk-people`

## 四、现状分析

### 4.1 前端现状

当前 `PreliminaryCheck.vue` 在页面加载时并发请求：

- `GET /ccdi/project/overview/dashboard`
- `GET /ccdi/project/overview/risk-people`
- `GET /ccdi/project/overview/risk-models/cards`
- `GET /ccdi/project/overview/suspicious-transactions`
- `GET /ccdi/project/overview/employee-credit-negative`

其中 `risk-people` 的返回结果被直接注入 `currentData.riskPeople.overviewList`，`RiskPeopleSection.vue` 直接用该数组渲染表格，没有分页状态，也没有独立二次加载链路。

### 4.2 后端现状

当前 `CcdiProjectOverviewController.getRiskPeople(Long projectId)` 只接收项目 ID。

`CcdiProjectOverviewServiceImpl.getRiskPeopleOverview(Long projectId)` 的实现为：

1. 校验项目存在
2. 调用 `overviewMapper.selectRiskPeopleOverviewByProjectId(projectId)` 查询全量员工结果
3. 在 Java 层逐行映射为 `overviewList`
4. 返回 `CcdiProjectRiskPeopleOverviewVO { overviewList }`

当前 SQL 直接从 `ccdi_project_overview_employee_result` 查询并排序：

- `risk_level_sort asc`
- `model_count desc`
- `rule_count desc`
- `staff_id_card asc`

现状能够提供正确列表语义，但不具备分页能力。

## 五、方案对比

### 5.1 方案 A：改造现有 `risk-people` 为标准分页接口

做法：

- 保持接口路径不变
- 入参扩展为 `projectId + pageNum + pageSize`
- 返回结构改为 `rows + total + pageNum + pageSize`
- 后端通过数据库分页查询返回当前页数据
- 前端首屏与翻页统一走该接口

优点：

- 满足“改接口”的明确要求
- 与 `risk-models/people`、`employee-credit-negative` 的分页风格一致
- 逻辑单一，没有重复接口
- 数据量增大时性能与语义都正确

缺点：

- 需要同步调整前后端契约与测试

### 5.2 方案 B：新增 `risk-people/page`，保留原接口不动

做法：

- 原 `risk-people` 继续返回全量列表
- 新增单独分页接口供前端切换

问题：

- 留下两个语义重复的接口
- 不符合“改接口”的要求
- 增加后续维护成本

### 5.3 方案 C：后端仍全量查，Java 或前端再切页

做法：

- 接口表面返回分页结构
- 实际仍走全量查询后截断

问题：

- 不是真分页
- 数据量增大时性能与语义都不成立
- 属于补丁式方案，不符合最短路径要求

### 5.4 结论

采用方案 A。

## 六、接口设计

### 6.1 接口路径

- `GET /ccdi/project/overview/risk-people`

### 6.2 入参

- `projectId`: 项目 ID，必填
- `pageNum`: 页码，非必填，默认 `1`
- `pageSize`: 每页条数，非必填，默认 `5`

说明：

- 前端固定传 `pageSize = 5`
- 后端默认值同样收敛为 `5`，避免前端漏传时行为偏移

### 6.3 返回结构

返回结构统一为：

```json
{
  "rows": [
    {
      "name": "李四",
      "idNo": "330000000000000001",
      "department": "信息二部",
      "riskCount": 5,
      "riskLevel": "中风险",
      "riskLevelType": "warning",
      "modelCount": 4,
      "riskPoint": "大额单笔收入、疑似兼职",
      "actionLabel": "查看项目"
    }
  ],
  "total": 18,
  "pageNum": 1,
  "pageSize": 5
}
```

说明：

- 原 `overviewList` 字段移除，统一改为 `rows`
- 单行字段保持现有页面绑定语义不变
- 本次不引入额外统计字段

## 七、后端设计

### 7.1 控制器

`CcdiProjectOverviewController.getRiskPeople` 改为接收独立 DTO，例如：

- `CcdiProjectRiskPeopleQueryDTO`

DTO 仅包含：

- `projectId`
- `pageNum`
- `pageSize`

不额外引入筛选项，保持最短路径。

### 7.2 服务层

`ICcdiProjectOverviewService.getRiskPeopleOverview` 改为接收查询 DTO，并返回分页 VO。

服务层职责：

1. 校验项目存在
2. 规范化分页参数，默认 `pageNum=1`、`pageSize=5`
3. 构造 MyBatis Plus `Page`
4. 调用 mapper 分页查询
5. 将记录映射为现有员工列表行结构
6. 返回 `rows + total + pageNum + pageSize`

### 7.3 VO 调整

`CcdiProjectRiskPeopleOverviewVO` 改为标准分页 VO，字段包括：

- `List<CcdiProjectRiskPeopleOverviewItemVO> rows`
- `Long total`
- `Long pageNum`
- `Long pageSize`

说明：

- `CcdiProjectRiskPeopleOverviewItemVO` 本身字段不做语义调整
- 既有的风险等级映射与异常点标签来源保持不变

### 7.4 Mapper 与 SQL

Mapper 从“全量列表查询”改为“分页查询”，直接在数据库层完成分页。

排序规则保持现状不变：

- `risk_level_sort asc`
- `model_count desc`
- `rule_count desc`
- `staff_id_card asc`

数据来源继续使用 `ccdi_project_overview_employee_result`，不新增统计口径，不回退到历史复杂聚合链路。

### 7.5 默认值与边界

- `projectId` 为空时沿用现有参数校验/项目不存在校验逻辑
- `pageNum <= 0` 时按 `1` 处理
- `pageSize <= 0` 或为空时按 `5` 处理
- 本次不开放前端修改每页条数，接口虽接收 `pageSize`，但页面固定使用 5

## 八、前端设计

### 8.1 API 封装

`ruoyi-ui/src/api/ccdi/projectOverview.js` 中：

- `getOverviewRiskPeople` 从接收单个 `projectId` 改为接收 `params`
- 透传：
  - `projectId`
  - `pageNum`
  - `pageSize`

### 8.2 首屏加载

`PreliminaryCheck.vue` 首次加载结果总览时，请求：

- `getOverviewRiskPeople({ projectId, pageNum: 1, pageSize: 5 })`

返回结果注入 `currentData.riskPeople` 时，直接保存分页结构：

- `rows`
- `total`
- `pageNum`
- `pageSize`

页面是否进入 `loaded` 状态的判断，从原来的 `overviewList.length` 改为 `rows.length`。

### 8.3 风险总览员工列表组件

`RiskPeopleSection.vue` 调整为：

1. 表格数据源从 `sectionData.overviewList` 改为 `sectionData.rows`
2. 在表格下方增加分页组件
3. 分页组件固定：
   - `:page-sizes="[5]"`
   - `layout="total, prev, pager, next, jumper"`
4. 页码变化时触发独立请求，仅刷新员工列表分页数据

分页条展示规则：

- `total > 0` 时展示
- `total = 0` 时隐藏

### 8.4 页面刷新策略

翻页时只刷新风险总览员工列表，不重新拉取：

- 风险仪表盘
- 风险模型卡片
- 风险明细涉疑交易
- 风险明细员工负面征信

这样可以避免其他区块闪动，也避免把简单翻页放大成整页重载。

### 8.5 交互保持不变

以下内容本次保持不变：

- 表格列顺序
- 风险等级标签渲染
- 核心异常点标签拆分与色板逻辑
- 操作列文案与点击事件
- 空态文案

## 九、测试设计

### 9.1 后端测试

新增或调整以下验证：

1. Controller 测试
   - 断言 `/risk-people` 接口改为接收 DTO
   - 断言返回 `rows + total + pageNum + pageSize`

2. Service 测试
   - 断言服务层使用分页参数构造 `Page`
   - 断言现有字段映射未变化
   - 断言默认分页参数回落为 `1 / 5`

3. Mapper/SQL 测试
   - 断言风险人员查询改为分页查询方法
   - 断言排序字段未变化
   - 断言数据来源仍为 `ccdi_project_overview_employee_result`

### 9.2 前端测试

新增或调整以下验证：

1. API 封装测试
   - 断言 `getOverviewRiskPeople(params)` 透传 `projectId/pageNum/pageSize`

2. 页面接入测试
   - 断言 `PreliminaryCheck.vue` 首次加载传 `pageNum: 1`
   - 断言固定传 `pageSize: 5`
   - 断言风险人员数据改为读取 `rows`

3. 风险总览组件测试
   - 断言 `RiskPeopleSection.vue` 存在分页组件
   - 断言分页绑定 `rows/total/pageNum/pageSize`
   - 断言分页大小固定为 5

## 十、实施文档要求

本次设计确认后，按仓库规范继续产出两份实施计划：

- 后端实施计划：`docs/plans/backend/`
- 前端实施计划：`docs/plans/frontend/`

实施完成后，补充对应实施记录，记录本次真实改动内容。

## 十一、结论

本次需求本质是将结果总览中的风险员工列表从“全量列表展示”升级为“标准分页列表展示”。

最终方案为：

1. 保持 `GET /ccdi/project/overview/risk-people` 路径不变
2. 改为标准分页接口，返回 `rows + total + pageNum + pageSize`
3. 后端在数据库层做真分页，默认每页 5 条
4. 前端首屏和翻页统一走该接口
5. 翻页仅刷新员工列表，不重载结果总览其他区块

该方案满足需求边界明确、链路完整、实现路径最短，且不引入额外补丁接口或兼容分支。
