我的任務(My Tasks,受稽方執行)¶
功能群:稽核執行|輪次狀態機與 AP→AR→POA&M 資料模型先讀 功能群總覽
事實基準:2026-07-05 從 FE / BE code 掃出(FR-038 v2 + 證據蒐集任務執行);API schema 逐條對照
api/grc/serializers/job.py、api/grc/serializers/job_batch_complete.py與app/participant/service/task_assignee_service.py
變更紀錄¶
| 日期 | FR | 變更 |
|---|---|---|
| 2026-08-29 | CM-1425 | 失敗執行紀錄可從 UI 刪除 + 卡住的執行自動收斂:①執行紀錄失敗列出現刪除鈕(確認框),整組全失敗時卡片層另有「刪除整組」;只准刪 failed——succeeded 掛著報告與證據(刪了證據鏈斷)、running/scheduled 要先取消、cancelled 是有意義的操作軌跡,成功項不出鈕且直打 API 回 409016;刪除寫稽核事件(6120/6121)。刪單筆不重算群組彙總狀態(刻意:刪掉一筆失敗不代表這次執行變成功,重算會把「四台掛三台」改寫成部分失敗)。②背景排程每 15 分鐘檢查,running/scheduled 超過 DETECTION_EXECUTION_TIMEOUT_HOURS(預設 4 小時)且 agent 無回報 → 自動判 failed(訊息註明逾時無回報)並重算 group,收斂後即可被①刪除。詳見 §11.2 |
| 2026-08-26 | FR-067(驗收回饋,v1.16.0 後續補項,歸下版) | 執行抽屜三項顯示修正:①執行紀錄的掃描目標改印使用者原始寫法+BE 算好的真實台數(192.168.50.1-5(5 台)而非 5 個 IP 並排;來源=派工當下快照 _scan_target_spec,透傳欄位無快照就地算,存量舊紀錄不重跑也正確),列上講真正被掃的欄位(有 targets 講它——nmap 的 hosts 是跳板機不是掃描目標,判準看資料不看工具清單)、詳情長 label 改短標籤+ⓘ tooltip;②「檢測設定」卡多組時改逐組分塊(跨列「/」併陳在 SonarQube 混模式講不出哪列是哪列;單組版型不變),上傳槽與擋門訊息改標「第 N 組(辨識參數)」(組號用全域序號);③單組重跑前置放寬——群組還在跑也可重跑失敗組(只看該組自己最新一筆是否 failed/cancelled,409012 退役無 raiser);未掃到警示字配色改 theme-aware token(亮色主題白底可讀)。詳見 §11.1(CM-1395/1396/1397) |
| 2026-08-25 | FR-067.8 | 源碼包上傳改成「每個 upload 列一個檔案槽」:任務設定改每列自足後(見任務配置),同一任務可有兩個 upload 列各掃各的包(前後端各一包是典型用法),單槽「一個任務一包」表達不了。抽屜上傳區改逐 upload 列各一槽(非 upload 列不出現)、各列各自 sticky、擋門逐列且 tooltip 指出是哪一列缺檔、上傳中狀態 per 列。execute 改送 source_files({分派列: {uid}}),只帶這次剛換過檔的列。另抽屜四處原本讀任務層掃描參數(上傳模式判定、檢測設定顯示、執行主機/掃描目標清單)改讀各分派列跨列彙整(值不同併陳、主機清單取聯集),零列時退回任務層走存量相容——不改的話新任務那幾處會全部空白且不報錯,看起來像「這個任務沒設定」。詳見 §11.1 |
| 2026-08-25 | FR-067 | 檢測執行紀錄群組化(多 Agent 分派):執行紀錄 API 改回兩層形狀(群組 → 各台執行紀錄),抽屜以 group 卡片呈現——彙總徽章(succeeded 綠/partial_failed 黃沿 CM-952 語彙/failed 紅/running 動畫/「⏱ 排程中」中性色)、每 assignment 一列(agent 名稱/目標/狀態/報告)、失敗列「重跑」鈕、running 列「取消」鈕、卡片層「整組取消」、排程中的組「立即開始」鈕、重跑歷史收合;legacy 舊資料單筆自成一組(is_legacy,形狀一致不分叉);既有 job 層取消端點改委派整組取消(best-effort,全部不可達才 409);彙總通知改收口時一封(每台一列,取代每筆一封);掃描失敗訊息帶出 stderr(agent 0.2.29,原本冒號後一片空白)。詳見 §11.1「執行紀錄群組化」 |
| 2026-08-01 | FR-058.7 | SonarQube 上傳模式的執行時上傳:scan_mode=upload 的檢測任務,抽屜執行區出現源碼壓縮包上傳區(.zip/.tar/.tar.gz/.tgz、預設 100MB 上限)——首次執行必須上傳(無檔不給按執行)、已上傳檔案 sticky 保留供重掃沿用(顯示「目前檔案:xxx.zip」,直接按執行=掃同一包,換版再上傳替換、舊檔保留);execute 請求只送 file uid,檔名等 metadata 由 BE 從 upload_files 權威取回;執行紀錄 scan_params 快照含 source_file: {uid, file_name} 顯示檔名非裸 UUID。檢測掃描完成通知加厚(D34):內容擴為九項(專案/控制項群組/控制項/AO/任務/工具/掃描設定/時間/狀態)+報告檔名,失敗也通知(含錯誤訊息、收件人與成功相同),管道補齊 Email/Discord/Telegram 三管(見 §11.1) |
| 2026-08-01 | FR-059 | 檢測任務抽屜的 profile 顯示接掃描設定檔庫:param_schema 宣告 options_source: "profile_library" 的欄位(CINC Auditor / GCB 的 profile)值可能是 profile:<uid> 庫內參照,抽屜另向 profile menu API 取「值→顯示名」對照,執行紀錄與任務資訊摘要卡顯示設定檔名稱而非裸參照;對照拿不到(或參照已停用不在 menu)退回顯示原值,不擋畫面(見 §11.1)。設定檔維護見 掃描設定檔管理 |
| 2026-07-31 | FR-058 | 執行紀錄新增取消執行中掃描的獨立入口(POST /detection-tools/jobs/{job_uid}/cancel,僅 running 那筆顯示;按鈕刻意縮小一級並與 info icon 對調位置);任務資訊摘要卡與執行詳情視窗分開顯示「執行主機」與「掃描目標」(Nmap 兩者不同——Agent 登入執行主機再掃目標網段),標題一律沿用 BE param_schema 的 label 不在 FE 寫死 |
| 2026-07-29 | FR-057 | 執行歷史回應擴充人事時地物 + 多檔報告:新增 reports(該次執行的所有報告清單,OpenSCAP 逐台一份)、detection_tool_name、scan_params(憑證已剝除的快照)、created_user/created_user_name;report_file_uid 單值欄位保留供舊讀取端相容,FE 已改讀 reports(見 §11.1) |
| 2026-07-27 | FR-056 | 新增 detection_tool 任務類型:抽屜出現執行紀錄區塊(可收合、最新在上、running/最新失敗預設展開);重新執行依有無執行中紀錄分岔(無→直接派工,有→確認框→真中斷→才派新工單,見 §11);PDF 證據 + 發現項目統計;cancelled 執行狀態 Tag |
| 2026-07-24 | v1.10.0 | 任務清單加輪次欄(round_no/round_type/round_uid,NULL fallback 顯示第 1 輪)+ 輪次篩選(CM-846);分段按鈕視覺改 outline 統一 |
1. 功能描述¶
我的任務是受稽方執行人員的跨專案任務收件匣:列出當前使用者被指派的證據蒐集任務(prep-job,由專案規劃頁的任務指派 + 開始執行任務產生),提供上傳證據 / 填問卷 / 留言 / 完成任務的執行面。與 AP/AR/POA&M 頁不同,本頁不綁單一輪次階段,而是以任務狀態(TODO / PROCESSING / COMPLETED)為 gate,跨所有專案彙整當前使用者的待辦。
主要使用者:任務被指派人(assignee,受稽方);覆核人(reviewer / is_approver)另有退回權。
角色速覽(本頁角色是任務層,不是專案層的 manager/auditor/viewer):
| 角色 | 一句話 |
|---|---|
| assignee | 任務被指派人 — 受稽方,執行任務、上傳證據、完成 |
| reviewer | 覆核人(is_approver)— 可退回任務要求補件 |
任務的來源與文案真相在專案規劃頁的 prep-job(BPMN userTask 衍生,見 專案規劃頁);本頁是其執行下游。PM 在規劃頁「開始執行任務」把任務 TODO→PROCESSING 後,任務才出現在受稽方的收件匣可執行。
1.1 功能總覽(本頁全部功能)¶
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 任務清單(分頁 / 排序) | 跨專案彙整當前使用者被指派任務;欄:專案 / 群組·控制項 / AO / 任務名 / 類型 / 狀態 / 輪次(round_no+round_type,NULL fallback「第 1 輪」) | 左清單面板(55%) | §6.2 |
| 2 | 關鍵字搜尋 | 任務名關鍵字 | 清單工具列 | §6.2 |
| 3 | 狀態篩選 | 全部 / TODO / PROCESSING / COMPLETED(預設 PROCESSING) | 清單工具列 | §4 |
| 4 | 專案篩選 | 依專案下拉過濾 | 清單工具列 | §6.1 |
| 4a | 輪次篩選(v1.10.0) | 依 round_uid 精確過濾(CM-846,多輪次易混淆) |
清單工具列 | §6.2 |
| 5 | 批次完成 | 多選可完成任務一鍵批次完成(逐專案分送) | 清單工具列(選取後出現) | UC-MT-02 |
| 6 | 任務明細抽屜 | 點列開右抽屜(45%):描述 / 指南 / 指派 / 部門 / 設備 / 問卷 / 證據 / 連結 / 留言 | 右抽屜 JobExecutionDrawer |
UC-MT-01 |
| 7 | 上傳檔案證據 | 拖放 / 瀏覽上傳檔案(FILE 證據),可預覽 / 刪除 | 抽屜證據區 | UC-MT-01 |
| 8 | 新增 / 編輯連結證據 | 標題 + URL(LINK 證據)增改刪 | 抽屜連結區 | UC-MT-03 |
| 9 | 填問卷 | 開問卷填寫頁(survey-v2-fill,新分頁);狀態顯示(0 TODO…9 完成) | 抽屜問卷區 | §11 |
| 10 | 留言 | 任務留言增改刪(作者本人可改刪) | 抽屜留言區 | UC-MT-04 |
| 11 | 完成任務 | 就緒後完成(general 需 ≥1 證據;survey 需全問卷完成) | 抽屜底部按鈕 | UC-MT-01 |
| 12 | 退回任務 | 覆核人(reviewer)退回任務(需理由) | 抽屜底部按鈕(reviewer 模式) | UC-MT-05 |
| 13 | 檢測工具執行(FR-056) | 任務類型為 detection_tool 時:抽屜顯示「開始執行 / 重新執行」+ 執行歷史區塊(可收合、狀態/發現項目統計/報告下載)+ 任務資訊摘要卡(工具 / profile / 完成模式 / 執行主機與掃描目標分開列 Tag,FR-058);SonarQube scan_mode=upload 任務另有源碼壓縮包上傳區(首次必上傳、重掃可沿用,FR-058.7) |
抽屜「檢測設定」+「執行紀錄」區塊 | §11.1 |
| 14 | 取消執行中的掃描(FR-058) | 執行紀錄中 status='running' 的那筆出現「取消執行」按鈕 → 確認 → 直推 Agent 真中斷,成功才標記 cancelled |
執行紀錄列(僅 running 且 canEdit) |
§11.1 |
| 15 | 刪除失敗的執行紀錄(CM-1425) | 失敗列出現刪除鈕(確認框);整組全失敗時卡片層另有「刪除整組」。只有 failed 可刪,成功/執行中/已取消不出鈕 |
執行紀錄列 / 群組卡片 | §11.2 |
| 16 | 逾時執行自動收斂(CM-1425) | 背景排程每 15 分鐘把「卡住超過 4 小時且 agent 無回報」的執行判為 failed,收斂後即可刪除 | (背景,無 UI 入口) | §11.2 |
UC(§2)展開本頁所有具實質後端寫入邏輯的功能:單任務執行完成(含上傳 FILE 證據,1/7/11)、批次完成(5)、連結證據增改刪(8)、任務留言(10)、覆核人退回(12)。純顯示 / 導覽類(1 清單、2 搜尋、3/4 篩選)見 §5 / §6.2;填問卷(9)另有所屬頁面(開
survey-v2-fill新分頁填寫,本頁只顯示狀態並作為完成 gate,見 §11),於此只交叉連結不重複;檢測工具執行(13)另有獨立編排鏈路(非 flow-engine complete,見 §11),本頁只交叉連結不重複。
2. Use Case¶
角色速覽(本頁角色是任務層,不是專案層的 manager/auditor/viewer):
| 角色 | 一句話 |
|---|---|
| assignee | 任務被指派人 — 受稽方,執行任務、上傳證據、完成 |
| reviewer | 覆核人(is_approver)— 可退回任務要求補件 |
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-MT-01 執行並完成單一任務¶
| 項目 | 內容 |
|---|---|
| 角色 | assignee |
| 前置條件 | 任務狀態 = PROCESSING(PM 已在規劃頁「開始執行任務」);為任務指派人 |
| 產出 / 後置條件 | 證據寫 compliance.job_evidences;就緒後 complete → job_executions.status COMPLETED(走 flow-engine) |
UC-MT-02 批次完成任務¶
| 項目 | 內容 |
|---|---|
| 角色 | assignee |
| 前置條件 | 多個 PROCESSING 任務皆就緒(可完成才可勾選) |
| 產出 / 後置條件 | 逐專案分送批次完成;per-job 成功 / 失敗回報(NO_EVIDENCE / SURVEY_INCOMPLETE);統一批次通知 |
UC-MT-03 新增 / 編輯連結證據(LINK)¶
| 項目 | 內容 |
|---|---|
| 角色 | assignee |
| 前置條件 | 任務狀態 = PROCESSING 且為指派人(COMPLETED 唯讀) |
| 產出 / 後置條件 | compliance.job_evidences 新增 / 更新 / 刪除 LINK 型(標題 + URL);attachment_count 反映(general 就緒判定) |
UC-MT-04 任務留言增改刪¶
| 項目 | 內容 |
|---|---|
| 角色 | assignee(新增);留言作者本人(修改 / 刪除) |
| 前置條件 | 開任務明細抽屜留言區 |
| 產出 / 後置條件 | compliance.job_execution_comments 新增 / 更新 / 刪除;修改與刪除限作者本人 |
UC-MT-05 覆核人退回任務¶
| 項目 | 內容 |
|---|---|
| 角色 | reviewer(覆核人,isReviewer 由專案 participant role=reviewer 決定,FE 判定顯示退回鈕) |
| 前置條件 | 覆核模式;填退回理由 |
| 產出 / 後置條件 | job_executions.status 回退 PROCESSING(走 flow-engine revert,BPMN userTask 回退) |
3. 權限矩陣¶
本頁以「任務指派關係」為權限基礎(非專案層角色):
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 讀我的任務清單 | 登入即可(清單本身即過濾成當前使用者) | 以 user_id 查使用者任務佇列(僅回自己被指派的) |
app/participant/service/task_assignee_service.py::list_my_grc_jobs |
| 上傳證據 / 留言 / 完成 | 任務在 PROCESSING 且為指派人 | 走 job / flow-engine service(任務層) | app/grc/service/job_* |
| 退回任務 | isReviewer(專案 participant role=reviewer,FE 判定) |
flow-engine revert | flow-engine |
| 批次完成 | 只勾選 canComplete() 的任務 |
per-job 就緒複驗(NO_EVIDENCE / SURVEY_INCOMPLETE) | app/grc/service/job_batch_complete_service.py:30 |
本頁清單天然過濾成「當前使用者被指派」,故無專案角色守門;權限邊界在「任務是否指派給你」+「任務狀態是否 PROCESSING」。退回權(reviewer)是 FE 依專案 participant role 顯示。
4. 狀態機與前置條件¶
本頁 gate 是任務狀態(不是輪次階段):
TODO ──(PM 於規劃頁「開始執行任務」)──▶ PROCESSING ──(assignee 完成 / 批次完成)──▶ COMPLETED
▲ │
└────(reviewer 退回)────┘
| 條件 | 效果 | 出處 |
|---|---|---|
| 預設狀態篩選 = PROCESSING | 進頁先看進行中任務(TODO 尚未啟動不顯示) | MyTasksView.vue(mount 設 statusFilter) |
status === 'PROCESSING' |
可上傳 / 留言 / 完成 | FE canComplete/canEdit |
status === 'COMPLETED' |
抽屜唯讀(可看不可改) | FE |
| general 任務就緒 | attachment_count > 0(≥1 檔案 / 連結) |
FE canComplete |
| survey 任務就緒 | 所有 task_surveys.status === 9(全完成) |
FE canComplete |
輪次階段與本頁的關係:prep-job 在規劃期(planning)建立、PM「開始執行任務」啟動;受稽方在此執行。任務完成度回饋規劃頁的「證據蒐集執行進度卡」(見 專案規劃頁 §10)。
5. UI 設計¶
版面骨架與區塊職責(左清單 + 右分割抽屜;body 加 page-fullscreen 讓抽屜獨立捲動;彩色區塊可點跳對應小節錨點,API 僅標代表性):
實機截圖(STG,亮色模式,2026-07-05,帳號 blsadmin):


