稽核判定工作區(Audit Verdict)
功能群:稽核執行|階段轉換、AP→AR→POA&M 資料模型、AO 判定粒度先讀 功能群總覽|上層導航頁見 稽核執行頁
事實基準:2026-07-05 從 FE
src/views/project/RoundAuditReviewView.vue+AuditTreeNav.vue/AuditControlRef.vue/EvidencePreviewDialog.vue+ BEassessment_result_app_service.py/ serializerapi/project/serializers/audit_round.py掃出(FR-038 v2 B5 AR + FR-041 稽核指引)
變更紀錄
| 日期 | FR | 變更 |
|---|---|---|
| 2026-07-06 | FR-047 | 自 audit-review.md 拆出:Tab0 三欄判定工作區(控制項樹
/ 參考面板 / 判定 + 內嵌風險 + 觀察)獨立成子頁 |
1. 功能描述
稽核判定工作區是 稽核執行頁 的
Tab0「控制項稽核判定」:三欄版面——左欄控制項樹導覽(AuditTreeNav)、中欄控制項參考面板(AuditControlRef:稽核指引
/ SSP 現況 / AO 與各 AO 證據頁籤)、右欄判定面板(verdict SelectButton +
說明、not_met 時內嵌風險、觀察區)。稽核員選中控制項後在右欄逐 AO
做判定(met / not_met / not_applicable /
pending),記錄觀察(看到什麼),判定為 not_met
時可就地帶出風險。判定的控制項母體來自凍結 SSP 快照(見
_overview §4)。
主要使用者:稽核員(auditor)、專案經理(manager,可代行)。
角色速覽(完整定義見 _overview §3;本群主角是 auditor):
| 角色 | 一句話 |
|---|---|
| auditor | 稽核員 — 判定 / 觀察 / 內嵌風險的執行者 |
| manager | 專案經理 — 可代行 auditor |
| viewer | 一般參與者 — 只能檢視 |
1.1 功能總覽(本頁全部功能)
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 控制項樹導覽 / 搜尋 / 篩選 | 群組→控制項樹;判定色點、繼承圖示、findings 數徽章;搜尋 + 展開/收合 | 左欄 AuditTreeNav |
§5 |
| 2 | 控制項參考面板 | 選中控制項的指引 / SSP 現況 / AO 與各 AO 的證據頁籤 | 中欄 AuditControlRef |
§5 |
| 3 | 判定(verdict) | 對控制項選 met / not_met / not_applicable / pending,填判定說明 | 右欄 SelectButton + 說明 | UC-AR-01 |
| 4 | 判定內嵌風險(not_met 時) | 判定為 not_met 時就地填風險等級 / 影響 / 矯正建議,存判定同時建/更新風險 | 右欄(editState==='not_met' 顯示) |
UC-AR-01 |
| 5 | 觀察(observation)新增 / 編輯 / 刪除 | 記錄「看到什麼」:方法(EXAMINE…)+ 描述 + 受評對象 + 引用證據 | 右欄觀察區 | UC-AR-01 |
| 6 | 證據瀏覽 | 觀察卡的證據以按鈕收合,點開 dialog 列出檔案 / 問卷 / 連結,可預覽 | 觀察卡 → 證據 dialog + EvidencePreviewDialog |
§5 |
展開成 UC:UC-AR-01(判定控制項並記錄觀察,含 not_met 內嵌風險)。系統風險本體管理與跨風險關聯見 稽核風險管理;頁級 finalize / 統計徽章 / 唯讀模式見 稽核執行頁 hub。純顯示 / 導覽類(1 樹導覽、2 參考面板、6 證據瀏覽)於 §5 描述,不另開 UC。
2. Use Case
角色速覽(完整定義見 _overview §3;本群主角是 auditor):
| 角色 | 一句話 |
|---|---|
| auditor | 稽核員 — 判定 / 觀察 / 內嵌風險的執行者 |
| manager | 專案經理 — 可代行 auditor |
| viewer | 一般參與者 — 只能檢視 |
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-AR-01 判定控制項並記錄觀察
| 項目 | 內容 |
|---|---|
| 角色 | auditor(manager 可代行) |
| 前置條件 | 已登入、為專案參與者;輪次狀態 = auditing |
| 產出 / 後置條件 | oscal.assessment_findings.target_status_state
更新(met→satisfied / not_met→not-satisfied);觀察寫
assessment_observations;統計徽章刷新 |
3. 權限矩陣
專案層角色基調見 _overview §3。本頁(Tab0 判定 / 觀察)檢查點(FE / BE 對齊,phase guard BE 硬擋):
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 讀 findings / 樹 / 證據 | 參與者即可(JWT) | 讀走 writable=False(finalize 後仍可讀) |
assessment_result_app_service.py::get_findings |
| 判定 / 觀察 寫入 | canEdit = roundStatus==='auditing'(RoundAuditReviewView.vue:49) |
_check_auditor +
assert_round_phase({auditing}) |
assessment_result_app_service.py:111-118、round_guard.py:31 |
內嵌風險(not_met 時)走風險寫入路徑,權限見 稽核風險管理 §3;finalize / 匯入等頁級操作見 稽核執行頁 §3。
本頁 FE / BE 權限對齊良好:兩端都以 auditing 階段 + auditor/manager 判定;BE
assert_round_phase硬擋——過階段直接打 API 仍回 412(見 _overview §5 坑 2)。
4. 狀態機與前置條件
輪次狀態機見 專案管理群 _overview §2;本群階段轉換見 _overview §2。本頁相關 gate:
| 條件 | 效果 | 出處 |
|---|---|---|
roundStatus === 'auditing' |
canEdit=true:判定 / 觀察 可寫 |
RoundAuditReviewView.vue:49 |
| 其餘狀態 | canEdit=false:唯讀 banner + 全表單禁用 |
同上 + Message(:686-688) |
| BE 寫入 phase guard | AR 寫入一律 assert_round_phase({auditing}),過階段回
412 |
round_guard.py:31 |
| 範圍過濾 | inScopeTreeGroups 只顯示有 finding 的控制項(AP
可能縮小 scope) |
RoundAuditReviewView.vue:327-332 |
SelectButton unselectable=false |
防止取消選取造成 null 判定(一律有 verdict 值) | RoundAuditReviewView.vue(verdict SelectButton) |
finalize 前置(無 pending / 風險皆關聯)等頁級 gate 見 稽核執行頁 §4。
5. UI 設計
實機截圖(STG,亮色模式,2026-07-05,帳號 blsadmin,專案「亞○ CMMC L1 合規」/ auditing 輪次):



