# 创建项目集成流水分析平台实施计划

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** 在创建项目时同步调用流水分析平台获取projectId并保存到数据库

**Architecture:** Service层集成LsfxAnalysisClient，同步调用getToken接口，使用Spring事务管理确保数据一致性

**Tech Stack:** Spring Boot 3.5.8, MyBatis Plus 3.0.5, Lombok, JUnit 5

---

## 前置条件

- [ ] 流水分析平台 Mock Server 可用（http://localhost:8000）
- [ ] 数据库连接正常
- [ ] 后端项目可正常启动

---

## Task 1: 执行数据库变更

**Files:**
- Execute: `doc/design/2026-03-04-add-lsfx-project-id.sql`

**Step 1: 连接数据库执行SQL脚本**

使用MCP工具连接数据库并执行SQL：

```bash
# 或者使用MySQL客户端
mysql -h 116.62.17.81 -u root -p ccdi < doc/design/2026-03-04-add-lsfx-project-id.sql
```

**Step 2: 验证字段已添加**

```sql
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'ccdi'
  AND TABLE_NAME = 'ccdi_project'
  AND COLUMN_NAME = 'lsfx_project_id';
```

预期结果：应返回字段信息，类型为 `INT(11)`，允许为空

**Step 3: 提交数据库变更**

```bash
git add doc/design/2026-03-04-add-lsfx-project-id.sql
git commit -m "chore: 添加流水分析平台项目ID字段到ccdi_project表"
```

---

## Task 2: 修改CcdiProject实体类

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/CcdiProject.java`

**Step 1: 打开实体类文件**

定位到 `lowRiskCount` 字段的位置（约第50行）

**Step 2: 添加新字段**

在 `lowRiskCount` 字段之后，`delFlag` 字段之前添加：

```java
/** 低风险人数 */
private Integer lowRiskCount;

/** 流水分析平台项目ID */
private Integer lsfxProjectId;

/** 删除标志：0-存在，2-删除 */
@TableLogic
private String delFlag;
```

**Step 3: 验证代码**

确保：
- Lombok 的 `@Data` 注解存在（会自动生成 getter/setter）
- 字段顺序与数据库表结构一致
- 注释清晰

**Step 4: 提交变更**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/CcdiProject.java
git commit -m "feat: CcdiProject实体类添加lsfxProjectId字段"
```

---

## Task 3: 修改CcdiProjectVO视图对象

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectVO.java`

**Step 1: 打开VO文件**

定位到 `lowRiskCount` 字段的位置

**Step 2: 添加新字段**

在 `lowRiskCount` 字段之后添加：

```java
/** 低风险人数 */
private Integer lowRiskCount;

/** 流水分析平台项目ID */
private Integer lsfxProjectId;

/** 创建时间 */
private Date createTime;
```

**Step 3: 验证代码**

确保字段顺序与实体类一致

**Step 4: 提交变更**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectVO.java
git commit -m "feat: CcdiProjectVO添加lsfxProjectId字段"
```

---

## Task 4: 准备测试环境

**Files:**
- Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectServiceImplTest.java`

**Step 1: 创建测试目录（如果不存在）**

```bash
mkdir -p ccdi-project/src/test/java/com/ruoyi/ccdi/project/service
```

**Step 2: 创建测试类**

```java
package com.ruoyi.ccdi.project.service;

import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.domain.request.GetTokenRequest;
import com.ruoyi.lsfx.domain.response.GetTokenResponse;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 项目Service测试类
 */
@SpringBootTest
@Transactional
public class CcdiProjectServiceImplTest {

    @Resource
    private ICcdiProjectService projectService;

    @Resource
    private LsfxAnalysisClient lsfxAnalysisClient;

    @Test
    public void testCreateProject_Success() {
        // 准备数据
        CcdiProjectSaveDTO dto = new CcdiProjectSaveDTO();
        dto.setProjectName("测试项目");
        dto.setDescription("测试描述");
        dto.setConfigType("default");

        // 执行
        CcdiProjectVO result = projectService.createProject(dto);

        // 验证
        assertNotNull(result);
        assertNotNull(result.getProjectId());
        assertNotNull(result.getLsfxProjectId(), "流水分析平台项目ID不应为空");
        assertEquals("测试项目", result.getProjectName());
    }
}
```

**Step 3: 运行测试（预期失败）**

```bash
cd ccdi-project
mvn test -Dtest=CcdiProjectServiceImplTest#testCreateProject_Success
```

预期结果：测试失败，因为 `CcdiProjectServiceImpl` 尚未注入 `LsfxAnalysisClient`

**Step 4: 提交测试代码**

```bash
git add ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectServiceImplTest.java
git commit -m "test: 添加项目创建集成流水分析平台的测试用例"
```

---

## Task 5: 修改CcdiProjectServiceImpl - 注入依赖

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`

