feat: implement automation module

- Event-based triggers (Phase 1):
  - Trigger/TriggerLog models with field_change type
  - TriggerService for condition evaluation and action execution
  - Trigger CRUD API endpoints
  - Task integration (status, assignee, priority changes)
  - Frontend: TriggerList, TriggerForm components

- Weekly reports (Phase 2):
  - ScheduledReport/ReportHistory models
  - ReportService for stats generation
  - APScheduler for Friday 16:00 job
  - Report preview/generate/history API
  - Frontend: WeeklyReportPreview, ReportHistory components

- Tests: 23 new tests (14 triggers + 9 reports)
- OpenSpec: add-automation change archived

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beabigegg
2025-12-29 22:59:00 +08:00
parent 3108fe1dff
commit 95c281d8e1
32 changed files with 3163 additions and 3 deletions

View File

@@ -0,0 +1,185 @@
## Context
自動化系統需要處理兩種類型的自動化:
1. 事件驅動 - 任務欄位變更時觸發
2. 時間驅動 - 排程執行(如週報)
## Goals / Non-Goals
**Goals:**
- 提供欄位變更觸發器status, assignee, priority
- 實作每週五 16:00 自動週報
- 整合現有通知系統
- 記錄所有觸發器執行日誌
**Non-Goals:**
- 複雜的工作流引擎
- 跨任務觸發器
- Email 發送(僅系統內通知)
## Decisions
### 1. 排程方案
**Decision:** 使用 APScheduler 而非 Celery
**Rationale:**
- 無需額外 worker 進程,減少部署複雜度
- 嵌入 FastAPI 應用內運行
- 足夠處理週報等簡單定時任務
- 未來如需擴展可遷移至 Celery
**Configuration:**
```python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
scheduler = AsyncIOScheduler()
scheduler.add_job(
generate_weekly_reports,
CronTrigger(day_of_week='fri', hour=16, minute=0),
id='weekly_report'
)
```
### 2. 觸發器評估策略
**Decision:** 同步評估,任務更新時直接執行
**Rationale:**
- 簡化實作,無需消息隊列
- 觸發器執行速度快(僅發送通知)
- 失敗可即時回報
**Flow:**
```
Task Update → Detect Changes → Find Matching Triggers → Execute Actions → Log Results
```
### 3. 條件定義格式
**Decision:** 使用 JSON 結構定義條件
```json
{
"field": "status_id",
"operator": "equals",
"value": "uuid-of-testing-status"
}
```
**Supported Operators:**
- `equals` - 等於
- `not_equals` - 不等於
- `changed_to` - 變更為特定值
- `changed_from` - 從特定值變更
### 4. 動作定義格式
**Decision:** 使用 JSON 陣列定義動作
```json
[
{
"type": "notify",
"target": "assignee",
"template": "任務 {task.title} 狀態已變更為 {new_value}"
}
]
```
**Supported Actions (Phase 1):**
- `notify` - 發送系統通知
**Target Types:**
- `assignee` - 任務指派者
- `creator` - 任務建立者
- `project_owner` - 專案擁有者
- `user:<user_id>` - 指定使用者
## Data Model
```sql
-- 觸發器表
pjctrl_triggers
├── id: UUID (PK)
├── project_id: UUID (FK -> projects)
├── name: VARCHAR(200)
├── description: TEXT
├── trigger_type: ENUM('field_change', 'schedule')
├── conditions: JSON
├── actions: JSON
├── is_active: BOOLEAN DEFAULT true
├── created_by: UUID (FK -> users)
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
-- 觸發器執行日誌
pjctrl_trigger_logs
├── id: UUID (PK)
├── trigger_id: UUID (FK -> triggers)
├── task_id: UUID (FK -> tasks, nullable)
├── executed_at: TIMESTAMP
├── status: ENUM('success', 'failed')
├── details: JSON
└── error_message: TEXT
-- 排程報告設定
pjctrl_scheduled_reports
├── id: UUID (PK)
├── report_type: ENUM('weekly')
├── recipient_id: UUID (FK -> users)
├── is_active: BOOLEAN DEFAULT true
├── last_sent_at: TIMESTAMP
└── created_at: TIMESTAMP
-- 報告歷史
pjctrl_report_history
├── id: UUID (PK)
├── report_id: UUID (FK -> scheduled_reports)
├── generated_at: TIMESTAMP
├── content: JSON
├── status: ENUM('sent', 'failed')
└── error_message: TEXT
```
## API Design
```
# Triggers
POST /api/projects/{project_id}/triggers # 建立觸發器
GET /api/projects/{project_id}/triggers # 列出專案觸發器
GET /api/triggers/{id} # 觸發器詳情
PUT /api/triggers/{id} # 更新觸發器
DELETE /api/triggers/{id} # 刪除觸發器
GET /api/triggers/{id}/logs # 觸發器執行日誌
# Reports
GET /api/reports/weekly/preview # 預覽週報
POST /api/reports/weekly/generate # 手動觸發週報
GET /api/reports/history # 報告歷史
```
## Risks / Trade-offs
| Risk | Mitigation |
|------|------------|
| 大量觸發器影響效能 | 限制每專案觸發器數量,建立索引 |
| APScheduler 單點故障 | 記錄 last_sent_at 防止重複,考慮多實例鎖 |
| 觸發器條件複雜度 | Phase 1 僅支援簡單條件,後續擴展 |
## Integration Points
1. **Task Update Hook**
-`tasks/router.py` 的 update_task 後調用 TriggerService.evaluate()
2. **Notification Integration**
- 使用現有 NotificationService.create_notification()
3. **Audit Integration**
- 觸發器執行記錄至 TriggerLog非 AuditLog
## Open Questions
- [ ] 是否需要支援 Email 發送?(目前僅系統內通知)
- [ ] 週報收件者如何設定?(主管自動訂閱 or 手動設定)

