Files
PROJECT-CONTORL/openspec/specs/task-management/spec.md
beabigegg 35c90fe76b feat: implement 5 QA-driven security and quality proposals
Implemented proposals from comprehensive QA review:

1. extend-csrf-protection
   - Add POST to CSRF protected methods in frontend
   - Global CSRF middleware for all state-changing operations
   - Update tests with CSRF token fixtures

2. tighten-cors-websocket-security
   - Replace wildcard CORS with explicit method/header lists
   - Disable query parameter auth in production (code 4002)
   - Add per-user WebSocket connection limit (max 5, code 4005)

3. shorten-jwt-expiry
   - Reduce JWT expiry from 7 days to 60 minutes
   - Add refresh token support with 7-day expiry
   - Implement token rotation on refresh
   - Frontend auto-refresh when token near expiry (<5 min)

4. fix-frontend-quality
   - Add React.lazy() code splitting for all pages
   - Fix useCallback dependency arrays (Dashboard, Comments)
   - Add localStorage data validation in AuthContext
   - Complete i18n for AttachmentUpload component

5. enhance-backend-validation
   - Add SecurityAuditMiddleware for access denied logging
   - Add ErrorSanitizerMiddleware for production error messages
   - Protect /health/detailed with admin authentication
   - Add input length validation (comment 5000, desc 10000)

All 521 backend tests passing. Frontend builds successfully.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 23:19:05 +08:00

410 lines
15 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.

