# OpsPilot API Contract and Data Model（API 契約與資料模型）

日期（Date）：2026-06-23  
作品（Work）：OpsPilot AI Dashboard（AI 營運中樞）  
關聯前端（Frontend）：`works/app/operations-suite-dashboard/source/opspilot-workspace-planning-v1.html`  
關聯後端（Backend）：`works/backend/operations-api/`  
狀態（Status）：api-contract-v1（API 契約第一版）

## 目的（Purpose）
本文件把 OpsPilot AI Dashboard（AI 營運中樞）的前端展示資料、操作行為與未來本機 Go API（本機 Go 服務）對齊。它是下一階段拆分 component/state model（元件與狀態模型）、OpenAPI spec（OpenAPI 規格）與 PostgreSQL schema（PostgreSQL 結構）的驗收基準。

## 邊界（Scope）
第一版先定義 REST API（REST 介面）、Data Model（資料模型）、RBAC Action（角色權限動作）、Audit Event（稽核事件）與 AI Recommendation（AI 建議）的契約。這不是已完成後端實作，也不是最終 OpenAPI YAML（OpenAPI YAML 檔）。

## 共通規則（Common Rules）
| 項目（Item） | 規則（Rule） |
|---|---|
| Base URL（基礎網址） | 本機展示使用 `http://localhost:8010/api/v1`。Vercel demo（Vercel 展示）仍使用 mock data（模擬資料）。 |
| Format（格式） | Request（請求）與 Response（回應）皆為 JSON。 |
| Time（時間） | API 使用 ISO 8601 UTC，例如 `2026-06-23T01:42:00Z`；前端可轉成 `09:42` 顯示。 |
| IDs（識別碼） | Account（客戶）`A-`、Opportunity（商機）`O-`、Project（專案）`P-`、Task（任務）`T-`、Workflow Rule（工作流規則）`W-`、AI Recommendation（AI 建議）`R-`、Audit Event（稽核事件）`E-`。 |
| Auth（驗證） | V1 local demo（本機展示）使用 `x-demo-role` 與 `x-request-id`。正式 OAuth/JWT（正式驗證）延後。 |
| Role（角色） | `Owner（營運負責人）`、`Manager（專案負責人）`、`Sales（客戶負責人）`、`Operator（執行成員）`、`Auditor（稽核管理者）`。 |
| Audit（稽核） | 所有 mutation（變更操作）都必須產生 Audit Event（稽核事件）。 |
| Theme（主題） | Light/Dark Theme（亮色/深色主題）只屬於前端 UI state（前端狀態），不進 API。 |

## Error Shape（錯誤格式）
```json
{
  "error": {
    "code": "forbidden",
    "message": "Role cannot perform this action.",
    "fields": [],
    "requestId": "req_20260623_001"
  }
}
```

## Role Actions（角色權限動作）
| Action（動作） | 說明（Description） | 可用角色（Allowed Roles） |
|---|---|---|
| `queue` | Priority Queue（優先佇列）處理。 | Owner（營運負責人）、Manager（專案負責人）、Operator（執行成員） |
| `crm` | CRM Workspace（客戶關係工作區）操作。 | Owner（營運負責人）、Sales（客戶負責人） |
| `project` | Projects Workspace（專案工作區）操作。 | Owner（營運負責人）、Manager（專案負責人） |
| `task` | Tasks Workspace（任務工作區）操作。 | Owner（營運負責人）、Manager（專案負責人）、Sales（客戶負責人）、Operator（執行成員）、Auditor（稽核管理者） |
| `kanban` | Kanban Workspace（看板工作區）任務移動。 | Owner（營運負責人）、Manager（專案負責人）、Operator（執行成員）、Auditor（稽核管理者） |
| `workflow` | Workflows Workspace（工作流工作區）規則切換。 | Owner（營運負責人）、Manager（專案負責人） |
| `audit` | Audit Workspace（稽核工作區）檢視與審查。 | Owner（營運負責人）、Auditor（稽核管理者） |
| `ai` | AI Copilot（AI 助理）建議採納。 | Owner（營運負責人）、Manager（專案負責人）、Sales（客戶負責人）、Auditor（稽核管理者） |

## Data Model（資料模型）

