批次匯入匯出功能設計規格書

1. 概述

提供 Excel 批次匯入匯出機制,讓使用者能高效維護專案內的 SSP 控制項現況說明與 Job 任務設置,取代逐筆 UI 操作。

1.1 功能範圍

模組 匯出內容 匯入可設定欄位
SSP 控制項現況 控制項 + 檢查項目的實作狀態與現況描述 實作狀態、現況描述
Job 任務設置 所有 Job 的任務類型、指派人員、部門、設備 任務類型、指派人員、參與部門、設備

1.2 設計原則

  • 匯出即範本:匯出的 Excel 帶有現有資料,使用者在此基礎上修改後直接匯入
  • 三步驟流程:上傳 → 驗證預覽 → 確認寫入,降低誤操作風險
  • 支援重新驗證:前端可在表格中修正錯誤後,送 JSON 重新驗證,不需重新上傳 Excel
  • 防禦性寫入:confirm 階段也做基本驗證,防止未經 validate 直接送入錯誤資料

2. 共用流程

兩個模組共用相同的匯入匯出流程架構:

                    ┌──────────────┐
                    │   匯出 Excel  │
                    │  GET /export  │
                    └──────┬───────┘
                           │
                    瀏覽器下載 .xlsx
                  (灰底=唯讀,黃底=可編輯)
                           │
                    使用者離線編輯 Excel
                           │
                    ┌──────▼───────┐
                    │  上傳 Excel   │
                    │ POST /import  │
                    │(multipart)    │
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │  驗證結果預覽  │
                    │ valid / error │
                    └──────┬───────┘
                           │
                  ┌────────┼────────┐
                  │有錯誤            │全部有效
                  ▼                  ▼
          前端表格編輯         ┌─────────────┐
                  │           │  確認匯入     │
                  ▼           │POST /confirm │
          ┌─────────────┐    └──────┬──────┘
          │  重新驗證     │           │
          │POST /validate│    批次寫入完成
          │  (JSON)      │    刷新列表
          └──────┬──────┘
                 │
          回到驗證結果預覽
          (可重複多次)

2.1 API 端點模式

每個模組提供 4 支 API:

端點 Method Content-Type 用途
/export GET 下載 Excel
/import POST multipart/form-data 上傳 Excel + 驗證
/import/validate POST application/json JSON 重新驗證
/import/confirm POST application/json 確認寫入

2.2 Excel 格式規範