View File

@@ -0,0 +1,52 @@
# Change: Add Automation System
## Why
專案管理需要自動化功能來減少重複性工作:
- 當任務狀態變更時自動通知相關人員
- 每週自動生成進度報告發送給主管
- 減少人工追蹤與提醒的負擔
## What Changes
- **新增 Trigger 模型** - 定義觸發條件與動作
- **新增 TriggerService** - 觸發器評估與執行
- **新增 ReportService** - 週報生成邏輯
- **新增背景排程** - APScheduler 處理定時任務
- **整合任務 API** - 任務變更時評估觸發器
## Impact
- Affected specs: `automation`
- Affected code:
- `backend/app/models/` - 新增 trigger, scheduled_report 模型
- `backend/app/api/` - 新增 triggers, reports router
- `backend/app/services/` - 新增 trigger_service, report_service
- `backend/app/api/tasks/router.py` - 整合觸發器評估
- `frontend/src/` - 新增觸發器管理頁面
## Implementation Phases
### Phase 1: Event-Based Triggers
- Trigger 模型與 CRUD API
- 欄位變更觸發器status, assignee, priority
- 通知動作執行
- 整合任務更新流程
### Phase 2: Weekly Reports
- ScheduledReport 模型
- 週報生成邏輯(彙整任務統計)
- APScheduler 背景排程
- 系統內通知發送
### Phase 3: Advanced Features (Optional)
- 時間條件觸發器
- 複合條件支援
- 更新欄位動作
- 自動指派動作
## Dependencies
- notification (已完成) - 用於發送觸發通知
- audit-trail (已完成) - 記錄觸發器執行日誌
## Technical Decisions
- **使用 APScheduler** 而非 Celery - 輕量級,無需額外 worker 進程
- **同步觸發器評估** - 任務更新時同步執行,避免複雜的異步處理
- **JSON 欄位儲存條件** - 靈活的條件定義格式

View File

