FR-141 · 討論稿 · 2026-10-15
計畫概要與評估目標兩個頁籤、一份兩頁 Excel 匯出即匯入範本、亞航格式報告稿匯出
現在的稽核計畫頁以「行程」為單位、查核方法只能填到控制項,顧問實際工作是逐條評估目標(AO)決定怎麼查、查誰、誰來查;而且計畫沒辦法匯出成報告稿。本案把這一頁整個重做:「計畫概要」頁籤放亞航報告稿的六段表頭,「評估目標」頁籤用左樹右表單逐條填查核方法與對象;一份兩頁的 Excel 匯出就是匯入範本,匯出三選一(報告稿、Excel、打包)。資料照 OSCAL 評估計畫的標準欄位存。D1~D4 已由決策者裁定,請先開 雛形 點一遍,再回來看規格。
雛形審過、拍板後寫 design.md,本稿不再改寫。
狀態:討論稿(D1~D4 已裁,雛形第二輪完成待審;P1~P3 待裁) | 日期:2026-10-15 | 母卡:CM-2799 | 雛形卡:CM-2800 | 雛形:prototype/index.html
路徑慣例:BE 路徑相對 compliance-manager-be/;pkg/ 代表 ~/Projects/Jedicogy/module/jedi-python-package/<套件>/<套件底線名>/;FE 路徑相對 compliance-manager-fe/。
名詞:AP=稽核計畫(assessment plan);AO=評估目標(assessment objective),每條控制項底下的檢查項目,全系統用 part_id 當鍵(格式 <控制項代碼>_obj.N);計畫概要=一份 AP 的表頭,原稱「場次」(依據、目的、稽核單位、日期起迄、範圍、方法論、稽核人員);activity=OSCAL AP 裡「一個查核動作」的物件,本案一條 AO 一個;稽核次=FR-140 新增的概念,一輪內每做一次稽核(內部或外部)一筆,每筆自己一份 AP;受評對象=被查的人或系統元件、資產;查表頁=Excel 裡隱藏的工作表,放下拉選項。
| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-15 | 初稿:需求、兩份範例檔結構、已裁 D1~D4、大量 AO 策略、現況盤點、OSCAL 落點、Excel 契約、匯出三選一、三棒拆卡草案 | 母卡 CM-2799 |
| 2026-10-16 | 決策者看第二輪雛形後:D1 改判為一格多值(換行或分號)+填寫說明頁+可選清單頁;頁籤「場次」改名「計畫概要」;稽核單位改單行只填公司名稱;方法組不限數量 | D1 |
| 2026-10-15 | 決策者看第一版雛形後:AO 頁籤改左樹右表單、匯入預覽併入同畫面、稽核次麵包屑;回答「內外稽核不會混,各自一份 AP」 | 雛形第二輪 |
整案一句話:稽核計畫頁換成「計畫概要」+「評估目標」兩個頁籤,顧問逐條 AO 填怎麼查、查誰、誰查;一份兩頁 Excel 匯出即匯入範本;一鍵匯出亞航格式報告稿。
| 棒 | 做什麼 | 產出 | 完成怎麼判定(決策者親手做什麼、看到什麼) |
|---|---|---|---|
| 1 雛形 | 純前端假資料刻兩個頁籤、匯入預覽、報告稿預覽,加一份範例 Excel | prototype/ 四頁+sample/*.xlsx |
開雛形索引頁點一遍:切頁籤、左樹點控制項、右側加方法組、套用到所有 AO、匯入預覽對應未知方法;用 Mac Excel 開範例檔點每個下拉 |
| 2 計畫概要+報告稿 | 套件加「條款與說明」表;計畫概要頁籤;Excel 讀寫骨架與第一頁;docx/pdf 匯出;FR-043 docx 匯入改填新欄位 | 新表 migration、計畫概要頁籤、匯出按鈕三選一(先通報告稿與 Excel 第一頁) | DEV 開一輪的稽核計畫,填六段文字與日期起迄存檔,重整還在;按匯出選報告稿 docx,用 Word 開看到 1~6 段與自己填的內容;選 Excel 開第一頁看到同樣內容 |
| 3 AO+Excel 第二頁 | 套件 activity 寫 AO、受評對象、負責人員;評估目標頁籤(左樹右表單、控制項預設套用、篩選);Excel 第二頁讀寫與查表頁;匯入預覽未知方法對應;覆核輪與 FR-140 第 2 次內稽的 AP 複製改 AO 粒度;AR 讀 AP 改逐 AO | 評估目標頁籤、Excel 兩頁完整來回、clone 改寫 | DEV 在評估目標頁籤填三條 AO 存檔,匯出 Excel 第二頁看到那三列有值、其他列預帶控制項預設;改兩格匯回,預覽頁標出那兩格、確認後畫面更新;啟動覆核輪,新 AP 只剩不合格的 AO |
依賴:1 → 審雛形 → design.md 與開 2、3 的卡 → 2、3 可平行(碰的檔不同)。第 3 棒的 AP 複製要接在 FR-140 卡 3(CM-2788)之後,同一支 audit_round_app_service.py 周邊不同時派。AR 側改逐 AO 另開 FR。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
P[棒 1 雛形<br/>CM-2800] --> R{決策者審}
R --> D[design.md<br/>開棒 2、3 的卡]
D --> B2[棒 2 計畫概要+報告稿<br/>terms 表、docx、Excel 第一頁]
D --> B3[棒 3 AO+Excel 第二頁<br/>activity 改 AO 粒度、clone]
F3[FR-140 卡 3 CM-2788<br/>開新稽核次、AP 複製] -.先做.-> B3
B3 -.另開 FR.-> AR[AR 逐 AO 判定與匯出]
亞航-CMMC L1內部稽核報告20260612(稿).docx)只有段落、沒有表格,七段:1 依據、2 目的(三點條列)、3 稽核單位、4 稽核日期(一天+時段)、5 稽核範圍(指向 SSP 文件編號)、6 稽核方法(三段方法論文字)、7 稽核結果(三點發現+附件指向 Excel)。第 7 段是稽核結果,屬 AR 那一側,本案在報告稿留佔位。
…附件-稽核紀錄.xlsx)一張工作表,表頭 4 列(專案名稱、受驗型態、受稽核人數、稽核日期),正文 59 列=59 條 AO、15 條控制項。欄:序號、索引號(控制項代碼)、實務要求、執行說明、Objectives 控制目標(AO)、Status(實際內容是「方法:指引」多行)、Evidence 佐證(多行檔名)、稽核結果(全 MET)。同一控制項的實務要求只寫第一列。「方法」前綴七種:文件檢查、文件檢視、訪談、現場觀察、電腦設定比對、門禁系統設定比對、測試。
一頁兩個頁籤,頁首是稽核次麵包屑「專案 › 第 1 輪 › 第 N 次 內部/外部稽核 › 稽核計畫」。
| 頁籤 | 內容 | 填寫方式 |
|---|---|---|
| 計畫概要 | 依據、目的、稽核單位、稽核日期起迄、稽核範圍、稽核方法論六段+這場的稽核人員名單;上方唯讀標示「第 N 次 內部/外部」(從稽核次讀) | 多行文字框;稽核單位是單行(只填公司名稱,報告稿印成「由○○協助辦理。」);日期時間起迄;人員多選(可當場新增) |
| 評估目標 | 左側控制族群→控制項樹(完成狀態點、搜尋、篩選「全部/尚未填/已覆寫稽核人員」);右側一次一條控制項:實務要求與執行說明(唯讀)、「控制項預設」區塊(方法組+受評人員+受評元件/資產+稽核人員,「套用到底下所有 AO」)、每條 AO 一張小卡(方法組可加減、受評人員、受評元件/資產、稽核人員預帶計畫概要名單可覆寫);上一條/下一條 | 下拉、多選、文字框;「總覽」子檢視是唯讀大表格 |
匯入預覽與正式頁同一個畫面、進入「預覽模式」:左樹節點標「有變更/有問題」,右側被覆寫的欄位高亮、原值灰字劃掉,未知方法就地出對應下拉,頂部橫幅計數與「確認匯入」。
D1 Excel 多值表示=一格多值,靠填寫說明與預覽檢查兜底(2026-10-16 改判)
為什麼:決策者看雛形後判斷顧問不會用往右延伸的固定格數。業界匯入範本(Salesforce 多選欄位、Jira、ServiceNow)多數也是一格多值加分隔符,配「填寫說明+可選清單+匯入檢查」三件套;固定格數表太寬、第 N+1 個不知道放哪。
被排除:固定格數每格一般下拉(2026-10-15 原裁,表寬約 22 欄,決策者看過後否決);xlsm 多選巨集(Mac/LibreOffice 不相容、資安擋);逗號當方法分隔(與指引內文衝突)。
D2 稽核人員=計畫概要預設、AO 可覆寫
計畫概要頁籤填整場名單,AO 每列預帶、可個別改,改過的列標「已覆寫」可還原;Excel 每列印有效值。
為什麼:只在計畫概要層看不出「這條誰查」;只在 AO 層每列都要重填、與報告稿「稽核單位」段脫節。
D3 受評對象拆兩欄
「受評人員」一欄、「受評系統元件/資產」一欄。元件與資產在 OSCAL 都是 subject、只是 type 不同,合一欄選。
為什麼:現況一個分組多選把人員、元件、資產混在同一欄,決策者點名不好用;拆三欄則 Excel 與畫面各多一欄,沒有對應的業務需要。
D4 匯入遇到選單沒有的方法詞=預覽頁提示、使用者對應
例「文件檢視」「門禁系統設定比對」目前後台選單沒有。匯入預覽標出,使用者逐個對應到既有選項或略過;選單本身由管理員在後台 system_menus 維護。
為什麼:直接擋下顧問會卡住;自動新增選項會讓打錯字也變一個選項、選單長髒。
大量 AO 的填寫策略(決策者 2026-10-15 同意)
CMMC L2 約 110 條控制項、320 條 AO。控制項層先填一組方法/人員再「套用到底下所有 AO」只改例外;左樹預設收合、可篩「尚未填」;Excel 匯出時預帶值已填好、使用者只改少數格。「填好」的定義:至少一個方法,且受評人員或元件/資產至少一個。
內外稽核不會混:每一次稽核各自一份 AP
FR-140 之後每一筆稽核次(第 1 次內稽、第 2 次內稽、外稽)自己一份完整的 AP,各有計畫概要表頭與 AO 表。要分開的是進入點,不是 AP 內部:總覽頁選第幾次進來(FR-140 卡 10)、稽核計畫頁首麵包屑帶「第 N 次 內部/外部」、可切換同一輪其他次唯讀看。第 2 次內稽從第 1 次複製來的 AO 標「承襲自第 1 次」,改過就消。AP 內容本身不需要任何「內部/外部」欄位。
AR 另案
評估結果(AR)之後另開 FR 改成逐 AO 讀取與匯出,Excel 的「佐證資料」「稽核結果」兩欄到那時接上。本案 AP 匯出不印那兩欄、匯入忽略。本案第 3 棒只把 AR 「從 AP 反推方法」的讀取改成逐 AO(不改 AR 畫面)。
P1 第 2 次內稽複製 AP 縮範圍的單位(FR-140 卡 3 接點)
FR-140 卡 3 定「複製上一次內稽的 AP、縮到不合格的控制項」。改成 AO 粒度後有兩種縮法:
我的建議:乙。AR 判定逐 AO,但稽核現場是整條控制項一起看;且 FR-140 卡 3、覆核輪 launch_reverify 既有邏輯都是控制項粒度,乙改動最小。甲留給未來「AO 層勾選」需求。
P2 報告稿附件表要不要多印「受評對象」「稽核人員」兩欄
亞航原表沒有這兩欄(它們在原表是混在 Status 文字裡或根本沒寫)。雛形第一版多印了。建議:印,顧問交報告時這兩欄有用;但做成匯出時可勾選。
P3 方法選單對齊
雛形用六個方法(訪談、文件檢查、現場觀察、電腦設定比對、測試、門禁系統設定比對);DEV 的 system_menus AUDIT_METHOD 群組現有 INTERVIEW/DOCUMENT_REVIEW/EXAMINE/COMPUTER_CONFIG_COMPARE/TEST/ACCESS_CONTROL_CONFIG_COMPARE(pkg/jedi-compliance-audit/app/service/import_adapter/method_keywords.py:11),有「檢視」沒有「現場觀察」。建議:第 2 棒出一支 phase=seed migration 補「現場觀察」,不在程式寫死。
座標以 2026-10-15 為準,runner 開工先開檔核一次。
oscal,pkg/jedi-oscal-v2/infra/model/ap/)| 表 | 現況 | 本案動作 |
|---|---|---|
assessment_plans(oscal_ap.py:26) |
uuid、metadata_id(1:1 oscal.metadata)、status、import_ssp_id、local_definitions_remarks |
不改 |
ap_reviewed_controls(:22,與 AP 1:1) |
control_selections 必填、control_objective_selections 選填——從未寫入,只在 clone 時原樣搬(主專案 assessment_plan_app_service.py:401) |
第 3 棒寫入 AO 的 part_id |
ap_assessment_activities(:23) |
uuid、title、description、steps JSONB(每筆 {uuid,title,description,props[method]})、related_controls(只寫 control-selections,到控制項層)、responsible_roles |
第 3 棒:一條 AO 一個 activity;related_controls 加 control-objective-selections;responsible_roles 帶 party |
ap_tasks(:24) |
parent_id、type、title、description、timing、associated_activities、subjects、responsible_roles JSONB |
第 2 棒:一份 AP 一個 task 當計畫概要容器,timing.within-date-range 放日期起迄;第 3 棒:associated_activities[].subjects 放每條 AO 的受評對象 |
ap_task_subjects、ap_task_participants(:25) |
FR-040 的行程層受評對象與參與人員連結表 | 第 3 棒後退役(新模型不再用行程層對象);migration 不刪表,舊資料留 |
ap_assessment_subjects(:25) |
AP 層受評對象 | 保留,存檔時由 AO 列聯集回填(沿用 FR-040 做法) |
ap_local_objectives(:23) |
control_id、parts JSONB,主專案與套件都沒有呼叫端 |
不用(AO 以 part_id 引用 catalog,不複製) |
oscal.parties/oscal.roles |
掛在 AP metadata 下;稽核人員已是 party、角色放 props responsible-role=assessor(assessment_plan_app_service.py:70-71) |
第 2 棒:稽核單位存 organization 型 party |
沒有表:terms-and-conditions、assessment-assets、objectives-and-methods、AP 的 back-matter |
FR-037 差距審查時略過 | 第 2 棒新增 ap_terms_and_conditions;其餘不做 |
套件服務:pkg/jedi-oscal-v2/app/service/ap/assessment_plan_service.py:43 的 set_reviewed_controls(:113)、set_assessment_subjects(:153)、set_tasks(:184)一律整批先刪後寫,task uuid 每次重產;app/service/io/oscal_io_service.py:263 export_oscal 不支援 assessment-plan。
| 接入點 | 現況 | 本案動作 |
|---|---|---|
端點(api/project/routes/audit_round_route.py:144-205) |
GET /audit-round/<round_uid>/ap、PUT /ap/<ap_uid>/reviewed-controls、PUT …/assessment-subjects、PUT …/tasks、`GET |
POST …/parties`;FE 實際只呼叫 tasks 與 parties |
App service app/flow_control/service/assessment_plan_app_service.py:74(730 行) |
_METHOD_PROP(:68)、_methods_to_props(:115)、指引放 activity.steps[](:130-165)、related_controls 只到控制項(:169-178)、_ap_detail(:247)、clone_ap_for_round(:330,覆核輪 clone 並收斂範圍)、set_tasks(:583)、resolve_ap_and_check_auditor(:190,限 auditor/manager,過了規劃階段唯讀) |
檔已近上限,新邏輯一律新檔:ap_terms_app_service.py(第 2 棒)、ap_objective_app_service.py、ap_workbook_app_service.py、ap_clone_service.py(第 3 棒),原檔一行呼叫。權限守門沿用 resolve_ap_and_check_auditor |
AR 反推 AP(pkg/jedi-compliance-audit/app/service/assessment_result_app_service.py:303) |
把 AP activities 反推成 {control_id: {methods, guidance, subjects}} |
第 3 棒改成 {ao_part_id: …},控制項層由 AO 聚合;AR 畫面不動 |
覆核輪 clone(pkg/jedi-compliance-audit/app/service/audit_round_app_service.py:596 launch_reverify) |
clone AP 縮到 not_met 控制項;FR-140 卡 3 會抽成共用函式供「第 2 次內稽」重用 | 第 3 棒接在 FR-140 卡 3 之後改成 AO 粒度(P1 裁法) |
FR-043 docx 匯入(pkg/jedi-compliance-audit/app/service/ap_docx_import_app_service.py:288-336) |
adapter 已解析出 basis/objective/audit_org/methodology/scope/audit_date(ap_report_parser/base.py:13-31),但全部塞進每筆 task 的 description |
第 2 棒改填 terms 表與 organization party;adapter 不擴充 |
AO key(pkg/jedi-compliance-audit/app/service/ao_derivation.py) |
part_id <control_id>_obj.N,控制項無 AO 時退回 control_id;wecm、AR、SSP Excel 隱藏欄、程序書池全用同一把 key |
第 3 棒沿用,不另造 |
| 能力 | 位置 | 本案用法 |
|---|---|---|
| SSP docx/pdf 匯出 | app/oscal/service/export/ssp_export_app_service.py:27、ssp_docx_generator.py:47(docxtpl 範本+python-docx 追加)、ssp_libreoffice_converter.py:57(LIBREOFFICE_CMD,逾時 60 秒)、範本 app/oscal/templates/ssp/ssp_cmmc_template.docx 由 scripts/generate_ssp_docx_template.py 產 |
第 2 棒照同套路加 ap_report_template.docx 與 ap_docx_generator.py,pdf 走同一支 converter |
| Excel 範本產生器 | app/module_frame/excel_template/generator.py:132 ExcelTemplateGenerator,同目錄 data_validation_builder.py、lookup_builder.py、cell_protection.py、styles.py |
第 2、3 棒:查表頁、list 型資料驗證、鎖唯讀欄 |
| 隱藏 A 欄放 AO part_id | app/oscal/service/ssp_control_impl_import_service.py 檔頭與 export_excel(:105)、confirm_import(:375) |
第 3 棒第二頁同慣例 |
| 公式注入防護 | jedi_common/utils/export_safety.py set_text_cell、neutralize_formula |
所有寫入儲存格 |
| FE 匯入 dialog | src/components/grc/JobImportDialog.vue(426 行,AP 範圍的 job 匯入,驗證→逐列→確認) |
第 3 棒上傳步驟參考;預覽改在正式頁的預覽模式 |
| FE 匯出 | src/composables/useSspDocumentExport.js(docx/pdf/odt 選格式+downloadBlob) |
第 2 棒擴成三選一 |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
填寫頁 src/views/project/RoundApAuthoringView.vue(995 行,已超標) |
以行程為單位:TaskRow(:34-44)、方法 MultiSelect(:719)+每方法指引 Textarea(:727)、控制項 MultiSelect 只攤到控制項(:142-145)、受評對象分組 MultiSelect 混人員/元件/資產(:146-160、:743)、參與人員 MultiSelect(:754);單一存檔 PUT /ap/:apUid/tasks(:566) |
整頁換掉:新開 RoundApPlanView.vue(殼)+ap-plan/SessionTab.vue、ObjectiveTab.vue(左樹右表單)、ObjectiveControlForm.vue、ApImportPreviewBanner.vue、composable useApPlan.js、useApObjectiveTree.js。舊頁留到新頁上線後再刪 |
路由 src/config/router/index.js:1118 project-ap-authoring-round |
指向舊頁 | 改指新頁 |
API 常數 src/config/api/api.js:212-221、:252-253 |
AUDIT_ROUND、AP_AUTHORING、AP_DOCX_IMPORT | 加 terms、objectives、export、xlsx-imports |
總覽頁 ProjectAuditorOverview.vue:1150(2128 行) |
按鈕導到 ap-authoring | FR-140 卡 10 加稽核次切換;本案頁首麵包屑讀同一組資料 |
i18n src/config/locales/i18n/zh-tw/ap-authoring.json |
94 行 | 新開 ap-plan.json |
🔴 三個靜默落差
control_objective_selections 欄位存在但從未寫入——系統其他部分(規劃任務卡、AR 判定)早已逐 AO,只有 AP 停在控制項層,AR 反推時再用控制項聚合。本案是把 AP 拉齊,不是另起一套。set_tasks 先刪後寫、task uuid 每次重產:任何下游拿 task uuid 當鍵都會斷。第 2 棒改成「一份 AP 一個 task」時要查 wecm 與 AR 有沒有引用 task uuid(探脈時未見,runner 開工 grep 一次)。| 畫面欄位 | OSCAL 路徑 | 套件表/欄 | 本案動作 |
|---|---|---|---|
| 依據、目的、稽核範圍、稽核方法論 | assessment-plan.terms-and-conditions.parts[](name=basis/objective/scope/methodology,prose) |
新表 oscal.ap_terms_and_conditions |
第 2 棒 |
| 稽核單位 | metadata.parties[type=organization]+responsible-parties[role=assessor] |
oscal.parties(既有) |
第 2 棒 |
| 稽核人員(計畫概要) | metadata.parties[type=person],props responsible-role=assessor |
oscal.parties(既有做法) |
不改 |
| 稽核日期起迄 | tasks[0].timing.within-date-range{start,end} |
ap_tasks.timing JSONB |
第 2 棒:一份 AP 固定一個 task |
| 第 N 次 內部/外部 | 稽核次表(FR-140);OSCAL 匯出時 props audit_nature、nature_seq(FR-140 卡 10) |
compliance.audit_round_passes |
讀,不寫 |
| 每條 AO 的查核方法與指引 | activity.steps[](title=指引,props method) |
ap_assessment_activities.steps(既有結構) |
第 3 棒:一條 AO 一個 activity |
| AO 本身 | activity.related-controls.control-objective-selections[].include-objectives[].objective-id=part_id |
ap_assessment_activities.related_controls |
第 3 棒 |
| 每條 AO 的受評人員、元件/資產 | tasks[0].associated-activities[activity-uuid].subjects[](type=party/component/inventory-item) |
ap_tasks.associated_activities JSONB |
第 3 棒 |
| 每條 AO 的稽核人員 | activity.responsible-roles[role=assessor].party-uuids |
ap_assessment_activities.responsible_roles |
第 3 棒;空=沿用計畫概要(D2) |
| 控制項層預設 | 不進 OSCAL;只是畫面與 Excel 的填寫輔助,存檔時展開到每條 AO | — | 第 3 棒 FE 邏輯 |
oscal.ap_terms_and_conditions| 欄 | 型別 | 說明 |
|---|---|---|
id |
bigserial PK | |
assessment_plan_id |
FK → assessment_plans.id |
與 AP 1:N(一段一列) |
part_name |
varchar | basis/objective/scope/methodology;值集合進 system_menus group ap_terms_part 供畫面標籤與順序 |
title |
text | 可空 |
prose |
text | 內容 |
sort_order |
int | |
props、links |
jsonb | 共用欄 |
| 審計欄 | 同其他 oscal 表 |
migration 進套件 jedi-oscal-v2/migrations/,主專案不另出。GRANT 給 cm_app。
「計畫概要」頁:直向「欄位/內容」兩欄,列:依據、目的、稽核單位(只填公司名稱)、稽核日期起、稽核日期迄、稽核範圍、稽核方法論、稽核人員(換行或分號隔開,匯入比對人名)。隱藏列放 AP uid 與匯出時間戳當版本比對用。
「評估目標」頁:
| 欄 | 內容 | 匯入 |
|---|---|---|
| A(隱藏) | AO part_id | 比對鍵;找不到→預覽標「找不到 AO」略過 |
| B 序號、C 控制項代碼、D 實務要求、E 執行說明、F 評估目標 | 灰底唯讀 | 忽略 |
| G 查核方法與指引 | 一行一組「方法:指引」 | 依換行切組、依第一個全形或半形冒號切方法與指引;沒有冒號的行當指引、方法空白;方法詞不在選單→D4 就地對應 |
| H 受評人員 | 換行或分號隔開 | 名字比對 SSP parties;對不到→預覽就地對應 |
| I 受評元件/資產 | 同上 | 比對 SSP components+inventory-items |
| J 稽核人員 | 同上;空白=沿用概要 | 與概要名單相同=不算覆寫 |
一條 AO 一列,表頭凍結,同控制項的 D/E 只在第一列寫。
「填寫說明」頁:每一欄怎麼填、格內換行快捷鍵(Mac:Option+Enter;Windows:Alt+Enter)、匯入流程。
「可選清單」頁(看得到):查核方法、受評人員、受評元件/資產、稽核人員四欄,匯出時由 SSP 與 system_menus 帶入,讓使用者複製正確寫法。
| 選項 | 內容 | 實作 |
|---|---|---|
| 報告稿 docx/pdf | 1~6 段照亞航排版(標題帶「第 N 次 內部/外部」)、第 7 段「稽核結果」佔位「(評估結果完成後帶入)」、附件 AO 表(方法與指引合回「方法:指引」一行一組;P2 決定是否印受評對象與稽核人員) | docxtpl 範本+LibreOffice 轉 pdf |
| Excel 完整資料 | 上面兩頁檔,匯出即可改完匯回;空 AP 匯出就是空白範本 | ExcelTemplateGenerator |
| 打包 zip | docx+xlsx | 兩者串起來 |
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
participant U as 顧問
participant FE as 稽核計畫頁
participant BE as AP 服務
participant PK as jedi-oscal-v2
U->>FE: 計畫概要頁籤填六段+日期起迄+稽核人員
FE->>BE: PUT /ap/{uid}/terms
BE->>PK: 寫 ap_terms_and_conditions、organization party、tasks[0].timing
U->>FE: 評估目標頁籤:控制項預設→套用→改例外
FE->>BE: PUT /ap/{uid}/objectives(展開後逐 AO)
BE->>PK: 每 AO 一個 activity(steps、objective-selections、responsible-roles)+ tasks[0].associated-activities.subjects
U->>FE: 匯出 → 報告稿 pdf
FE->>BE: GET /ap/{uid}/export?format=pdf
BE->>BE: 讀同一份內部模型 → docxtpl → LibreOffice
BE-->>U: 下載
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
participant U as 顧問
participant FE as 稽核計畫頁(預覽模式)
participant BE as AP 工作簿服務
U->>FE: 上傳兩頁 Excel
FE->>BE: POST /ap/{uid}/xlsx-imports/parse
BE->>BE: 串流讀、列數上限、第一頁→計畫概要 diff、第二頁→依 part_id 比對、方法詞比對選單
BE-->>FE: parse_uid+diff(變更/找不到 AO/未知方法)
FE->>U: 左樹標徽章、右側高亮、未知方法就地對應
U->>FE: 處理完待辦 → 確認
FE->>BE: POST /ap/{uid}/xlsx-imports/{parse_uid}/confirm(帶對應表)
BE->>BE: 寫 terms+objectives(同存檔路徑)
BE-->>FE: 新狀態
第 1 棒已建卡(CM-2800)。第 2、3 棒在 design.md 定案後一次建完。排序以「決策者何時看得到畫面」為準。
| 棒 | 卡 | 做什麼 | 碰哪裡 | 套件 | 依賴 | 驗收 |
|---|---|---|---|---|---|---|
| 1 | CM-2800 | 雛形兩輪 | prototype/ |
— | — | 決策者點過 |
| 2 | 2a | terms 表+organization party+單一 task 日期起迄;PUT …/terms;FR-043 匯入改填新欄位 |
套件 model/migration/service;主專案 ap_terms_app_service.py、route |
jedi-oscal-v2、jedi-compliance-audit | design | DEV 存六段重整還在;匯入亞航 docx 六段進新欄位、行程說明不再重複 |
| 2 | 2b | 計畫概要頁籤(新頁殼+SessionTab)+稽核次麵包屑讀 FR-140 卡 10 的 API(卡 10 未完成時先讀輪次) | FE 新檔、路由改指 | — | 2a | 畫面填存;麵包屑顯示第 N 次 |
| 2 | 2c | 報告稿 docx/pdf 匯出+Excel 第一頁匯出+zip;FE 匯出三選一 | ap_docx_generator.py、範本、ap_workbook_app_service.py(第一頁)、route、FE composable |
— | 2a | Word 開 docx 看 1~6 段;Excel 第一頁內容相同 |
| 3 | 3a | activity 改 AO 粒度:PUT …/objectives、_ap_detail 回逐 AO;AR 反推改逐 AO;assessment_subjects 聯集回填 |
套件 service;主專案 ap_objective_app_service.py;jedi-compliance-audit AR 讀取 |
jedi-oscal-v2、jedi-compliance-audit | 2a | API 存三條 AO 再讀回一致;AR 頁方法顯示不變 |
| 3 | 3b | 評估目標頁籤:左樹右表單、控制項預設套用、篩選、總覽子檢視、承襲標籤 | FE ObjectiveTab.vue 等新檔 |
— | 3a | 照雛形第二輪操作一遍 |
| 3 | 3c | Excel 第二頁匯出(查表頁、資料驗證、鎖格、隱藏 A 欄)+匯入 parse/confirm+預覽模式 FE | ap_workbook_app_service.py 第二頁、ap_xlsx_import_service.py、FE 預覽模式 |
— | 3a、3b | 匯出改兩格匯回、預覽標出、確認後畫面更新;未知方法對應 |
| 3 | 3d | AP 複製改 AO 粒度(覆核輪+FR-140 第 2 次內稽,P1 裁法) | ap_clone_service.py(抽自 clone_ap_for_round 與 FR-140 卡 3 的共用函式) |
jedi-compliance-audit | 3a、FR-140 卡 3 CM-2788 完成 | 啟動覆核輪新 AP 只剩不合格項且 AO 列的方法/人員承襲 |
| 3 | 3e | 方法選單 seed(P3)+ap_terms_part 選單 seed+i18n+舊頁下線 |
scripts/sql/*-seed-*.sql、FE i18n、路由 |
— | 3b | 後台看到新選項;舊路由不可達 |
| 像但不做 | 為什麼 |
|---|---|
| AR 畫面改逐 AO、「佐證資料」「稽核結果」欄 | 另開 FR;本案只改 AR 從 AP 讀方法那一段 |
| AP 的 OSCAL JSON 匯出 | 套件 export_oscal 不支援 AP;列為第 3 棒可選,不進驗收 |
| docx 匯入擴充(其他格式 adapter、AO 表匯入) | 新主路徑是 Excel;FR-043 只改落點 |
| 一份 AP 多個行程 | 決策者定「計畫概要就是 docx 那份」,一份 AP 一個;多次稽核由 FR-140 的多稽核次承接 |
assessment-assets、objectives-and-methods、AP back-matter |
本案用不到 |
刪 ap_task_subjects/ap_task_participants 表 |
舊資料留;下線舊頁後另開清理卡 |
| # | 坑 | 影響 | 對策 |
|---|---|---|---|
| 1 | set_* 先刪後寫、task uuid 重產 |
下游拿 task uuid 當鍵會斷 | 2a 改單一 task 前 grep wecm、AR、job 有無引用 task uuid |
| 2 | 四槽位讀取者十餘處(FR-140 盤點) | FR-140 改完前,AP 掛在輪次上;改完後掛在稽核次 | 本案所有讀 AP 的新碼一律經 FR-140 卡 1 的 pass domain service 取「目前稽核次」,不直接讀輪次 |
| 3 | 覆核輪 clone 靠「同名」重對應 party(assessment_plan_app_service.py:348) |
同名稽核人員會對錯 | 3d 改以 party uuid 對應,同名時才退回名字 |
| 4 | ProjectAuditorOverview.vue 2128 行、RoundApAuthoringView.vue 995 行皆超標 |
不能往裡加 | 新頁全新檔;總覽頁只改一行導向 |
| 5 | Excel 下拉的查表頁選項是匯出當下快照 | 匯出後 SSP 加了人員,舊檔選不到 | 匯入時人名比對不到→預覽標出讓使用者對應(同 D4 機制) |
| 6 | DEV 方法選單與雛形不一致(P3) | 匯入亞航檔「現場觀察」會變未知 | seed migration 補選項 |
| 7 | 控制項無 AO 時 key 退回 control_id | Excel 第二頁該控制項只有一列、part_id=control_id | 匯出匯入都要處理這個退路(沿用 ao_derivation.py) |
| 8 | 同一支 audit_round_app_service.py 周邊,FR-140 卡 3/4/11 與本案 3d 都要改 |
平行派會互撞 | 3d 排在 CM-2788 之後,母卡標序列組 |