# SSP 程序書池 + 多對多關聯 — 前端整合規格

## 功能概述

將 SSP 程序書改為「池 + 關聯」架構：
- **程序書池**：SSP 級別，上傳一次即可
- **關聯**：同一份程序書可關聯到多個控制項和 AO

---

## 概念說明

```
之前（一對一，重複上傳）：
  控制項 A → 上傳 程序書1.pdf
  控制項 B → 再上傳一次 程序書1.pdf（重複）
  AO [a]   → 又上傳一次 程序書1.pdf（第三次）

現在（池 + 多對多）：
  SSP 程序書池：
    📄 程序書1.pdf（上傳一次）
    📄 程序書2.pdf
    📄 程序書3.pdf

  控制項 A → 關聯 程序書1, 程序書2
  控制項 B → 關聯 程序書1, 程序書3
  AO [a]   → 關聯 程序書1
  （同一份文件，不同地方使用，不需重複上傳）
```

---

## 使用流程

```
Step 1: 管理者進入 SSP → 「程序書管理」頁面
  → 拖拽批次上傳所有程序書
  → 每份可填寫描述（如「資訊安全政策 v3.0」）

Step 2: 在控制項詳情頁
  → 點「關聯程序書」→ Dialog 顯示池中所有程序書
  → 多選 → 確認

Step 3: 在 AO 詳情頁
  → 同上操作
```

---

## API 規格

### 一、程序書池 CRUD

#### 列出池

```
GET /api/1.0/ssp/<ssp_uid>/document-pool
Authorization: Bearer <token>
```

**Response：**

```json
{
  "status": true,
  "data": [
    {
      "uid": "doc-uuid-1",
      "file_uid": "file-uuid-1",
      "file_name": "資訊安全政策v3.0.pdf",
      "file_size": 1024000,
      "description": "資訊安全政策 v3.0",
      "mapping_count": 5,
      "created_at": "2026-03-29T10:00:00"
    },
    {
      "uid": "doc-uuid-2",
      "file_uid": "file-uuid-2",
      "file_name": "存取控制管理辦法.docx",
      "file_size": 512000,
      "description": "存取控制管理辦法 v2.1",
      "mapping_count": 3,
      "created_at": "2026-03-29T10:05:00"
    }
  ]
}
```

| 欄位 | 說明 |
|------|------|
| `uid` | 程序書 UID（用於關聯和刪除） |
| `file_uid` | 檔案 UID（用於下載） |
| `file_name` | 檔案名稱 |
| `file_size` | 檔案大小（bytes） |
| `description` | 程序書描述 |
| `mapping_count` | 被關聯的次數（控制項 + AO 的總數） |
| `created_at` | 上傳時間 |

#### 上傳到池

```
POST /api/1.0/ssp/<ssp_uid>/document-pool
Authorization: Bearer <token>
Content-Type: application/json

{
  "documents": [
    {
      "file_uid": "已上傳檔案的 UID（透過 /file/upload 取得）",
      "description": "資訊安全政策 v3.0"
    },
    {
      "file_uid": "另一個 file_uid",
      "description": "存取控制管理辦法"
    }
  ]
}
```

**流程：** 先用 `POST /file/upload` 上傳實體檔案取得 `file_uid`，再呼叫此 API 加入池。

#### 從池刪除

```
DELETE /api/1.0/ssp/<ssp_uid>/document-pool/<doc_uid>
```

**注意：** 刪除會 **cascade 刪除所有關聯**。建議前端在刪除前：
- 檢查 `mapping_count > 0` 時顯示警告：「此程序書已關聯 N 個控制項/檢查項目，刪除後關聯也會被移除」

---

### 二、控制項關聯

#### 列出控制項的關聯程序書

```
GET /api/1.0/ssp/<ssp_uid>/control-implementation/<control_identifier>/document-mappings
```

**Response：**

```json
{
  "status": true,
  "data": [
    {
      "uid": "doc-uuid-1",
      "file_uid": "file-uuid-1",
      "file_name": "資訊安全政策v3.0.pdf",
      "file_size": 1024000,
      "description": "資訊安全政策 v3.0"
    }
  ]
}
```

#### 批次建立控制項關聯

```
POST /api/1.0/ssp/<ssp_uid>/control-implementation/<control_identifier>/document-mappings
Content-Type: application/json

{
  "document_uids": ["doc-uuid-1", "doc-uuid-2", "doc-uuid-3"]
}
```

**Response：**

```json
{
  "status": true,
  "data": {
    "added_count": 3
  }
}
```

**注意：** 已存在的關聯會自動跳過，不會重複建立。

#### 取消控制項關聯

```
DELETE /api/1.0/ssp/<ssp_uid>/control-implementation/<control_identifier>/document-mapping/<doc_uid>
```

---

### 三、AO 關聯

API 格式與控制項關聯完全相同，只是 URL 多了 `/objective/<statement_identifier>` 層級。

#### 列出 AO 關聯

```
GET /api/1.0/ssp/<ssp_uid>/control-implementation/<ctrl_id>/objective/<stmt_id>/document-mappings
```

#### 建立 AO 關聯