# Task Management
## Purpose
任務管理核心系統,支援多層級架構、自定義欄位與多維視角。
## Requirements
### Requirement: Hierarchical Task Structure
系統 SHALL 支援多層級任務架構:空間 (Space) > 專案 (Project) > 任務 (Task) > 子任務 (Sub-task)。
#### Scenario: 建立空間
- **GIVEN** 使用者擁有建立空間的權限
- **WHEN** 使用者建立新空間
- **THEN** 系統建立空間並設定擁有者
- **AND** 空間可包含多個專案
#### Scenario: 建立專案
- **GIVEN** 使用者在某空間內擁有建立專案的權限
- **WHEN** 使用者建立新專案
- **THEN** 系統建立專案並關聯至該空間
- **AND** 設定專案的 Owner、Budget、Timeline、Security_Level
#### Scenario: 建立任務與子任務
- **GIVEN** 使用者在專案內擁有建立任務的權限
- **WHEN** 使用者建立任務或子任務
- **THEN** 系統建立任務並維護父子關係
- **AND** 子任務繼承父任務的部分屬性
### Requirement: Custom Fields
系統 SHALL 支援自定義欄位,包含下拉選單、公式、人員標籤等類型。
#### Scenario: 新增自定義欄位
- **GIVEN** 專案管理者需要追蹤特定資料(如:封裝類型、機台編號、預計良率)
- **WHEN** 管理者在專案中新增自定義欄位
- **THEN** 系統建立欄位定義並套用至該專案所有任務
#### Scenario: 編輯自定義欄位
- **GIVEN** 專案已有自定義欄位
- **WHEN** 管理者修改欄位名稱或選項
- **THEN** 系統更新欄位定義
- **AND** 現有任務的欄位值保持不變
#### Scenario: 刪除自定義欄位
- **GIVEN** 專案已有自定義欄位且有任務包含該欄位的值
- **WHEN** 管理者刪除該欄位
- **THEN** 系統顯示確認對話框說明將刪除所有相關值
- **AND** 確認後刪除欄位定義及所有任務的該欄位值
#### Scenario: 公式欄位計算
- **GIVEN** 任務包含公式類型的自定義欄位
- **WHEN** 相依欄位的值發生變更
- **THEN** 系統自動重新計算公式欄位的值
#### Scenario: 公式欄位循環引用檢查
- **GIVEN** 管理者建立公式欄位
- **WHEN** 公式引用自己或形成循環引用
- **THEN** 系統拒絕建立並顯示錯誤訊息
#### Scenario: 人員標籤欄位
- **GIVEN** 任務包含人員標籤類型的自定義欄位
- **WHEN** 使用者選擇人員
- **THEN** 系統驗證人員存在並建立關聯
- **AND** 被標籤的人員可收到相關通知
#### Scenario: 下拉選單欄位
- **GIVEN** 任務包含下拉選單類型的自定義欄位
- **WHEN** 使用者選擇選項
- **THEN** 系統儲存選擇的值
- **AND** 選項列表由欄位定義提供
#### Scenario: 自定義欄位值顯示
- **GIVEN** 任務有自定義欄位值
- **WHEN** 使用者在列表或看板視角查看任務
- **THEN** 自定義欄位值顯示在任務資訊中
- **AND** 公式欄位顯示計算結果(唯讀)
#### Scenario: 欄位數量限制
- **GIVEN** 專案已有 20 個自定義欄位
- **WHEN** 管理者嘗試新增第 21 個欄位
- **THEN** 系統拒絕新增並顯示數量已達上限的訊息
### Requirement: Input Length Validation
All text input fields SHALL have maximum length constraints to prevent abuse and database issues.
#### Scenario: Task title exceeds limit
- **WHEN** user creates a task with title exceeding 500 characters
- **THEN** request is rejected with 422 Validation Error
- **AND** error indicates field length exceeded
#### Scenario: Description within limit
- **WHEN** user creates a task with description 10000 characters or less
- **THEN** task is created successfully
#### Scenario: Description exceeds limit
- **WHEN** user creates a task with description exceeding 10000 characters
- **THEN** request is rejected with 422 Validation Error
#### Scenario: Comment content limit
- **WHEN** user submits a comment exceeding 5000 characters
- **THEN** request is rejected with 422 Validation Error
### Requirement: Multiple Views
系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。
#### Scenario: 甘特圖視角
- **GIVEN** 使用者選擇甘特圖視角
- **WHEN** 系統載入專案任務
- **THEN** 任務依時間軸顯示為水平條狀
- **AND** 顯示任務相依關係與里程碑
#### Scenario: 甘特圖時間軸縮放
- **GIVEN** 使用者正在查看甘特圖
- **WHEN** 使用者切換縮放層級(日、週、月)
- **THEN** 時間軸相應調整顯示密度
- **AND** 任務條保持正確的相對位置
#### Scenario: 拖拉調整任務日期
- **GIVEN** 使用者正在查看甘特圖
- **WHEN** 使用者拖拉任務條改變位置或長度
- **THEN** 系統更新任務的 start_date 和 due_date
- **AND** 驗證日期合理性start_date <= due_date
#### Scenario: 顯示任務依賴關係
- **GIVEN** 任務之間存在依賴關係
- **WHEN** 使用者查看甘特圖
- **THEN** 系統顯示連接任務的箭頭
- **AND** 箭頭方向表示依賴方向(前置任務 → 後續任務)
#### Scenario: 新增任務依賴
- **GIVEN** 使用者在甘特圖上選擇兩個任務
- **WHEN** 使用者建立依賴關係
- **THEN** 系統儲存依賴關係
- **AND** 顯示連接箭頭
#### Scenario: 刪除任務依賴
- **GIVEN** 任務之間存在依賴關係
- **WHEN** 使用者刪除該依賴
- **THEN** 系統移除依賴記錄
- **AND** 連接箭頭消失
#### Scenario: 循環依賴檢測
- **GIVEN** 使用者嘗試建立依賴關係
- **WHEN** 該依賴會形成循環A → B → C → A
- **THEN** 系統拒絕建立並顯示錯誤訊息
- **AND** 現有依賴關係保持不變
#### Scenario: 依賴類型支援
- **GIVEN** 使用者建立任務依賴
- **WHEN** 使用者選擇依賴類型
- **THEN** 系統支援以下類型:
- Finish-to-Start (FS): 前置完成後開始
- Start-to-Start (SS): 前置開始後開始
- Finish-to-Finish (FF): 前置完成後完成
- Start-to-Finish (SF): 前置開始後完成
### Requirement: Task Status Management
系統 SHALL 管理任務狀態,包含標準狀態與自定義狀態。
#### Scenario: 狀態變更
- **GIVEN** 使用者擁有更新任務的權限
- **WHEN** 使用者變更任務狀態
- **THEN** 系統更新狀態並記錄變更時間
- **AND** 觸發相關自動化規則(如有設定)
#### Scenario: 阻礙標記
- **GIVEN** 任務遇到阻礙無法進行
- **WHEN** 工程師將任務標記為 "Blocked"
- **THEN** 系統設定 Blocker_Flag = true
- **AND** 強制發送通知給主管要求介入排解
### Requirement: Task Assignment
系統 SHALL 支援任務指派與時間估算。
#### Scenario: 指派任務
- **GIVEN** 使用者擁有指派任務的權限
- **WHEN** 使用者將任務指派給某人
- **THEN** 系統更新 Assignee 並發送通知
- **AND** 任務計入被指派者的工作負載
#### Scenario: 時間估算與追蹤
- **GIVEN** 任務已被指派
- **WHEN** 使用者設定 Original_Estimate 與回報 Time_Spent
- **THEN** 系統記錄並計算剩餘時間
- **AND** 更新資源負載統計
### Requirement: Kanban View
The system SHALL provide a Kanban board view for tasks with drag-and-drop status management.
#### Scenario: View Kanban board
- **GIVEN** user is on the Tasks page
- **WHEN** user selects Kanban view
- **THEN** tasks are displayed in columns grouped by status
- **AND** each column header shows the status name and task count
#### Scenario: Drag task to change status
- **GIVEN** user is viewing the Kanban board
- **WHEN** user drags a task card to a different status column
- **THEN** the task status is updated via API
- **AND** the card moves to the new column
- **AND** other users viewing the board see the update
#### Scenario: View toggle persistence
- **GIVEN** user switches to Kanban view
- **WHEN** user navigates away and returns
- **THEN** the Kanban view is still selected
### Requirement: Task Detail Modal
The system SHALL provide a task detail modal with comments and attachments.
#### Scenario: Open task detail
- **GIVEN** user is viewing tasks in any view
- **WHEN** user clicks on a task
- **THEN** a modal opens showing task details
- **AND** the modal includes comments section
- **AND** the modal includes attachments section
#### Scenario: Edit task in modal
- **GIVEN** user has task detail modal open
- **WHEN** user modifies task fields and saves
- **THEN** the task is updated via API
- **AND** the task list/board reflects the changes
### Requirement: Task Assignment UI
The system SHALL allow assigning tasks to users during creation and editing.
#### Scenario: Assign task during creation
- **GIVEN** user is creating a new task
- **WHEN** user selects an assignee from the dropdown
- **THEN** the task is created with the selected assignee
#### Scenario: Change task assignee
- **GIVEN** user has task detail modal open
- **WHEN** user changes the assignee
- **THEN** the task assignee is updated
- **AND** the new assignee receives a notification
#### Scenario: Set due date and time estimate
- **GIVEN** user is creating or editing a task
- **WHEN** user sets due date and time estimate
- **THEN** the values are saved with the task
- **AND** the task appears on the appropriate date in calendar view
### Requirement: Space Deletion
系統 SHALL 允許空間擁有者刪除空間(軟刪除)。
#### Scenario: 刪除空白空間
- **GIVEN** 使用者是空間的擁有者
- **AND** 空間內沒有任何專案
- **WHEN** 使用者點擊刪除按鈕並確認
- **THEN** 系統將空間標記為已刪除 (is_active = false)
- **AND** 空間不再顯示於列表中
- **AND** 顯示成功通知
#### Scenario: 刪除含專案的空間
- **GIVEN** 使用者是空間的擁有者
- **AND** 空間內包含一個或多個專案
- **WHEN** 使用者點擊刪除按鈕
- **THEN** 系統顯示警告對話框,說明包含 N 個專案
- **AND** 要求使用者輸入空間名稱以確認刪除
- **WHEN** 使用者正確輸入名稱並確認
- **THEN** 系統將空間標記為已刪除
- **AND** 空間內的專案同時被軟刪除
- **AND** 顯示成功通知
#### Scenario: 非擁有者無法刪除空間
- **GIVEN** 使用者不是空間的擁有者
- **WHEN** 使用者嘗試刪除空間
- **THEN** 系統拒絕操作
- **AND** 顯示權限不足的錯誤訊息
### Requirement: Project Deletion
系統 SHALL 允許專案擁有者刪除專案(軟刪除),並記錄於稽核日誌。
#### Scenario: 刪除專案
- **GIVEN** 使用者是專案的擁有者
- **WHEN** 使用者點擊刪除按鈕
- **THEN** 系統顯示確認對話框,說明專案內的任務數量
- **WHEN** 使用者確認刪除
- **THEN** 系統將專案標記為已刪除 (is_active = false)
- **AND** 專案不再顯示於空間的專案列表中
- **AND** 系統記錄刪除事件至稽核日誌
- **AND** 顯示成功通知
#### Scenario: 非擁有者無法刪除專案
- **GIVEN** 使用者不是專案的擁有者
- **WHEN** 使用者嘗試刪除專案
- **THEN** 系統拒絕操作
- **AND** 顯示權限不足的錯誤訊息
#### Scenario: 刪除專案的稽核記錄
- **GIVEN** 專案擁有者刪除專案
- **WHEN** 刪除操作完成
- **THEN** 稽核日誌記錄以下資訊:
- event_type: "project.delete"
- resource_type: "project"
- action: DELETE
- user_id: 執行刪除的使用者
- resource_id: 被刪除的專案 ID
- changes: [{ field: "is_active", old_value: true, new_value: false }]
### Requirement: Task Dependency Cycle Prevention
The system SHALL detect and prevent circular dependencies between tasks to ensure Gantt charts can be properly rendered.
#### Scenario: Direct circular dependency rejected
- **WHEN** user attempts to create dependency where Task A depends on Task B and Task B depends on Task A
- **THEN** system rejects the operation with 400 Bad Request
- **THEN** error message includes the cycle path (e.g., "Circular dependency detected: A -> B -> A")
#### Scenario: Indirect circular dependency rejected
- **WHEN** user attempts to create dependency that would form a cycle (A -> B -> C -> A)
- **THEN** system rejects the operation with 400 Bad Request
- **THEN** error message includes the full cycle path
#### Scenario: Valid dependency chain accepted
- **WHEN** user creates dependencies forming a valid DAG (directed acyclic graph)
- **THEN** system accepts and saves the dependencies
- **THEN** Gantt chart renders correctly with proper task ordering
### Requirement: Optimistic Locking for Task Updates
The system SHALL use optimistic locking to prevent concurrent update conflicts on tasks.
#### Scenario: Concurrent update detected
- **WHEN** user A and user B both load task at version 1
- **WHEN** user A saves changes, incrementing version to 2
- **WHEN** user B attempts to save with version 1
- **THEN** system returns 409 Conflict error
- **AND** response includes a human-readable message instructing refresh and retry
- **AND** response includes the current and provided version numbers
#### Scenario: Sequential updates succeed
- **WHEN** user loads task at version N
- **WHEN** user saves changes with correct version N
- **THEN** system accepts update and increments version to N+1
### Requirement: Soft Delete Cascade Restore
The system SHALL restore child tasks when parent task is restored from soft delete.
#### Scenario: Parent task restored with children
- **WHEN** soft-deleted parent task is restored
- **THEN** system identifies child tasks deleted at same timestamp
- **THEN** system recursively restores all matching child tasks
- **THEN** audit log records restoration of parent and children
#### Scenario: Selective restore without children
- **WHEN** user explicitly requests restore without cascade
- **THEN** only parent task is restored
- **THEN** child tasks remain in deleted state
## Data Model
```
pjctrl_spaces
├── id: UUID (PK)
├── name: VARCHAR(200)
├── description: TEXT
├── owner_id: UUID (FK -> users)
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
pjctrl_projects
├── id: UUID (PK)
├── space_id: UUID (FK -> spaces)
├── title: VARCHAR(200)
├── description: TEXT
├── owner_id: UUID (FK -> users)
├── budget: DECIMAL
├── start_date: DATE
├── end_date: DATE
├── security_level: ENUM('public', 'department', 'confidential')
├── status: VARCHAR(50)
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
pjctrl_tasks
├── id: UUID (PK)
├── project_id: UUID (FK -> projects)
├── parent_task_id: UUID (FK -> tasks, nullable)
├── title: VARCHAR(500)
├── description: TEXT
├── assignee_id: UUID (FK -> users)
├── priority: ENUM('low', 'medium', 'high', 'urgent')
├── status: VARCHAR(50)
├── original_estimate: DECIMAL (hours)
├── time_spent: DECIMAL (hours)
├── blocker_flag: BOOLEAN DEFAULT false
├── due_date: DATETIME
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
pjctrl_custom_fields
├── id: UUID (PK)
├── project_id: UUID (FK -> projects)
├── name: VARCHAR(100)
├── field_type: ENUM('text', 'number', 'dropdown', 'date', 'person', 'formula')
├── options: JSON (for dropdown)
├── formula: TEXT (for formula type)
├── is_required: BOOLEAN
└── created_at: TIMESTAMP
pjctrl_task_custom_values
├── id: UUID (PK)
├── task_id: UUID (FK -> tasks)
├── field_id: UUID (FK -> custom_fields)
├── value: TEXT
└── updated_at: TIMESTAMP
```
## Real-time Sync
系統使用 WebSocket 實現即時同步,當多人同時編輯同一個專案看板時,狀態能即時更新而不需刷新頁面。