@@ -0,0 +1,105 @@
## MODIFIED Requirements
### Requirement: Trigger-Based Automation
系統 SHALL 支援觸發器 (Triggers),當特定條件滿足時自動執行動作。
#### Scenario: 狀態變更觸發通知
- **GIVEN** 專案設定了「當任務狀態變更為待測試時,通知指派者」的觸發器
- **WHEN** 任務狀態變更為「待測試」
- **THEN** 系統自動發送通知給任務指派者
- **AND** 觸發器執行記錄至 TriggerLog
#### Scenario: 建立觸發器
- **GIVEN** 專案管理者需要建立自動化規則
- **WHEN** 管理者透過 API 設定觸發條件與動作
- **THEN** 系統儲存觸發器規則
- **AND** 規則立即生效
### Requirement: Trigger Conditions
系統 SHALL 支援欄位變更觸發條件。
#### Scenario: 欄位變更條件
- **GIVEN** 觸發器設定為「當 status_id 欄位變更為特定值」
- **WHEN** 任務的 status_id 欄位變更為該值
- **THEN** 觸發器被觸發
- **AND** 支援運算子: equals, not_equals, changed_to, changed_from
### Requirement: Trigger Actions
系統 SHALL 支援發送通知動作。
#### Scenario: 發送通知動作
- **GIVEN** 觸發器動作設定為 notify
- **WHEN** 觸發器被觸發
- **THEN** 系統使用 NotificationService 發送通知
- **AND** 通知目標支援: assignee, creator, project_owner, user:<id>
- **AND** 通知內容可使用變數模板
### Requirement: Automated Weekly Report
系統 SHALL 每週五下午 4:00 自動彙整本週任務狀態發送給主管。
#### Scenario: 週報自動生成
- **GIVEN** APScheduler 排程設定為每週五 16:00
- **WHEN** 到達排程時間
- **THEN** ReportService 彙整使用者所屬專案的任務狀態
- **AND** 生成週報並透過 NotificationService 發送
#### Scenario: 週報內容
- **GIVEN** 週報生成中
- **WHEN** 系統彙整資料
- **THEN** 週報 JSON 包含:
- completed_count: 本週已完成任務數
- in_progress_count: 進行中任務數
- overdue_count: 逾期任務數
- tasks: 詳細任務清單
## MODIFIED Data Model
```
pjctrl_triggers
├── id: UUID (PK)
├── project_id: UUID (FK -> projects)
├── name: VARCHAR(200)
├── description: TEXT
├── trigger_type: ENUM('field_change', 'schedule')
├── conditions: JSON
│ └── { "field": "status_id", "operator": "changed_to", "value": "uuid" }
├── actions: JSON
│ └── [{ "type": "notify", "target": "assignee", "template": "..." }]
├── is_active: BOOLEAN DEFAULT true
├── created_by: UUID (FK -> users)
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
pjctrl_trigger_logs
├── id: UUID (PK)
├── trigger_id: UUID (FK -> triggers)
├── task_id: UUID (FK -> tasks, nullable)
├── executed_at: TIMESTAMP
├── status: ENUM('success', 'failed')
├── details: JSON
└── error_message: TEXT
pjctrl_scheduled_reports
├── id: UUID (PK)
├── report_type: ENUM('weekly')
├── recipient_id: UUID (FK -> users)
├── is_active: BOOLEAN DEFAULT true
├── last_sent_at: TIMESTAMP
└── created_at: TIMESTAMP
pjctrl_report_history
├── id: UUID (PK)
├── report_id: UUID (FK -> scheduled_reports)
├── generated_at: TIMESTAMP
├── content: JSON
├── status: ENUM('sent', 'failed')
└── error_message: TEXT
```
## MODIFIED Technical Notes
- 使用 APScheduler (AsyncIOScheduler) 處理排程任務,嵌入 FastAPI 應用內運行
- 觸發器評估採用同步處理,任務更新時直接執行,避免複雜的異步處理
- 所有觸發器執行都記錄至 TriggerLog 供追蹤
- Phase 1 僅支援 notify 動作
- 條件運算子: equals, not_equals, changed_to, changed_from

View File