### Account（客戶）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Account ID（客戶識別碼），例如 `A-1001`。 |
| `name` | string | yes | Account Name（客戶名稱），需保留中英文展示名。 |
| `tier` | string | yes | Tier（客戶級別），例如 `Enterprise（企業）`。 |
| `owner` | string | yes | Owner（客戶負責人）。 |
| `health` | number | yes | Health Score（健康分數），0-100。 |
| `risk` | string | yes | Risk Level（風險等級）：`ok`、`watch`、`danger`。 |
| `lastContactAt` | string | yes | Last Contact（最後聯絡時間）。 |

### Opportunity（商機）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Opportunity ID（商機識別碼），例如 `O-2101`。 |
| `accountId` | string | yes | Account ID（客戶識別碼）。 |
| `name` | string | yes | Opportunity Name（商機名稱）。 |
| `stage` | string | yes | Stage（階段），例如 `Qualified（已確認）`、`Proposal（提案）`、`Won（成交）`。 |
| `value` | number | yes | Value（商機金額），單位為千元展示值。 |
| `probability` | number | yes | Probability（成交機率），0-100。 |
| `nextAction` | string | yes | Next Action（下一步行動）。 |

### Project（專案）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Project ID（專案識別碼），例如 `P-3101`。 |
| `accountId` | string | yes | Account ID（客戶識別碼）。 |
| `name` | string | yes | Project Name（專案名稱）。 |
| `owner` | string | yes | Owner（專案負責人）。 |
| `status` | string | yes | Status（狀態），例如 `On Track（正常）`、`At Risk（有風險）`、`Blocked（阻塞）`。 |
| `risk` | string | yes | Risk Level（風險等級）：`ok`、`watch`、`danger`。 |
| `dueDate` | string | yes | Due Date（到期日）。 |
| `progress` | number | yes | Progress（進度），0-100。 |

### Task（任務）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Task ID（任務識別碼），例如 `T-01`。 |
| `projectId` | string | yes | Project ID（專案識別碼）。 |
| `title` | string | yes | Task Title（任務標題）。 |
| `assignee` | string | yes | Assignee（指派對象）。 |
| `status` | string | yes | Kanban Status（看板狀態）：`backlog`、`progress`、`review`、`done`。 |
| `priority` | string | yes | Priority（優先級）：`P0`、`P1`、`P2`。 |
| `dueDate` | string | yes | Due Date（到期日）。 |
| `blocked` | boolean | yes | Blocked（是否阻塞）。 |

### Workflow Rule（工作流規則）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Workflow Rule ID（工作流規則識別碼），例如 `W-01`。 |
| `name` | string | yes | Rule Name（規則名稱）。 |
| `trigger` | string | yes | Trigger（觸發條件）。 |
| `action` | string | yes | Action（執行動作）。 |
| `enabled` | boolean | yes | Enabled（是否啟用）。 |
| `sla` | string | yes | SLA（服務時限）。 |

### Audit Event（稽核事件）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Audit Event ID（稽核事件識別碼），例如 `E-9001`。 |
| `createdAt` | string | yes | Created At（建立時間）。 |
| `actor` | string | yes | Actor（操作者），可為 Role（角色）或 AI Copilot（AI 助理）。 |
| `action` | string | yes | Action（操作內容）。 |
| `targetType` | string | yes | Target Type（目標類型），例如 `Task（任務）`。 |
| `targetId` | string | no | Target ID（目標識別碼）。 |
| `before` | string | no | Before（變更前）。 |
| `after` | string | no | After（變更後）。 |
| `ai` | boolean | yes | AI Marker（AI 標記）。 |
| `sensitive` | boolean | yes | Sensitive Marker（敏感操作標記）。 |

### AI Recommendation（AI 建議）
| Field（欄位） | Type（型別） | Required（必填） | 說明（Description） |
|---|---|---:|---|
| `id` | string | yes | Recommendation ID（建議識別碼），例如 `R-01`。 |
| `workspace` | string | yes | Workspace（工作區），例如 `command`、`crm`、`projects`。 |
| `title` | string | yes | Title（標題）。 |
| `reason` | string | yes | Reason（理由）。 |
| `evidence` | string | yes | Evidence（依據）。 |
| `action` | string | yes | Action（可採納動作）。 |
| `accepted` | boolean | yes | Accepted（是否已採納）。 |