```
POST .../objective/<stmt_id>/document-mappings
Body: { "document_uids": ["doc-uid-1", "doc-uid-2"] }
```

#### 取消 AO 關聯

```
DELETE .../objective/<stmt_id>/document-mapping/<doc_uid>
```

---

## 前端 UI 設計建議

### 1. 程序書管理頁面（SSP 級別）

**入口：** SSP 詳情頁面新增「程序書管理」tab 或左側選單項目

```
┌──────────────────────────────────────────────────────────────┐
│  SSP 程序書管理                              [上傳程序書]      │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────┬──────────┬───────────┬──────────┬──────────┐  │
│  │ 檔案名稱  │ 描述     │ 檔案大小   │ 關聯數量  │ 操作     │  │
│  ├──────────┼──────────┼───────────┼──────────┼──────────┤  │
│  │ 資訊安全  │ v3.0     │ 1.0 MB    │ 5        │ 🔗 🗑   │  │
│  │ 政策.pdf  │          │           │          │          │  │
│  ├──────────┼──────────┼───────────┼──────────┼──────────┤  │
│  │ 存取控制  │ v2.1     │ 512 KB    │ 3        │ 🔗 🗑   │  │
│  │ 管理辦法  │          │           │          │          │  │
│  └──────────┴──────────┴───────────┴──────────┴──────────┘  │
│                                                              │
│  拖拽檔案到此處上傳，或點擊「上傳程序書」按鈕                   │
└──────────────────────────────────────────────────────────────┘
```

**操作：**
- 「上傳程序書」→ 支援多檔選擇或拖拽，上傳後填寫描述
- 🔗 → 查看此程序書關聯了哪些控制項/AO
- 🗑 → 刪除（`mapping_count > 0` 時警告）

### 2. 控制項/AO 詳情頁的關聯操作

在控制項和 AO 詳情頁面，「參考程序書」區塊改為：

```
┌────────────────────────────────────────────┐
│ 參考程序書                    [關聯程序書]   │
├────────────────────────────────────────────┤
│ 📄 資訊安全政策v3.0.pdf        [取消關聯]   │
│ 📄 存取控制管理辦法.docx       [取消關聯]   │
│                                            │
│ （尚未關聯程序書時顯示空狀態提示）           │
└────────────────────────────────────────────┘
```

### 3. 關聯程序書 Dialog

點擊「關聯程序書」按鈕，彈出從池中選擇的 Dialog：

```
┌────────────────────────────────────────────┐
│ 關聯程序書                     [搜尋...]    │
├────────────────────────────────────────────┤
│ ☑ 資訊安全政策v3.0.pdf     （已關聯）       │
│ ☐ 存取控制管理辦法.docx                     │
│ ☐ 實體安全管理規範.pdf                      │
│ ☑ 事件應變計畫.pdf         （已關聯）       │
├────────────────────────────────────────────┤
│              [取消]  [確認關聯]              │
└────────────────────────────────────────────┘
```

**邏輯：**
- 從池 API 取得所有程序書 `GET /document-pool`
- 從 mapping API 取得已關聯的 `GET /document-mappings`
- 已關聯的預設勾選
- 使用者勾選/取消勾選後，計算差異
- 確認後：新增的打 `POST /document-mappings`，取消的打 `DELETE /document-mapping/<uid>`

### 4. 上傳流程

```javascript
// Step 1: 上傳實體檔案
const fileRes = await uploadFile(file)  // POST /file/upload
const fileUid = fileRes.data.uid

// Step 2: 加入程序書池
await api.post(`/ssp/${sspUid}/document-pool`, {
  documents: [{ file_uid: fileUid, description: '資訊安全政策 v3.0' }]
})

// Step 3: 刷新池清單
refreshPool()
```

---

## API 總覽

| Method | URL | 說明 |
|--------|-----|------|
| GET | `/ssp/<uid>/document-pool` | 列出程序書池 |
| POST | `/ssp/<uid>/document-pool` | 上傳到池 |
| DELETE | `/ssp/<uid>/document-pool/<doc_uid>` | 從池刪除（cascade） |
| GET | `.../control-implementation/<ctrl>/document-mappings` | 控制項的關聯清單 |
| POST | `.../control-implementation/<ctrl>/document-mappings` | 建立控制項關聯 |
| DELETE | `.../control-implementation/<ctrl>/document-mapping/<doc_uid>` | 取消控制項關聯 |
| GET | `.../objective/<stmt>/document-mappings` | AO 的關聯清單 |
| POST | `.../objective/<stmt>/document-mappings` | 建立 AO 關聯 |
| DELETE | `.../objective/<stmt>/document-mapping/<doc_uid>` | 取消 AO 關聯 |

---

## 與舊 API 的關係

| 舊 API | 狀態 | 說明 |
|--------|------|------|
| `POST .../reference-documents` | **保留但建議逐步遷移** | 舊的直接上傳到控制項/AO |
| `DELETE .../reference-document/<uid>` | **保留** | 舊的刪除 |
| 新 `document-pool` 系列 | **新增** | 池 + 關聯模式 |

建議前端先實作新的池 + 關聯 UI，舊的 API 暫時保留相容，後續視遷移進度決定是否廢棄。