@@ -0,0 +1,90 @@
## Phase 1: Event-Based Triggers
### 1.1 Database Schema
- [x] 1.1.1 建立 Trigger model (`pjctrl_triggers`)
- [x] 1.1.2 建立 TriggerLog model (`pjctrl_trigger_logs`)
- [x] 1.1.3 建立 Alembic migration
- [x] 1.1.4 定義 TriggerType enum (field_change, schedule)
### 1.2 Trigger Service
- [x] 1.2.1 建立 TriggerService 類別
- [x] 1.2.2 實作 evaluate_triggers(task, old_values, new_values) 方法
- [x] 1.2.3 實作 check_condition(condition, old_value, new_value) 方法
- [x] 1.2.4 實作 execute_action(action, task, user) 方法
- [x] 1.2.5 實作 log_execution(trigger, task, status, error) 方法
### 1.3 Trigger API
- [x] 1.3.1 建立 Trigger schemas (request/response)
- [x] 1.3.2 實作 POST `/api/projects/{project_id}/triggers` - 建立
- [x] 1.3.3 實作 GET `/api/projects/{project_id}/triggers` - 列表
- [x] 1.3.4 實作 GET `/api/triggers/{id}` - 詳情
- [x] 1.3.5 實作 PUT `/api/triggers/{id}` - 更新
- [x] 1.3.6 實作 DELETE `/api/triggers/{id}` - 刪除
- [x] 1.3.7 實作 GET `/api/triggers/{id}/logs` - 執行日誌
### 1.4 Task Integration
- [x] 1.4.1 修改 update_task endpoint 整合觸發器評估
- [x] 1.4.2 修改 update_task_status endpoint 整合觸發器評估
- [x] 1.4.3 修改 assign_task endpoint 整合觸發器評估
### 1.5 Frontend - Triggers
- [x] 1.5.1 建立 triggers.ts service
- [x] 1.5.2 建立 TriggerList 元件
- [x] 1.5.3 建立 TriggerForm 元件(條件/動作設定)
- [x] 1.5.4 整合至 Project 設定頁面
### 1.6 Testing - Phase 1
- [x] 1.6.1 TriggerService 單元測試
- [x] 1.6.2 Trigger API 端點測試
- [x] 1.6.3 觸發器執行整合測試
## Phase 2: Weekly Reports
### 2.1 Database Schema
- [x] 2.1.1 建立 ScheduledReport model (`pjctrl_scheduled_reports`)
- [x] 2.1.2 建立 ReportHistory model (`pjctrl_report_history`)
- [x] 2.1.3 建立 Alembic migration
### 2.2 Report Service
- [x] 2.2.1 建立 ReportService 類別
- [x] 2.2.2 實作 generate_weekly_report(user_id) 方法
- [x] 2.2.3 實作 get_weekly_stats(user_id, week_start) 方法
- [x] 2.2.4 實作 send_report_notification(user_id, report) 方法
- [x] 2.2.5 實作 save_report_history(report) 方法
### 2.3 Scheduler Setup
- [x] 2.3.1 安裝 APScheduler
- [x] 2.3.2 建立 scheduler.py 設定檔
- [x] 2.3.3 設定週五 16:00 排程任務
- [x] 2.3.4 整合至 main.py 啟動流程
### 2.4 Report API
- [x] 2.4.1 建立 Report schemas
- [x] 2.4.2 實作 GET `/api/reports/weekly/preview` - 預覽
- [x] 2.4.3 實作 POST `/api/reports/weekly/generate` - 手動觸發
- [x] 2.4.4 實作 GET `/api/reports/history` - 歷史紀錄
### 2.5 Frontend - Reports
- [x] 2.5.1 建立 reports.ts service
- [x] 2.5.2 建立 WeeklyReportPreview 元件
- [x] 2.5.3 建立 ReportHistory 元件
- [x] 2.5.4 新增管理員報告頁面
### 2.6 Testing - Phase 2
- [x] 2.6.1 ReportService 單元測試
- [x] 2.6.2 週報生成測試
- [x] 2.6.3 排程執行測試
## Phase 3: Advanced Features (Optional)
### 3.1 Schedule Triggers
- [ ] 3.1.1 支援 cron 表達式觸發器
- [ ] 3.1.2 截止日期提醒觸發器
### 3.2 Additional Actions
- [ ] 3.2.1 更新欄位動作
- [ ] 3.2.2 自動指派動作
### 3.3 Complex Conditions
- [ ] 3.3.1 AND/OR 複合條件
- [ ] 3.3.2 多欄位條件