Files
ccdi/docs/plans/backend/2026-03-28-risk-detail-employee-credit-negative-backend-implementation.md

251 lines
11 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.
# Risk Detail Employee Credit Negative 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:** 为结果总览“风险明细”新增项目维度的员工负面征信分页查询能力,保证页面可以按当前项目结果总览员工口径展示负面征信列表。
**Architecture:** 后端继续收敛到 `CcdiProjectOverviewController -> ICcdiProjectOverviewService -> CcdiProjectOverviewMapper` 这一条结果总览链路,不复用全局征信维护接口。查询以 `ccdi_project_overview_employee_result` 限定项目员工范围,再与 `ccdi_credit_negative_info` 关联,仅输出存在负面征信记录的员工,并按 `query_date desc, person_id asc` 分页返回。
**Tech Stack:** Java 21, Spring Boot 3, MyBatis Plus `Page`, MyBatis XML, Maven, JUnit 5, Mockito, Swagger/OpenAPI
---
### Task 1: 建立结果总览员工负面征信接口契约
**Files:**
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectEmployeeCreditNegativeQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectEmployeeCreditNegativeItemVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectEmployeeCreditNegativePageVO.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/service/CcdiProjectOverviewServiceStructureTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java`
- [ ] **Step 1: 先写失败测试,锁定新 DTO、VO、Service 方法和 Controller 路由**
```java
assertNotNull(clazz.getMethod(
"getEmployeeCreditNegative",
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO")
));
```
```java
assertEquals("/employee-credit-negative", getMapping.value()[0]);
assertEquals(List.of("projectId", "pageNum", "pageSize"), fieldNames);
```
- [ ] **Step 2: 运行结构测试和控制器契约测试,确认当前仓库尚未暴露该能力**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test
```
Expected:
- `CcdiProjectOverviewServiceStructureTest``NoSuchMethodException`
- `CcdiProjectOverviewControllerContractTest` 提示缺少 `/employee-credit-negative`
- `CcdiProjectOverviewControllerTest` 还没有对应控制器方法
- [ ] **Step 3: 写最小 DTO / VO / Controller / Service 签名**
```java
public class CcdiProjectEmployeeCreditNegativeQueryDTO {
private Long projectId;
private Integer pageNum;
private Integer pageSize;
}
```
```java
@GetMapping("/employee-credit-negative")
public AjaxResult getEmployeeCreditNegative(CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO) {
return AjaxResult.success(overviewService.getEmployeeCreditNegative(queryDTO));
}
```
- [ ] **Step 4: 回跑接口契约测试,确保新接口已被正确暴露**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest test
```
Expected: PASS
- [ ] **Step 5: 提交本任务**
```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectEmployeeCreditNegativeQueryDTO.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectEmployeeCreditNegativeItemVO.java \
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectEmployeeCreditNegativePageVO.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/service/CcdiProjectOverviewServiceStructureTest.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: 落地项目员工负面征信 SQL 查询与分页服务
**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`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java`
- Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceEmployeeCreditNegativeTest.java`
- [ ] **Step 1: 先写 SQL 结构测试和服务测试,锁定项目范围、负面字段与排序规则**
```java
assertTrue(employeeCreditSql.contains("from ccdi_project_overview_employee_result"), employeeCreditSql);
assertTrue(employeeCreditSql.contains("inner join ccdi_credit_negative_info"), employeeCreditSql);
assertTrue(employeeCreditSql.contains("result.project_id = #{query.projectId}"), employeeCreditSql);
assertTrue(employeeCreditSql.contains("order by neg.query_date desc, neg.person_id asc"), employeeCreditSql);
assertFalse(employeeCreditSql.contains("ccdi_debts_info"), employeeCreditSql);
```
```java
verify(overviewMapper).selectEmployeeCreditNegativePage(
any(Page.class),
argThat(query -> query.getProjectId().equals(40L))
);
```
- [ ] **Step 2: 运行测试,确认当前查询链路还不存在**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest test
```
Expected:
- SQL 测试提示缺少 `selectEmployeeCreditNegativePage`
- Service 测试编译失败或断言失败,提示未新增 mapper / service 能力
- [ ] **Step 3: 写最小 SQL 与服务实现**
```xml
<select id="selectEmployeeCreditNegativePage"
resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO">
select
coalesce(neg.person_name, result.staff_name) as personName,
neg.person_id as personId,
neg.query_date as queryDate,
ifnull(neg.civil_cnt, 0) as civilCnt,
ifnull(neg.civil_lmt, 0) as civilLmt,
ifnull(neg.enforce_cnt, 0) as enforceCnt,
ifnull(neg.enforce_lmt, 0) as enforceLmt,
ifnull(neg.adm_cnt, 0) as admCnt,
ifnull(neg.adm_lmt, 0) as admLmt
from ccdi_project_overview_employee_result result
inner join ccdi_credit_negative_info neg
on neg.person_id = result.staff_id_card
where result.project_id = #{query.projectId}
order by neg.query_date desc, neg.person_id asc
</select>
```
```java
Page<CcdiProjectEmployeeCreditNegativeItemVO> page = new Page<>(
defaultPageNum(queryDTO.getPageNum()),
defaultPageSize(queryDTO.getPageSize())
);
Page<CcdiProjectEmployeeCreditNegativeItemVO> resultPage =
overviewMapper.selectEmployeeCreditNegativePage(page, queryDTO);
```
- [ ] **Step 4: 回跑 SQL 与服务测试,验证只返回项目内有负面征信的员工**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest test
```
Expected:
- SQL 测试确认不关联 `ccdi_debts_info`
- Service 测试确认分页结果包含 `rows``total`
- 项目不存在时继续抛 `ServiceException("项目不存在")`
- [ ] **Step 5: 提交本任务**
```bash
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/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java \
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceEmployeeCreditNegativeTest.java
git commit -m "实现结果总览员工负面征信分页查询"
```
### Task 3: 补齐控制器返回与回归验证
**Files:**
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java`
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java`
- Reference: `docs/superpowers/specs/2026-03-28-risk-detail-employee-credit-negative-design.md`
- [ ] **Step 1: 在控制器测试里锁定返回字段和权限沿用**
```java
CcdiProjectEmployeeCreditNegativePageVO pageVO = new CcdiProjectEmployeeCreditNegativePageVO();
pageVO.setRows(List.of(item));
pageVO.setTotal(1L);
when(overviewService.getEmployeeCreditNegative(queryDTO)).thenReturn(pageVO);
AjaxResult result = controller.getEmployeeCreditNegative(queryDTO);
assertEquals(pageVO, result.get("data"));
```
- [ ] **Step 2: 回跑控制器相关测试,确认没有破坏既有结果总览契约**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewServiceStructureTest test
```
Expected: PASS
- [ ] **Step 3: 做一次后端最小回归**
Run:
```bash
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceEmployeeCreditNegativeTest,CcdiProjectOverviewControllerTest test
```
Expected:
- 新增员工负面征信链路测试全部通过
- 结果总览既有涉疑交易和项目分析控制器测试不回归
- [ ] **Step 4: 记录实施边界**
将以下结论补到提交说明或实施记录中:
- 本次只新增项目维度负面征信列表
- 未改动全局征信维护接口
- 未接入负债明细
- [ ] **Step 5: 提交本任务**
```bash
git add 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/CcdiProjectOverviewServiceStructureTest.java
git commit -m "补充结果总览员工负面征信后端回归测试"
```