Files
ccdi/docs/plans/backend/2026-03-19-results-overview-risk-api-backend-implementation.md

15 KiB
Raw Blame History

Results Overview Risk API 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.

Goal: 为结果总览页面实现风险仪表盘、风险人员总览、中高风险人员 TOP10 三个后端接口,并在项目流水标签打标完成后回写项目高、中、低风险人数。

Architecture:ccdi-project 模块内新增结果总览专用 Controller、Service、Mapper 与 VO查询统一基于 ccdi_projectccdi_bank_statement_tag_result 聚合,不新增兼容性补丁链路。员工风险等级按命中去重规则数分级,项目表高、中、低风险人数在标签任务成功结束后同步回写,保证项目列表与结果总览口径一致。

Tech Stack: Java 21, Spring Boot 3, MyBatis XML, MyBatis Plus, Maven, JUnit


Task 1: 定义结果总览 VO 与服务接口骨架

Files:

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewDashboardVO.java

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewStatVO.java

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewVO.java

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO.java

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectTopRiskPeopleVO.java

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectTopRiskPeopleItemVO.java

  • Create: ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectEmployeeRiskAggregateVO.java

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

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

  • Step 1: Write the failing test

新增结构测试,锁定服务接口与 VO 名称:

@Test
void shouldExposeOverviewServiceMethods() throws Exception {
    Class<?> clazz = Class.forName("com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService");
    assertNotNull(clazz.getMethod("getDashboard", Long.class));
    assertNotNull(clazz.getMethod("getRiskPeopleOverview", Long.class));
    assertNotNull(clazz.getMethod("getTopRiskPeople", Long.class));
    assertNotNull(clazz.getMethod("refreshProjectRiskCounts", Long.class, String.class));
}
  • Step 2: Run test to verify it fails

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceStructureTest

Expected:

  • FAIL

  • 原因是接口与 VO 尚未创建

  • Step 3: Write minimal implementation

最小落地如下:

  1. 为 3 个区块分别创建返回 VO。
  2. 新建员工聚合中间 VO字段至少包括
    • staffIdCard
    • staffName
    • deptName
    • ruleCount
    • modelCount
    • topRuleName
    • riskLevelCode
    • riskLevelName
    • riskLevelSort
  3. 新建 ICcdiProjectOverviewService,声明以下方法:
CcdiProjectOverviewDashboardVO getDashboard(Long projectId);

CcdiProjectRiskPeopleOverviewVO getRiskPeopleOverview(Long projectId);

CcdiProjectTopRiskPeopleVO getTopRiskPeople(Long projectId);

void refreshProjectRiskCounts(Long projectId, String operator);
  • Step 4: Run test to verify it passes

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceStructureTest

Expected:

  • PASS

  • Step 5: Commit

git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewDashboardVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectOverviewStatVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectTopRiskPeopleVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectTopRiskPeopleItemVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectEmployeeRiskAggregateVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java
git commit -m "定义结果总览风险接口基础结构"

Task 2: 新增结果总览 Mapper 与员工归并聚合 SQL

Files:

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

  • Create: 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: Write the failing test

新增 SQL 结构测试,锁定 Mapper XML 中必须包含员工归并和风险等级分段逻辑:

