# Special Check Extended Query Backend Implementation Plan

> **For agentic workers:** REQUIRED: Use superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 为专项核查页新增“拓展查询”后端查询能力，支持在专项核查同范围员工内查询采购、招聘、调动记录，并提供各主题列表与详情接口。

**Architecture:** 延续 `CcdiProjectSpecialCheckController + Service + Mapper` 这条专项核查专用查询链路，不复用信息采集模块现有 `/list` 接口做前端拼装。员工范围统一复用 `projectEmployeeScopeSql`，在 `ccdi-project` 内新增 3 组主题查询 DTO、列表 VO 和明细查询方法，保证项目范围口径与当前专项核查完全一致。

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

---

### Task 1: 定义拓展查询接口契约与主题 DTO/VO

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java`
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectSpecialCheckService.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedPurchaseQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedPurchaseDetailQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedRecruitmentQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedRecruitmentDetailQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedTransferQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedTransferDetailQueryDTO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedPurchaseListItemVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedPurchaseListVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedPurchaseDetailVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedRecruitmentListItemVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedRecruitmentListVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedRecruitmentDetailVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedTransferListItemVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedTransferListVO.java`
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedTransferDetailVO.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckExtendedQueryContractTest.java`

- [ ] **Step 1: Write the failing test**

新增控制器契约测试，锁定以下接口路径与方法：

- `GET /ccdi/project/special-check/extended-query/purchase/list`
- `GET /ccdi/project/special-check/extended-query/purchase/detail`
- `GET /ccdi/project/special-check/extended-query/recruitment/list`
- `GET /ccdi/project/special-check/extended-query/recruitment/detail`
- `GET /ccdi/project/special-check/extended-query/transfer/list`
- `GET /ccdi/project/special-check/extended-query/transfer/detail`

同时锁定 DTO / VO 基本字段：

- 采购列表入参：`projectId`、`applicantName`、`applyDateStart`、`applyDateEnd`、`pageNum`、`pageSize`
- 招聘列表入参：`projectId`、`interviewerName`、`pageNum`、`pageSize`
- 调动列表入参：`projectId`、`staffName`、`transferDateStart`、`transferDateEnd`、`pageNum`、`pageSize`
- 3 个详情入参都必须带 `projectId` 和主题主键
- 3 个列表 VO 都必须包含 `rows` 与 `total`
- 列表项只保留设计文档约定的必要字段
- 详情 VO 在 `ccdi-project` 模块内独立定义，不直接依赖 `ccdi-info-collection` 模块类

- [ ] **Step 2: Run test to verify it fails**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedQueryContractTest
```

Expected:

- `FAIL`
- 原因是新增接口契约、DTO、VO 尚未落地

- [ ] **Step 3: Write minimal implementation**

补齐控制器方法、服务接口方法、DTO 与列表 VO。

实现要求：

- 控制器继续使用 `@Tag`、`@Operation`
- 权限沿用 `@PreAuthorize("@ss.hasPermi('ccdi:project:query')")`
- 详情接口命名直接使用业务主键，不引入额外包装对象
- 采购主题查询字段必须命名为 `applicantName`，不要用泛化的 `staffName`
- 招聘主题查询字段必须命名为 `interviewerName`，不要用泛化的 `staffName`
- 3 个列表 DTO 都显式带 `pageNum / pageSize`，默认值与若依分页口径一致
- 3 个列表返回体都显式带 `rows / total`
- `projectId` 与 3 个详情主键字段都加非空校验
- 采购、招聘、调动 3 类详情 VO 都在 `ccdi-project` 中定义，避免引入新的模块依赖
- 详情 VO 字段分别对齐现有采购、招聘、调动独立页的详情展示结构
- 返回统一使用 `AjaxResult.success(...)`

- [ ] **Step 4: Run test to verify it passes**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedQueryContractTest
```

Expected:

- `PASS`

- [ ] **Step 5: Commit**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectSpecialCheckService.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedPurchaseQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedPurchaseDetailQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedRecruitmentQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedRecruitmentDetailQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedTransferQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectExtendedTransferDetailQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedPurchaseListItemVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedPurchaseListVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedPurchaseDetailVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedRecruitmentListItemVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedRecruitmentListVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedRecruitmentDetailVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedTransferListItemVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedTransferListVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectExtendedTransferDetailVO.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckExtendedQueryContractTest.java
git commit -m "定义专项核查拓展查询接口契约"
```

