Files
PROJECT-CONTORL/openspec/changes/archive/2025-12-29-add-automation/design.md
beabigegg 95c281d8e1 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>
2025-12-29 22:59:00 +08:00

4.7 KiB
Raw Blame History

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:

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 結構定義條件

{
  "field": "status_id",
  "operator": "equals",
  "value": "uuid-of-testing-status"
}

Supported Operators:

  • equals - 等於
  • not_equals - 不等於
  • changed_to - 變更為特定值
  • changed_from - 從特定值變更

4. 動作定義格式

Decision: 使用 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

-- 觸發器表
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 手動設定)