**Step 1: 打开Service实现类**

找到依赖注入区域（约第25行）

**Step 2: 添加LsfxAnalysisClient依赖**

```java
@Service
public class CcdiProjectServiceImpl implements ICcdiProjectService {

    @Resource
    private CcdiProjectMapper projectMapper;

    @Resource
    private LsfxAnalysisClient lsfxAnalysisClient;  // 新增依赖注入

    // ... 方法实现 ...
}
```

**Step 3: 添加必要的import**

```java
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.domain.request.GetTokenRequest;
import com.ruoyi.lsfx.domain.response.GetTokenResponse;
```

**Step 4: 验证编译**

```bash
cd ccdi-project
mvn compile
```

预期结果：编译成功

**Step 5: 提交变更**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java
git commit -m "feat: CcdiProjectServiceImpl注入LsfxAnalysisClient依赖"
```

---

## Task 6: 实现callLsfxPlatform私有方法

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`

**Step 1: 在类末尾添加私有方法**

在 `getStatusCounts()` 方法之后添加：

```java
/**
 * 调用流水分析平台获取projectId
 *
 * @param projectName 项目名称
 * @return 流水分析平台项目ID
 * @throws ServiceException 调用失败或响应无效时抛出
 */
private Integer callLsfxPlatform(String projectName) {
    // 构建请求参数
    GetTokenRequest request = new GetTokenRequest();
    request.setProjectNo("902000_" + System.currentTimeMillis());
    request.setEntityName(projectName);
    request.setUserId("902001");
    request.setUserName("902001");
    request.setRole("VIEWER");
    request.setOrgCode("902000");
    request.setAnalysisType("-1");
    request.setDepartmentCode("902000");

    // 调用流水分析平台（异常处理和日志已在 LsfxAnalysisClient 中完成）
    GetTokenResponse response = lsfxAnalysisClient.getToken(request);

    // 业务层校验：确保响应有效
    if (response == null || response.getData() == null) {
        throw new ServiceException("流水分析平台响应数据为空");
    }

    if (response.getData().getProjectId() == null) {
        throw new ServiceException("流水分析平台返回的projectId为空");
    }

    // 校验返回码
    if (!"200".equals(response.getCode())) {
        throw new ServiceException("流水分析平台返回错误: " + response.getMessage());
    }

    return response.getData().getProjectId();
}
```

**Step 2: 验证编译**

```bash
cd ccdi-project
mvn compile
```

预期结果：编译成功

**Step 3: 提交变更**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java
git commit -m "feat: 实现callLsfxPlatform方法调用流水分析平台"
```

---

## Task 7: 修改createProject方法集成流水分析

**Files:**
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`

**Step 1: 定位createProject方法**

找到 `createProject` 方法（约第29行）

**Step 2: 修改方法实现**

将现有实现修改为：

```java
@Override
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
    // 1. 调用流水分析平台获取projectId
    Integer lsfxProjectId = callLsfxPlatform(dto.getProjectName());

    // 2. 创建项目实体
    CcdiProject project = new CcdiProject();
    BeanUtils.copyProperties(dto, project);

    // 3. 设置默认值和流水分析平台ID
    project.setStatus("0"); // 进行中
    project.setIsArchived(0); // 未归档
    project.setTargetCount(0);
    project.setHighRiskCount(0);
    project.setMediumRiskCount(0);
    project.setLowRiskCount(0);
    project.setLsfxProjectId(lsfxProjectId);  // 设置流水分析平台ID

    // 4. 保存到数据库
    projectMapper.insert(project);

    // 5. 返回VO
    CcdiProjectVO vo = new CcdiProjectVO();
    BeanUtils.copyProperties(project, vo);
    return vo;
}
```

**Step 3: 确认事务注解存在**

确保方法上有 `@Transactional` 注解（如果没有则添加）：

```java
@Override
@Transactional(rollbackFor = Exception.class)
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
    // ...
}
```

需要添加import：
```java
import org.springframework.transaction.annotation.Transactional;
```

**Step 4: 验证编译**