### Task 2: 在 Mapper 中补采购主题项目范围列表与详情查询

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapper.java`
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedPurchaseSqlTest.java`

- [ ] **Step 1: Write the failing test**

新增采购主题 SQL 测试，锁定以下口径：

- 员工范围必须复用 `projectEmployeeScopeSql`
- 列表只能返回项目范围内员工的采购记录
- 员工姓名筛选命中 `applicant_name`
- 时间范围筛选命中 `apply_date`
- 分页必须按 `pageNum / pageSize` 生效，并返回总数
- 列表字段只返回：`purchaseId`、`projectName`、`subjectName`、`applicantName`、`applyDate`
- 详情按 `projectId + purchaseId` 查询，并校验记录属于项目范围员工

- [ ] **Step 2: Run test to verify it fails**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedPurchaseSqlTest
```

Expected:

- `FAIL`

- [ ] **Step 3: Write minimal implementation**

在 Mapper 中新增采购主题列表与详情查询。

实现要求：

- 范围表先去重员工姓名，避免一名员工在项目范围内重复放大采购记录
- 列表查询使用 MyBatis Plus `Page` 或等效分页结果承载 `rows / total`
- 列表按 `apply_date desc, create_time desc, purchase_id desc` 排序
- 采购详情映射到 `CcdiProjectExtendedPurchaseDetailVO`
- `CcdiProjectExtendedPurchaseDetailVO` 字段按采购现有详情展示结构定义：基本信息、金额信息、供应商信息、日期信息、申请人信息、采购负责人信息、审计信息
- 详情查不到时交给服务层统一转业务错误

- [ ] **Step 4: Run test to verify it passes**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedPurchaseSqlTest
```

Expected:

- `PASS`

- [ ] **Step 5: Commit**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedPurchaseSqlTest.java
git commit -m "补充专项核查采购拓展查询SQL"
```

### Task 3: 在 Mapper 中补招聘与调动主题项目范围查询

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapper.java`
- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedRecruitmentSqlTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedTransferSqlTest.java`

- [ ] **Step 1: Write the failing test**

新增招聘与调动 SQL 测试，锁定以下口径：

招聘：

- 员工范围必须复用 `projectEmployeeScopeSql`
- 员工姓名口径不使用 `candName`
- 检索字段必须是 `interviewer_name_1` 或 `interviewer_name_2`
- 同一条招聘记录若同时命中面试官 1 和面试官 2，列表结果必须按 `recruit_id` 去重
- 本次不增加招聘时间筛选
- 分页必须按 `pageNum / pageSize` 生效，并返回总数
- 列表字段只返回：`recruitId`、`recruitName`、`posName`、`interviewerNameSummary`、`admitStatus`

调动：

- 员工范围必须按范围员工姓名匹配到 `ccdi_base_staff.name`
- 时间范围命中 `transfer_date`
- 分页必须按 `pageNum / pageSize` 生效，并返回总数
- 列表字段只返回：`staffName`、`transferType`、`deptNameBefore`、`deptNameAfter`、`transferDate`
- 详情按 `projectId + id` 查询，并校验记录属于项目范围员工

- [ ] **Step 2: Run test to verify it fails**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedRecruitmentSqlTest,CcdiProjectSpecialCheckExtendedTransferSqlTest
```

Expected:

- `FAIL`

- [ ] **Step 3: Write minimal implementation**

补齐招聘与调动主题 SQL。

实现要求：

- 招聘列表中生成 `interviewerNameSummary`，格式统一为“面试官1 / 面试官2”，单边为空时只输出存在值
- 招聘列表查询必须按 `recruit_id` 去重，避免同一条记录因双面试官命中被重复返回并放大 `total`
- 招聘详情映射到 `CcdiProjectExtendedRecruitmentDetailVO`
- `CcdiProjectExtendedRecruitmentDetailVO` 字段按招聘现有详情展示结构定义：招聘项目信息、职位信息、候选人信息、录用信息、面试官信息、审计信息
- 调动列表通过 `ccdi_staff_transfer.staff_id -> ccdi_base_staff.staff_id` 关联员工姓名
- 调动详情映射到 `CcdiProjectExtendedTransferDetailVO`
- `CcdiProjectExtendedTransferDetailVO` 字段按调动详情展示结构定义：基本信息、调动前信息、调动后信息、审计信息
- 调动排序按 `transfer_date desc, create_time desc, id desc`