@Test
void shouldContainEmployeeRiskAggregationSql() throws Exception {
    String xml = Files.readString(Path.of("ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
    assertTrue(xml.contains("count(distinct base.rule_code)"));
    assertTrue(xml.contains("count(distinct base.model_code)"));
    assertTrue(xml.contains("when agg.rule_count >= 5 then 'HIGH'"));
    assertTrue(xml.contains("when agg.rule_count between 2 and 4 then 'MEDIUM'"));
}
  • Step 2: Run test to verify it fails

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest

Expected:

  • FAIL

  • 原因是 Mapper 文件还不存在

  • Step 3: Write minimal implementation

创建 CcdiProjectOverviewMapper 与 XML至少提供以下查询

  1. selectDashboardBaseByProjectId(Long projectId)
  2. selectRiskPeopleOverviewByProjectId(Long projectId)
  3. selectTopRiskPeopleByProjectId(Long projectId)
  4. selectRiskCountSummaryByProjectId(Long projectId)

SQL 设计要求:

  1. 先用公共子查询把标签结果归并到员工身份证:
    • 本人命中:object_type = 'STAFF_ID_CARD'
    • 流水本人命中:bank_statement_id -> ccdi_bank_statement.cret_no
    • 亲属命中:relation_cert_no -> person_id
  2. 外层按员工聚合:
count(distinct base.rule_code) as rule_count,
count(distinct base.model_code) as model_count
  1. 风险等级按规则数分段:
case
    when agg.rule_count >= 5 then 'HIGH'
    when agg.rule_count between 2 and 4 then 'MEDIUM'
    else 'LOW'
end
  1. riskPoint 使用“规则命中次数倒序 + rule_code 升序”取第一条规则名。
  • Step 4: Run test to verify it passes

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest

Expected:

  • PASS

  • Step 5: Commit

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: 实现结果总览 Service

Files:

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

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

  • Step 1: Write the failing test

为服务层编写测试,锁定仪表盘与列表组装逻辑:

@Test
void shouldBuildDashboardWithNoRiskCount() {
    // mock project targetCount=100 high=5 medium=10 low=15
    // assert noRiskCount == 70
}

@Test
void shouldMapRiskPeopleOverviewRows() {
    // mock aggregate row
    // assert riskCount/topRuleName/actionLabel mapping
}

@Test
void shouldMapTopRiskPeopleRows() {
    // mock aggregate row with HIGH
    // assert riskLevelType == "danger"
}
  • Step 2: Run test to verify it fails

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest

Expected:

  • FAIL

  • Step 3: Write minimal implementation

CcdiProjectOverviewServiceImpl 中完成:

  1. getDashboard
    • 读项目基础数据
    • 计算无风险人数,空值按 0 处理
  2. getRiskPeopleOverview
    • 调 Mapper 查询员工聚合结果
    • 映射为 overviewList
    • 固定补 actionLabel = "查看详情"
  3. getTopRiskPeople
    • 调 Mapper 查询 TOP10
    • HIGH/MEDIUM 映射为 高风险/中风险
    • HIGH -> dangerMEDIUM -> warning
  4. 若项目不存在,抛出业务异常

如需项目存在性校验,直接复用现有项目查询能力,不新增兜底流程。

  • Step 4: Run test to verify it passes

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest

Expected:

  • PASS

  • Step 5: Commit

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/CcdiProjectOverviewServiceImplTest.java
git commit -m "实现结果总览风险接口服务层"

Task 4: 新增结果总览 Controller 接口

Files:

  • Create: 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: Write the failing test

为控制器编写测试,锁定 3 个 GET 接口:

@Test
void shouldExposeDashboardEndpoint() {}

@Test
void shouldExposeRiskPeopleEndpoint() {}

@Test
void shouldExposeTopRiskPeopleEndpoint() {}

校验内容:

  • 路径为 /ccdi/project/overview/*

  • 权限为 ccdi:project:query

  • 返回 AjaxResult.success(...)

  • Step 2: Run test to verify it fails

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest

Expected:

  • FAIL

  • Step 3: Write minimal implementation

控制器实现:

@GetMapping("/dashboard")
public AjaxResult getDashboard(Long projectId) { ... }

@GetMapping("/risk-people")
public AjaxResult getRiskPeople(Long projectId) { ... }

@GetMapping("/top-risk-people")
public AjaxResult getTopRiskPeople(Long projectId) { ... }

要求:

  • 添加 Swagger 注释

  • 复用 @PreAuthorize("@ss.hasPermi('ccdi:project:query')")

  • 仅接收 projectId

  • Step 4: Run test to verify it passes

Run:

mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest

Expected:

  • PASS

  • Step 5: Commit

git add 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 5: 在标签任务成功后回写项目风险人数

Files:

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

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

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

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

  • Step 1: Write the failing test

新增测试,锁定打标成功后必须刷新项目风险人数:

@Test
void shouldRefreshProjectRiskCountsAfterTagRebuildSuccess() {
    // mock successful rule execution
    // verify overviewService.refreshProjectRiskCounts(projectId, operator)
}

并新增一条失败场景:

@Test
void shouldFailTaskWhenRiskCountRefreshFails() {
    // mock refreshProjectRiskCounts throws exception
    // assert task status becomes FAILED
}
  • Step 2: Run test to verify it fails

Run:

mvn test -pl ccdi-project -Dtest=CcdiBankTagServiceRiskCountRefreshTest

Expected:

  • FAIL

  • Step 3: Write minimal implementation

实施要求:

  1. CcdiBankTagServiceImpl.rebuildProject(...) 中:
    • 标签结果批量写入成功后
    • 任务状态改成功前
    • 调用 projectOverviewService.refreshProjectRiskCounts(projectId, operator)
  2. 在项目 Mapper 中新增更新人数方法:
int updateRiskCountsByProjectId(@Param("projectId") Long projectId,
                                @Param("highRiskCount") Integer highRiskCount,
                                @Param("mediumRiskCount") Integer mediumRiskCount,
                                @Param("lowRiskCount") Integer lowRiskCount,
                                @Param("updateBy") String updateBy);
  1. refreshProjectRiskCounts 内部先查员工聚合统计,再更新项目表。
  • Step 4: Run test to verify it passes

Run:

mvn test -pl ccdi-project -Dtest=CcdiBankTagServiceRiskCountRefreshTest

Expected:

  • PASS

  • Step 5: Commit

git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiBankTagServiceRiskCountRefreshTest.java
git commit -m "打标完成后回写项目风险人数"

Task 6: 补充后端验证记录与实施记录

Files:

  • Create: docs/tests/records/2026-03-19-results-overview-risk-api-backend-verification.md

  • Create: docs/reports/implementation/2026-03-19-results-overview-risk-api-backend-implementation.md

  • Step 1: Write the failing record skeleton

先创建验证记录模板:

# 结果总览风险接口后端验证记录

## 验证范围
- 风险仪表盘接口
- 风险人员总览接口
- 中高风险人员 TOP10 接口
- 打标完成后项目风险人数回写
  • Step 2: Run backend verification commands

Run:

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

Expected:

  • 相关测试全部 PASS

  • Step 3: Write minimal implementation record

在实施记录中说明:

  • 新增 3 个结果总览接口

  • 员工风险等级按规则数分级

  • 标签完成后回写项目风险人数

  • 未扩展风险模型区和风险明细区接口

  • Step 4: Re-run record review

Run:

sed -n '1,220p' docs/tests/records/2026-03-19-results-overview-risk-api-backend-verification.md
sed -n '1,220p' docs/reports/implementation/2026-03-19-results-overview-risk-api-backend-implementation.md

Expected:

  • 两份文档都能覆盖本次后端改动和验证结果

  • Step 5: Commit

git add docs/tests/records/2026-03-19-results-overview-risk-api-backend-verification.md docs/reports/implementation/2026-03-19-results-overview-risk-api-backend-implementation.md
git commit -m "补充结果总览风险接口后端记录"