C1 — Implementation Plan

對應 design:design-C1.md 級別:中型 預估工時:BE 0.5 天 + FE 0.5 天


§1

Pre-flight 驗證(開工前必跑)

# 1. 確認 system_menus 表存在且 schema 是 (group, key, value, sort, enable, public)
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "\d system_menus"

# 2. 確認 system_menus 是否已有 UNIQUE(group, key) constraint
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT conname FROM pg_constraint WHERE conrelid = 'system_menus'::regclass AND contype = 'u';"

# 3. 確認 oscal.oscal_roles 表 schema (C3-D5 同步 seed 需要)
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "\d oscal.oscal_roles"

# 4. 確認 oscal.oscal_roles 是否已有 UNIQUE(role_id) constraint
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT conname FROM pg_constraint WHERE conrelid = 'oscal.oscal_roles'::regclass AND contype = 'u';"

# 5. 確認 oscal.oscal_roles 現有資料(避免衝突)
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT role_id, title FROM oscal.oscal_roles ORDER BY role_id;"

# 6. 確認 GET /system/menu/<group> endpoint 路徑 + 回傳格式
grep -n "SystemGroupMenuRoute" api/system_menu/__init__.py
# 預期:/system/menu/<string:group>

# 7. 確認 jedi-system-menu 在 BE pyproject.toml 已 pin
grep "jedi-system-menu" pyproject.toml

Verification 結果填回 design-C1.md 對應段落(特別是兩個 UNIQUE 是否已存在)。


§2

任務清單

T1 — SQL Migration: seed 9 個角色到兩個表(BE)

檔案

  • 新建 scripts/sql/2026-MM-DD-seed-oscal-ssp-roles.sql
  • 內容對齊 design-C1.md §3.2(含 §3.2.A system_menus + §3.2.B oscal_roles)

行為

  • Part A: 9 個 INSERT 進 system_menus(UI dropdown 用),ON CONFLICT DO NOTHING idempotent
  • Part B: 9 個 INSERT 進 oscal.oscal_roles(OSCAL 文件用),同樣 idempotent
  • 跑完輸出兩個 SELECT COUNT(*) 確認各 9 筆

前置處理

  • 若 pre-flight 發現 system_menus 無 UNIQUE(group, key):
    • 先跑 SELECT "group", key, COUNT(*) FROM system_menus GROUP BY "group", key HAVING COUNT(*) > 1; 確認無重複
    • 加 UNIQUE:ALTER TABLE system_menus ADD CONSTRAINT uq_system_menus_group_key UNIQUE ("group", key);
    • 同 migration script 內處理
  • 若 pre-flight 發現 oscal.oscal_roles 無 UNIQUE(role_id):
    • 先 audit 有無重複 SELECT role_id, COUNT(*) FROM oscal.oscal_roles GROUP BY role_id HAVING COUNT(*) > 1;
    • 加 UNIQUE:ALTER TABLE oscal.oscal_roles ADD CONSTRAINT uq_oscal_roles_role_id UNIQUE (role_id);

驗證

SELECT * FROM system_menus WHERE "group" = 'ssp_party_role' ORDER BY sort;
-- 應看到 9 筆,sort 為 10, 20, ..., 90

SELECT role_id, title FROM oscal.oscal_roles WHERE role_id IN (
  'responsible-organization','system-owner','system-security-officer',
  'authorizing-official','information-owner','information-provider',
  'information-receiver','prepared-by','prepared-for'
) ORDER BY role_id;
-- 應看到 9 筆

Commit messagechore(sql): C1 seed OSCAL SSP 9 個角色到 system_menus + oscal_roles


T2 — Smoke test: API 可取得(BE)

驗證

# 取 JWT token 後
curl -H "Authorization: Bearer <token>" \
  http://localhost:8000/api/1.0/system/menu/ssp_party_role | jq '.data.menus | length'
# 預期:9

不需新增 BE 程式碼(沿用 jedi-system-menu 既有 endpoint)。


T3 — FE i18n 檔案(FE)

檔案

  • 新建 src/config/locales/i18n/zh-tw/oscal-role.json
  • 新建 src/config/locales/i18n/en/oscal-role.json
  • 內容對齊 design-C1.md §5

接入 i18n loader

  • 確認 src/config/locales/i18n/zh-tw/index.js(或對應 loader)有自動引入新檔
  • 若需手動註冊,加上 import

驗證

// FE console
$t('lang.oscal_role.ssp_party_role.system-owner.label')
// 預期:'系統所有者'

Commit messagefeat(i18n): C1 新增 oscal-role i18n 9 個 OSCAL SSP 角色


T4 — Changelog(BE + FE)

BE 檔案docs/changelog/YYYY-MM-DD-feat-oscal-ssp-roles-master-data.md

---
type: feat
breaking: false
modules: [system_menu]
---

## 需求說明
C1:OSCAL SSP 角色 master data 化 — 9 個標準 OSCAL role 種子進 system_menus,
供 C2 SSP party endpoint dropdown 使用。

## 變更範圍
### BE
- `scripts/sql/2026-MM-DD-seed-oscal-ssp-roles.sql`:9 個 INSERT + (optional) UNIQUE constraint

## 部署備註
- SQL migration 須在 dev DB 跑一次
- 不影響既有功能

FE 檔案:同步建立 changelog(FE repo 對應位置)


§3

測試規格

測試 工具 內容
BE smoke curl GET /system/menu/ssp_party_role 回 9 筆
FE i18n console $t(...) 解析正確
Idempotent psql seed SQL 重跑兩次不會重複

不需新 unit test(純資料 seed)。


§4

完成標準(DoD)


§5

風險 / Rollback

風險 緩解
seed 跑失敗 ON CONFLICT DO NOTHING idempotent,可重跑
既有 system_menus 同 group 已有同 key UNIQUE check 阻擋 / ON CONFLICT 跳過
不小心 seed 到 prod migration SQL 不會自動跑,須手動執行

Rollback

DELETE FROM system_menus WHERE "group" = 'ssp_party_role';