579 lines
13 KiB
Markdown
579 lines
13 KiB
Markdown
# 项目管理状态统计修复实施计划
|
||
|
||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||
|
||
**Goal:** 修复项目管理标签页状态统计功能,使标签页显示数据库中所有该状态的项目总数,而非当前页的数量。
|
||
|
||
**Architecture:** 后端新增独立的状态统计接口 `/ccdi/project/statusCounts`,前端在加载列表时并行调用统计接口,使用 `Promise.all` 同时获取列表数据和统计数据。
|
||
|
||
**Tech Stack:** Java 17, Spring Boot 3.5.8, MyBatis Plus 3.5.10, Vue.js 2.6.12, Element UI 2.15.14
|
||
|
||
---
|
||
|
||
## Task 1: 创建状态统计 VO 类
|
||
|
||
**Files:**
|
||
- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectStatusCountsVO.java`
|
||
|
||
**Step 1: 创建 VO 类文件**
|
||
|
||
创建新文件 `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectStatusCountsVO.java`:
|
||
|
||
```java
|
||
package com.ruoyi.ccdi.project.domain.vo;
|
||
|
||
import lombok.Data;
|
||
|
||
/**
|
||
* 项目状态统计VO
|
||
*
|
||
* @author ruoyi
|
||
*/
|
||
@Data
|
||
public class CcdiProjectStatusCountsVO {
|
||
/** 全部项目总数 */
|
||
private Long all;
|
||
|
||
/** 进行中项目数(状态0) */
|
||
private Long status0;
|
||
|
||
/** 已完成项目数(状态1) */
|
||
private Long status1;
|
||
|
||
/** 已归档项目数(状态2) */
|
||
private Long status2;
|
||
}
|
||
```
|
||
|
||
**Step 2: 验证文件创建**
|
||
|
||
Run: `ls ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectStatusCountsVO.java`
|
||
|
||
Expected: 文件存在
|
||
|
||
**Step 3: Commit**
|
||
|
||
```bash
|
||
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectStatusCountsVO.java
|
||
git commit -m "feat: 添加项目状态统计 VO 类"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 2: 添加 Service 接口方法
|
||
|
||
**Files:**
|
||
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectService.java`
|
||
|
||
**Step 1: 读取当前 Service 接口**
|
||
|
||
Run: Read `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectService.java`
|
||
|
||
Expected: 看到现有方法列表
|
||
|
||
**Step 2: 添加统计方法声明**
|
||
|
||
在 `ICcdiProjectService.java` 文件末尾(类定义的最后一个方法之后)添加:
|
||
|
||
```java
|
||
/**
|
||
* 查询各状态的项目总数(不受搜索条件影响)
|
||
*
|
||
* @return 状态统计
|
||
*/
|
||
CcdiProjectStatusCountsVO getStatusCounts();
|
||
```
|
||
|
||
**Step 3: 验证语法**
|
||
|
||
Run: `cd ccdi-project && mvn compile`
|
||
|
||
Expected: BUILD SUCCESS
|
||
|
||
**Step 4: Commit**
|
||
|
||
```bash
|
||
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectService.java
|
||
git commit -m "feat: Service 接口添加状态统计方法声明"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 3: 实现 Service 统计方法
|
||
|
||
**Files:**
|
||
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`
|
||
|
||
**Step 1: 读取当前 Service 实现类**
|
||
|
||
Run: Read `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`
|
||
|
||
Expected: 看到现有实现和依赖
|
||
|
||
**Step 2: 确认需要的 import**
|
||
|
||
检查文件顶部是否已有以下 import:
|
||
- `com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper`
|
||
- `com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO`
|
||
|
||
如果没有,添加它们。
|
||
|
||
**Step 3: 实现 getStatusCounts 方法**
|
||
|
||
在 Service 实现类末尾添加方法实现:
|
||
|
||
```java
|
||
@Override
|
||
public CcdiProjectStatusCountsVO getStatusCounts() {
|
||
CcdiProjectStatusCountsVO vo = new CcdiProjectStatusCountsVO();
|
||
|
||
// 统计全部项目
|
||
Long totalCount = ccdiProjectMapper.selectCount(null);
|
||
vo.setAll(totalCount);
|
||
|
||
// 统计进行中项目(状态0)
|
||
Long status0Count = ccdiProjectMapper.selectCount(
|
||
new LambdaQueryWrapper<CcdiProject>()
|
||
.eq(CcdiProject::getStatus, "0")
|
||
);
|
||
vo.setStatus0(status0Count);
|
||
|
||
// 统计已完成项目(状态1)
|
||
Long status1Count = ccdiProjectMapper.selectCount(
|
||
new LambdaQueryWrapper<CcdiProject>()
|
||
.eq(CcdiProject::getStatus, "1")
|
||
);
|
||
vo.setStatus1(status1Count);
|
||
|
||
// 统计已归档项目(状态2)
|
||
Long status2Count = ccdiProjectMapper.selectCount(
|
||
new LambdaQueryWrapper<CcdiProject>()
|
||
.eq(CcdiProject::getStatus, "2")
|
||
);
|
||
vo.setStatus2(status2Count);
|
||
|
||
return vo;
|
||
}
|
||
```
|
||
|
||
**Step 4: 验证编译**
|
||
|
||
Run: `cd ccdi-project && mvn compile`
|
||
|
||
Expected: BUILD SUCCESS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java
|
||
git commit -m "feat: 实现项目状态统计方法"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 4: 添加 Controller 接口
|
||
|
||
**Files:**
|
||
- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java`
|
||
|
||
**Step 1: 读取当前 Controller**
|
||
|
||
Run: Read `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java`
|
||
|
||
Expected: 看到现有接口定义
|
||
|
||
**Step 2: 添加状态统计接口**
|
||
|
||
在 Controller 类的最后一个方法之后添加:
|
||
|
||
```java
|
||
/**
|
||
* 查询项目状态统计
|
||
*/
|
||
@GetMapping("/statusCounts")
|
||
@Operation(summary = "查询项目状态统计")
|
||
@PreAuthorize("@ss.hasPermi('ccdi:project:list')")
|
||
public AjaxResult getStatusCounts() {
|
||
CcdiProjectStatusCountsVO counts = projectService.getStatusCounts();
|
||
return AjaxResult.success(counts);
|
||
}
|
||
```
|
||
|
||
**Step 3: 验证编译**
|
||
|
||
Run: `cd ccdi-project && mvn compile`
|
||
|
||
Expected: BUILD SUCCESS
|
||
|
||
**Step 4: Commit**
|
||
|
||
```bash
|
||
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java
|
||
git commit -m "feat: 添加项目状态统计接口"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 5: 测试后端接口
|
||
|
||
**Files:**
|
||
- None (测试验证)
|
||
|
||
**Step 1: 启动后端服务**
|
||
|
||
Run: `mvn spring-boot:run` 或 `ry.bat`
|
||
|
||
Expected: 服务启动成功,看到 "Started RuoYiApplication" 日志
|
||
|
||
**Step 2: 获取访问令牌**
|
||
|
||
Run (使用 curl 或浏览器):
|
||
|
||
```bash
|
||
curl -X POST "http://localhost:8080/login/test?username=admin&password=admin123"
|
||
```
|
||
|
||
Expected: 返回 JSON 包含 token
|
||
|
||
**Step 3: 测试状态统计接口**
|
||
|
||
在 Swagger UI 中测试:
|
||
1. 访问: http://localhost:8080/swagger-ui/index.html
|
||
2. 找到: "纪检初核项目管理" → "GET /ccdi/project/statusCounts"
|
||
3. 点击 "Try it out" → "Execute"
|
||
|
||
或使用 curl (替换 YOUR_TOKEN):
|
||
|
||
```bash
|
||
curl -X GET "http://localhost:8080/ccdi/project/statusCounts" \
|
||
-H "Authorization: Bearer YOUR_TOKEN"
|
||
```
|
||
|
||
Expected: 返回类似以下的响应:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
"all": 30,
|
||
"status0": 10,
|
||
"status1": 15,
|
||
"status2": 5
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 4: 验证数据正确性**
|
||
|
||
使用数据库工具连接 MySQL,执行:
|
||
|
||
```sql
|
||
SELECT
|
||
status,
|
||
COUNT(*) as count
|
||
FROM ccdi_project
|
||
GROUP BY status;
|
||
```
|
||
|
||
Expected: 统计数字与接口返回一致
|
||
|
||
---
|
||
|
||
## Task 6: 添加前端 API 方法
|
||
|
||
**Files:**
|
||
- Modify: `ruoyi-ui/src/api/ccdiProject.js`
|
||
|
||
**Step 1: 读取当前 API 文件**
|
||
|
||
Run: Read `ruoyi-ui/src/api/ccdiProject.js`
|
||
|
||
Expected: 看到现有的 API 方法定义
|
||
|
||
**Step 2: 添加状态统计 API 方法**
|
||
|
||
在文件末尾(最后一个 export 函数之后)添加:
|
||
|
||
```javascript
|
||
// 查询项目状态统计
|
||
export function getStatusCounts() {
|
||
return request({
|
||
url: '/ccdi/project/statusCounts',
|
||
method: 'get'
|
||
})
|
||
}
|
||
```
|
||
|
||
**Step 3: 验证语法**
|
||
|
||
Run: `cd ruoyi-ui && npm run lint -- --fix src/api/ccdiProject.js`
|
||
|
||
Expected: No errors
|
||
|
||
**Step 4: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ui/src/api/ccdiProject.js
|
||
git commit -m "feat: 前端 API 添加状态统计方法"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 7: 修改前端页面组件
|
||
|
||
**Files:**
|
||
- Modify: `ruoyi-ui/src/views/ccdiProject/index.vue`
|
||
|
||
**Step 1: 读取当前页面组件**
|
||
|
||
Run: Read `ruoyi-ui/src/views/ccdiProject/index.vue`
|
||
|
||
Expected: 看到现有代码结构
|
||
|
||
**Step 2: 修改 import 语句**
|
||
|
||
找到第 64 行左右的 import 语句:
|
||
|
||
```javascript
|
||
import {listProject} from '@/api/ccdiProject'
|
||
```
|
||
|
||
修改为:
|
||
|
||
```javascript
|
||
import {listProject, getStatusCounts} from '@/api/ccdiProject'
|
||
```
|
||
|
||
**Step 3: 重构 getList 方法**
|
||
|
||
找到 `getList()` 方法(大约第 122-134 行),完全替换为:
|
||
|
||
```javascript
|
||
/** 查询项目列表 */
|
||
getList() {
|
||
this.loading = true
|
||
|
||
// 并行请求列表数据和状态统计
|
||
Promise.all([
|
||
listProject(this.queryParams),
|
||
getStatusCounts()
|
||
]).then(([listResponse, countsResponse]) => {
|
||
// 处理列表数据
|
||
this.projectList = listResponse.rows
|
||
this.total = listResponse.total
|
||
|
||
// 处理状态统计
|
||
const counts = countsResponse.data
|
||
this.tabCounts = {
|
||
all: counts.all,
|
||
'0': counts.status0,
|
||
'1': counts.status1,
|
||
'2': counts.status2
|
||
}
|
||
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.loading = false
|
||
})
|
||
}
|
||
```
|
||
|
||
**Step 4: 删除旧的统计方法**
|
||
|
||
找到并删除 `calculateTabCounts()` 方法(大约第 135-145 行):
|
||
|
||
```javascript
|
||
// 删除这个方法
|
||
/** 计算标签页数量 */
|
||
calculateTabCounts() {
|
||
// 注意:这里需要后端API返回所有状态的数量统计
|
||
// 目前暂时使用当前页的数据进行计算
|
||
this.tabCounts = {
|
||
all: this.total,
|
||
'0': this.projectList.filter(p => p.status === '0').length,
|
||
'1': this.projectList.filter(p => p.status === '1').length,
|
||
'2': this.projectList.filter(p => p.status === '2').length
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 5: 验证语法**
|
||
|
||
Run: `cd ruoyi-ui && npm run lint -- --fix src/views/ccdiProject/index.vue`
|
||
|
||
Expected: No errors
|
||
|
||
**Step 6: Commit**
|
||
|
||
```bash
|
||
git add ruoyi-ui/src/views/ccdiProject/index.vue
|
||
git commit -m "refactor: 使用后端统计接口替换前端计算"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 8: 测试前端功能
|
||
|
||
**Files:**
|
||
- None (测试验证)
|
||
|
||
**Step 1: 确保后端服务运行**
|
||
|
||
确认后端服务在运行中。
|
||
|
||
**Step 2: 启动前端开发服务器**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
cd ruoyi-ui
|
||
npm run dev
|
||
```
|
||
|
||
Expected: 服务启动,看到 "App running at" 消息
|
||
|
||
**Step 3: 测试页面加载**
|
||
|
||
1. 打开浏览器访问: http://localhost:80
|
||
2. 登录系统 (admin/admin123)
|
||
3. 导航到 "项目管理" 页面
|
||
|
||
Expected:
|
||
- 页面正常加载
|
||
- 标签页显示正确的统计数字(例如:全部项目(30))
|
||
- 标签页数字不随分页变化
|
||
|
||
**Step 4: 测试搜索功能**
|
||
|
||
1. 在搜索框输入项目名称
|
||
2. 点击搜索按钮或按回车
|
||
|
||
Expected:
|
||
- 列表正确过滤
|
||
- 标签页数字保持不变(显示总数)
|
||
|
||
**Step 5: 测试分页功能**
|
||
|
||
1. 点击分页组件切换到第 2 页
|
||
|
||
Expected:
|
||
- 列表切换到第 2 页
|
||
- 标签页数字保持不变
|
||
|
||
**Step 6: 测试状态切换功能**
|
||
|
||
1. 点击"进行中"标签
|
||
|
||
Expected:
|
||
- 列表只显示进行中的项目
|
||
- 标签页数字保持不变(仍显示总数)
|
||
|
||
**Step 7: 测试浏览器控制台**
|
||
|
||
打开浏览器开发者工具的 Console 标签
|
||
|
||
Expected:
|
||
- 没有 JavaScript 错误
|
||
- 看到两个 API 请求成功(list 和 statusCounts)
|
||
|
||
---
|
||
|
||
## Task 9: 最终提交和文档更新
|
||
|
||
**Files:**
|
||
- Modify: `docs/plans/2026-02-27-project-status-counts-fix-design.md`
|
||
|
||
**Step 1: 更新设计文档状态**
|
||
|
||
修改设计文档的状态部分:
|
||
|
||
```markdown
|
||
## 文档信息
|
||
|
||
- **创建日期**: 2026-02-27
|
||
- **作者**: Claude Code
|
||
- **状态**: ✅ 已完成
|
||
```
|
||
|
||
**Step 2: 验收清单**
|
||
|
||
对照设计文档的验收标准,确认:
|
||
|
||
- [ ] 后端 `/statusCounts` 接口返回正确的统计数字
|
||
- [ ] 前端标签页显示数据库中的完整统计
|
||
- [ ] 搜索不影响标签页统计数字
|
||
- [ ] 分页不影响标签页统计数字
|
||
- [ ] 状态过滤不影响标签页统计数字
|
||
- [ ] 统计接口响应时间 < 100ms
|
||
- [ ] 页面加载时间无明显增加
|
||
|
||
**Step 3: 提交文档更新**
|
||
|
||
```bash
|
||
git add docs/plans/2026-02-27-project-status-counts-fix-design.md
|
||
git commit -m "docs: 更新项目状态统计修复设计文档状态为已完成"
|
||
```
|
||
|
||
**Step 4: 推送所有提交**
|
||
|
||
```bash
|
||
git push origin dev
|
||
```
|
||
|
||
---
|
||
|
||
## 验收清单
|
||
|
||
在完成所有任务后,验证以下内容:
|
||
|
||
### 功能验收
|
||
- [ ] 后端接口正确返回统计数字
|
||
- [ ] 前端标签页显示正确统计
|
||
- [ ] 搜索不影响统计数字
|
||
- [ ] 分页不影响统计数字
|
||
- [ ] 状态过滤不影响统计数字
|
||
|
||
### 性能验收
|
||
- [ ] 统计接口响应时间 < 100ms
|
||
- [ ] 页面加载流畅
|
||
|
||
### 代码质量
|
||
- [ ] 后端代码符合规范
|
||
- [ ] 前端代码符合规范
|
||
- [ ] 提交信息清晰
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
1. **测试数据准备**: 如果数据库中没有足够的项目数据,可以先插入一些测试数据以验证统计功能
|
||
2. **错误处理**: 当前实现中,如果统计接口失败,会在控制台显示错误但不阻塞列表加载
|
||
3. **性能考虑**: 如果项目数量很大(> 10000),建议后续优化为 GROUP BY 单次查询
|
||
|
||
---
|
||
|
||
## 回滚方案
|
||
|
||
如果实施后发现问题,可以通过以下步骤回滚:
|
||
|
||
1. **回滚前端代码**:
|
||
```bash
|
||
git revert <commit-hash-of-task-6-and-7>
|
||
```
|
||
|
||
2. **回滚后端代码**:
|
||
```bash
|
||
git revert <commit-hash-of-task-1-to-4>
|
||
```
|
||
|
||
3. **重新部署服务**
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
|
||
- 设计文档: `docs/plans/2026-02-27-project-status-counts-fix-design.md`
|
||
- 若依框架文档: 项目根目录的 `CLAUDE.md`
|
||
- MyBatis Plus 文档: https://baomidou.com/
|