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

14 KiB
Raw Blame History

项目详情风险明细统一导出后端 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 中新增一个测试,断言新方法:

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 中新增测试,约束控制器只做委托:

MockHttpServletResponse response = new MockHttpServletResponse();
controller.exportRiskDetails(response, 40L);
verify(overviewService).exportRiskDetails(same(response), same(40L));
  • Step 3: 运行后端定向测试,确认失败点正确

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test

Expected:

  • FAIL提示控制器缺少 exportRiskDetails 方法或签名不匹配

  • Step 4: 最小化补齐控制器方法签名

CcdiProjectOverviewController.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:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test

Expected:

  • PASS

  • Step 6: 提交本任务

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 的断言:

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

示例断言:

List<CcdiProjectEmployeeCreditNegativeExcel> rows = service.exportEmployeeCreditNegative(40L);
assertEquals("李四", rows.getFirst().getPersonName());
verify(overviewMapper).selectEmployeeCreditNegativeList(40L);
  • Step 3: 运行测试确认失败

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest test

Expected:

  • FAIL提示缺少 Mapper select 或服务方法

  • Step 4: 实现最小导出链路

按以下顺序补代码:

  1. 创建 CcdiProjectEmployeeCreditNegativeExcel.java
  2. ICcdiProjectOverviewService 增加:
List<CcdiProjectEmployeeCreditNegativeExcel> exportEmployeeCreditNegative(Long projectId);
  1. CcdiProjectOverviewMapper.java 增加:
List<CcdiProjectEmployeeCreditNegativeItemVO> selectEmployeeCreditNegativeList(@Param("projectId") Long projectId);
  1. CcdiProjectOverviewMapper.xml 新增非分页导出 SQL
  2. CcdiProjectOverviewServiceImpl 中增加映射方法,把 ItemVO 转为 Excel 行对象
  • Step 5: 重新运行定向测试

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest test

Expected:

  • PASS

  • Step 6: 提交本任务

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 读取结果验证。

关键断言示例:

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:

mvn -pl ccdi-project -Dtest=CcdiProjectRiskDetailWorkbookExporterTest test

Expected:

  • FAIL提示缺少导出器类

  • Step 3: 写最小工作簿导出器

创建 CcdiProjectRiskDetailWorkbookExporter,职责仅限:

  • 写入 涉疑交易明细 sheet
  • 写入 员工负面征信信息 sheet
  • 写入仅含表头的 异常账户人员信息 sheet
  • 统一设置文件名和响应头

不要在这个类里做项目校验或数据库查询。

  • Step 4: 重新运行工作簿测试

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectRiskDetailWorkbookExporterTest test

Expected:

  • PASS

  • Step 5: 提交本任务

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:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest test

Expected:

  • FAIL提示 exportRiskDetails 尚未真正实现

  • Step 3: 实现统一导出主流程

CcdiProjectOverviewServiceImpl 中补齐:

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:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest,CcdiProjectRiskDetailWorkbookExporterTest,CcdiProjectOverviewMapperSqlTest test

Expected:

  • PASS

可选补充:

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: 提交本任务

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

  • 运行:
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest,CcdiProjectRiskDetailWorkbookExporterTest,CcdiProjectOverviewMapperSqlTest test
  • 确认导出接口路径为 POST /ccdi/project/overview/risk-details/export
  • 确认导出文件包含 3 个 sheet第三个 sheet 只有表头
  • 确认旧的 /suspicious-transactions/export 仍保留,避免影响其他调用方