狀態呈現:樹節點帶判定色點與 findings 數;判定
per-按鈕 spinner(savingVerdict /
savingObs);證據 lazy 載入並 per-控制項快取。SelectButton
設 unselectable=false 防 null 判定。無 socket,判定後
refreshStats(重取統計,驅動頂部徽章與樹範圍),觀察增改刪後重取對應清單。
6. API 規格
Envelope:成功 {"code": 1, "data": …}、失敗
{"code": 0, "msg": "…"}。
6.1 總清單(本頁呼叫的 endpoint)
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 載入 | GET /ssp/{ssp_uid}/control-tree |
凍結 SSP 控制樹(中欄參考) | 見 project-planning §6.2 |
| 載入 | GET /audit-round/{round_uid}/ar/findings |
AO 全量判定矩陣(控制項分組 + 觀察 + 統計 + 稽核指引) | §6.2 |
| 證據 | GET /job-evidences?job_execution_uid={jobId}、GET
/grc/project/{uid}/job/{jobId} |
選中控制項的 AO 證據 / 問卷(lazy) | GAI-SD-02 |
| 判定 | PUT
/audit-round/{round_uid}/ar/finding/{finding_uid} |
存單一 AO 判定(met/not_met/pending + 說明 + 關聯觀察) | §6.3 |
| 觀察 | POST /ar/observations、PUT/DELETE
/ar/observation/{obs_uid} |
觀察增 / 改 / 刪 | §6.3 |
風險 / 風險關聯端點見 稽核風險管理 §6;finalize / 匯入 / 專案載入端點見 稽核執行頁 §6。
以下 §6.2~6.3 為本頁核心 endpoint 完整規格(欄位逐條對照 serializer /
service 組裝碼,出處標在各段末);標 GAI-SD-02 者屬常規 CRUD,細節見 API
規格書或 Swagger /swagger-ui/。
6.2 載入類
[GET] /audit-round/{round_uid}/ar/findings
AO 全量判定矩陣(控制項分組)。無 query 參數。Response
data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| ar_result_uid / round_uid | string | AR / 輪次識別 |
| stats | object | {total_ao, met, not_met, pending, controls_total, controls_with_not_met}(皆
int)——徽章來源 |
| controls[] | array | 控制項節點(下表) |
| assessment_subjects[] | array | OSCAL result.assessment-subjects[] |
Control 節點(controls[] 每項):
| 子欄位 | 型別 | 說明 |
|---|---|---|
| control_id | string | 控制項識別 |
| ao_findings[] | array | AO 判定(下表) |
| observations[] | array | 觀察(下表) |
| planning | object | FR-041 稽核指引(下表;源自 AP 行程) |
ao_findings[](每項):
| 欄位 | 型別 | 說明 |
|---|---|---|
| finding_uid | string | finding 識別 |
| ao_id | string | AO 識別(無 AO 時 fallback control_id) |
| state | string | met / not_met / pending |
| description | string | 判定說明 |
| observation_uid | string | 關聯觀察 uid(選填) |
| updated_user | string | 更新者 |
observations[](每項):
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | string | 觀察識別 |
| title | string | 標題 |
| description | string | 觀察內容 |
| methods | string[] | 蒐證方法 |
| subjects | array | 受評對象 |
| relevant_evidence | array | 引用證據 |
| collected | date | 蒐集時間 |
planning(FR-041 稽核指引):
| 欄位 | 型別 | 說明 |
|---|---|---|
| methods | string[] | 蒐證方法 |
| guidance | array | 每項 {method, description} |
| subjects | string[] | 受評對象 |
出處:app/grc/service/assessment_result_app_service.py:385-446(get_findings)。
6.3 判定 / 觀察類
[PUT] /audit-round/{round_uid}/ar/finding/{finding_uid}
存單一 AO 判定(upsert)。Request:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| state | string | 是 | OneOf met / not_met /
pending(JudgeFindingRequest 寫入僅接受這 3
值;第 4 值 not_applicable
不可由手動判定寫入——會被 OneOf 擋成 400,僅在唯讀路徑如 AR 匯入
/ matrix 服務可能出現,見 §12 坑 2) |
| description | string | 否 | 判定說明 / 缺失描述 |
| observation_uid | string | 否 | 關聯觀察 uid(軟連結) |
Response data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| finding_uid | string | finding 識別 |
| ao_id | string | AO 識別 |
| state | string | met / not_met / pending |
| description | string | 判定說明 |
| related_observations | array | 關聯觀察 |
| updated_user | string | 更新者 |
verdict 值 API↔︎DB
映射:assessment_findings.target_status_state
直接存 4 種 product
token——met→satisfied、not_met→not-satisfied、not_applicable→not-applicable、pending→pending(not-applicable/pending
是 OSCAL 規格外的 product token,直存於同一欄,非落
JSONB)。target_status JSONB 是全狀態鏡射(含 reason)非
pending 專屬。出處:serializer
JudgeFindingRequest(api/project/serializers/audit_round.py:114-122)、service
judge_finding(assessment_result_app_service.py:509-543)、jedi-oscal-v2 .../domain/service/ar/ar_finding_matrix_service.py。
[POST] /audit-round/{round_uid}/ar/observations
建立觀察(證據引用不複製)。Request:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| description | string | 是 | 觀察內容(看到什麼) |
| title | string | 否 | 標題 |
| methods | string[] | 否 | 蒐證方法(EXAMINE / INTERVIEW / TEST) |
| relevant_evidence | raw | 否 | 引用證據(自包含 file 中繼資料,不複製) |
| subjects | raw | 否 | OSCAL observation.subjects[](軟引用) |
Response data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | string | 觀察識別 |
| title | string | 標題 |
| description | string | 觀察內容 |
| methods | string[] | 蒐證方法 |
| subjects | array | 受評對象 |
| relevant_evidence | array | 引用證據 |
| collected | date | 蒐集時間 |
出處:serializer
CreateObservationRequest(audit_round.py:125-133)、service
create_observation(assessment_result_app_service.py:545-574)。
PUT /ar/observation/{obs_uid}(UpdateObservationRequest,全欄選填
partial update)/ DELETE(解除所有 finding 連結)同檔。
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/views/project/RoundAuditReviewView.vue |
主檢視(判定 / 觀察 CRUD、證據瀏覽、樹狀態管理皆在此檔;TabView 容器見 hub §7) |
src/components/grc/AuditTreeNav.vue |
左欄控制項樹(搜尋 / 篩選 / 展開;判定色點、繼承圖示) |
src/components/grc/AuditControlRef.vue |
中欄控制項參考(指引 / SSP 現況 / AO 證據頁籤) |
src/components/grc/EvidencePreviewDialog.vue |
證據預覽(檔案 / 連結 / 問卷) |
src/service/BaseService.js |
axios 封裝(AR findings / verdict / obs 直接呼叫) |
src/config/api/api.js |
端點常數(AUDIT_ROUND / SSP_CONTROL_TREE
…) |
Tab1 風險元件見 稽核風險管理 §7。
8. 後端檔案地圖
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 判定矩陣 | api/project/routes/audit_round_route.py::AuditRoundFindingsRoute(GET
/ar/findings) |
assessment_result_app_service.py::get_findings(AO 衍生
+ finding/observation 組裝 + FR-041 planning) |
ao_derivation.py::derive_ao_pairs + AR
finding/observation repo(jedi_oscal_v2) |
| 判定寫入 | 同檔 AuditRoundFindingRoute |
judge_finding(_check_auditor +
assert_round_phase({auditing}) + upsert finding) |
AssessmentResultService.upsert_finding(v2)→
assessment_findings |
| 觀察 | 同檔 AuditRoundObservationsRoute /
AuditRoundObservationRoute |
create_observation / update_observation /
delete_observation |
ArObservationRepoImpl →
assessment_observations |
風險鏈路見 稽核風險管理 §8;finalize / 階段推進鏈路見 稽核執行頁 §8。
DDD 提醒:判定 / 觀察 寫入 API 都照層級走(route
不碰 DB、app service
@transaction、_check_auditor +
assert_round_phase 雙檢查)——與 AP 頁一致的 phase-guard
樣板。
9. DB
9.1 資料表總清單(本頁讀寫的表)
| 表 | 讀/寫 | 說明 | 欄位詳述 |
|---|---|---|---|
| oscal.system_security_plans | 讀 | 凍結 SSP 快照(判定母體) | GAI-SD-03 |
| oscal.assessment_plans | 讀 | AP(import_ap_id 溯源;FR-041 稽核指引來源) |
見 ap-authoring §9.3 |
| oscal.assessment_results / ar_results | 讀/寫 | AR root / 每輪 result | §9.3 |
| oscal.assessment_findings | 寫 | 逐 AO 判定(target_status_state) | §9.3 |
| oscal.assessment_observations | 寫 | 觀察 | §9.3 |
| compliance.job_executions / evidence 相關 | 讀 | AO 證據(lazy 載入) | GAI-SD-03 |
風險相關表(assessment_risks /
assessment_finding_risks)欄位卡見 稽核風險管理
§9.3。
9.2 ER 圖(本頁讀寫範圍)
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)
oscal.assessment_results / ar_results — AR root / 每輪結果
assessment_results 是 AR 文件
root(import_ap_id → AP,NOT
NULL);ar_results 是 results[](一份 AR
多輪,每輪一筆 result)。
| 表.欄位 | 型別 | 說明 |
|---|---|---|
| assessment_results.import_ap_id | bigint | → oscal.assessment_plans(AR 必由本地 AP 建立) |
| ar_results.assessment_result_id | bigint | containment → assessment_results |
| ar_results.reviewed_controls | jsonb | 本結果涵蓋的控制項選取 |
| ar_results.start / end | timestamptz | 評估起 / 迄 |
oscal.assessment_findings — 逐 AO 判定
assessment-common finding(AR / POA&M 共用)。owner
= ar_result_id 或
poam_id(擇一,應用層保證)。
| 欄位 | 型別 | 說明 |
|---|---|---|
| ar_result_id | bigint|null | owner(AR result) |
| uuid | uuid | finding 身分(= FE 的 finding_uid) |
| target_type | varchar(60) | statement-id(AO 層)/ objective-id |
| target_id | varchar(255) | AO 識別(_obj.N)或 fallback control_id |
| target_status_state | varchar(40) | 判定值,直存 4 種 product
token:satisfied(met)/
not-satisfied(not_met)/
not-applicable(na)/ pending(未判定) |
| target_status | jsonb | 全狀態的冗餘鏡射(reason / product state);非 pending 專屬 |
| implementation_statement_uuid | uuid|null | 對應 SSP ssp_statements.uuid(跨文件軟參照) |
| related_observations / related_risks | jsonb | 關聯觀察 / 風險(雙寫保真) |
oscal.assessment_observations — 觀察
assessment-common observation。owner = ar_result_id /
poam_id。required=[collected, description, methods, uuid]。
| 欄位 | 型別 | 說明 |
|---|---|---|
| uuid | uuid | 觀察身分(= obs_uid) |
| description | text | [必填] 觀察內容 |
| methods | jsonb | [必填] EXAMINE / INTERVIEW / TEST / UNKNOWN |
| subjects | jsonb | 受評對象(軟引用) |
| relevant_evidence | jsonb | 佐證(href/description,自包含 file 中繼資料) |
| collected | timestamptz | [必填] 蒐集時間 |
10. 頁面邏輯與資料對應
關鍵欄位對應(畫面 ↔︎ API):
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 樹判定色點 | findingByControl[cid].state(AO roll-up) |
ao_findings[].state(控制項層聚合) |
| verdict SelectButton | editState |
PUT finding state |
| 判定說明 | editDesc |
PUT finding description |
| 內嵌風險(not_met) | inlineRiskSeverity / inlineRiskImpact /
inlineRemediation |
POST/PUT risk + link findings(見 稽核風險管理 §6) |
| 觀察卡 | selectedObservations |
control.observations[] |
儲存流程:判定 = PUT finding →
refreshStats(重取統計);not_met 內嵌風險同存(新建走 POST
risk + PUT risk/findings,既有走 PUT,端點見 稽核風險管理);觀察增改刪後重取對應清單。findingByControl
用淺拷貝觸發 notMetControls /
inScopeTreeGroups 重算。
錯誤對應:BE error envelope → toast;本頁常見碼
GRC_NOT_AUDITOR(403)、GRC_ROUND_PHASE_READONLY(412,過階段)。412
由 axios interceptor → event bus → App.vue toast 統一處理。finalize
相關錯誤碼見 稽核執行頁
§10。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| 通知 | 本頁判定 / 觀察無站內通知 |
| Socket | 本頁無 socket / 輪詢;各動作後手動重取 |
| jedi-* 套件 | jedi-oscal-v2(AR finding/observation repo +
AssessmentResultService)、jedi-auth(JWT) |
| 系統參數 | 蒐證方法來自 AP planning(FR-041)或 fallback OSCAL 三法;無 feature flag |
12. 邊界情況與已知坑
- phase guard BE 硬擋:判定 / 觀察 寫入只在 auditing,過階段直接打 API 也回 412(見 _overview §5 坑 2)
- verdict 值 API↔︎DB 映射:API
FindingState(met/not_met/pending+第 4 值not_applicable);DBtarget_status_state直存 4 種 product tokensatisfied/not-satisfied/not-applicable/pending,target_statusJSONB 是全狀態鏡射非 pending 專屬,比對以target_status_state為準(_overview §5 坑 4) - 判定 AO 層 / 顯示與 POA&M
控制項層:
stats有met/not_met/pending(AO 計數)與controls_with_not_met(控制項計數)兩套;控制項 verdict 由 AO roll-up(任一 AO not_met → 控制項 not_met) - 證據自包含 + lazy 快取:觀察
relevant_evidence內含 file 中繼資料(供 POA&M 免 N+1);證據 per-控制項 lazy 載入並快取,切控制項可能短暫顯示前一個的快取 - 範圍過濾:
inScopeTreeGroups只顯示有 finding 的控制項(AP reviewed-controls 可能縮小 scope),樹上看不到的控制項不代表不存在、是不在本輪範圍 - 內嵌風險走風險寫入路徑:not_met 就地帶的風險與 Tab1
是同一批資料,
notMetControls依賴refreshStats重算——改完 verdict 未刷新前,風險來源清單可能過時(見 稽核風險管理 §12)
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/ 部署文件) - 導航路徑:登入 → 專案 → 進入 auditing 狀態的輪次 → 稽核執行 → Tab「控制項稽核判定」
- 前置資料:輪次需已
start-auditing(AR 矩陣已初始化);控制項要有證據才判得動 - E2E:
compliance-manager-test/repo(Cucumber + Playwright);以稽核執行/audit-review/判定搜尋 - 相關文件:上層導航 稽核執行頁;稽核風險管理(Tab1 系統風險與關聯);FR-038(v2 遷移 B5 AR)、FR-041(稽核指引);DB / API 全量見 GAI-SD-02/03