## API Routes（API 路由）
| Method（方法） | Path（路徑） | Name（名稱） | Role Action（權限動作） | Audit（稽核） |
|---|---|---|---|---|
| GET | `/session` | Session Detail（工作階段明細） | none | no |
| PATCH | `/session/role` | Switch Role（切換角色） | none | yes |
| GET | `/command/summary` | Command Summary（營運總覽摘要） | none | no |
| POST | `/command/queue/{queueItemId}/resolve` | Resolve Queue Item（處理佇列項目） | `queue` | yes |
| GET | `/accounts` | Account List（客戶列表） | none | no |
| GET | `/accounts/{accountId}` | Account Detail（客戶明細） | none | no |
| POST | `/accounts/{accountId}/follow-ups` | Create Follow-up（建立追蹤） | `crm` | yes |
| PATCH | `/opportunities/{opportunityId}` | Update Opportunity（更新商機） | `crm` | yes |
| GET | `/projects` | Project List（專案列表） | none | no |
| PATCH | `/projects/{projectId}/risk-review` | Review Project Risk（審查專案風險） | `project` | yes |
| GET | `/tasks` | Task List（任務列表） | none | no |
| PATCH | `/tasks/{taskId}` | Update Task（更新任務） | `task` | yes |
| PATCH | `/tasks/{taskId}/move` | Move Kanban Task（移動看板任務） | `kanban` | yes |
| PATCH | `/tasks/{taskId}/priority` | Update Task Priority（更新任務優先級） | `task` | yes |
| GET | `/workflow-rules` | Workflow Rule List（工作流規則列表） | none | no |
| PATCH | `/workflow-rules/{ruleId}` | Toggle Workflow Rule（切換工作流規則） | `workflow` | yes |
| GET | `/audit-events` | Audit Event List（稽核事件列表） | `audit` | no |
| POST | `/audit-events/review-notes` | Append Review Note（追加審查備註） | `audit` | yes |
| GET | `/ai/recommendations` | AI Recommendation List（AI 建議列表） | none | no |
| POST | `/ai/recommendations/{recommendationId}/accept` | Accept AI Recommendation（採納 AI 建議） | `ai` | yes |
| POST | `/ai/recommendations/{recommendationId}/dismiss` | Dismiss AI Recommendation（略過 AI 建議） | `ai` | yes |

## Endpoint Details（端點細節）

### GET `/command/summary` - Command Summary（營運總覽摘要）
Response（回應）：
```json
{
  "requestId": "req_20260623_001",
  "workspace": "command",
  "metrics": [
    { "label": "P0 Blockers（P0 阻塞）", "value": 3, "change": "+1" },
    { "label": "SLA Risk（服務時限風險）", "value": 2, "change": "Today（今日）" }
  ],
  "priorityQueue": [
    {
      "id": "Q-01",
      "title": "Resolve SLA conflict（處理 SLA 衝突）",
      "targetType": "Task（任務）",
      "targetId": "T-05",
      "priority": "P0"
    }
  ]
}
```

### PATCH `/tasks/{taskId}/move` - Move Kanban Task（移動看板任務）
Request（請求）：
```json
{
  "status": "review",
  "reason": "Move blocked task to review（將阻塞任務移到審核）"
}
```

Response（回應）：
```json
{
  "requestId": "req_20260623_002",
  "task": {
    "id": "T-04",
    "projectId": "P-3103",
    "status": "review",
    "blocked": false
  },
  "projectSummary": {
    "id": "P-3103",
    "risk": "watch",
    "progress": 39
  },
  "auditEventId": "E-9002"
}
```

### POST `/ai/recommendations/{recommendationId}/accept` - Accept AI Recommendation（採納 AI 建議）
Request（請求）：
```json
{
  "applyAction": true,
  "note": "Accepted from AI Copilot（從 AI 助理採納）"
}
```

Response（回應）：
```json
{
  "requestId": "req_20260623_003",
  "recommendation": {
    "id": "R-05",
    "accepted": true
  },
  "appliedChanges": [
    {
      "targetType": "Task（任務）",
      "targetId": "T-04",
      "field": "status",
      "before": "progress",
      "after": "review"
    }
  ],
  "auditEventId": "E-9003"
}
```

