| 項目 | 內容 |
|---|---|
| 版本 | v1.0 |
| 日期 | 2026-05-24 |
| 作者 | Claude (overnight session) |
| Branch | feature/real_doc_import |
| Status | Stage 1 in progress |
| 對應功能 | /module-frame/import-docx 解析能力對齊 /module-frame/import-excel |
v1.1.0 release 後,SSP Excel 匯入(A1~A5 phases)已能解析以下 OSCAL 欄位:
但 Docx 匯入今天只解控制項 + AO + 部分 parties / leveraged,metadata / system_characteristic / 多數 party 欄位都還沒 reach。客戶上傳真實 SSP docx 時 BE 解出的資料量遠少於 excel — UX 不一致。
讓 Docx 匯入解出與 Excel 對齊的 OSCAL 欄位範圍(樣板加表議題押後,本期只在「樣板已經有的 anchor」內把欄位補完整)。
實作分兩階段:
| Stage | 範圍 | 預期成果 |
|---|---|---|
| Stage 1 (本次) | Parser 補欄位 + Adapter 塞進既有 ParsedSsp shape | 上傳 docx → parse_job.parsed_result 含 metadata / parties / leveraged 完整欄位 + revision_history;preview API 回傳給 FE 看到資料 |
| Stage 2 (後續) | Adapter 重構輸出 ParsedExcelEntityBundle、confirm 切 SspEntityReconciliationOrchestrator + WriteStrategy、parse_jobs.schema_version 獨立 column + 雙 path 並存、FE 預覽改 reuse excel component |
Docx confirm 流程完全 unified 到 excel pipeline;新欄位 (devices/info_systems) 一次寫成一條 |
亞航-CMMC-SSP-20260520-1會議討論版.docx 結構驗證)| Anchor | 位置 | OSCAL / ParsedSsp 對應 |
|---|---|---|
| Table #0 | 文件首個 table(H1 Introduction 前) | metadata.{document_serial_no, version, published, title} + system_characteristics.organization_name |
| Tables #1~#5 | H1 Introduction 下、H2 之前的 K/V tables | parties (org + person) — name/title/address/phone/email |
| Table #6 | H2 Leveraged External Systems 下,col0 = CSP/CSO Name |
leveraged service (provider/service_name + 新增 4 欄塞 props 風格) |
| Table #7 | H2 Leveraged External Systems 下,col0 = 類別 Category |
leveraged service (category + name + purpose + protocol + security_auth) |
| Table #24 | H1 APPENDIX 2 Revision History 下 | metadata.revision_history (list of {version, date, amendment, description}) |
| 控制項 + AO | H3 + 下方 AO check 表 | matched_controls — 既有實作沿用,不改 |
不在 Stage 1 範圍:
詳見 implementation-plan-stage1.md 各 extractor 規格。核心原則:
re.sub(r'[\s::\.]', '', txt).lower())[...] 形式 → 視為樣板 placeholder skip直接在既有 domain/oscal/adapter/cmmc_ssp_adapter.py「ParsedSsp 組裝」階段呼叫 docx_section_extractors 一輪:
existing adapter.adapt(parsed, structure, candidates)
├─ existing logic (matched_controls / 基本 parties / 基本 leveraged)
└─ NEW: section_orchestrator.parse_all(doc, structure)
├─ extract_metadata_table → 補 ParsedMetadata 欄位
├─ extract_party_tables → 補 ParsedParty 欄位(telephone/address 等)
├─ extract_leveraged_csp_table → 補 ParsedLeveragedService 欄位 (CMMC props 走 framework_specific_props dict)
├─ extract_leveraged_category_table → 補 ParsedLeveragedService(category 等)
└─ extract_revision_history_table → ParsedMetadata.revision_history
注意:ParsedLeveragedService dataclass 目前沒有 props/framework_specific_props 欄位。Stage 1 解法是把 CMMC-only 欄位塞進 remarks(free text)或在 dataclass 加一個 props: Dict[str, Any] 欄位。後者較乾淨,但會影響其他 caller。本期採折衷:在 ParsedLeveragedService 加 props: Optional[Dict[str, Any]] = None 欄位(純加、不影響既有 caller — 沒讀就是 None)。
Stage 1 不動 confirm path — confirm 流程仍走既有 ssp_docx_import_app_service 邏輯。新解出的 metadata / parties 補欄位 / leveraged 新欄位會出現在 parse_job.parsed_result JSONB,但 confirm 時是否寫入 DB 看既有 write logic 是否處理該欄位(多數有,部分如 leveraged 新 props 欄位可能會被忽略 — 等 Stage 2 重構寫入 path 才完整 propagate)。
parse_jobs.schema_version column 押到 Stage 2(Stage 1 不需要 — 因為沒分流)。
Stage 1 不動 FE。明早 user 試上傳,可從以下方式驗 BE 解出資料:
POST /api/1.0/ssp-docx-imports/parseGET /api/1.0/ssp-docx-import/<uid> response JSONSELECT parsed_result FROM oscal.ssp_docx_parse_jobs WHERE uid=...如果 FE 既有 preview component 顯示「正常」欄位 — 那解出來新欄位也會帶上去(但可能沒漂亮 render,只是 raw key 出現)。
| 工作項 | 動到的範圍 | 預估工時 |
|---|---|---|
ParsedExcelEntityBundle 擴 parsed_parties + parsed_metadata |
A4 dataclass + 各 reconciler | S |
| Docx adapter 重構輸出 ParsedExcelEntityBundle | 整個 docx adapter | M |
| Confirm 切 SspEntityReconciliationOrchestrator | confirm app service path | M |
parse_jobs.schema_version column + 雙 path 並存 |
DB migration + service router | S |
| FE 預覽 reuse excel SspImportPreview component | FE Vue | M |
| 樣板加表(System Components / Hardware Inventory) + template download endpoint | 樣板設計 + BE generator + FE 下載 | L |
| 風險 | 緩解 |
|---|---|
| extractor regex / heuristic 對客戶 docx 失敗 | 用客戶實際檔當 fixture,所有 extractor 都跑過 |
| 既有 docx import e2e test 被改壞 | S6 commit 前跑全套既有 test,紅就 revert |
| BE 重啟卡住、user 早上 endpoint 不可用 | S8 重啟後 curl 自我驗證,fail 時寫詳細 handoff 不靜默 |
| 半夜 push 推到 main | 絕對不 push,commit on local branch only |
| 客戶檔被 commit 進 git history | .gitignore 加 docs/reference/亞航* + git rm --cached(已在 S0 完成) |
Stage 1 上線後(明早 user 確認):
metadata.title = System Security Plan (SSP) | 系統安全計畫(草稿)metadata.revision_history = 1 筆 (V1.0 / 2026.0X.XX / Initial / NA)leveraged_services ≥ 2 筆(T6 row2 + T7 rows)parties 結構含 column 對應(即使值空 — 5 個 placeholder party)docs/reference/亞航-CMMC-SSP-20260520-1會議討論版.docxdocs/features/FR-011.2-2605-ssp-import-export-phase2/reference/ASIA-CMMC-SSP-DRAFT-with-user-info-202604.docxdomain/oscal/parser/ssp_intermediate.pydomain/oscal/parser/docx_parser_core.pydomain/oscal/adapter/cmmc_ssp_adapter.pyapp/oscal/service/ssp_docx_import_app_service.py