Files
DevFlow/docs/05-代码审查/任务模块问题分析-2026-06-16.md

352 lines
17 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.
# 任务模块问题分析报告(核对版)
> **日期**: 2026-06-16
> **范围**: 任务模块全栈Rust 后端 + Tauri IPC + Vue 3 前端)
> **审查文件**: `task.rs` / `tasks.ts` / `Tasks.vue` / `TaskDetail.vue` / `crud.rs` / `models.rs` / `types.rs` / `project.ts` / `state.rs` / `migrations.rs` / `router/index.ts`
> **核对方法**: Explore 代理并行验源码,逐项 `file:line` 取证
> **核对结论**: 原清单 18 项中 **真 bug 8 项**P0×2 P1×6、增强 5 项、假/部分假 3 项、与现有 todo 去重 3 项
---
## 〇、核对结论速览
| # | 原清单结论 | 核实 | 处置 |
|---|-----------|------|------|
| 1 | 状态枚举前后端不一致5 vs 7 | ✅ 真 | 三方分裂,记 B-260616-12 |
| 2 | DDL priority 默认(1) vs 代码(2) | ✅ 真 | 潜在非现患,记 B-260616-14 |
| 3 | delete_task 硬删除无恢复 | ✅ 真 | 记 B-260616-13 |
| 4 | priority 无值域校验 | ✅ 真 | 记 B-260616-15 |
| 5 | id/created_at/project_id 可篡改 | ✅ 真 | 记 B-260616-16 |
| 6 | updateTask 无 try/catch | ✅ 真 | 记 B-260616-17 |
| 7 | TaskDetail 绕过 store | ✅ 真 | 记 B-260616-18 |
| 8 | /tasks/:id 路由未注册 | ✅ 真 | 🔄 去重 = B-260616-09 已存在 |
| 9 | 无分页全量加载 | 🔄 去重 | = F-260615-03 已立 |
| 10 | watch 重复 IPC | 🔄 去重 | = B-260615-29 设计决策 |
| 11 | 列表组件重复 | ❌ 假 | 两文件模板结构不同(.task-item vs .task-card排除 |
| 12 | project_id 无存在性校验 | ⚠️ 部分假 | `db.rs:22 PRAGMA foreign_keys=ON` 兜底拦截,降级 |
| 13 | description 无长度限制 | 🟡 增强 | 待产品决策 |
| 14 | branch_name 无格式校验 | 🟡 增强 | 待产品决策 |
| 15 | TaskDetail 只读无编辑 | 🟡 增强 | 待产品决策 |
| 16 | 无批量操作 | 🟡 增强 | 待产品决策 |
| 17 | 无排序选项 | 🟡 增强 | 待产品决策 |
| 18 | 无搜索能力 | 🔄 去重 | ≈ F-260615-07 思路 |
---
## 一、模块架构概览
```
┌─────────────────────────────────────────────────────────┐
│ 前端 (Vue 3 + TS) │
│ Views │ Tasks.vue (列表) → TaskDetail.vue │
│ Store │ stores/project/tasks.ts │
│ API │ api/task.ts │
│ Types │ api/types.ts → TaskRecord │
│ Constants │ constants/project.ts (状态/优先级映射) │
├─────────────────────────────────────────────────────────┤
│ IPC 层 (Tauri) │
│ Commands │ commands/task.rs (5 个命令) │
│ State │ state.rs → TaskRepo │
├─────────────────────────────────────────────────────────┤
│ 后端 (Rust Crates) │
│ df-core │ types.rs → TaskStatus 枚举 │
│ df-storage │ models.rs → TaskRecord 结构体 │
│ │ crud.rs → TaskRepo (CRUD 实现) │
│ │ migrations.rs → tasks 表 DDL │
└─────────────────────────────────────────────────────────┘
```
### IPC 命令5 个)
| 命令 | 签名 | 说明 |
|------|------|------|
| `list_tasks` | `(project_id?) → Vec<TaskRecord>` | 列出任务,可按项目筛选 |
| `get_task_by_id` | `(id) → TaskRecord` | 按 ID 查单个任务 |
| `create_task` | `(input) → TaskRecord` | 创建任务,默认 status=todo, priority=2 |
| `update_task` | `(id, field, value) → bool` | 更新单字段status 值有枚举校验 |
| `delete_task` | `(id) → bool` | 删除任务(硬删除) |
### TaskStatus 枚举(后端 7 种)
| 状态值 | 说明 |
|--------|------|
| `todo` | 待开始 |
| `in_progress` | 进行中 |
| `in_review` | 代码审查中 |
| `testing` | 测试中 |
| `done` | 已完成 |
| `blocked` | 已阻塞 |
| `cancelled` | 已取消 |
### 优先级映射
| 值 | 标签 | 样式 |
|----|------|------|
| 0 | P0 | critical紧急 |
| 1 | P1 | high |
| 2 | P2 | medium默认 |
| 3 | P3 | low |
---
## 二、问题清单(含核对标注)
### 🔴 P0 — 数据一致性 / 功能断裂
#### 1. 前后端状态枚举完全不一致 【✅ 真 · 核对确认】
| 层 | 状态值集合 | 证据 |
|----|-----------|------|
| 后端枚举 `TaskStatus` | `todo, in_progress, in_review, testing, done, blocked, cancelled`7 | `crates/df-core/src/types.rs:131-146` |
| 前端类型注释 | 同后端 7 种 | `src/api/types.ts:88` |
| 前端常量 `TASK_STATUS_LABELS` | `todo, in_progress, review_ready, merged, abandoned`5 | `src/constants/project.ts:56-62` |
| 前端 `taskStatusClass` | 同常量 5 种,未定义回退 `status-todo` | `src/constants/project.ts:76-78` |
| 前端 i18n `tasks.ts` | 同常量 5 种 | `src/i18n/{en,zh-CN}/tasks.ts:47-54` |
**影响链**
- 后端 `update_task``TaskStatus::is_valid()``task.rs:96`)只接受 7 种;前端筛选器的 `review_ready/merged/abandoned` 后端全部拒绝
- AI 工具或后端写入 `in_review/testing/blocked` 时,前端 `TASK_STATUS_LABELS` 查不到 → 回退显示原始 key用户见 `in_review` 而非中文)
- `taskStatusClass` 查不到 → 回退 `status-todo`,视觉无法区分
- Dashboard 统计 `activeTasks` 仅查 `in_progress``testing/blocked` 不计入
**根因**:前端状态集是早期 Git 工作流导向review_ready/merged/abandoned后端枚举后来规范化为通用状态机两者从未对齐。`types.ts` 注释随了后端,但常量/i18n/样式仍停在旧集。
**人定决策点**:前端对齐到后端 7 种纯状态机,还是保留 Git 工作流 5 种语义做映射?
---
#### 2. DDL 默认值与代码默认值矛盾 【✅ 真 · 潜在非现患】
| 来源 | `priority` 默认值 | 证据 |
|------|-------------------|------|
| DDL | `DEFAULT 1`high | `crates/df-storage/src/migrations.rs:304` |
| Rust `default_priority()` | `2`medium | `src-tauri/src/commands/task.rs:26-28` |
| 前端 `Tasks.vue` | `2`medium | `src/views/Tasks.vue:62` |
当前 `create_task` 显式传 `priority`(走代码默认 2DDL 默认值不生效。但未来若有直连 SQL 写入路径或手动改库,`priority=1` 与应用层 `2` 不一致。低危,可并入下次 migration 对齐。
---
#### 3. `delete_task` 硬删除 — 无恢复机制 【✅ 真 · 核对确认】
```rust
// src-tauri/src/commands/task.rs:113
state.tasks.delete(&id).await.map_err(err_str) // 物理删
// crates/df-storage/src/crud.rs:194 — DELETE FROM tasks WHERE id=?
```
`TaskRecord``crates/df-storage/src/models.rs:53-66`)无 `deleted_at` 字段。对比 `ProjectRecord` 有完整软删除(`deleted_at` + `list_deleted` + `restore`)。
**风险**:误删永久丢失;关联 `branches.task_id` 外键变悬空(无 `ON DELETE CASCADE/SET NULL`)。
**人定决策点**:任务是否需要软删除 + 恢复(对标 projects还是物理删除即可任务粒度小、误删可重建
---
### 🟠 P1 — 安全 / 竞态 / 架构
#### 4. `update_task` 缺少 `priority` 值域校验 【✅ 真 · 核对确认】
```rust
// src-tauri/src/commands/task.rs:96-102 — 仅 status 校验
if field == "status" && !TaskStatus::is_valid(&value) { ... }
// priority 直接透传 update_field无范围检查
```
`update_task(id, "priority", "999")``"abc"` 静默落库。前端 `PRIORITY_LABELS``constants/project.ts:82`key 0-3查不到 → 回退 `P999` + 样式 `priority-low``project.ts:95-97`)。
**修复**`field == "priority"` 时校验 `value.parse::<i32>()``0..=3`
---
#### 5. `update_task` 缺少 `id` / `created_at` 等不可变字段保护 【✅ 真 · 核对确认】
```rust
// crates/df-storage/src/crud.rs:324-327
"tasks" => &["id", "project_id", "title", "description", "status", "priority",
"branch_name", "assignee", "workflow_def_id", "base_branch",
"created_at", "updated_at"],
```
白名单含 `id` / `created_at` / `project_id` → AI 工具或恶意调用可改主键、篡改创建时间、把任务移到别的项目。前端未暴露,但 IPC 层无防护。
**修复**tasks 白名单移除 `id`/`created_at``project_id` 若允许跨项目移动则保留但加目标项目存在性校验。
---
#### 6. 前端 `updateTask` store 无错误处理 【✅ 真 · 核对确认】
```typescript
// src/stores/project/tasks.ts:29-35 — 无 try/catch
async function updateTask(id, field, value) {
await taskApi.update(id, field, value)
const idx = state.tasks.findIndex(t => t.id === id)
if (idx >= 0) (state.tasks[idx] as any)[field] = value
}
```
对比同文件 `loadTasks`(10-16) / `createTask`(18-27) / `deleteTask`(37-44) 都有 try/catch。IPC 失败(如非法 status 被后端拒)→ Promise reject 冒泡,错误不写 `state.error`,用户无提示。
**修复**:补 try/catch失败写 `state.error` + toast。
---
#### 7. `TaskDetail.vue` 绕过 store 直接调 API 【✅ 真 · 核对确认】
```typescript
// src/views/TaskDetail.vue:101 import { taskApi, projectApi } from '@/api'
// src/views/TaskDetail.vue:137-141 直接 taskApi.get() / projectApi.list()
```
其他视图走 `useProjectStore()`TaskDetail 直调 API
- 不享受 AR-11 `df-data-changed` 联动刷新(其他窗口改任务,本页不自动刷新)
- `projectApi.list()` 全量拉项目列表仅为解析 `project_id → name`
**修复**:改走 store.loadTasks/store.projects或单独监听 `df-data-changed` 重载当前 task。
---
#### 8. `/tasks/:id` 路由未注册 【✅ 真 · 导航断裂 · 🔄 去重 = B-260616-09 已存在】
```typescript
// src/router/index.ts:45-49 — 只有 /tasks
{ path: '/tasks', name: 'Tasks', component: () => import('../views/Tasks.vue') }
// src/views/Tasks.vue:57 — 任务卡点击跳转
router.push('/tasks/${task.id}') // 路由表无此路径 → 落空
```
对比 `/ideas/:id``/projects/:id` 均已注册,唯独 `/tasks/:id` 遗漏。点击任务卡片匹配不到路由(落入 catch-all 或空白页。1 行改动速赢。
---
### 🟡 P2 — 性能 / 体验 / 代码质量
#### 9. 无分页 — 全量加载所有任务 【🔄 去重 = F-260615-03】
```rust
// src-tauri/src/commands/task.rs:32-41
None => state.tasks.list_all().await, // 无 limit/offsetORDER BY created_at DESC
```
已有任务 **F-260615-03** 覆盖,不重复立项。
---
#### 10. `Tasks.vue` 筛选切换导致重复全量请求 【🔄 去重 = B-260615-29】
```typescript
// src/views/Tasks.vue:229-233
watch(activeProject, (key) => { store.loadTasks(key === 'all' ? undefined : key) })
// src/views/Tasks.vue:165-192 filteredGroups computed 已做纯前端 filter
```
这是 **B-260615-29** 的设计决策(避免跨项目视图不同步),代价是每次切换 IPC 往返。不重复立项。
---
#### 11. ~~`Tasks.vue` 和 `ProjectDetail.vue` 任务列表样式/逻辑重复~~ 【❌ 假 · 排除】
核对:`Tasks.vue:57-73``.task-item` 布局,`ProjectDetail.vue:137-151``.task-card` 布局,模板结构不同。两者引用相同常量函数(状态/优先级映射),但模板本身非重复。**排除**。
---
#### 12. `create_task` 无 `project_id` 存在性校验 【⚠️ 部分假 · 降级】
```rust
// src-tauri/src/commands/task.rs:59-84 — create_task 不校验 project_id
project_id: input.project_id, // 直接使用
```
`crates/df-storage/src/db.rs:22` 已开 `PRAGMA foreign_keys=ON`DB 层外键约束会拦截指向不存在 project 的 insert。代码层无显式校验但风险被 DB 兜住。**降级为非漏洞**,仅留注释说明依赖外键。
---
#### 13. `description` 字段无长度限制 【🟡 增强】
`TaskRecord.description``String`DDL 为 `TEXT NOT NULL DEFAULT ''`。无前后端长度校验,超大文本影响 Markdown 渲染性能。待产品决策加上限。
---
#### 14. `branch_name` / `base_branch` 无 Git 分支名格式校验 【🟡 增强】
允许任意字符串(空格/特殊字符/中文),可能与实际 Git 分支不兼容。待产品决策加格式校验。
---
### 🔵 P3 — 增强建议(均待产品决策)
| # | 项 | 说明 |
|---|----|------|
| 15 | TaskDetail 纯只读 | 无编辑/改状态/改优先级 UI列表页也无内联编辑 → 当前无任何 UI 改任务状态,只能靠 AI 工具/API |
| 16 | 无批量操作 | 无法批量改状态/删除/分配 |
| 17 | 无排序选项 | 固定 `ORDER BY created_at DESC`,无法按优先级/状态/更新时间排 |
| 18 | 无搜索能力 | ≈ F-260615-07 思路 |
---
## 三、问题汇总矩阵(含核对标注)
| # | 严重度 | 类型 | 问题 | 影响 | 核实 |
|---|--------|------|------|------|------|
| 1 | P0 | 数据一致性 | 前后端状态枚举不一致5 vs 7 种) | 全局 | ✅ 真 |
| 2 | P0 | 数据一致性 | DDL priority 默认值(1) vs 代码(2) | 数据层 | ✅ 真(潜在) |
| 3 | P0 | 数据安全 | delete_task 硬删除无恢复 | 数据丢失 | ✅ 真 |
| 4 | P1 | 安全校验 | priority 无值域校验 | 数据质量 | ✅ 真 |
| 5 | P1 | 安全校验 | id/created_at/project_id 可被篡改 | 数据完整性 | ✅ 真 |
| 6 | P1 | 健壮性 | updateTask store 无 try/catch | 用户体验 | ✅ 真 |
| 7 | P1 | 架构 | TaskDetail 绕过 store | 数据同步 | ✅ 真 |
| 8 | P1 | 功能缺陷 | /tasks/:id 路由未注册 | 导航断裂 | ✅ 真 |
| 9 | P2 | 性能 | 无分页全量加载 | 扩展性 | 🔄 F-260615-03 |
| 10 | P2 | 性能 | 筛选切换重复 IPC | 响应速度 | 🔄 B-260615-29 |
| 11 | P2 | 代码质量 | 任务列表组件重复 | 可维护性 | ❌ 假 |
| 12 | P2 | 安全校验 | project_id 无存在性校验 | 数据完整性 | ⚠️ 部分假(外键兜底) |
| 13 | P2 | 安全校验 | description 无长度限制 | 性能 | 🟡 增强 |
| 14 | P2 | 安全校验 | branch_name 无格式校验 | 数据质量 | 🟡 增强 |
| 15 | P3 | 功能缺失 | TaskDetail 无编辑能力 | 用户体验 | 🟡 增强 |
| 16 | P3 | 功能缺失 | 无批量操作 | 效率 | 🟡 增强 |
| 17 | P3 | 功能缺失 | 无排序选项 | 用户体验 | 🟡 增强 |
| 18 | P3 | 功能缺失 | 无搜索能力 | 用户体验 | 🔄 ≈F-260615-07 |
---
## 四、todo 编号映射
记入 `docs/todo.md`B-260615-58 起,沿用 6.15 编号保连续):
| todo 号 | 原清单 # | 等级 | 摘要 |
|---------|---------|------|------|
| B-260616-12 | 1 | P0 | 状态枚举前后端分裂7 vs 5需人定Git 工作流 vs 纯状态机 |
| B-260616-13 | 3 | P0 | delete_task 硬删除无恢复,需人定:是否要任务软删除 |
| B-260616-14 | 2 | P1 | DDL priority DEFAULT 1 vs 代码 2migration 对齐 |
| B-260616-15 | 4 | P1 | update_task priority 无值域校验0..=3 |
| B-260616-16 | 5 | P1 | allowed_columns 含 id/created_at/project_id 可篡改 |
| B-260616-17 | 6 | P1 | updateTask store 无 try/catch |
| B-260616-18 | 7 | P1 | TaskDetail 绕 store不享受联动刷新 |
| B-260616-09 | 8 | P1 | /tasks/:id 路由未注册 — **已存在去重**todo 排查会话区块) |
> 编号沿用 B-260616 批次(与 B-260616-08~11 同批,今日 06-16 发现)。
> 增强 #13-17 归 F- 类,待产品决策后立项。
---
## 五、建议修复顺序
1. **#8** 路由注册 `/tasks/:id`1 行速赢)
2. **#6** updateTask store 加 try/catch
3. **#4** priority 值域校验0..=3
4. **#5** 不可变字段保护id/created_at 移出白名单)
5. **#1** 状态枚举对齐(**需人定决策**
6. **#3** 软删除支持(**需人定决策**
7. **#2** DDL 默认值修正
8. **#7** TaskDetail 接入 store
9. 其余 P2/P3 按需排期
---
## 六、人定决策点(需用户拍板,非大模型推断)
1. **状态枚举方向**#1):前端对齐到后端 7 种纯状态机,还是保留 Git 工作流 5 种语义review_ready/merged/abandoned做映射影响筛选器/i18n/统计全链。
2. **任务软删除**#3):任务是否需要软删除 + 恢复(对标 projects还是物理删除即可任务粒度小、误删可重建
这两项是产品/架构取舍,与模型能力无关,决策后记入功能决策记录。