設計定案見 design.md。
狀態:討論稿(決策已裁,等決策者看雛形拍板) | 日期:2026-10-12 | 卡片:Notion CM-2717(母卡 CM-2714) | 雛形:prototype/index.html
路徑慣例同探脈檔:BE 路徑相對 compliance-manager-be/;pkg/ 代表 ~/Projects/Jedicogy/module/jedi-python-package/;FE 路徑相對 compliance-manager-fe/。
| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-12 | v1:已裁 D1~D13 展開、補裁 D14~D18;資料模型草案、端到端流程、五卡拆卡草案、三頁雛形;畫面放法(週期用 Dialog、通知紀錄放專案內) | CM-2717 |
整案一句話:專案管理者在規劃頁替任務設週期;到期時系統生一筆新任務(第 N 期)並通知執行人,掃描類任務直接自動開掃;SBOM 掃完若比上一次多出 high 以上的弱點,就寄信給額外指定的人(例如 CRA 的通報聯絡人),每封都留紀錄可查。
給非工程讀者的背景:稽核裡很多工作不是做一次就好——每月掃一次源碼弱點、每季填一次問卷、每年檢視一次程序書。現在系統的任務做完就結束,下一期要人記得重建。本案讓任務自己「到期長出下一期」;掃描類更進一步不用人按;而 CRA 要求製造商知悉高風險漏洞要及時處理,所以掃到「新出現」的高風險時多通知一個負責對外通報的人。
| 卡 | 做什麼 | 產出 | 決策者檢查法 |
|---|---|---|---|
| ① 週期設定畫面 | 規劃頁任務表單多一行週期摘要+「設定」開週期 Dialog;任務頁籤有週期的加 ↻;頻率選項進 system_menus |
migration(任務新欄、頻率 seed)、BE 讀寫、FE 週期 Dialog 子元件、FE 四處寫死頻率改讀 menu | 對照 雛形頁 1:Dialog 設好存檔、重開還在,表單其餘欄位不變 |
| ② 到期生新任務+通知執行人 | 每小時排程查到期任務,生第 N+1 期(複製指派、部門、設備、檢測綁定、問卷空白快照),寄信給執行人 | 排程 job、單筆任務複製 service、任務抽屜標題期數+檢測設定卡三行 | 對照 雛形頁 2:把下次到期改成過去時間,一小時內看到第 N+1 期且收到信 |
| ③ 掃描自動執行 | 自動執行開著的掃描類任務,新一期生出來直接開掃(檢測工具派 agent、SBOM 沿用上次源碼包) | 新任務轉執行中並觸發既有派掃掛鉤 | 抽屜執行紀錄出現「第 N 期 自動執行」那筆 |
| ④ 門檻通知+紀錄 | SBOM 掃完與上一次比,新出現 high 以上就寄給額外通知對象,每封記一筆 | 通知紀錄表、掃完匯合點加判斷、專案內「通知紀錄」頁(入口在專案概覽頁頂端) | 對照 雛形頁 3:同一份源碼包重掃不寄、換一包多了新 high 會寄 |
| ⑤ 支援期提醒接入 | 資訊系統版本支援期到期前 90/30/7 天提醒,走同一支排程與紀錄表 | 排程內加一段、紀錄表加一種對象 | 建一筆 30 天後到期的版本,隔天收到一封、不會重複 |
依賴:①→②→③ 依序;④ 只依賴 ①(額外通知對象欄位),可與 ②③ 平行;⑤ 依賴 ② 的排程與 ④ 的紀錄表。
%%{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
C1[① 週期設定畫面] --> C2[② 到期生新任務<br/>+通知執行人]
C2 --> C3[③ 掃描自動執行]
C1 --> C4[④ 門檻通知+紀錄]
C2 --> C5[⑤ 支援期提醒]
C4 --> C5
細節與完整行號在 探脈盤點 各節,這裡只列本案會碰的接入點。
| 接入點 | 現況 | 本案動作 |
|---|---|---|
| 任務週期欄位 | job_executions 沒有到期日、週期、下次執行(pkg/jedi-flow-engine/.../job_execution.py:15-41) |
加欄(見資料模型) |
| 任務屬於哪一輪 | 在 workflow_execution_control_mapping.round_id(prep_job_generation_service.py:257-260) |
新一期掛同一輪(D2) |
| 複製單筆任務 | 沒有;覆核輪是整輪重生+逐對承襲(reverify_inheritance_service.py:34、reverify_clone_query.py:69-143) |
新寫單筆複製,承襲子方法的寫法當參考,不綁母輪語意 |
| 建 job | create_job(flow_control_job_repo_impl.py:639) |
複製時沿用 |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
on_tasks_activated 掛鉤 |
唯一有呼叫的掛鉤(task_execution_service.py:94) |
③ 沿用:新一期轉執行中後觸發它 |
| 完成/到期掛鉤 | 沒有 | 不加(D4,BE 排程直查) |
system_menus TASK_TYPE |
五個 key 與申報三型別對不上 | 只記錄、本案不處理(D12) |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
| APScheduler(api 模式) | 12 支 job、寫法統一(core/scheduler.py) |
加一支每小時「到期掃描」 |
| 多副本防重 | 無分散式鎖,靠冪等 | 唯一鍵擋(D11) |
background_jobs |
入列需租戶身分;排程裡包 tenant_context(core/plugins/_source_scan_vulndb_fetch.py:127-143 先例) |
SBOM 自動掃沿用既有入列 |
| 營運時區 | BUSINESS_TZ 寫死 Asia/Taipei(common/util/common_util.py:36) |
沿用(D10) |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
send_mail_notification |
背景可用、不需登入者;SMTP 未啟用回 False(notification_service.py:75-84) |
沿用;回 False 記成「寄送失敗」 |
| 新任務通知信 | notify_user_todo_job 寄指派人(workflow_execution_service.py:1240-1300) |
② 沿用 |
| 寄信紀錄表 | BE 與 jedi-notification 都沒有 | 新建,放 BE(D9) |
| 信件語系 | 排程裡固定 zh_Hant_TW | 沿用(D10) |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
| 首次派掃 | _auto_dispatch_first_scans 只派「沒有執行紀錄」的任務(app/detection_tools/task_type_declaration.py:43,79) |
新一期天生沒有執行紀錄,觸發掛鉤即會派——不用改它 |
| 檢測工具排未來時間 | agent_tasks.scheduled_at 拉單過濾(agent_task_repo_impl.py:18-33) |
沿用(D6) |
| SBOM 源碼來源 | 不帶新檔即沿用綁定上次那份(pkg/jedi-detection/.../server_side_dispatch.py:118-137),背景以 input_file_id 重掃(source_scan_job_steps.py:102) |
新一期複製綁定時連 tool_params 一起帶,沿用即成立 |
| 掃完匯合點 | _close_group_if_terminal(detection_orchestration_service.py:1626-1675),不看嚴重度、無可外掛事件 |
④ 門檻判斷改由 jedi-source-scan SBOM 完成回呼觸發(D14) |
| 與上一次比對 | SBOM 完成時已算 summary.delta,配對鍵 fingerprint(pkg/jedi-source-scan/.../source_scan_diff_service.py:71-83、source_scan_job_steps.py:113,123-130) |
④ 沿用 diff() 取「新出現」再濾等級 |
| SBOM 嚴重度 | source_scan_findings.severity 六級大寫 |
門檻=CRITICAL、HIGH |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
module_frames.frequency |
未刪除列皆小寫標準值+none,無人讀 |
建專案時帶入任務預設(D3) |
| 頻率選項 | FE 四處寫死、system_menus 無 group |
進 system_menus,四處改讀(D12) |
last_execute_date/next_execute_date |
全 NULL、無寫入者 | 棄用不啟用(D12) |
| 接入點 | 現況 | 本案動作 |
|---|---|---|
| 問卷快照 | 規劃頁路徑深度複製(survey_snapshot_domain_service.py:36-71) |
新一期建全新空白快照(D13) |
| 填完自動完成任務 | 沒有 | 本案不處理(D13) |
compliance.job_executions 新欄| 欄 | 型別 | 用途 |
|---|---|---|
recur_unit |
varchar(32) 可空 | 週期單位,值=頻率 menu 的 key(daily/weekly/monthly/quarterly/semi_annually/annually);空=不重複 |
recur_interval |
int 預設 1 | 每幾個單位一次(每 2 個月=monthly+2) |
next_due_at |
timestamptz 可空 | 下一期該生出來的時間;排程只看這欄 |
period_no |
int 預設 1 | 第幾期 |
source_job_id |
bigint 可空 | 從哪一筆生出來(第 1 期為空) |
auto_execute |
boolean 預設 false | 新一期生出來是否直接開掃;只有掃描類可設 |
alert_recipients |
jsonb 可空 | 掃到新出現 high 以上時額外通知誰:[{type: "participant", user_id}] 或 [{type: "party_role", role_id: "vulnerability-contact"}];只有掃描類可設 |
唯一鍵 UNIQUE (source_job_id, period_no):同一筆源任務的第 N 期只會有一筆(D11)。
next_due_at 只寫在「目前最新一期」上:生出第 N+1 期時,把週期設定整組搬到新任務、舊任務的 next_due_at 清空。這樣排程永遠只撈到每條週期鏈的最後一筆,不會同一期生兩次。
compliance.task_alert_notifications(通知紀錄)| 欄 | 用途 |
|---|---|
id/uid/tenant_id/org_unit_id |
標準欄;RLS 依租戶 |
job_execution_id |
哪一筆任務(第幾期) |
scan_uid |
哪一次掃描(source_scans.uid) |
vs_scan_uid |
跟哪一次比(首掃為空) |
new_critical_count/new_high_count |
新出現的 critical、high 筆數 |
recipient_email |
收件信箱(一人一列) |
recipient_label |
顯示用:參與者暱稱或 Party 名稱 |
sent_at |
寄出時間 |
result |
sent/failed(SMTP 未啟用或寄送失敗) |
唯一鍵 UNIQUE (scan_uid, recipient_email):同一次掃描同一人只寄一封(D11)。排程先 INSERT 搶到列才寄,搶不到跳過——FR-135 支援期提醒草案的同一招。
public.system_menus 頻率 seed主線 phase=seed migration,ON CONFLICT DO NOTHING,先例 scripts/sql/2026-05-22-seed-oscal-ssp-roles.sql。group 名 task_frequency(D18),六個 key:daily/weekly/monthly/quarterly/semi_annually/annually,i18n 標籤走 lang.task_frequency.<key>。資源庫的 none 不進 menu:任務上「不重複」用週期開關表示,資源庫 none 帶入時就是開關關閉。
FE 四處寫死改讀 menu:src/stores/menuStore.js:168-174、src/components/grc/ModuleFrameBasicInfoPanel.vue:171-176、src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue:1133-1138、src/components/grc/ssp-excel-import/SheetPreviewBasic.vue:53-58。
%%{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 TD
MF[資源庫 frequency] -.建專案時帶預設.-> P[規劃頁任務週期設定<br/>頻率・間隔・下次到期・自動執行・額外通知]
P --> S[開始執行任務]
S --> T{{每小時 tick<br/>撈 next_due_at 已到的任務}}
T -.-> N[生第 N+1 期新任務<br/>同一輪・複製指派與綁定・問卷空白快照]
N --> K{掃描類且<br/>自動執行開著?}
K -- 否 --> M[寄信通知執行人]
K -- 是 --> A[轉執行中+觸發既有派掃掛鉤]
A --> D1[檢測工具:派 agent]
A --> D2[SBOM:沿用上次源碼包排背景掃]
D1 --> C[掃完匯合<br/>_close_group_if_terminal]
D2 --> C
C -.SBOM.-> F{與上一次比<br/>有新出現 high 以上?}
F -- 否 --> X[不寄・不記]
F -- 是 --> R[寄給額外通知對象<br/>+寫通知紀錄]
排程每小時跑一次的步驟:
next_due_at <= now() 的任務(跨租戶,system_context+每租戶 tenant_context)。INSERT ... (source_job_id, period_no) 建新任務,唯一鍵撞到就跳過(另一個副本已建)。next_due_at;舊任務的 next_due_at 清空。notify_user_todo_job 寄執行人;自動執行 → 轉執行中並呼叫 _run_activation_hooks,由既有 _auto_dispatch_first_scans 派掃。現行規劃頁「控制項實作」頁籤:左邊三層樹(群組 → 控制項 → 檢查項目);選到檢查項目時,右邊由上往下是檢查項目標頭、參考條文、證據來源、檢查項目現況說明、任務區塊(ProjectPlanningView.vue)。任務區塊在 2 筆以上時,上面是頁籤列、下面是單一表單(AoTaskSection.vue),表單欄位依序為名稱、描述、指南、任務類型、指派人員、部門、設備、問卷、檢測工具+Agent 分派、完成模式、刪除/儲存(AoTaskForm.vue)。檢測工具任務展開 Agent 分派後,表單已超過一個螢幕。
週期設定用「表單一行摘要+Dialog」,不攤進表單(提案,等決策者看雛形確認)
↻ 每月 · 第 2 期 · 下次到期 2026-11-01 · 自動執行 · 高風險額外通知 2 人 [設定];沒設週期時顯示「不重複 [+ 設定週期]」。理由:週期設定一次很少改,各期列表是執行資訊、不是規劃欄位;攤進表單會讓已經很長的表單再長一截,各期列表還會被擠到最底下。Dialog 跟頁首「快速配置」的做法一致。檢查項目內容與任務表單其餘欄位都不動。
被排除:表單內整塊展開(太長);另開一欄任務清單(現行沒有這一欄,要重排整頁)。
通知紀錄放在專案裡:專案概覽頁頂端那排按鈕(開始執行任務、專案規劃、稽核計畫等,ProjectAuditorOverview.vue 頁首)多一顆「通知紀錄」,點進去是專案內頁、只列本專案;頁首規格同規劃頁,有返回鍵。
✅ D1 — 重開語意:每期生一筆新任務
定案:週期到了生一筆新任務,每期一筆、歷史各自獨立;不在同一筆退回重做。
理由:每期的證據、執行紀錄、問卷答案要分開查;退回路徑會把歷次結果疊在同一列。
✅ D2 — 新任務屬同一輪
定案:新任務掛在源任務的同一輪;任務多「期數」與「源自哪筆」兩欄,不另立「週期輪」概念。
理由:輪次是稽核範圍的單位,週期是任務層的節奏;混在一起會跟覆核輪的母輪關係打架。
✅ D3 — 週期設定存在任務層
定案:任務層新欄位(週期單位、間隔、下次到期、自動執行開關);資源庫 module_frames.frequency 只在建專案時帶入預設,不影響既有專案。
理由:一個評估目標底下會有多筆任務、各自頻率不同;config_schema 只到租戶層且平台不讀。
✅ D4 — BE 排程直查,不動申報表
定案:排程每小時 tick 一支「到期掃描」直接查任務表;不在 jedi-task-platform 加到期/完成掛鉤。
理由:週期是產品業務規則,比照檢測派掃從平台搬出的原則(app/detection_tools/task_type_declaration.py:47-51);也免一次套件異動。
⛔ D5 — 作廢
SBOM 重新執行本來就沿用上次上傳的源碼包(見探脈盤點 ⑤ 節),週期自動掃走同一條路:到期 → 建新任務 → 依型別自動派掃或通知人。不需另外決定源碼來源。
✅ D6 — 兩種掃描同一套排程機制
定案:到期判斷都在同一支每小時 tick;派掃時檢測工具沿用 agent_tasks.scheduled_at、SBOM 沿用 background_jobs 排單。
理由:兩條派掃路都已存在且可靠,排程只負責「何時生新任務」,不重做派工。
✅ D7 — 門檻等級第一版只做 SBOM
定案:門檻用 SBOM 標準六級,high(含)以上=CRITICAL、HIGH;檢測工具結果沒有統一等級,第一版不做。
理由:檢測工具單次結果只有各工具不同鍵的 summary,要先正規化才談得上門檻。
✅ D8 — 只在「新出現」high 以上時發(決策者裁)
定案:與上一次掃描比 fingerprint 差集,差集裡有 high 以上才通知。
理由:同一批老弱點每期都寄等於沒寄,收件人很快就會無視;新出現的才是要動作的。SBOM 完成時已算過與上一次的差異(summary.delta),配對鍵 fingerprint 不含版本,升版仍受同一弱點影響不算新的。
✅ D9 — 通知紀錄第一版只記門檻通知
定案:紀錄表只記門檻通知(掃描 uid、等級、收件人、寄出時間、結果),放 BE 主專案,不動 jedi-notification。
理由:決策者要的是「哪次掃描、什麼等級、何時通知誰」可查;指派/完成信納入要改所有既有寄信點,不在本案。
✅ D10 — 語系時區第一版寫死
定案:信件 zh_Hant_TW、時區 Asia/Taipei,沿用現況。
理由:使用者與租戶都沒有語系/時區欄位,補上是獨立工程。
✅ D11 — 冪等靠唯一鍵
定案:新任務唯一鍵「源任務+期數」;門檻通知唯一鍵「掃描 uid+收件人」;第一版承諾單副本,多副本靠唯一鍵擋重複。
理由:現有 12 支排程 job 都靠冪等,沒有分散式鎖;唯一鍵是最小的保險。
✅ D12 — 頻率進 system_menus,閒置欄棄用
定案:頻率值進 system_menus(六個 key),FE 四處寫死改讀 menu;module_frames.last_execute_date/next_execute_date 棄用不啟用;TASK_TYPE 五 key 與申報三型別對不上一事只記錄,本案不處理。
理由:下拉選項不寫死是專案鐵則;閒置欄放在資源庫範本上,語意是「範本的」執行日,跟任務層週期是兩回事。
✅ D13 — 問卷週期重開空白重填(決策者裁)
定案:新一期問卷建全新空白快照;「填完問卷不自動完成任務」本案不處理。
理由:每期的回覆要反映當期狀況,帶上期答案容易被直接送出。
✅ 通知對象
定案:預設=任務執行人員;掃描類多一個「掃到新出現 high(含)以上時額外通知誰」,可選專案參與者或 Party 角色(CRA 填通報聯絡人 vulnerability-contact,FR-135 已預建於 cra_2024.py:136-138)。
理由:執行人是做事的人;對外通報的責任人常常不是執行人。
✅ D14 — 動兩支套件
定案:jedi-flow-engine 的 job_executions 照 D3 直接加欄;SBOM 門檻判斷的觸發點在 jedi-source-scan 的 SBOM 完成轉回處(DetectionResultForwarder,pkg/jedi-source-scan/.../sbom_scan_job.py:74-79)加一個「掃完」回呼,由 BE 註冊。開發期走 path dependency,發版等決策者明示。
理由:週期是任務本身的資料,放套件不另開 sidecar 表;回呼只影響 SBOM,與 D7 一致,範圍比改 jedi-detection 的匯合點 _close_group_if_terminal 小。
被排除:BE 另開一對一任務週期表(sidecar);改 jedi-detection 匯合點(會牽動檢測工具全部收口邏輯)。
✅ D15 — 支援期提醒併入通知紀錄表
定案:通知紀錄表第一版欄位照門檻通知設計;卡 ⑤ 開工時加 object_type/stage 兩欄,把支援期 90/30/7 天提醒記進同一張表,唯一鍵同步擴成含這兩欄。
理由:「寄過了沒」與「何時寄給誰」只有一處可查;不在版本列上另掛戳記欄。
被排除:版本列單欄戳記(三個階段要三欄,且查不到寄給誰)。
✅ D16 — 通報三階段期限提醒不做
定案:卡 ⑤ 只做支援期提醒;24 小時預警/72 小時通報/最終報告提醒不做。
理由:起算點是通報案件的「知悉時間」,通報案件頁籤已裁不做,系統裡沒有這筆資料。日後恢復通報案件時再接同一套排程與紀錄表。
✅ D17 — 首掃的 high 以上全算新出現
定案:沒有上一次可比的掃描(summary.delta 為空),high 以上全部當新出現、照寄。
理由:第一次掃描就是第一次知道它們存在。
✅ D18 — 頻率 menu group 名 task_frequency
定案:system_menus group=task_frequency,任務與資源庫共用。
理由:與近期新增的小寫 group(ssp_party_role、risk_reference_standard)一致。
另兩點照常理處理:上一期還沒做完,下一期照樣到期就生(D1 各期獨立);週期開關關掉時清空 next_due_at,已生出的各期不動。
| 順序 | 卡 | 規模 | 主要檔案 | 前置 |
|---|---|---|---|---|
| 1 | ① 週期設定畫面 | M | migration(任務新欄+頻率 seed)、jedi-flow-engine ORM、規劃頁任務讀寫 service、FE 週期 Dialog 新子元件,AoTaskForm.vue(733 行)只加一行摘要+開 Dialog、AoTaskSection.vue 頁籤加 ↻、FE 四處頻率改讀 menu |
D14 |
| 2 | ② 到期生新任務+通知執行人 | M | core/scheduler.py 加一支、單筆任務複製 service(新檔)、任務抽屜:JobDrawerHeader.vue 加期數,JobExecutionDrawer.vue 檢測設定卡加三行(該檔超標,三行抽成新子元件) |
① |
| 3 | ③ 掃描自動執行 | S | 新任務轉執行中+呼叫 _run_activation_hooks;確認檢測綁定複製帶 tool_params |
② |
| 4 | ④ 門檻通知+紀錄 | M | 通知紀錄表 migration、jedi-source-scan SBOM 完成回呼+BE 註冊門檻判斷(D14)、專案內「通知紀錄」頁+專案概覽頁頂端入口按鈕(ProjectAuditorOverview.vue 頁首那排) |
① |
| 5 | ⑤ 支援期提醒接入 | S | 排程加一段、收件人反查(資訊系統 → SSP 盤點 → CRA 專案 manager,FR-135 草案已列路徑)、紀錄表加 object_type/stage(D15) |
②、④ |
e2e 屬母卡收口,不在單卡做。
system_menus TASK_TYPE 與申報型別對齊(D12)。