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

12 KiB
Raw Blame History

Project Detail Risk People Export Backend 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.

Repo note: 本仓库 AGENTS.md 明确禁止开启 subagent执行本计划时请在当前会话使用 superpowers:executing-plans

Goal: 为项目详情“结果总览 -> 风险总览”人员列表补齐后端 Excel 导出能力,导出当前项目全部风险人员,并保证导出口径与页面表格字段一致。

Architecture: 后端继续沿用 CcdiProjectOverviewController -> ICcdiProjectOverviewService -> CcdiProjectOverviewServiceImpl -> CcdiProjectOverviewMapper.xml 这条结果总览链路,不新增平行模块或补丁接口。分页查询与导出查询共用同一组风险人员列映射与排序口径,服务层继续复用 buildRiskPeopleItem(...) 统一组装页面字段,再映射为导出 Excel 对象,避免页面与导出出现两套业务口径。

Tech Stack: Java 21, Spring Boot 3, MyBatis Plus, MyBatis XML, ExcelUtil, JUnit 5, Mockito, Maven, Swagger/OpenAPI


Task 1: 建立风险人员导出契约

Files:

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectRiskPeopleOverviewExcel.java

  • Modify: ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java

  • Modify: ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java

  • Test: ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java

  • Step 1: 先写失败测试锁定导出接口路径、Service 新签名和 Excel 对象列

Method method = CcdiProjectOverviewController.class.getMethod(
    "exportRiskPeople",
    HttpServletResponse.class,
    Long.class
);
assertEquals("/risk-people/export", method.getAnnotation(PostMapping.class).value()[0]);
assertNotNull(ICcdiProjectOverviewService.class.getMethod("exportRiskPeopleOverview", Long.class));
assertNotNull(CcdiProjectRiskPeopleOverviewExcel.class.getDeclaredField("name"));
assertNotNull(CcdiProjectRiskPeopleOverviewExcel.class.getDeclaredField("riskPoint"));
  • Step 2: 运行控制器结构测试,确认当前仓库还没有风险人员导出入口

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest test

Expected:

  • FAIL提示 exportRiskPeopleexportRiskPeopleOverview 尚不存在

  • Step 3: 写最小导出契约

@PostMapping("/risk-people/export")
@Operation(summary = "导出风险人员总览")
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
public void exportRiskPeople(HttpServletResponse response, Long projectId) {
    List<CcdiProjectRiskPeopleOverviewExcel> rows = overviewService.exportRiskPeopleOverview(projectId);
    ExcelUtil<CcdiProjectRiskPeopleOverviewExcel> util =
        new ExcelUtil<>(CcdiProjectRiskPeopleOverviewExcel.class);
    util.exportExcel(response, rows, "风险人员总览");
}
default List<CcdiProjectRiskPeopleOverviewExcel> exportRiskPeopleOverview(Long projectId) {
    return List.of();
}
@Excel(name = "姓名")
private String name;
  • Step 4: 回跑控制器结构测试,确认导出外壳建立完成

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest test

Expected:

  • PASS

  • 控制器测试能断言 /risk-people/export

  • 权限仍为 ccdi:project:query

  • Step 5: 提交本任务

git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiProjectRiskPeopleOverviewExcel.java \
  ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java \
  ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java \
  ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java
git commit -m "补充风险总览人员导出接口契约"

Task 2: 让分页查询与导出查询共用同一套风险人员口径

Files:

  • Modify: ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java

  • Modify: ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml

  • Test: ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java

  • Step 1: 先写失败测试,锁定导出查询、排序和共用 SQL 片段

String xml = readMapperXml();
assertTrue(xml.contains("<sql id=\"riskPeopleOverviewSelectColumns\">"), xml);
assertTrue(xml.contains("<sql id=\"riskPeopleOverviewOrderBy\">"), xml);
assertTrue(xml.contains("selectRiskPeopleOverviewPage"), xml);
assertTrue(xml.contains("selectRiskPeopleOverviewList"), xml);
assertTrue(xml.contains("order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc"), xml);
  • Step 2: 运行 SQL 测试,确认当前 Mapper 还没有导出列表查询

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest test

Expected:

  • FAIL提示缺少 selectRiskPeopleOverviewList

  • FAIL提示未抽出共用 SQL 片段

  • Step 3: 写最小共用查询结构

List<CcdiProjectEmployeeRiskAggregateVO> selectRiskPeopleOverviewList(@Param("projectId") Long projectId);
<sql id="riskPeopleOverviewSelectColumns">
    result.staff_id_card,
    result.staff_name,
    result.dept_id,
    result.dept_name,
    result.rule_count,
    result.model_count,
    result.hit_count,
    null as top_rule_code,
    null as top_rule_name,
    result.risk_point,
    result.risk_level_code,
    case
        when result.risk_level_code = 'HIGH' then '高风险'
        when result.risk_level_code = 'MEDIUM' then '中风险'
        else '低风险'
    end as risk_level_name,
    case
        when result.risk_level_code = 'HIGH' then 1
        when result.risk_level_code = 'MEDIUM' then 2
        else 3
    end as risk_level_sort
</sql>
<sql id="riskPeopleOverviewOrderBy">
    order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc
</sql>
  • Step 4: 回跑 SQL 测试,确认分页与导出将使用同一套排序和列映射

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest test

