352 lines
17 KiB
Markdown
352 lines
17 KiB
Markdown
# 任务模块问题分析报告(核对版)
|
||
|
||
> **日期**: 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`(走代码默认 2),DDL 默认值不生效。但未来若有直连 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/offset,ORDER 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 代码 2,migration 对齐 |
|
||
| 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),还是物理删除即可(任务粒度小、误删可重建)?
|
||
|
||
这两项是产品/架构取舍,与模型能力无关,决策后记入功能决策记录。
|