Files
ccdi/docs/plans/backend/2026-03-30-project-detail-risk-details-unified-export-implementation-plan.md

386 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目详情风险明细统一导出后端 Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
>
> 仓库约束:当前仓库明确禁止开启 subagent执行时统一使用 `superpowers:executing-plans`。
**Goal:** 为项目详情风险明细新增统一后端导出能力,输出一个包含 `涉疑交易明细``员工负面征信信息``异常账户人员信息` 三个 sheet 的 Excel 文件。
**Architecture:** 复用现有涉疑交易导出查询口径,补充员工负面征信的非分页导出查询,再由后端统一组装工作簿并输出响应流。异常账户人员信息本轮不开发真实查询,只输出固定表头的空白 sheet保证导出文件结构稳定。
**Tech Stack:** Java 21, Spring Boot 3, MyBatis XML, EasyExcel, JUnit 5, Mockito
---
## File Map
**Modify:**
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java`
- 新增统一导出接口,保留现有查询接口不变
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java`
- 新增统一导出方法和员工负面征信导出方法定义
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java`
- 组装统一导出数据、校验项目、调用工作簿导出器
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java`
- 新增员工负面征信导出列表查询方法
- `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml`
- 新增员工负面征信非分页导出 SQL
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java`
- 覆盖新接口的委托行为
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java`
- 覆盖新接口路径、注解和方法签名
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceEmployeeCreditNegativeTest.java`
- 补充员工负面征信导出查询映射与项目校验
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java`
- 覆盖新增导出 SQL 的表、关联和排序口径
- `docs/reports/implementation/2026-03-30-project-detail-risk-details-unified-export-backend-implementation.md`
- 记录后端实际改动与验证结果
**Create:**
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectEmployeeCreditNegativeExcel.java`
- 定义 `员工负面征信信息` sheet 行结构
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectRiskDetailWorkbookExporter.java`
- 统一负责生成 3-sheet 工作簿并写入响应流
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectRiskDetailWorkbookExporterTest.java`
- 断言 sheet 顺序、表头和空白异常账户 sheet
## Task 1: 锁定统一导出接口契约
**Files:**
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java`
- [ ] **Step 1: 先写控制器契约测试**
`CcdiProjectOverviewControllerContractTest` 中新增一个测试,断言新方法:
```java
Method method = controllerClass.getMethod(
"exportRiskDetails",
HttpServletResponse.class,
Long.class
);
PostMapping postMapping = method.getAnnotation(PostMapping.class);
assertEquals("/risk-details/export", postMapping.value()[0]);
```
- [ ] **Step 2: 补控制器单测,先让它失败**
`CcdiProjectOverviewControllerTest` 中新增测试,约束控制器只做委托:
```java
MockHttpServletResponse response = new MockHttpServletResponse();
controller.exportRiskDetails(response, 40L);
verify(overviewService).exportRiskDetails(same(response), same(40L));
```
- [ ] **Step 3: 运行后端定向测试,确认失败点正确**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test
```
Expected:
- FAIL提示控制器缺少 `exportRiskDetails` 方法或签名不匹配
- [ ] **Step 4: 最小化补齐控制器方法签名**
`CcdiProjectOverviewController.java` 中增加方法骨架:
```java
@PostMapping("/risk-details/export")
@Operation(summary = "导出风险明细")
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
public void exportRiskDetails(HttpServletResponse response, Long projectId) {
overviewService.exportRiskDetails(response, projectId);
}
```
- [ ] **Step 5: 重新运行控制器测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test
```
Expected:
- PASS
- [ ] **Step 6: 提交本任务**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java
git commit -m "补充风险明细统一导出接口契约"
```
## Task 2: 补齐员工负面征信导出数据链路
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectEmployeeCreditNegativeExcel.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java`
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceEmployeeCreditNegativeTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java`
- [ ] **Step 1: 先写 Mapper SQL 断言**
`CcdiProjectOverviewMapperSqlTest` 中新增对 `selectEmployeeCreditNegativeList` 的断言:
```java
String exportSql = extractSelect(xml, "selectEmployeeCreditNegativeList");
assertTrue(exportSql.contains("from ccdi_project_overview_employee_result"));
assertTrue(exportSql.contains("inner join ccdi_credit_negative_info"));
assertTrue(exportSql.contains("order by neg.query_date desc, neg.person_id asc"));
```
- [ ] **Step 2: 再写服务层失败测试**
`CcdiProjectOverviewServiceEmployeeCreditNegativeTest` 中新增两个测试:
- 导出时项目存在且能返回 `List<CcdiProjectEmployeeCreditNegativeExcel>`
- 项目不存在时抛出 `ServiceException`
示例断言:
```java
List<CcdiProjectEmployeeCreditNegativeExcel> rows = service.exportEmployeeCreditNegative(40L);
assertEquals("李四", rows.getFirst().getPersonName());
verify(overviewMapper).selectEmployeeCreditNegativeList(40L);
```
- [ ] **Step 3: 运行测试确认失败**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest test
```
Expected:
- FAIL提示缺少 Mapper select 或服务方法
- [ ] **Step 4: 实现最小导出链路**
按以下顺序补代码:
1. 创建 `CcdiProjectEmployeeCreditNegativeExcel.java`
2.`ICcdiProjectOverviewService` 增加:
```java
List<CcdiProjectEmployeeCreditNegativeExcel> exportEmployeeCreditNegative(Long projectId);
```
3.`CcdiProjectOverviewMapper.java` 增加:
```java
List<CcdiProjectEmployeeCreditNegativeItemVO> selectEmployeeCreditNegativeList(@Param("projectId") Long projectId);
```
4.`CcdiProjectOverviewMapper.xml` 新增非分页导出 SQL
5.`CcdiProjectOverviewServiceImpl` 中增加映射方法,把 `ItemVO` 转为 `Excel` 行对象
- [ ] **Step 5: 重新运行定向测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest test
```
Expected:
- PASS
- [ ] **Step 6: 提交本任务**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectEmployeeCreditNegativeExcel.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java \
ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceEmployeeCreditNegativeTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java
git commit -m "补充风险明细负面征信导出链路"
```
## Task 3: 实现统一工作簿导出器
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectRiskDetailWorkbookExporter.java`
- Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectRiskDetailWorkbookExporterTest.java`
- [ ] **Step 1: 先写工作簿测试**
新增 `CcdiProjectRiskDetailWorkbookExporterTest`,覆盖:
- 工作簿固定包含 3 个 sheet
- 顺序为 `涉疑交易明细``员工负面征信信息``异常账户人员信息`
- 第三个 sheet 只有表头
建议使用 `ByteArrayOutputStream + EasyExcel.read/POI` 读取结果验证。
关键断言示例:
```java
assertEquals("涉疑交易明细", workbook.getSheetAt(0).getSheetName());
assertEquals("员工负面征信信息", workbook.getSheetAt(1).getSheetName());
assertEquals("异常账户人员信息", workbook.getSheetAt(2).getSheetName());
assertEquals("账号", workbook.getSheetAt(2).getRow(0).getCell(0).getStringCellValue());
```
- [ ] **Step 2: 运行测试确认失败**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectRiskDetailWorkbookExporterTest test
```
Expected:
- FAIL提示缺少导出器类
- [ ] **Step 3: 写最小工作簿导出器**
创建 `CcdiProjectRiskDetailWorkbookExporter`,职责仅限:
- 写入 `涉疑交易明细` sheet
- 写入 `员工负面征信信息` sheet
- 写入仅含表头的 `异常账户人员信息` sheet
- 统一设置文件名和响应头
不要在这个类里做项目校验或数据库查询。
- [ ] **Step 4: 重新运行工作簿测试**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectRiskDetailWorkbookExporterTest test
```
Expected:
- PASS
- [ ] **Step 5: 提交本任务**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectRiskDetailWorkbookExporter.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectRiskDetailWorkbookExporterTest.java
git commit -m "补充风险明细多sheet导出器"
```
## Task 4: 串起服务实现并完成后端验证
**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java`
- Modify: `docs/reports/implementation/2026-03-30-project-detail-risk-details-unified-export-backend-implementation.md`
- [ ] **Step 1: 先写服务层失败测试**
如果现有 `CcdiProjectOverviewServiceImplTest` 更合适,就在其中新增;否则新建一个导出专用测试类。至少覆盖:
- 服务内部调用现有 `exportSuspiciousTransactions`
- 服务调用 `exportEmployeeCreditNegative`
- 服务把两份真实数据交给 `CcdiProjectRiskDetailWorkbookExporter`
- 项目不存在时直接失败
- [ ] **Step 2: 运行服务测试确认失败**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest test
```
Expected:
- FAIL提示 `exportRiskDetails` 尚未真正实现
- [ ] **Step 3: 实现统一导出主流程**
`CcdiProjectOverviewServiceImpl` 中补齐:
```java
public void exportRiskDetails(HttpServletResponse response, Long projectId) {
ensureProjectExists(projectId);
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
queryDTO.setProjectId(projectId);
queryDTO.setSuspiciousType("ALL");
List<CcdiProjectSuspiciousTransactionExcel> suspiciousRows = exportSuspiciousTransactions(queryDTO);
List<CcdiProjectEmployeeCreditNegativeExcel> creditRows = exportEmployeeCreditNegative(projectId);
workbookExporter.export(response, projectId, suspiciousRows, creditRows);
}
```
- [ ] **Step 4: 跑完整后端回归**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest,CcdiProjectRiskDetailWorkbookExporterTest,CcdiProjectOverviewMapperSqlTest test
```
Expected:
- PASS
可选补充:
```bash
mvn -pl ccdi-project test
```
- [ ] **Step 5: 写后端实施记录**
`docs/reports/implementation/2026-03-30-project-detail-risk-details-unified-export-backend-implementation.md` 中记录:
- 新增接口与方法
- 多 sheet 工作簿实现
- 员工负面征信导出链路
- 异常账户空白 sheet 处理
- 实际执行的测试命令与结果
- [ ] **Step 6: 提交本任务**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java \
docs/reports/implementation/2026-03-30-project-detail-risk-details-unified-export-backend-implementation.md
git commit -m "完成风险明细统一导出后端实现"
```
## Final Verification
- [ ] 运行:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest,CcdiProjectRiskDetailWorkbookExporterTest,CcdiProjectOverviewMapperSqlTest test
```
- [ ] 确认导出接口路径为 `POST /ccdi/project/overview/risk-details/export`
- [ ] 确认导出文件包含 3 个 sheet第三个 sheet 只有表头
- [ ] 确认旧的 `/suspicious-transactions/export` 仍保留,避免影响其他调用方