Expected:

  • PASS

  • selectRiskPeopleOverviewPageselectRiskPeopleOverviewList 共用列与排序片段

  • Step 5: 提交本任务

git add 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/mapper/CcdiProjectOverviewMapperSqlTest.java
git commit -m "统一风险总览人员查询与导出口径"

Task 3: 实现服务层导出映射并复用现有字段组装逻辑

Files:

  • Modify: ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java

  • Test: ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java

  • Step 1: 先写失败测试,锁定“导出全部人员”和字段映射

when(projectMapper.selectById(40L)).thenReturn(project);
when(overviewMapper.selectRiskPeopleOverviewList(40L)).thenReturn(List.of(aggregate));
when(overviewMapper.selectRiskHitTagsByScope(40L, "330000000000000001", null)).thenReturn(List.of(
    buildHitTag("LARGE_TRANSACTION", "大额交易模型", "RULE_A", "大额单笔收入", "HIGH"),
    buildHitTag("PART_TIME", "兼职取酬模型", "RULE_B", "疑似兼职", "MEDIUM")
));

List<CcdiProjectRiskPeopleOverviewExcel> rows = service.exportRiskPeopleOverview(40L);

assertEquals(1, rows.size());
assertEquals("李四", rows.getFirst().getName());
assertEquals("高风险", rows.getFirst().getRiskLevel());
assertEquals("大额单笔收入、疑似兼职", rows.getFirst().getRiskPoint());
verify(overviewMapper).selectRiskPeopleOverviewList(40L);
  • Step 2: 运行 Service 测试,确认当前实现还没有导出映射

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest test

Expected:

  • FAIL提示 exportRiskPeopleOverview 尚未实现

  • Step 3: 写最小服务实现

@Override
public List<CcdiProjectRiskPeopleOverviewExcel> exportRiskPeopleOverview(Long projectId) {
    ensureProjectExists(projectId);

    return defaultList(overviewMapper.selectRiskPeopleOverviewList(projectId)).stream()
        .map(aggregate -> buildRiskPeopleItem(projectId, aggregate))
        .map(this::buildRiskPeopleExcelRow)
        .toList();
}
private CcdiProjectRiskPeopleOverviewExcel buildRiskPeopleExcelRow(CcdiProjectRiskPeopleOverviewItemVO item) {
    CcdiProjectRiskPeopleOverviewExcel row = new CcdiProjectRiskPeopleOverviewExcel();
    row.setName(item.getName());
    row.setIdNo(item.getIdNo());
    row.setDepartment(item.getDepartment());
    row.setRiskCount(item.getRiskCount());
    row.setRiskLevel(item.getRiskLevel());
    row.setModelCount(item.getModelCount());
    row.setRiskPoint(item.getRiskPoint());
    return row;
}
  • Step 4: 回跑 Service 测试,确认导出复用了页面口径

Run:

mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest test

Expected:

  • PASS

  • 测试明确断言导出走 selectRiskPeopleOverviewList

  • 导出字段与页面表格字段一致

  • 项目不存在时继续抛 ServiceException

  • Step 5: 提交本任务

git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java \
  ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java
git commit -m "实现风险总览人员列表后端导出"

Task 4: 补齐导出控制器回归与文档留痕

Files:

  • Modify: ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java

  • Create: docs/reports/implementation/2026-03-30-project-detail-risk-people-export-backend-record.md

  • Create: docs/tests/records/2026-03-30-project-detail-risk-people-export-backend-verification.md

  • Verify: docs/design/2026-03-30-project-detail-risk-people-export-design.md

  • Step 1: 补一条控制器导出回归测试

MockHttpServletResponse response = new MockHttpServletResponse();
when(overviewService.exportRiskPeopleOverview(40L)).thenReturn(List.of(new CcdiProjectRiskPeopleOverviewExcel()));

controller.exportRiskPeople(response, 40L);

verify(overviewService).exportRiskPeopleOverview(40L);
  • Step 2: 跑本次后端最小回归集

Run:

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

Expected:

  • PASS

  • Step 3: 记录测试结果

docs/tests/records/2026-03-30-project-detail-risk-people-export-backend-verification.md 记录:

  • 执行命令

  • 通过情况

  • 导出接口路径

  • 字段口径与页面一致的核对结论

  • Step 4: 记录实施内容

docs/reports/implementation/2026-03-30-project-detail-risk-people-export-backend-record.md 记录:

  • 新增导出接口与 Excel 对象

  • 复用 buildRiskPeopleItem(...) 的实现方式

  • 分页查询与导出查询共用排序/字段 SQL 片段

  • 未引入新的筛选、菜单或权限

  • Step 5: 提交本任务

git add ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java \
  docs/reports/implementation/2026-03-30-project-detail-risk-people-export-backend-record.md \
  docs/tests/records/2026-03-30-project-detail-risk-people-export-backend-verification.md
git commit -m "补充风险总览人员导出后端记录"

最终验证

  • 运行后端回归:
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceImplTest test
  • 确认以下结果:

    • POST /ccdi/project/overview/risk-people/export 已接通
    • 导出范围为当前项目全部风险人员
    • 导出列仅包含页面展示列,不包含操作列
    • 导出字段顺序与设计文档一致
  • 准备执行时,只暂存本次任务相关后端文件后再提交