Bugfix Session 3 SUMMARY — Excel Update Flow 覆蓋語義修補

時程:2026-05-21(接續同日 session 2 SUMMARY:2026-05-21-bugfix-session2-SUMMARY.md跨 repo:compliance-manager-be + compliance-manager-fe


1. 這段時間在幹嘛(一句話)

Excel re-import(update flow)時,舊資料沒有被覆蓋(append 而非 overwrite),以及基本資料 / 控制項清單沒有隨 Excel 更新,本次統一修補。


2. 修補項目與根因

# Bug 描述 根因 修正位置
1 Re-import devices/info_systems/leveraged → 重複 row WriteStrategy.write() 只 add,未先刪舊 row write_strategy/base.py
2 取消勾選的控制項 default 殘留 write_excel_control_defaults() 只 upsert module_frame_write_strategy.py
3 移除的參與人員 link 殘留 write_parties() 只 upsert,stale link 未刪 module_frame_write_strategy.py
4 Template-edit 控制項清單不隨 Excel 更新 _confirm_update_flow 未更新 oscal_profile.include_controls(頁面讀 profile,非 defaults) ssp_excel_import_app_service.py
5 基本資料(名稱/類別/描述)不隨 Excel 更新 _confirm_update_flow 未呼叫 update_module_frame();即使呼叫也需傳 locale,否則只改主表不改 module_frame_trans(failover query 優先翻譯表) ssp_excel_import_app_service.py
6 導回 template-edit 時參與人員顯示舊筆數 useModuleFrameParties 永久 module-scope cache,import 後未 invalidate ImportExcelPreviewPage.vue + useModuleFrameParties.js

3. Commit 清單(本次 session 3 未 commit,待使用者確認後提交)

BE 未 commit 的改動

  • app/oscal/service/ssp_excel_import_app_service.py_confirm_update_flow + _resolve_include_uids_for_update
  • domain/oscal/service/write_strategy/base.py — delete-before-insert
  • domain/oscal/strategy/module_frame_write_strategy.py — full overwrite + stale link cleanup
  • di_containers/oscal/oscal_containers.pyrefdoc_mapping_domain_service 注入

FE 未 commit 的改動

  • src/views/module_frame/ImportExcelPreviewPage.vue — confirm dialog + parties cache invalidate
  • src/config/locales/i18n/*/ssp-excel-import.jsonconfirm_overwrite_* keys

Changelog

  • docs/changelog/2026-05-21-fix-ssp-excel-import-overwrite-mode.md(已建立)

4. 架構關鍵決策

WriteStrategy 整份覆蓋 vs upsert

  • devices / info_systems / leveraged:整份覆蓋(delete all + insert)— 因為 row 本身無穩定 natural key,重複 row 無法去重
  • control_defaults + objectives:整份覆蓋(先清空 MF 全部 defaults + refdoc mappings,再只寫 in-profile 的)
  • responsible_party links:snapshot → upsert → 刪 stale(有 party_uuid 可追蹤,不需全刪全插)
  • reference_documents(程序書):保持 append-only(設計明確不覆蓋)

module_frame_trans locale 陷阱

  • BaseRepositoryImpl.update(entity, locale=None) 只改主表 module_frames
  • GET /module-frame/{uid} 用 failover query,優先讀 module_frame_trans(翻譯表)
  • 解法:update_module_frame 必須傳 localeuser_context.locale fallback zh_Hant_TW

5. Follow-up

項目 說明
Track B(SSP 匯出) B1–B6 全部 pending,尚未開始
Design-A4.md §11.3 本次偏差已補入 §11.3
Cucumber E2E A5 update flow 整份覆蓋行為尚無 regression test