項目 規格
格式 .xlsx(OpenXML)
大小限制 10 MB
Header 行 第 1 行,藍底白字,凍結
唯讀欄位 灰底(#F2F2F2),匯入時忽略修改
可編輯欄位 淡黃底(#FFFFC0,同 Excel 備註色)
下拉選單 openpyxl DataValidation,限制合法值
框線 全部資料格加細框線

2.3 驗證結果格式

{
  "status": true,
  "data": {
    "valid_count": 45,
    "error_count": 3,
    "items": [
      {
        "row": 2,
        "...欄位資料...",
        "errors": []
      },
      {
        "row": 5,
        "...欄位資料...",
        "errors": [
          {"field": "欄位名", "message": "錯誤訊息"}
        ]
      }
    ]
  }
}
  • errors 為空陣列 → 該行有效
  • errors 有值 → 該行有錯誤,前端標紅顯示

2.4 Confirm 防護機制

即使未經 validate 直接呼叫 confirm,也會:

  • 驗證必填欄位(跳過無效行)
  • 驗證合法值(如 status、job_type)
  • 回傳 skipped_count 告知跳過筆數

3. SSP 控制項現況匯入匯出

3.1 API 端點

Method URL 說明
GET /api/1.0/ssp/<ssp_uid>/control-implementations/export 匯出 Excel
POST /api/1.0/ssp/<ssp_uid>/control-implementations/import 上傳 Excel + 驗證
POST /api/1.0/ssp/<ssp_uid>/control-implementations/import/validate JSON 重新驗證
POST /api/1.0/ssp/<ssp_uid>/control-implementations/import/confirm 確認匯入

3.2 Excel 結構(7 欄)

標題 控制項行 檢查項目行 說明
A 控制項識別碼 灰底(有值) 灰底(重複) e.g. AC.L1-3.1.1
B 控制項名稱 灰底(有值) 灰底 e.g. 授權存取控制
C 實作狀態 黃底(可填) 灰底 下拉選單
D 現況描述 黃底(可填) 灰底 自由文字
E 檢查項目名稱 灰底 灰底(有值) e.g. [a] authorized users...
F 檢查項目實作狀態 灰底 黃底(可填) 下拉選單
G 檢查項目現況描述 灰底 黃底(可填) 自由文字

3.3 實作狀態合法值

implemented, partial, not_applicable, not_implemented, inherited, unknown

3.4 行類型判斷

匯出時:

  • 每個控制項先輸出一行(控制項行)
  • 接著輸出該控制項下的所有檢查項目(每個一行)

匯入時:

  • 有 C 或 D 欄值 → 控制項行
  • 有 F 或 G 欄值(且 C/D 無值) → 檢查項目行
  • 檢查項目行按控制項下的順序對應 statement_identifier

3.5 資料來源

資料 來源
控制項識別碼 system_security_plan_control_implementations.control_identifier
控制項名稱 assessment_plan_controls.control_title(透過 SSP → AP → Controls)
檢查項目名稱 assessment_plan_tasks.title(透過 SSP → AP → Controls → TaskControls → Tasks)
實作狀態/現況描述 system_security_plan_control_implementations / ssp_control_implementation_objectives

3.6 驗證結果欄位

欄位 說明 前端顯示
control_identifier 控制項識別碼 顯示
control_name 控制項名稱 顯示
objective_id 檢查項目內部 ID 不顯示(原封帶回)
objective_name 檢查項目名稱 顯示
implementation_status 控制項實作狀態 控制項行顯示
implementation_description 控制項現況描述 控制項行顯示
obj_implementation_status 檢查項目實作狀態 檢查項目行顯示
obj_implementation_description 檢查項目現況描述 檢查項目行顯示

3.7 Confirm Response

{
  "updated_controls": 15,
  "updated_objectives": 30,
  "total": 45,
  "skipped_count": 0
}

4. Job 任務設置匯入匯出

4.1 API 端點

Method URL 說明
GET /api/1.0/grc/project/<pid>/ap/<apid>/jobs/export 匯出 Excel
POST /api/1.0/grc/project/<pid>/ap/<apid>/jobs/import 上傳 Excel + 驗證
POST /api/1.0/grc/project/<pid>/ap/<apid>/jobs/import/validate JSON 重新驗證
POST /api/1.0/grc/project/<pid>/ap/<apid>/jobs/import/confirm 確認匯入(需 manager 角色)

4.2 Excel 結構(10 欄,A 欄隱藏)

標題 底色 可見 必填 說明
A job_uid 隱藏 程式用,匯入時精確匹配 Job
B 控制項群組 灰底 可見 group name
C 控制項識別碼 灰底 可見 e.g. AC.L1-3.1.1
D 控制項名稱 灰底 可見 control title
E 檢查項目名稱 灰底 可見 AO name
F 任務名稱 灰底 可見 job name
G 任務類型 黃底 可見 必填 下拉:general / survey
H 指派人員 黃底 可見 必填 逗號分隔 login_name
I 參與部門 黃底 可見 選填 逗號分隔部門名稱
J 設備 黃底 可見 選填 逗號分隔設備名稱

4.3 排序

匯出資料按 控制項群組 → 控制項識別碼 → 檢查項目名稱 升序排列。

4.4 隱藏欄位設計

  • A 欄(job_uid)使用 openpyxl column_dimensions["A"].hidden = True
  • 使用者在 Excel 中看不到此欄
  • openpyxl iter_rows(values_only=True) 仍能讀取隱藏欄的值
  • 匯入時用 job_uid 精確匹配,解決同一 AO 下多個同名 Job 的問題

4.5 驗證規則

欄位 規則 錯誤訊息
job_uid(A 欄) 必須存在且屬於此 AP 「任務不屬於此稽核計畫」
job_uid 重複 同一 Excel 不允許重複 「重複的任務識別碼」
任務類型 必填,generalsurvey 「無效的任務類型」
指派人員 必填,每個 login_name 須存在(不分大小寫) 「使用者不存在:xxx」
參與部門 選填,名稱須存在且不重名 「部門不存在」/「部門名稱重複,請改用 UI」
設備 選填,名稱須存在且不重名 「設備不存在」/「設備名稱重複,請改用 UI」

4.6 部門/設備重名處理

  • 驗證階段:檢測到重名部門/設備時報錯,提示使用者改用 UI 設定
  • Confirm 階段:重名的 name → uid mapping 已排除,不會指定到錯誤的部門/設備

4.7 指派人員更新機制

  • update_job 需要 control_uid 才能更新 assignees(repo 層限制)
  • Confirm 時透過 JobImportLookupQuery.get_job_control_uid_map() 批次查詢 job_uid → control_uid 的 mapping
  • 查詢路徑:Job → WorkflowExecution → AP Task → TaskControl → Control → uid

4.8 權限控制

  • Confirm 端點需要 manager 角色
  • 透過 _check_manager_role() 在 service 層驗證
  • 非 manager 回傳 403 GRC_403002

4.9 驗證結果欄位

欄位 說明 前端顯示
job_uid Job 內部 ID 不顯示(原封帶回)
job_name 任務名稱 顯示
control_name 控制項名稱 顯示
ao_name 檢查項目名稱 顯示
job_type 任務類型 顯示,可編輯
assignees 指派人員(逗號分隔) 顯示,可編輯
departments 參與部門(逗號分隔) 顯示,可編輯
devices 設備(逗號分隔) 顯示,可編輯

4.10 Confirm Response

{
  "updated_count": 30,
  "skipped_count": 0
}

5. Error Code

錯誤碼 HTTP 訊息 適用模組
GRC_400001 400 上傳檔案格式無效,請使用匯出的 Excel 範本 SSP
GRC_400002 400 上傳檔案超過大小限制(10MB) SSP
GRC_400003 400 上傳檔案格式無效,請使用匯出的 Excel 範本 Job
GRC_400004 400 上傳檔案超過大小限制(10MB) Job
GRC_403002 403 使用者不具備管理者角色 Job confirm
GRC_404012 404 SSP 系統安全計畫不存在 SSP
GRC_404015 404 稽核計畫不存在 Job

6. 架構設計

6.1 DDD 層級分佈

api/                          ← Route 層(HTTP 邊界)
├── oscal/routes/ssp/
│   └── ssp_control_impl_import_route.py    ← SSP 匯入匯出 routes
├── oscal/serializers/ssp/
│   └── ssp_control_impl_import.py          ← SSP Marshmallow schemas
├── grc/routes/
│   └── job_import_route.py                 ← Job 匯入匯出 routes
└── grc/serializers/
    └── job_import.py                       ← Job Marshmallow schemas

app/                          ← App Service 層(業務邏輯編排)
├── oscal/service/
│   └── ssp_control_impl_import_service.py  ← SSP 匯入匯出 service
└── grc/service/
    └── job_import_service.py               ← Job 匯入匯出 service

infra/                        ← 基礎設施層(ORM 查詢)
├── oscal/repository/
│   └── ssp_catalog_title_query.py          ← SSP 控制項/AO 標題查詢
└── grc/repository/
    ├── job_export_query.py                 ← Job 匯出資料查詢
    └── job_import_lookup_query.py          ← Job 匯入驗證/確認 lookup

6.2 DI Container

Container Provider 類別
oscal_container ssp_catalog_title_query SspCatalogTitleQuery (Singleton)
oscal_container ssp_control_impl_import_service SspControlImplImportService (Factory)
grc_container job_export_query JobExportQuery (Singleton)
grc_container job_import_lookup_query JobImportLookupQuery (Singleton)
grc_container job_import_service JobImportService (Factory)

6.3 依賴關係

SSP Import Service:

SspControlImplImportService
├── SystemSecurityPlanDomainService     (SSP 查詢)
├── ControlImplementationDomainService  (控制項查詢)
├── ControlImplementationObjectiveDomainService (Objective 查詢)
├── SspControlImplementationService     (upsert 複用)
└── SspCatalogTitleQuery                (標題查詢,infra)

Job Import Service:

JobImportService
├── JobService                          (update_job 複用)
├── JobExportQuery                      (匯出資料查詢,infra)
├── JobImportLookupQuery                (驗證/確認 lookup,infra)
├── ProjectDomainService                (權限檢查用)
└── ProjectParticipantDomainService     (權限檢查用)

7. 變更檔案清單

7.1 新增檔案

檔案 職責
api/oscal/serializers/ssp/ssp_control_impl_import.py SSP Marshmallow schemas
api/oscal/routes/ssp/ssp_control_impl_import_route.py SSP 4 支 API routes
app/oscal/service/ssp_control_impl_import_service.py SSP 匯入匯出 service
infra/oscal/repository/ssp_catalog_title_query.py SSP 標題查詢(infra)
infra/oscal/__init__.py package init
infra/oscal/repository/__init__.py package init
api/grc/serializers/job_import.py Job Marshmallow schemas
api/grc/routes/job_import_route.py Job 4 支 API routes
app/grc/service/job_import_service.py Job 匯入匯出 service
infra/grc/repository/job_export_query.py Job 匯出資料查詢(infra)
infra/grc/repository/job_import_lookup_query.py Job 匯入驗證/確認 lookup(infra)

7.2 修改檔案

檔案 變更內容
api/oscal/__init__.py 註冊 SSP import/export routes
api/grc/__init__.py 註冊 Job import/export routes
di_containers/oscal/oscal_containers.py SSP DI wiring
di_containers/grc/grc_containers.py Job DI wiring
common/code/grc_error_code.py 新增 4 個 error codes

8. 不在範圍內的功能

項目 原因 替代方案
問卷配置匯入 問卷有類型、審核流程、per-user 權限,Excel 無法表達 保留在 UI 操作
指派人員審核者設定 增加 Excel 複雜度,使用頻率低 保留在 UI 操作
SSP 備註欄位 UI 暫無此功能,為預留欄位 未來需要時加回
新增 Job 匯入只更新現有 Job,不支援新增 使用現有 Job create API