- [ ] **Step 4: Run test to verify it passes**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedRecruitmentSqlTest,CcdiProjectSpecialCheckExtendedTransferSqlTest
```

Expected:

- `PASS`

- [ ] **Step 5: Commit**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedRecruitmentSqlTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedTransferSqlTest.java
git commit -m "补充专项核查招聘调动拓展查询SQL"
```

### Task 4: 完成服务层组装、项目校验与详情归属校验

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckServiceImpl.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckExtendedQueryServiceImplTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckControllerTest.java`

- [ ] **Step 1: Write the failing test**

新增服务层与控制器测试，覆盖：

- 项目不存在时统一返回“项目不存在”
- 3 个列表接口都返回空列表而不是 `null`
- 3 个列表接口都返回正确的 `total`
- 分页参数变化时列表记录数和总数符合预期
- 3 个详情接口在记录不属于当前项目范围员工时返回明确业务错误
- 招聘列表的查询字段确实是 `interviewerName`
- 控制器都走 `AjaxResult.success(...)`

- [ ] **Step 2: Run test to verify it fails**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedQueryServiceImplTest,CcdiProjectSpecialCheckControllerTest
```

Expected:

- `FAIL`

- [ ] **Step 3: Write minimal implementation**

在服务层中：

- 复用当前 `ensureProjectExists` 校验思路
- 新增 3 个列表方法和 3 个详情方法
- 列表统一封装 `rows + total`
- 空数据场景统一封装成 `rows = List.of()`、`total = 0`
- 详情空数据统一抛出“当前记录不属于该项目专项核查范围”或同义明确错误
- 采购、招聘、调动 3 个详情接口错误文案保持同一风格，便于前端提示和测试断言
- 不引入缓存、异步刷新或额外结果表

在控制器中：

- 继续使用 `GET` 查询风格
- 列表与详情返回结构保持专项核查域一致

- [ ] **Step 4: Run test to verify it passes**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedQueryServiceImplTest,CcdiProjectSpecialCheckControllerTest
```

Expected:

- `PASS`

- [ ] **Step 5: Commit**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckExtendedQueryServiceImplTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckControllerTest.java
git commit -m "完成专项核查拓展查询服务组装"
```

### Task 5: 运行后端回归验证并沉淀测试记录

**Files:**
- Modify: `docs/tests/records/2026-03-24-special-check-extended-query-backend-verification.md`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckExtendedQueryContractTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedPurchaseSqlTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedRecruitmentSqlTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckExtendedTransferSqlTest.java`
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckExtendedQueryServiceImplTest.java`

- [ ] **Step 1: Run focused backend test suite**

Run:

```bash
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckExtendedQueryContractTest,CcdiProjectSpecialCheckExtendedPurchaseSqlTest,CcdiProjectSpecialCheckExtendedRecruitmentSqlTest,CcdiProjectSpecialCheckExtendedTransferSqlTest,CcdiProjectSpecialCheckExtendedQueryServiceImplTest
```

Expected:

- 所有新增专项核查拓展查询测试 `PASS`
- 分页相关断言一并通过

- [ ] **Step 2: Run module regression**

Run:

```bash
mvn test -pl ccdi-project
```

Expected:

- `PASS`
- 若存在既有失败，需在记录中注明与本次改动是否相关

- [ ] **Step 3: Write verification record**

在 `docs/tests/records/2026-03-24-special-check-extended-query-backend-verification.md` 记录：

- 实际执行命令
- 结果
- 是否发现与项目范围口径相关的偏差
- 若有失败，明确是否为既有问题

- [ ] **Step 4: Commit**

```bash
git add docs/tests/records/2026-03-24-special-check-extended-query-backend-verification.md
git commit -m "补充专项核查拓展查询后端验证记录"
```