批次完成 dialog 截圖從缺:勾選框僅在任務已上傳至少 1 份證據(
attachment_count > 0)時才會啟用(見MyTasksView.vue::canComplete),2026-07-05 盤點 STG 目前所有任務證據數皆為 0,暫無可勾選資料;上傳證據屬寫入動作,不在本次唯讀截圖範圍內。
狀態呈現:任務狀態徽章(TODO/PROCESSING/COMPLETED);可完成任務才可勾選(否則 checkbox disabled 帶原因 tooltip);無 socket,動作後重取清單 / 明細。URL ?job=<uid> / ?execution=<uid> 可深鏈自動開任務(進頁後清 URL)。狀態篩選分段按鈕(TODO/PROCESSING/COMPLETED pills)v1.10.0 起改 outline 視覺(全站 SelectButton 統一調整)。
6. API 規格¶
Envelope:成功 {"code": 1, "data": …}、失敗 {"code": 0, "msg": "…"}。分頁列表回 PageDataDto(meta + data)。
6.1 總清單(本頁呼叫的全部 endpoint)¶
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 載入 | POST /grc/jobs/my/list |
我的任務分頁清單(filters 7 欄,見 §6.2) | §6.2 |
| 載入 | GET /grc/jobs/my/projects/menu |
專案篩選下拉 | GAI-SD-02 |
| 明細 | GET /grc/project/{project_uid}/job/{job_uid} |
任務明細(指派 / 部門 / 設備 / 問卷 / can_revert_job) | §6.3 |
| 證據 | GET /job-evidences?job_execution_uid={job_uid} |
任務證據(FILE / LINK) | GAI-SD-02 |
| 證據 | POST /job-evidences(FILE)・POST /job-evidence(LINK)・PUT/DELETE /job-evidence/{uid} |
證據增改刪 | GAI-SD-02 |
| 留言 | GET/POST /grc/job-executions/{job_uid}/comments、PUT/DELETE /grc/job/comment/{uid} |
任務留言 | GAI-SD-02 |
| 檢測 | POST /detection-tools/jobs/{job_uid}/execute |
開始 / 重新執行掃描(force=true 先中斷再重派;FR-058.7:scan_mode=upload 任務可帶 {"source_file":{"uid":"..."}} 指定本次要掃的壓縮包,不帶=沿用綁定現值) |
§11.1 |
| 檢測 | POST /file/upload(upload_type='DETECTION_SOURCE') |
(FR-058.7)上傳源碼壓縮包拿 file uid(既有上傳端點的新分類,非新端點) | §11.1 |
| 檢測 | POST /detection-tools/jobs/{job_uid}/cancel |
(FR-058)取消執行中的掃描——無 running 執行回 DETECTION_TOOLS_409005、取消失敗回 DETECTION_TOOLS_409004 |
§11.1 |
| 檢測 | GET /detection-tools/jobs/{job_uid}/executions |
執行歷史(狀態 / 摘要 / 報告清單 / 參數快照) | §11.1 |
| 檢測 | DELETE /detection-tools/executions/{execution_uid} |
(CM-1425)刪一筆失敗執行紀錄——非 failed 回 DETECTION_TOOLS_409016、查無/跨租戶回 DETECTION_EXECUTION_404001 |
§11.2 |
| 檢測 | DELETE /detection-tools/execution-groups/{group_uid} |
(CM-1425)整組刪(底下全 failed 才准)——混合組回 DETECTION_TOOLS_409017 |
§11.2 |
| 完成 | POST /flow-engine/task/complete/{template_job_id} |
單任務完成(走 BPMN) | GAI-SD-02 |
| 退回 | POST /flow-engine/task/revert/{template_job_id} |
覆核人退回任務 | GAI-SD-02 |
| 批次 | POST /grc/project/{project_uid}/jobs/batch-complete |
批次完成(逐專案分送) | §6.4 |
| 問卷 | GET /task-survey/{task_survey_uid}、POST /task-assignees |
問卷角色檢查 | GAI-SD-02 |
以下 §6.2~6.4 為核心 endpoint 完整規格(欄位逐條對照 serializer / service 組裝碼,出處標在各段末)。
6.2 我的任務清單¶
[POST] /grc/jobs/my/list¶
Request(MyJobListRequestSchema,繼承 RequestMetaSchema):
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| pager | object | 是 | {page, page_size, with_total} |
| sort | array | 否 | 排序條件 |
| filters | object | 否 | MyJobFiltersSchema 共 8 欄皆選填:keyword / status / exclude_status / project_uid / group_keyword / control_code / ao_keyword / round_uid(v1.10.0 新增,CM-846;api/grc/serializers/job.py:192-203;UI 常用前三項+round_uid,其餘供跨群 / 控制項篩選) |
Response data(MyJobResponseSchema,每項):
| 欄位 | 型別 | 說明 |
|---|---|---|
| id | string | 任務 uid |
| name / job_type / status | string | 任務名 / general·survey / TODO·PROCESSING·COMPLETED |
| project_uid / project_name | string | 所屬專案 |
| control_group_ / control_ / ao_* | string | 群組 / 控制項 / AO 定位 |
| workflow_execution_uid | string | BPMN 執行實體 |
| attachment_count | int | 證據數(general 就緒判定) |
| survey_completed | bool | 問卷全完成(survey 就緒判定) |
| round_uid / round_no / round_type | string / int / string | v1.10.0 新增:所屬輪次(vw_user_job_queue LEFT JOIN project_audit_rounds);round_id 為 NULL 的歷史資料 fallback round_no=1、round_type='initial'(GrcMyJobDto.from_view_row) |
出處:serializer api/grc/serializers/job.py:205(Request)/ :210(Response);service app/participant/service/task_assignee_service.py:269(list_my_grc_jobs:查使用者任務佇列 → 補 attachment_count / survey_completed);DTO app/grc/dto/job_dto.py::GrcMyJobDto.from_view_row
6.3 任務明細¶
[GET] /grc/project/{project_uid}/job/{job_uid}¶
Response data(JobDetailResponseSchema):
| 欄位 | 型別 | 說明 |
|---|---|---|
| id | string | 任務 uid |
| name | string | 任務名 |
| description | string | 任務描述 |
| guide | string | 任務指南 |
| job_type | string | general / survey |
| status | string | TODO / PROCESSING / COMPLETED |
| template_job_id | string | BPMN 範本 userTask id |
| can_revert_job | bool | 是否可退回 |
| workflow_execution_uid | string | BPMN 執行實體 |
| assignees | array | 每項 {id, name, avatar, is_approver} |
| departments | array | 部門關聯 |
| devices | array | 設備關聯 |
| surveys | array | 每項 {id, name, question_count, source_survey_uid, snapshot_uid, approval_required, task_surveys[{task_survey_uid, device_uid, device_name, status, approval_required}]} |
出處:api/grc/serializers/job.py:160
6.4 批次完成¶
[POST] /grc/project/{project_uid}/jobs/batch-complete¶
Request(BatchCompleteRequestSchema):
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| job_uids | string[] | 是 | 要批次完成的任務 uid 陣列 |
| comment | string | 否 | 選填,預設 "" |
Response(BatchCompleteResponseSchema):
| 欄位 | 型別 | 說明 |
|---|---|---|
| total | int | 送出任務總數 |
| completed_count | int | 成功完成數 |
| failed_count | int | 失敗數 |
| results | array | 每項 {job_uid, success, error};error 為 NO_EVIDENCE / SURVEY_INCOMPLETE / 任務不存在 / 例外訊息 |
逐專案分送(FE 依 project 分組多次呼叫);per-job suppress_notify,最後統一批次通知。出處:serializer api/grc/serializers/job_batch_complete.py:5(Request)/ :16(Response);service app/grc/service/job_batch_complete_service.py:30
7. 前端檔案地圖(compliance-manager-fe/)¶
| 檔案 | 角色 |
|---|---|
src/views/project/MyTasksView.vue |
主檢視(左清單 55% + 右抽屜 45%;搜尋 / 篩選 / 批次完成) |
src/components/grc/JobExecutionDrawer.vue |
任務明細抽屜(證據 / 連結 / 留言 CRUD、完成 / 退回) |
src/config/router/index.js:876-880 |
route project-my-tasks(path /project/task-manage) |
| (留言輸入無獨立元件) | 留言 UI 內嵌於 JobExecutionDrawer.vue(約 :151-330),非 CommentInput.vue(該檔不存在) |
src/config/api/api.js |
端點常數 |
src/config/locales/i18n/{zh-tw,en}/my-tasks.json |
頁面 i18n(namespace lang.my_tasks.*) |
導覽入口:DashboardView「我的任務」區塊 / 「查看全部」→
router.push({name:'project-my-tasks', query:{job}});問卷開新分頁到survey-v2-fill。本頁不連 workflow-setup / workflow-view(那是 BPMN 範本編輯,屬另一群)。
8. 後端檔案地圖(本頁核心鏈路)¶
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 我的任務清單 | api/grc/routes/job_route.py:212::MyJobListResource |
app/participant/service/task_assignee_service.py:269::list_my_grc_jobs(查佇列 + 補 attachment_count / survey_completed) |
使用者任務佇列 view(依 task_assignees × job_executions)+ job_evidences count + task_surveys 完成集 |
| 任務明細 | api/grc/routes/job_route.py:72::ProjectJobDetailResource |
app/grc/service/job_service.py::get_job |
job_executions + 關聯表 |
| 批次完成 | api/grc/routes/job_batch_complete_route.py:19::JobBatchCompleteRoute |
app/grc/service/job_batch_complete_service.py:30::batch_complete(per-job 就緒複驗 → flow-engine complete → 留言 → 統一通知) |
infra/grc/repository/job_batch_complete_query.py + workflow service |
| 證據 / 留言 | job evidence / comment route | job_evidence_service / job_comment_service |
job_evidences / job_execution_comments |
DDD 提醒:清單走 app service(不在 route 查 DB);批次完成的就緒複驗在 service 層、每筆獨立成敗回報(不因一筆失敗整批 rollback)。
9. DB¶
9.1 資料表總清單(本頁讀寫的全部表)¶
| 表 | 讀/寫 | 說明 | 欄位詳述 |
|---|---|---|---|
| compliance.job_executions | 讀/寫(狀態) | 任務本體(prep-job):PROCESSING→COMPLETED | 見 project-planning §9.3 |
| compliance.task_assignees | 讀 | 任務指派人員(過濾成當前使用者) | 見 project-planning §9.3 |
| compliance.job_evidences | 寫 | 任務證據(FILE / LINK) | §9.3 |
| compliance.job_execution_comments | 寫 | 任務留言 | §9.3 |
| compliance.job_execution_devices / _org_units / _surveys | 讀 | 任務的設備 / 部門 / 問卷關聯(明細顯示) | GAI-SD-03 |
| survey.task_surveys | 讀 | 問卷執行狀態(就緒判定 status=9) | GAI-SD-03 |
| compliance.projects / project_participants | 讀 | 專案名 / reviewer 角色判定 | GAI-SD-03 |
| compliance.detection_executions | 讀(本頁)/ 寫(BE 編排服務) | 檢測工具執行歷史(狀態/摘要/報告參照),僅 detection_tool 型任務有資料 |
檢測工具管理 §9 |
| compliance.agent_tasks | 讀(間接,反查 agent_task_uid) | Agent 派工單(pending/dispatched/running/succeeded/failed/cancelled) | 檢測工具管理 §9 |
清單讀取走「使用者任務佇列」查詢(
task_assigneesjoinjob_executions過濾 user_id),批次補job_evidences計數與task_surveys完成集。v1.10.0:vw_user_job_queue補 LEFT JOINcompliance.project_audit_rounds(byworkflow_execution_control_mapping.round_id),SELECT 加round_no/round_uid/round_type/round_name四欄(scripts/sql/2026-07-23-vw-user-job-queue-add-round-info.sql)。
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)¶
compliance.job_evidences — 任務執行佐證資料¶
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | — | 證據識別 |
| job_execution_id | — | → job_executions(任務本體) |
| evidence_type | — | FILE(檔案)/ LINK(連結) |
| reference_url | — | LINK 型 URL |
| (檔案中繼) | — | 名稱 / 大小 / drive 參照等 |
compliance.job_execution_comments — 任務執行留言表¶
| 欄位 | 型別 | 說明 |
|---|---|---|
| job_execution_id | — | → job_executions |
| content | — | 留言內容 |
| (作者審計欄) | — | 作者 / 建立時間;作者本人可改刪 |
job_executions/task_assignees欄位卡見 專案規劃頁 §9.3(本頁不重複;那裡以寫入者視角、本頁以執行者視角讀寫同組表)。
10. 頁面邏輯與資料對應¶
載入時序:
關鍵欄位對應(畫面 ↔ API):
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 任務狀態徽章 | job.status |
my/list status |
| 可完成(勾選) | canComplete(job) |
general: attachment_count>0;survey: task_surveys 全 status=9 |
| 證據清單 | 抽屜 evidence | /job-evidences |
| 完成按鈕 | canCompleteJob |
flow-engine complete {workflow_execution_uid, comment} |
| 批次結果 | 兩步 dialog | batch-complete results[{job_uid, success, error}] |
儲存流程:證據上傳 / 留言後重取明細;完成 / 退回走 flow-engine(BPMN 前進);批次完成逐專案分送、per-job 成敗獨立、最後統一通知。
錯誤對應:BE error envelope → toast;GRC_JOB_NOT_FOUND(404)、GRC_PROJECT_PARTICIPANT_NOT_FOUND(404);批次錯誤碼 NO_EVIDENCE / SURVEY_INCOMPLETE(在 results[].error,非 GRC 碼,FE 對 i18n 顯示)。
11. 背景行為與外部依賴¶
| 類型 | 內容 |
|---|---|
| 通知 | 批次完成 per-job suppress_notify、最後送一則批次摘要通知(threading);檢測工具掃描完成與失敗另發獨立通知(九項內容+報告檔名,Email/Discord/Telegram 三管道,FR-058.7 D34,見 §11.1) |
| BPMN | 完成 / 退回走 flow-engine/task/complete|revert(BPMN userTask 前進 / 回退);prep-job 的 workflow_execution 由規劃期綁定 |
| 問卷 | survey 型任務開 survey-v2-fill(新分頁);task_surveys.status 追蹤(9=完成) |
| 檢測工具(FR-056) | detection_tool 型任務不走 flow-engine complete 觸發掃描——見下方獨立小節 |
| Socket | 本頁無 socket / 輪詢;動作後重取 |
| jedi-* 套件 | jedi-flow-engine(complete / revert)、jedi-survey(問卷)、jedi-auth(JWT) |
| 系統參數 | 無 feature flag |
11.1 檢測工具執行(FR-056,job_type='detection_tool')¶
任務類型設為「檢測工具執行」(見 任務配置)的 AO 任務,在本頁抽屜顯示「開始執行 / 重新執行」按鈕與「執行紀錄」區塊,走獨立於 flow-engine complete 的編排鏈路:
開始執行 / 重新執行(JobExecutionDrawer.vue::startExecution()):
- 無執行中紀錄:直接呼叫
POST /detection-tools/jobs/{job_uid}/execute(無force),BE 建立agent_tasks(pending)+detection_executions(running)派工給具detection_scancapability 的 Agent,不彈確認框。 - 有執行中紀錄:先彈確認框(「上一次掃描將被中斷,確定重新執行?」)→ 確認後帶
force: true呼叫同一 endpoint → BE 先透過既有 FR-039 mTLS 通道直推 AgentPOST /detection/cancel真正中斷上一次掃描(OpenVASstop_task)→ 確認取消成功才標記舊執行cancelled並派新工單;取消失敗(Agent 離線等)→ 回DETECTION_TOOLS_409004,不默默派新的。 - 未帶
force但已有執行中紀錄 → BE 回DETECTION_TOOLS_409003(409),防止繞過 FE 直打 API 疊出並行掃描。
SonarQube 上傳模式的執行時上傳(FR-058.7,scan_mode=upload)(⚠️ 單槽形狀,FR-067.8 起已改逐列,見本段末「逐列檔案槽」):任務設定選了「上傳原始碼壓縮包」模式的檢測任務(模式選擇見任務配置 §5),抽屜執行區多一塊源碼壓縮包上傳區——壓縮包是「這一次要掃的源碼快照」,故在發起執行時提供而非任務設定表單(D26):
- 格式與限額:收
.zip/.tar/.tar.gz/.tgz四格式(裸.gz不收——解開只有一個檔,對源碼掃描無意義),上傳上限預設 100MB(DETECTION_UPLOAD_MAX_MB環境變數可調;另有解壓後總量 500MB/檔案數上限等 agent 端防線,D27/D28/D30)。FE 先驗副檔名(source_file_format_invalidtoast),走既有POST /file/upload(upload_type='DETECTION_SOURCE')存進 tenant 儲存空間拿 uid。 - 首次執行必須上傳:綁定無 sticky 檔案且本次未上傳 → 「開始執行」按鈕 disabled+tooltip 提示(FE 前置擋門);繞過直打 API 由 BE 保底回
DETECTION_TOOLS_400005(DETECTION_TOOL_SOURCE_FILE_REQUIRED)。 - 重掃沿用(D29):已上傳過的任務,上傳區顯示「目前檔案:xxx.zip」——不上傳新檔直接按執行=沿用同一包(execute 不帶 body);要掃新版按「更換檔案」上傳新檔替換(execute body 帶
{"source_file":{"uid":"..."}},覆寫綁定的 sticky 參照)。替換不刪舊檔、歷史版本全保留,每筆執行紀錄的scan_params快照都能對回當時實際掃描的檔案(稽核回溯)。 - FE 只送 uid,不送檔名(D31):BE 收到 execute 後從
upload_files表權威取回 file_name/size/sha256 寫進快照與派工 payload;uid 查無或不屬本租戶 →DETECTION_TOOLS_404003、格式不符 →400006、超限 →400007。執行紀錄與 info 視窗顯示source_file的檔名而非裸 UUID(_PRIMARY_PARAM_KEYS含source_file)。 - Agent 取檔走 mTLS 主動下載(D25):檔案參照隨心跳 payload 下發,agent 磁碟檢查(不足即報「agent 磁碟空間不足」,歸類環境問題非掃描失敗,D28)→
GET /api/1.0/agents/files/{uid}(純 mTLS、驗證該 uid 屬該 agent 名下 pending/running 任務,否則DETECTION_TOOLS_404004)→ sha256 對帳 → 安全解壓進 workspace → 接進既有 scan 掃描管線;agent workspace 掃完即刪(tenant 儲存空間上的壓縮包保留)。 - 上傳無進度條(known limitation):檔案較大時維持轉圈等待至完成。
出處:FE JobExecutionDrawer.vue:296-380(上傳與 sticky 判定)/:741-750(execute 只送新 uid);BE route api/detection_tools/routes/detection_tool_route.py:176-196、util common/util/detection_source_file.py、agent 下載端點 api/remote_agent/routes/remote_agent_route.py::AgentFileDownloadRoute;error code common/code/detection_tools_error_code.py(400005–400007、404003、404004);限額 config/config.py:171-172。
逐列檔案槽(FR-067.8,2026-08-25):任務設定改「每列自足」之後,scan_mode 是每個
Agent 分派列各自的參數——同一任務可以一列 pull、兩列 upload,而兩個 upload 列掃的是
不同的源碼包。上述單槽形狀隨之改為逐列:
- 上傳區逐 upload 列各一個檔案槽(非 upload 列不出現——混模式下 pull 列本來就沒有源碼包 這個概念);多列時標出是哪一列(agent 名),單列時不多這個標籤。
- 各列各自 sticky:優先讀綁定回來的該列
source_file(BE 從該列參數轉出),讀不到才退回 執行紀錄快照(存量單槽任務的相容路徑)。sticky 只存 uid(metadata 每次派工現查,不留可能 過期的副本),故常拿不到檔名 → 顯示「已上傳(沿用上次)」而非一串裸 UUID。 - execute payload 改送
source_files({assignment_uid: {uid}}),只帶「這次剛換過檔」 的列——沒送的列代表沿用該列既有的包。無分派列的 fallback 仍同時帶舊的單槽source_file欄位(BE 兩邊都收,_default為路徑保留字)。 - 擋門逐列且擋在建 group 之前(零工單零執行紀錄,與多 agent 展開的逐台驗證同一立場): 任一 upload 列缺檔就不給按執行,tooltip/BE 訊息都指出是哪一列(agent 名)——只說 「請先上傳源碼包」的話,使用者面對兩三個槽不知道漏了哪個(已上傳的那幾個看起來一切正常)。
- 單組重跑沿用該列自己的包;agent 零改動(憑工單 uid 逐工單下載本就獨立,授權 resolver 讀的是該工單自己的參數)。
- 自動派發判斷細到列:原本只看任務層參數,會漏掉「任務層 pull、某列 upload」的任務而判成 可自動派——派下去必然打中首次無檔的 400,該錯誤被吞掉只留 warning,且失敗不產生執行紀錄 使冪等條件永遠成立,每按一次「開始執行任務」就再靜默失敗一次。
⚠️ 抽屜四處原本讀任務層掃描參數(上傳模式判定、「檢測設定」摘要卡、執行主機清單、掃描 目標清單)在參數搬到列上之後會全部空白且不報錯,看起來像「這個任務沒設定」。已改為讀各 分派列;零列時退回任務層走存量相容。 出處:FE
JobExecutionDrawer.vue(BEfda3921b/FEa4c5ab4)。
「檢測設定」卡多組時逐組分塊(CM-1396,2026-08-25):初版的跨列彙整(同欄位值不同「/」 併陳、主機清單取聯集)在各組同構時讀得通,SonarQube 混模式(一組「原始碼掃描」、一組 「上傳壓縮包」)直接打穿——「執行模式:上傳原始碼壓縮包/原始碼掃描」下面掛一個 Git 網址, 看不出哪一組是哪個模式、網址是誰的。改法:
- 多組任務一組一小塊:組號+agent+該組關鍵參數+該組掃描目標清單+該組技術參數摺疊區 (各自開合)。工具名稱與完成模式留卡片頂部(任務級資訊)。單組任務維持原單框版型 (「第 1 組」對單組是純噪音)。
- 逐組參數過 condition(與任務設定頁擋門、BE 逐列驗證同一套規則)——upload 組上可能躺著 切換模式前殘留的 Git 網址,照列會讓執行者以為那組也要掃該 repo。逐組呈現後不再跨組取 聯集(聯集正是「看不出誰是誰」的來源)。
- 上傳槽與擋門訊息改標「第 N 組(辨識參數)」(兩處共用同一支函式):agent 名不是穩定 識別(同 agent 可多組、自動挑機的組沒名字)。括號放該組模式+模式專屬識別欄位(Git 網址/ 專案代碼/後綴),刻意不放 profile(基準名太長擠掉真正要區分的);無辨識參數退回 agent 名,再無只留組號。🔴 組號用全部分派列的全域序號(非 upload 列內部序號), 上傳槽與檢測設定卡的組號才對得起來。
掃描目標的台數與原始寫法顯示(CM-1395,2026-08-25/26):CM-1383 的 CIDR/range 語法 落地後,顯示端改「原始寫法+BE 真實台數」原則:
- 台數由 BE 供、FE 不算(不在 FE 複製第二套展開規則):BE
scan_target_spec.display_counts_of()為唯一入口,「一段代表多台」的寫法(CIDR/range)才給台數;CIDR 語意依欄位分流——展開型 算可用主機(/24=254)、透傳型算整段(256,工具實際處理的量);逐台寫法不給值(段數即 台數)。分派列摘要、執行前確認框、任務資訊區、執行紀錄列台數 chip、執行詳情視窗全吃它。 - 執行紀錄印原始寫法(
192.168.50.1-5(5 台)一個 Tag,非 5 個 IP 並排;逐台清單留在 詳情 ⓘ):來源是派工當下快照(工單 params_scan_target_spec)——忠於「那一次掃了 什麼」,不回查分派列現在的設定;快照優先、無快照就地算(透傳欄位存的就是原始寫法, 存量舊紀錄不重跑也能正確顯示台數)。 - 列上講真正被掃的欄位:執行紀錄回應補
targets(原只挑hosts);有targets就講它 ——nmap 的hosts是 agent SSH 登入去執行 nmap 的跳板機,只講它會讓使用者以為網段沒 帶到。判準看資料不看工具清單(「有 targets」=該工具把執行主機與掃描目標分兩欄), 單欄位工具顯示完全不變。台數 chip 與列文字同欄位同 param_schema 短名。 - FE 過期防護:分派列摘要以 BE 回的
scan_target_spec字串與畫面現值比對,改了目標未 存檔時退回段數顯示(不顯示過期台數)。 - 順手:執行詳情長 label(用法寫進標題撐爆兩欄 grid)改
paramShortLabel短標籤+括號段掛 ⓘ tooltip;未掃到/無實質檢查警示字改 theme-aware token(--color-orange/--color-red, 抽.exec-warn-text/.exec-error-text),亮色主題白底可讀(原--yellow-400淡黃不可讀)。
取消執行中的掃描(FR-058,POST /detection-tools/jobs/{job_uid}/cancel):執行紀錄中 status='running' 的那筆才顯示「取消執行」按鈕(另需 canEdit)→ 確認框 → BE 走與 force 重派同一條底層 _cancel_running_execution(CM-931 既有):反查 agent_task_uid → agent_id → agent base_url,經 mTLS 直推 Agent 取消,成功才把 agent_tasks 與 detection_executions 標記為 cancelled。兩種 409:無 running 執行(已結束/從未開始)→ DETECTION_TOOLS_409005(DETECTION_TOOL_NO_RUNNING_EXECUTION);取消失敗(Agent 離線、查無 agent_task 等)→ DETECTION_TOOLS_409004(DETECTION_TOOL_CANCEL_FAILED),不標記 cancelled(狀態必須反映真實情況,不能讓使用者以為停了其實還在跑)。守門與其他 job 操作端點一致:assert_project_participant(非該專案參與者不可操作)。出處:app/detection_tools/service/detection_orchestration_service.py::cancel_execution:133-155、_cancel_running_execution:157-181;FE JobExecutionDrawer.vue::cancelExecution:611-633。
按鈕視覺刻意壓小:取消鈕比同列其他按鈕再小一級(
text-xs px-2 py-1)並置於 info icon 之後。取消是破壞性、且僅在執行中短暫出現的動作,若與長駐的「下載報告」等重,會把視線拉去一個多數情況下不該按的按鈕。
執行歷史區塊(GET /detection-tools/jobs/{job_uid}/executions):新到舊排序(BE ORDER BY started_at DESC, id DESC,CM-930);區塊可收合,有 running 或最新一筆 failed 時預設展開;每筆狀態 running/succeeded/failed/cancelled(Tag 顏色 success/danger/secondary/warning);成功筆顯示「發現 {count} 項」(讀 summary.findings)+「下載報告」。列上只放關鍵資訊(工具名 / profile / 時間 / 觸發者 / 機器數 / 失敗台數 / content 不符警示),完整參數與報告清單走 info 視窗。
執行紀錄群組化(FR-067,2026-08-25):任務設了多組 Agent 分派後(設定端見任務配置 §5「Agent 分派區塊」),一次執行展開為一個執行群組(group)+N 筆執行紀錄(每組 agent+目標各一筆)。API response 改兩層形狀——外層群組(uid/status/total_count/closed_at/is_legacy)、內層 executions[](每筆帶 agent_uid/agent_name/assignment_uid/scan_targets/is_latest):
- 群組卡片:彙總徽章
succeeded綠/partial_failed黃(部分台失敗,沿 CM-952 語彙)/failed紅/running動畫;明細含scheduled筆(排程等待中)時顯示「⏱ 排程中」中性徽章與 running 的動態感區隔。卡片內每台一列:agent 名稱/掃描目標/狀態/報告連結。 - 單組重跑(
POST /detection-tools/execution-groups/{g}/assignments/{a}/rerun):前置只看該組自己最新一筆是否 failed/cancelled(否則DETECTION_TOOLS_409013,語意涵蓋 succeeded 與仍在跑)——群組整體還在 running 也可重跑失敗組(CM-1397 放寬:原「群組非 running 才可重跑」讓 30 秒就失敗的組乾等同群組掃一小時的慢組;409012自此無 raiser,碼位保留不回收,FE i18n 不刪)。同 group 追加新筆;群組已收口才撥回 running 清closed_at(仍 running 就只追加,不多餘 reopen);收口併發靠lock_for_close列鎖序列化,不會出現「一筆 running 掛在 closed 群組下」。重跑一律立即不套排程。重跑歷史(is_latest: false)收合顯示。FE 重跑鈕條件同步放寬(原多擋一層「群組有任一組 active 就不出鈕」已拿掉)。 - 單組取消(
.../assignments/{a}/cancel):排程中的組走短路(工單還沒被領走,直接標 cancelled 不打 agent)。 - 整組取消(
POST /detection-tools/execution-groups/{g}/cancel):best-effort——可達的台都取消、不可達的記入failed清單,全部不可達才 409(409014)。⚠️ FE 讀回應內容呈現結果,不是只看 HTTP 200——failed清單裡那幾台的掃描還在對方機器上跑。既有 job 層取消端點(上方 FR-058 段)已改委派整組取消,多台情境不會只取消第一筆。 - 立即開始(
.../assignments/{a}/start-now):催跑排程中的組(非排程中409015)。 - 排程組的執行確認框(FR-067.7):按「開始執行」前若有任何排程組(分派列設了
scheduled_at,設定端見任務配置 §5),確認框列清單——每組一行「agent(目標,含台數,CM-1395)+ 立即/⏱ 時間開始」;時間已過顯示「立即」不顯示過期時間。資料源是綁定讀回的agent_assignments,不另打 API。排程等待期間群組是 running(守門窗變長)——不能再按執行,要重來先取消整組。 - legacy 舊資料(group 化之前的筆,
group_uidNULL 不 backfill):is_legacy: true單筆自成一組,形狀與真群組完全一致,FE 不分叉渲染。 - auto 完成模式:群組全部成功才自動完成任務;
partial_failed不自動完成,留人工判斷。 - 彙總通知:群組收口時發一封(每台一列:agent/目標/狀態/發現數/時間;發現數空值顯示「—」非 0),取代每筆一封;三通道(Email/Telegram/Discord)同步。失敗原因自 agent 0.2.29 起帶 stderr 尾段(原本 command not found/content 檔不存在這類原因被排空丟棄,UI 只見「執行失敗(exit=1):」空白)。
- fallback 自動分派(無分派列)仍建群組(
total_count=1、assignment_uidNULL,路徑用保留字_default),收口邏輯只有一套。
profile 值的顯示名對照(FR-059):param_schema 宣告
options_source: "profile_library"的欄位(CINC Auditor / GCB 的profile),選項已改由掃描設定檔庫動態餵、schema 內不再有靜態 options 可查 label,且值可能是profile:<uid>庫內參照。抽屜取 param_schema 後只在真的有宣告時另打一次 profile menu API(fetchProfileLibraryLabels,帶工具 uid)建「值→設定檔名稱」對照,摘要卡與執行詳情因此顯示「TWGCB-01-007 Windows Server 2016 v1.3」而非profile:<uuid>裸值;對照拿不到(API 失敗或該參照已停用不在 menu 內)退回顯示原值,不擋畫面也不擋執行。無宣告的工具一次多餘請求都不發。出處:JobExecutionDrawer.vue::fetchProfileLibraryLabels:273-289、值對照:416。庫的維護與profile:<uid>參照機制見 掃描設定檔管理。
執行主機與掃描目標分開顯示(FR-058):任務資訊的「檢測設定」摘要卡與執行詳情 info 視窗,皆把 hosts 與 targets 各自整理成一區 Tag 清單(其餘參數走鍵值列,並排除這兩個 key 避免重複)。兩者語意不同——OpenSCAP / OpenVAS / CINC Auditor 這類「登進去掃自己」的工具只有 hosts(維持單框,畫面與加此功能前相同);Nmap 則是 Agent 登入 hosts(執行主機)再對 targets(可能是整個網段)送探測封包,兩者是不同機器。區塊標題一律沿用 BE param_schema 的 label(BE 已依工具各自給對),FE 不寫死「掃描目標」;label 常帶括號說明(如「執行主機(Agent 登入並執行 nmap 的機器,逗號分隔)」),取括號前的短詞當標題以免和後綴的「(N 台)」疊成兩層括號;schema 查無定義時(label 退回原 key)才用 i18n fallback,不顯示英文原始 key。出處:JobExecutionDrawer.vue::paramShortLabel、toolHostList/toolTargetList;執行詳情側自 CM-1395 起改走 execTargetField()(原 execHostList/execTargetList 已刪——只回清單、台數由 caller 數長度的第二套讀法,正是 range 被說成 1 台的來源)。
報告下載(reports,FR-057/CM-952 擴充多檔):DetectionExecutionResponse(api/detection_tools/serializers/detection_tool.py)除舊有單值 report_file_uid 外,新增:
| 欄位 | 型別 | 說明 |
|---|---|---|
| reports | array | 該次執行產生的所有報告,每項 {uid, file_name}。真相來源是 job_evidences(handler 逐檔寫入,四台掃描=四筆),非 detection_executions.report_file_id(單值 soft-ref,只留得住第一筆代表);歸屬靠 job_evidences.description 的 [檢測工具] {agent_task_uid} 前綴反解(EVIDENCE_DESC_PREFIX 常數) |
| report_file_uid | string|null | 舊語意保留(單值,供舊讀取端相容);FE 已改讀 reports,不再依賴此欄位 |
| detection_tool_name | string|null | 該次執行的工具顯示名——忠於當筆的 detection_tool_id(任務綁定可能換過工具,歷史列不可一律套當前綁定名稱) |
| scan_params | object|null | 當次實際派工的參數快照(agent_tasks.params 反查),已剝除 _credentials 等底線開頭內部欄位——憑證明文絕不可出 API;重掃前可能改過參數,快照忠於當次而非任務當前綁定值 |
| created_user / created_user_name | string|null | 發動者(審計欄位規範:login_name 配 nickname 成對回傳) |
OpenSCAP 逐台各一份報告語意:一次 OpenSCAP 執行掃 N 台目標主機,reports 陣列即含 N 筆——每筆對應該次執行中的一台目標主機(檔名格式 OpenSCAP掃描報告_<host>_<日期>.html),FE 執行紀錄列的「下載報告」改為開清單視窗逐份下載(原本綁單值 report_file_uid 的下載鈕只能拿到第一筆,四台只下載得到一份)。OpenVAS 單台單份的既有行為不受影響(reports 陣列僅一筆)。
完成模式分岔:掃描成功後 BE 依該任務綁定的 completion_mode(manual/auto,見 任務配置)決定是否自動呼叫 flow-engine complete;manual(預設)任務維持 PROCESSING,需使用者確認報告後在本頁抽屜手動按「完成任務」(走既有 UC-MT-01 路徑)。
掃描結果通知(FR-058.7 D34 加厚;原 FR-056 僅成功一句話 Email):掃描結束時(on_scan_succeeded/on_scan_failed 皆會)發通知,失敗也通知——失敗更需要管理層知道,收件人與成功相同:該任務指派人 ∪ 所屬專案 manager/reviewer(以 email 收斂去重;專案反查升級走輪次鏈,legacy 任務 fallback task_assignees.project_id)。內容為九項明細列+條件列:狀態(成功/失敗)、專案、控制項群組、控制項(代碼+名稱)、AO(title→prose→part_id fallback)、任務名稱、工具名稱、目標主機(hosts 獨立一列,照實顯示不遮蔽)、掃描設定(複用執行紀錄同一份剝除——_ 前綴內部欄位+secret envelope 整 key 不出現;profile:<uid> 庫內參照轉譯成設定檔名稱)、開始/結束時間;有報告時另列報告檔名(多檔頓號串接),失敗時另列錯誤訊息。三管道:Email(HTML 表格)+ Discord/Telegram(純文字,依租戶通知設定啟用與否),兩版共用同一份明細列順序不漂移;發送走 thread 不阻塞掃描回報。已知限制:無站內鈴鐺通知(平台無 notifications 表,另案);通知在 agent request context 發、語系一律 zh_Hant_TW(既有限制)。出處:detection_orchestration_service.py::_notify_scan_result:651-/_build_notify_rows:733-/_dispatch_notification:703-;收件人與 8 層 join 查詢 infra/detection_tools/repository/detection_job_notify_query.py::get_notify_context。
scan_params快照已剝除任務層加密參數(FR-058.0):ZAP 登入後掃描的login_password屬secret: true參數,在 DB 以加密 envelope 存放;scan_params快照除了既有的「剝除_credentials等底線開頭內部欄位」外,加密 envelope 欄位也整個 key 不出現。因此 info 視窗的參數列不會、也不可能顯示任何密碼。詳見 任務配置 §5。
出處:BE app/detection_tools/service/detection_orchestration_service.py::start_execution / cancel_execution / list_executions / _resolve_reports;error code common/code/detection_tools_error_code.py(DETECTION_TOOLS_409003 已有執行中 / 409004 取消失敗 / 409005 無可取消的執行);FE JobExecutionDrawer.vue(execReports() 讀 reports,Array.isArray 為空才 fallback 單值 report_file_uid)。完整 API / DB 見 檢測工具管理 與 任務配置(規劃頁「開始執行任務」自動派首掃)。
11.2 執行紀錄刪除與逾時收斂(CM-1425)¶
刪除失敗的執行紀錄¶
在此之前執行紀錄無法從 UI 刪除,整測與實務上的失敗紀錄(agent 缺工具、連線失敗、設定錯)只會一直累積,只能請原廠下 SQL 清。
| 狀態 | 可否刪除 | 理由 |
|---|---|---|
failed |
✅ | 清理對象 |
succeeded |
❌ DETECTION_TOOLS_409016 |
掛著 report_file_id / evidence_id,刪了證據鏈就斷(稽核時找不到當初的報告) |
running / scheduled |
❌ 同上 | 東西還在跑,要刪得先按「取消」(取消功能既有) |
cancelled |
❌ 同上 | 使用者自己按的,屬有意義的操作軌跡,不是垃圾 |
| 端點 | 前置 | 行為 |
|---|---|---|
DELETE /detection-tools/executions/{execution_uid} |
該筆為 failed |
硬刪該筆;群組列不動、彙總狀態不重算 |
DELETE /detection-tools/execution-groups/{group_uid} |
群組底下每一筆皆 failed |
刪順序 executions → group |
三個刻意的設計決定:
- 刪單筆不重算群組彙總狀態——刪掉一筆失敗紀錄不代表這次執行變成功了。若重算,「四台掛三台」刪掉一台後會變成
partial_failed,等於把歷史事實改寫掉。要讓整組消失請走整組刪。 - 整組刪看「每一筆」而非「每 assignment 最新一筆」——收合起來的重跑歷史裡若夾著一筆
succeeded,代表這組留有報告與證據,不該整組消失。 - 刪除順序 executions → group(兩表無 DB FK,順序由程式保證)——反過來刪會在中途失敗時留下沒有群組的孤兒 execution,列表會把它們渲染成 legacy 單筆卡。
權限:與同頁其他寫入操作(重跑/取消/立即開始)同一軸——專案參與者(assert_project_participant)+ route 層 @require_license("plugin")。非參與者回 GRC_403053。查無與跨租戶一律 404 不區分(DETECTION_EXECUTION_404001),避免成為租戶探測管道。
稽核:刪除寫 system_logs——6120(單筆)/6121(整組),帶操作者、uid 與當時狀態。「刪掉一筆掃描發生過的證據」本身就是稽核關切點。
逾時收斂¶
問題場景:掃描進行中網路整段不可達、agent 再也沒回報 → 該筆永遠掛 running,刪不掉(只准刪 failed)、狀態誤導人,且整組卡 running 會擋掉同一任務的下一次執行。
| 項目 | 值 |
|---|---|
| 觸發 | 背景排程,每 15 分鐘一輪 |
| 判定 | running/scheduled 超過 DETECTION_EXECUTION_TIMEOUT_HOURS 且 agent 從未回報 |
| 閾值 | 預設 4 小時(環境變數可調;設 0 或負數=停用收斂) |
| 單輪上限 | DETECTION_EXECUTION_TIMEOUT_BATCH(預設 200 筆,存量一次撈爆的保險;剩下的下一輪接著處理) |
| 結果 | 判 failed,error_message 註明「逾時無回報」,並走既有收口路徑重算 group 狀態(與收斂共用同一支,不是兩份真相) |
為什麼閾值訂 4 小時:這類掃描本來就慢——逐台登入型工具單台預設逾時就是 1 小時,一次派 256 台是十幾個小時的量級,OpenVAS 全掃數小時也正常。訂太短會把還在正常跑的長掃描誤殺,那比留著卡住更糟:使用者會以為掃壞了而重跑,但實際掃描還在對方機器上跑。
為什麼選背景排程而非「開列表時 lazy 收斂」:lazy 版只在有人打開那個任務的抽屜時才會收斂,但卡住的掃描恰恰是最沒人盯的那些;且整組卡在 running 會擋掉下一次執行,使用者按執行被擋卻不知道要先去開一次抽屜看一眼。排程版跟有沒有人看無關。
兩個功能是串起來的:逾時收斂把卡住的執行判成
failed之後,它才能被上面的刪除功能刪掉。
12. 邊界情況與已知坑¶
- 本頁 gate 是任務狀態不是輪次階段:TODO 未啟動不顯示;PROCESSING 才可執行;跨專案彙整,非單輪次頁
- 就緒條件按任務類型:general 需 ≥1 證據;survey 需全
task_surveysstatus=9;不就緒 checkbox disabled 帶原因 - 批次完成逐專案分送、per-job 獨立成敗:一筆失敗(NO_EVIDENCE / SURVEY_INCOMPLETE)不影響其他;結果摘要逐筆回報,非整批 rollback
- 全選只選當前分頁的可完成任務:不跨頁全選(防誤選未見任務)
- 退回權是 reviewer(FE 判定):
isReviewer由專案 participant role=reviewer 決定,只在覆核模式顯示退回 - 深鏈自動開任務:
?job=/?execution=進頁自動選任務後清 URL(避免連鎖導航) - 證據自成 FILE / LINK 兩型:FILE 走
/job-evidences(multipart)、LINK 走/job-evidence(FormData json);端點路徑單複數不同,改動注意 - 不連 workflow-setup / workflow-view:那是 BPMN 範本編輯頁(另一群),本頁任務執行走抽屜、非全頁導航
- 輪次 NULL fallback 語意:
round_id未回填的歷史資料(wecm 補欄前產生)fallback 顯示「第 1 輪」+round_type='initial',對齊project_audit_rounds定義的 initial round 語意,非真的查詢該筆屬於第幾輪(v1.10.0,CM-846)
13. 開發與驗證¶
- 跑起來:BE
python main_socketio.py(port 8000,log 在log/app.log);FE 在compliance-manager-fe/起 Vite dev server。BE 改 service code 後必須重啟(無 hot reload) - 測試帳號:dev 環境被指派任務的受稽方帳號(密碼見
.env/ 部署文件) - 導航路徑:登入 → 儀表板「我的任務」或直接
/project/task-manage - 前置資料:需有專案在規劃頁指派任務給該帳號且 PM 已「開始執行任務」(任務 PROCESSING)
- E2E:
compliance-manager-test/repo(Cucumber + Playwright);以我的任務/my-tasks搜尋 - 相關文件:任務來源見 專案規劃頁;稽核員視角的清單見 我的稽核;DB / API 全量見 GAI-SD-02/03