## 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:` - 指定使用者 ## 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 手動設定)