```bash
cd ccdi-project
mvn compile
```

预期结果：编译成功

**Step 5: 提交变更**

```bash
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java
git commit -m "feat: createProject方法集成流水分析平台调用"
```

---

## Task 8: 运行单元测试验证功能

**Files:**
- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectServiceImplTest.java`

**Step 1: 确保Mock Server运行**

```bash
cd lsfx-mock-server
python app.py
```

确认输出：`Running on http://localhost:8000`

**Step 2: 运行测试**

```bash
cd ccdi-project
mvn test -Dtest=CcdiProjectServiceImplTest#testCreateProject_Success
```

预期结果：
```
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
```

**Step 3: 如果测试失败，检查日志**

常见问题：
- Mock Server 未启动：启动 Mock Server
- 数据库连接失败：检查数据库配置
- 字段映射错误：检查 BeanUtils.copyProperties 是否正常

**Step 4: 提交测试结果**

如果测试通过，无需额外提交（测试代码已提交）

---

## Task 9: 添加异常测试用例

**Files:**
- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectServiceImplTest.java`

**Step 1: 添加测试方法**

在测试类中添加：

```java
@Test
public void testCreateProject_WithNullProjectName() {
    // 准备数据 - 项目名称为null
    CcdiProjectSaveDTO dto = new CcdiProjectSaveDTO();
    dto.setProjectName(null);
    dto.setDescription("测试描述");
    dto.setConfigType("default");

    // 执行并验证异常
    assertThrows(ServiceException.class, () -> {
        projectService.createProject(dto);
    });
}
```

**Step 2: 运行新测试**

```bash
cd ccdi-project
mvn test -Dtest=CcdiProjectServiceImplTest#testCreateProject_WithNullProjectName
```

预期结果：测试可能失败（因为没有验证项目名称），这是正常的

**Step 3: 提交测试代码**

```bash
git add ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectServiceImplTest.java
git commit -m "test: 添加项目名称为null的异常测试用例"
```

---

## Task 10: 使用Swagger进行集成测试

**Files:**
- 无需修改文件

**Step 1: 启动后端应用**

```bash
cd ruoyi-admin
mvn spring-boot:run
```

等待启动完成，确认无错误

**Step 2: 访问Swagger UI**

浏览器打开：`http://localhost:8080/swagger-ui/index.html`

**Step 3: 找到创建项目接口**

导航到：`纪检初核项目管理` → `POST /ccdi/project`

**Step 4: 准备测试数据**

点击 "Try it out"，输入请求体：

```json
{
  "projectName": "集成测试项目001",
  "description": "测试集成流水分析平台",
  "configType": "default"
}
```

**Step 5: 执行请求**

点击 "Execute"

**Step 6: 验证响应**

检查响应体：

```json
{
  "code": 200,
  "msg": "项目创建成功",
  "data": {
    "projectId": 1,
    "projectName": "集成测试项目001",
    "lsfxProjectId": 77,  // 必须有值
    "description": "测试集成流水分析平台",
    "configType": "default",
    "status": "0",
    ...
  }
}
```

**Step 7: 验证数据库**

使用MCP工具或MySQL客户端查询：

```sql
SELECT project_id, project_name, lsfx_project_id, create_time
FROM ccdi_project
WHERE project_name = '集成测试项目001';
```

预期结果：`lsfx_project_id` 字段有值（如77）

**Step 8: 记录测试结果**

无代码提交，手动记录测试通过

---

## Task 11: 测试异常场景

**Files:**
- 无需修改文件

**Step 1: 停止Mock Server**

在 Mock Server 运行的终端按 `Ctrl+C` 停止

**Step 2: 尝试创建项目**

使用 Swagger 或 curl：

```bash
curl -X POST http://localhost:8080/ccdi/project \
  -H "Content-Type: application/json" \
  -d '{
    "projectName": "异常测试项目",
    "description": "测试流水分析平台不可用",
    "configType": "default"
  }'
```

**Step 3: 验证响应**

预期结果：
```json
{
  "code": 500,
  "msg": "调用流水分析平台失败: ..."  // 包含错误信息
}
```

**Step 4: 验证数据库没有脏数据**

```sql
SELECT COUNT(*) FROM ccdi_project WHERE project_name = '异常测试项目';
```

预期结果：`0`（事务已回滚）

**Step 5: 重启Mock Server**

```bash
cd lsfx-mock-server
python app.py
```

**Step 6: 验证功能恢复**

再次创建项目，确认功能正常