### POST `/audit-events/review-notes` - Append Review Note（追加審查備註）
Request（請求）：
```json
{
  "targetEventId": "E-9003",
  "note": "Reviewed AI accepted action（已審查 AI 採納動作）"
}
```

Response（回應）：
```json
{
  "requestId": "req_20260623_004",
  "auditEvent": {
    "id": "E-9004",
    "actor": "Auditor（稽核管理者）A",
    "action": "Appended review note（追加審查備註）",
    "targetType": "Audit Event（稽核事件）",
    "targetId": "E-9003",
    "ai": false,
    "sensitive": true
  }
}
```

## Frontend State Contract（前端狀態契約）
| Slice（切片） | 內容（Content） |
|---|---|
| `session` | Role（角色）、Request ID（請求識別碼）、Access（權限）。 |
| `workspace` | Current Workspace（目前工作區）、Navigation State（導覽狀態）。 |
| `accounts` | Account（客戶）、Opportunity（商機）、Follow-up（追蹤）。 |
| `projects` | Project（專案）、Project Summary（專案摘要）。 |
| `tasks` | Task（任務）、Kanban Columns（看板欄位）、Task Filters（任務篩選）。 |
| `workflowRules` | Workflow Rule（工作流規則）。 |
| `auditEvents` | Audit Event（稽核事件）、Audit Filter（稽核篩選）。 |
| `recommendations` | AI Recommendation（AI 建議）。 |
| `ui` | Theme（主題）、Drawer（抽屜）、Toast（提示）、Loading（載入狀態）。 |

## Derived Selectors（衍生選擇器）
| Selector（選擇器） | 用途（Purpose） |
|---|---|
| `visibleRecommendations(workspace)` | 依 Workspace（工作區）取得 AI Recommendation（AI 建議）。 |
| `filteredTasks(priority, blocked, search)` | 依 Priority（優先級）、Blocked（阻塞）與 Search（搜尋）篩選 Task（任務）。 |
| `kanbanColumns(tasks)` | 將 Task（任務）轉為 Kanban Columns（看板欄位）。 |
| `projectTaskSummary(projectId)` | 取得 Project（專案）的任務數、阻塞數與完成度。 |
| `filteredAuditEvents(filter)` | 依 all / ai / sensitive（全部 / AI / 敏感）篩選 Audit Event（稽核事件）。 |
| `roleProfile(role)` | 取得 Role（角色）對應 Action（動作）與 Access（可見權限）。 |

## Mutation Flow（變更流程）
1. Check Role Action（檢查角色權限動作）。
2. Patch Domain Entity（更新領域資料），例如 Task（任務）或 Project（專案）。
3. Sync Derived State（同步衍生狀態），例如 Kanban Columns（看板欄位）與 Project Summary（專案摘要）。
4. Append Audit Event（追加稽核事件）。
5. Return Updated Snapshot（回傳更新後快照）。
6. Render Toast and Refresh Widgets（顯示提示並刷新受影響元件）。

## Acceptance Checklist（驗收清單）
- 每個 API Response（API 回應）都包含 `requestId`。
- 每個 mutation（變更操作）都建立 Audit Event（稽核事件）。
- Kanban Move（看板移動）會同步 Task（任務）、Project Summary（專案摘要）與 Audit Event（稽核事件）。
- Accept AI Recommendation（採納 AI 建議）產生 `ai=true` 的 Audit Event（稽核事件）。
- Auditor（稽核管理者）可篩選 AI Event（AI 事件）與 Sensitive Event（敏感事件），也可追加 Review Note（審查備註）。
- Sales（客戶負責人）不能執行 Project Risk Review（專案風險審查）或 Workflow Toggle（工作流切換）。
- Operator（執行成員）可以移動 Kanban Task（看板任務），但不能切換 Workflow Rule（工作流規則）。
- Theme Toggle（主題切換）不影響 API payload（API 資料）。

## Deferred（暫緩）
- OAuth/JWT Auth（正式驗證授權）。
- Full OpenAPI YAML（完整 OpenAPI YAML）。
- PostgreSQL Migration（PostgreSQL 遷移檔）。
- Redis Key Design（Redis key 設計）。
- Webhook（Webhook 事件）。
- Notification System（通知系統）。
- Export CSV（CSV 匯出）。
