日期:2026-05-22 用途:B1 session 開工時貼入
你正在接手 SSP 匯入匯出 Phase 2 — Track B,從 B1(Docx 樣板 + Generator 骨架)開始。
## 背景
Track A(Excel 匯入)全部完成,jedi-oscal 0.0.17 已發版。
請先讀:
1. docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md(phase tracker)
2. docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-21-b-phase-analysis.md(B 階段前置分析)
## 已確認的架構決策
- **D1 LibreOffice**:server 已安裝,現有 PDF 轉換在用 ✅
- **D2 中間層**:B2 使用 `SspExportDataModel` dataclass(主專案 domain layer)✅
- **D3 Generator**:使用 **docxtpl**(Jinja2-in-Word .docx 模板),不用純 python-docx code ✅
- 原因:樣板會頻繁變動(樣式、章節、客製化),docxtpl 讓非工程師可直接在 Word 更新模板
- 匯出 docx **不設計成 round-trip 匯入格式**,優先做可讀交付物
- **PDF/ODT**:LibreOffice headless subprocess(B4 phase)
## B1 目標(本 session)
1. **分析 CMMC reference docx 章節結構**
- 用 python-docx 讀 `docs/features/FR-011.2-2605-ssp-import-export-phase2/reference/ASIA-CMMC-SSP-DRAFT-with-user-info-202604.docx`
- dump 所有 Heading + 大標題,確認最終章節架構
2. **安裝 docxtpl 依賴**
- `pyproject.toml` 加 `docxtpl`
3. **建立 SSP docxtpl 模板** (`app/oscal/templates/ssp/ssp_cmmc_template.docx`)
- 從 reference docx 提取章節結構
- 加入 Jinja2 變數標籤(`{{ system_name }}`、`{%tr for ctrl in controls %}`等)
- 章節對應 OSCAL 區塊:metadata / parties / system-implementation / control-implementation / back-matter
4. **建 SspDocxGenerator 骨架**(`app/oscal/service/export/ssp_docx_generator.py`)
- 接受 `SspExportDataModel`(B2 定義,B1 先用 stub dataclass)
- 用 docxtpl render → 回傳 `BytesIO`
- 驗收:可產出空殼 docx(有封面/章節框架,data 用假資料)
## 注意事項
- DDD 規範:Generator 放 app layer(`app/oscal/service/export/`)
- `SspExportDataModel` B1 先用簡單 stub(只有 title + system_name + controls=[]),B2 再補完整
- docxtpl 依賴要加到 pyproject.toml,讓使用者 `poetry update` 後生效
- jedi-oscal 本 phase 暫不動(docxtpl 是主專案 app layer 的事)
## 參考路徑
- Reference docx:`docs/features/FR-011.2-2605-ssp-import-export-phase2/reference/ASIA-CMMC-SSP-DRAFT-with-user-info-202604.docx`
- 同目錄還有 `ASIA-CMMC-SSP-DRAFT-202604.docx`(無 user-info 版本,可對照)
- 既有 system-design DOCX 生成參考(python-docx 模式):`docs/system-design/scripts/generate_*.py`
- 現有 OSCAL service 路徑:`app/oscal/service/`