---

## Task 12: 清理和文档更新

**Files:**
- Modify: `doc/design/2026-03-04-create-project-integrate-lsfx-design.md`

**Step 1: 更新设计文档状态**

修改设计文档开头：

```markdown
**状态**: 已实施 ✅
```

**Step 2: 更新变更清单**

将变更清单中的状态更新为"已完成"：

```markdown
| 类型 | 文件 | 变更内容 | 状态 |
|-----|------|---------|------|
| 数据库 | `ccdi_project` 表 | 新增 `lsfx_project_id` 字段 | ✅ 已完成 |
| SQL | `2026-03-04-add-lsfx-project-id.sql` | 数据库迁移脚本 | ✅ 已执行 |
| 实体类 | `CcdiProject.java` | 新增 `lsfxProjectId` 属性 | ✅ 已修改 |
| VO | `CcdiProjectVO.java` | 新增 `lsfxProjectId` 属性 | ✅ 已修改 |
| Service | `CcdiProjectServiceImpl.java` | 注入 `LsfxAnalysisClient`，添加调用逻辑 | ✅ 已修改 |
```

**Step 3: 提交文档更新**

```bash
git add doc/design/2026-03-04-create-project-integrate-lsfx-design.md
git commit -m "docs: 更新设计文档状态为已实施"
```

**Step 4: 创建实施总结**

```bash
git log --oneline --graph > doc/design/2026-03-04-implementation-summary.txt
```

提交总结：

```bash
git add doc/design/2026-03-04-implementation-summary.txt
git commit -m "docs: 添加实施总结"
```

---

## Task 13: 最终验收

**Files:**
- 无需修改文件

**Step 1: 运行所有测试**

```bash
cd ccdi-project
mvn test
```

预期结果：所有测试通过

**Step 2: 检查代码质量**

```bash
mvn checkstyle:check
```

如果失败，根据提示修复代码风格问题

**Step 3: 验证数据库字段**

```sql
DESC ccdi_project;
```

确认 `lsfx_project_id` 字段存在

**Step 4: 端到端测试**

通过前端或 Swagger 完整测试创建项目流程：
1. 创建项目成功
2. 查询项目列表，确认 `lsfxProjectId` 显示
3. 查询项目详情，确认 `lsfxProjectId` 正确

**Step 5: 记录验收结果**

在项目文档中记录：
- 功能验收通过 ✅
- 测试覆盖率：100%
- 性能测试：创建项目耗时约1-2秒（取决于网络）
- 异常处理：已验证

---

## 后续任务（可选）

### Task 14: 添加前端展示（可选）

如果需要在项目列表页面展示 `lsfxProjectId`：

**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/index.vue`
- Modify: `ruoyi-ui/src/api/ccdi/project.js`

**Step 1: 修改表格列定义**

在 `index.vue` 的表格列中添加：

```javascript
{
  label: '流水分析项目ID',
  prop: 'lsfxProjectId',
  width: '120'
}
```

**Step 2: 提交前端变更**

```bash
git add ruoyi-ui/src/views/ccdiProject/index.vue
git commit -m "feat: 前端项目列表展示流水分析平台项目ID"
```

---

## 实施注意事项

### 关键点

1. **事务一致性**：确保 `@Transactional` 注解存在，任何异常都会回滚
2. **参数正确性**：严格按照《兰溪-流水分析对接-新版.md》配置固定参数
3. **错误提示**：给用户清晰的错误提示，便于排查问题
4. **测试覆盖**：必须测试成功和失败两种场景

### 常见问题

**Q1: 测试时提示找不到 LsfxAnalysisClient**
- 检查 ccdi-project 模块是否依赖 ccdi-lsfx 模块
- 检查 Spring 组件扫描是否包含 `com.ruoyi.lsfx` 包

**Q2: 数据库字段添加失败**
- 确认数据库连接正常
- 检查是否有权限修改表结构

**Q3: Mock Server 无法启动**
- 检查 8000 端口是否被占用
- 确认 Python 环境正常

**Q4: 事务回滚不生效**
- 确认方法上有 `@Transactional` 注解
- 检查异常是否被捕获后未重新抛出

---

## 参考资料

- 设计文档: `doc/design/2026-03-04-create-project-integrate-lsfx-design.md`
- 流水分析对接文档: `assets/对接流水分析/兰溪-流水分析对接-新版.md`
- 项目规范: `CLAUDE.md`

---

**计划创建完成！**
