diff --git a/docs/design/2026-03-30-project-detail-risk-people-export-design.md b/docs/design/2026-03-30-project-detail-risk-people-export-design.md new file mode 100644 index 00000000..3442f1bd --- /dev/null +++ b/docs/design/2026-03-30-project-detail-risk-people-export-design.md @@ -0,0 +1,340 @@ +# 项目详情风险总览人员列表导出设计文档 + +**模块**: 项目详情 - 结果总览 - 风险总览人员列表导出 +**日期**: 2026-03-30 +**作者**: Codex +**状态**: 已确认 + +## 一、背景 + +当前项目详情页 `结果总览 -> 风险总览` 中的人员列表已经支持分页展示与“查看项目”操作,但列表上方的“导出”按钮仍未接入真实导出能力。 + +结合现有页面语义与本次确认结果,本轮需求要求: + +1. 在 `开发风险总览` 的人员列表中补齐真实导出能力。 +2. 导出范围为当前项目下的全部风险人员,而不是当前分页 5 条。 +3. 导出字段口径必须与页面表格保持一致。 +4. 导出文件中不包含“操作”列。 + +## 二、目标 + +本次设计目标如下: + +1. 为风险总览人员列表新增独立导出接口。 +2. 前端点击“导出”按钮后可直接下载 Excel 文件。 +3. 导出字段与页面展示字段保持一致: + - 姓名 + - 身份证号 + - 所属部门 + - 疑似违规数 + - 风险等级 + - 命中模型数 + - 核心异常点 +4. 分页展示与导出逻辑解耦,翻页不影响导出范围。 + +## 三、范围 + +### 3.1 本次范围 + +- 新增风险总览人员列表导出接口 +- 新增风险总览人员导出 Excel 对象 +- 为前端“导出”按钮绑定真实下载动作 +- 补充本次设计文档,以及后续前后端实施计划入口 + +### 3.2 不在本次范围 + +- 不新增筛选条件、排序条件、搜索条件 +- 不修改风险总览人员列表当前分页查询逻辑 +- 不修改风险模型区、风险明细区功能 +- 不修改人员详情、项目分析弹窗链路 +- 不新增导出确认弹窗、二次筛选或降级方案 + +## 四、现状分析 + +### 4.1 前端现状 + +当前核心组件为: + +- `ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue` + +当前组件已经具备: + +1. 表格字段展示 +2. “查看项目”操作 +3. 固定每页 5 条的分页能力 +4. 顶部“导出”按钮文案 + +但当前“导出”按钮尚未绑定点击事件,也未接入下载接口。 + +### 4.2 后端现状 + +当前结果总览控制器为: + +- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java` + +已具备以下相关能力: + +1. `GET /ccdi/project/overview/risk-people`:分页查询风险人员总览 +2. `POST /ccdi/project/overview/suspicious-transactions/export`:导出涉疑交易明细 +3. `POST /ccdi/project/overview/risk-details/export`:统一导出风险明细 + +说明当前结果总览域内已经存在标准后端导出模式: + +- 控制器暴露导出接口 +- 服务层返回导出行对象列表 +- 使用 `ExcelUtil` 输出 Excel 文件 + +风险人员列表本身已经有稳定的查询与字段组装逻辑: + +1. 服务层通过 `getRiskPeopleOverview(CcdiProjectRiskPeopleQueryDTO queryDTO)` 查询分页数据 +2. 使用 `buildRiskPeopleItem(projectId, aggregate)` 统一组装单行字段 + +因此,本次导出最合理的方式是复用现有字段组装逻辑,而不是重新设计一套独立口径。 + +## 五、方案对比 + +### 5.1 方案 A:新增后端导出接口,复用现有人员字段组装逻辑 + +做法: + +- 新增 `POST /ccdi/project/overview/risk-people/export` +- 前端导出按钮直接调用下载接口 +- 后端按当前项目查询全部风险人员 +- 复用现有人员列表组装逻辑,再映射到 Excel 对象 + +优点: + +- 最符合当前仓库导出模式 +- 前端改动最小 +- 页面字段与导出口径最容易保持一致 +- 不会把分页展示逻辑和导出逻辑耦合在一起 + +缺点: + +- 需要补一个导出 Excel 对象与对应测试 + +### 5.2 方案 B:前端请求超大分页,自行导出 Excel + +做法: + +- 点击导出时调用 `risk-people` 接口并传超大 `pageSize` +- 前端拿到全量数据后自行生成文件 + +问题: + +- 不符合当前仓库常用导出模式 +- 页面查询接口与导出职责混杂 +- 前端文件生成链路与现有维护方式不一致 + +### 5.3 方案 C:新增导出接口,但单独写一套导出 SQL 和字段组装 + +做法: + +- 后端新写独立导出查询 +- 页面查询与导出查询分别维护 + +问题: + +- 页面与导出会形成两套口径 +- 后续字段扩展时最容易出现偏移 +- 不符合最短路径要求 + +### 5.4 结论 + +采用 **方案 A:新增后端导出接口,复用现有人员字段组装逻辑**。 + +## 六、总体设计 + +### 6.1 交互链路 + +点击“导出”后的数据流如下: + +1. 用户在 `RiskPeopleSection.vue` 点击“导出”按钮。 +2. 前端通过 `this.download(...)` 调用新的后端导出接口。 +3. 前端仅传 `projectId`,不传当前页码、页大小等分页参数。 +4. 控制器接收请求后调用 `overviewService.exportRiskPeopleOverview(projectId)`。 +5. 服务层校验项目存在后,查询当前项目下全部风险人员。 +6. 服务层复用现有风险人员字段组装逻辑,生成 Excel 行对象列表。 +7. 控制器使用 `ExcelUtil` 输出 `xlsx` 文件。 + +### 6.2 导出范围 + +导出范围固定为: + +- 当前项目下的全部风险人员 + +说明: + +- 不受当前分页影响 +- 不只导出当前页 5 条 +- 不增加额外筛选条件 + +### 6.3 导出字段 + +导出列固定如下: + +1. 姓名 +2. 身份证号 +3. 所属部门 +4. 疑似违规数 +5. 风险等级 +6. 命中模型数 +7. 核心异常点 + +约束: + +- 不导出“操作”列 +- 不导出页面专用辅助字段,例如 `riskLevelType` +- 核心异常点展示口径需与页面一致 + +## 七、后端设计 + +### 7.1 控制器 + +在 `CcdiProjectOverviewController` 中新增导出接口: + +- `POST /ccdi/project/overview/risk-people/export` + +接口入参: + +- `projectId` + +权限控制沿用当前结果总览域的查询权限,不单独新增本轮设计范围外的权限体系。 + +### 7.2 服务层 + +在 `ICcdiProjectOverviewService` 与实现类中新增方法: + +- `List exportRiskPeopleOverview(Long projectId)` + +服务层职责: + +1. 校验项目存在 +2. 查询当前项目全部风险人员 +3. 复用现有人员列表字段组装逻辑 +4. 将页面字段映射为导出对象 +5. 返回 Excel 数据列表 + +### 7.3 查询策略 + +导出查询不复用当前分页接口返回值,也不走前端多页聚合。 + +建议实现方式: + +1. 后端直接基于现有风险人员数据来源查询当前项目全部记录 +2. 排序口径与分页列表保持一致: + - `risk_level_sort asc` + - `model_count desc` + - `rule_count desc` + - `staff_id_card asc` + +这样可以保证页面与导出的记录顺序一致。 + +### 7.4 导出对象 + +新增导出对象: + +- `CcdiProjectRiskPeopleOverviewExcel` + +字段包含: + +- `name` +- `idNo` +- `department` +- `riskCount` +- `riskLevel` +- `modelCount` +- `riskPoint` + +其中: + +- `riskPoint` 需要输出与页面“核心异常点”一致的文本口径 +- 如果页面当前优先使用 `riskPointTagList` 组装展示,则导出时也应使用同样的归并顺序 + +## 八、前端设计 + +### 8.1 API 封装 + +在 `ruoyi-ui/src/api/ccdi/projectOverview.js` 中新增导出调用入口,供组件通过 `this.download(...)` 使用。 + +接口路径: + +- `ccdi/project/overview/risk-people/export` + +### 8.2 组件交互 + +在 `RiskPeopleSection.vue` 中: + +1. 为当前“导出”按钮绑定点击事件 +2. 点击后调用下载方法 +3. 仅传 `projectId` +4. 文件名统一使用时间戳后缀 + +建议文件名: + +- `风险人员总览_.xlsx` + +### 8.3 交互约束 + +- 不新增确认弹窗 +- 不新增 loading 外挂交互 +- 不修改现有分页逻辑 +- 不影响“查看项目”按钮行为 + +## 九、异常处理 + +本次导出错误处理遵循结果总览当前后端风格: + +1. `projectId` 为空或项目不存在时,导出直接失败。 +2. 查询异常或 Excel 生成异常时,导出整体失败。 +3. 不生成空白文件作为兜底结果。 +4. 不引入静默降级或兼容性补丁分支。 + +字段空值处理约束: + +- 若部分字段为空,按现有页面语义导出为空字符串或统一文本结果 +- 不因单行字段为空而跳过整行 + +## 十、测试口径 + +### 10.1 后端验证 + +需要验证: + +1. 新增导出接口路径正确,且能委托到服务层 +2. 导出服务会校验项目存在 +3. 导出结果为当前项目全部风险人员,而不是当前页 +4. 导出字段顺序与名称正确 +5. 导出数据口径与页面表格一致 + +### 10.2 前端验证 + +需要验证: + +1. “导出”按钮已绑定下载动作 +2. 下载请求只传 `projectId` +3. 翻页功能仍正常 +4. “查看项目”功能仍正常 + +## 十一、实施计划输出要求 + +根据仓库约定,进入实施阶段时默认同步输出两份实施计划: + +1. 后端实施计划:`docs/plans/backend/` +2. 前端实施计划:`docs/plans/frontend/` + +同时补充: + +- 实施记录:`docs/reports/implementation/` +- 测试记录:`docs/tests/records/` + +## 十二、结论 + +本次功能采用“后端新增导出接口 + 复用现有风险人员字段组装逻辑 + 前端按钮直连下载”的实现方式。 + +该方案满足以下要求: + +1. 导出范围为当前项目全部风险人员 +2. 导出字段与页面口径一致 +3. 不引入额外筛选、补丁或兼容性分支 +4. 改动路径短,符合当前仓库的导出实现风格