稽核風險管理(Audit Risk)
功能群:稽核執行|階段轉換、AP→AR→POA&M 資料模型、AO 判定粒度先讀 功能群總覽|上層導航頁見 稽核執行頁
事實基準:2026-07-05 從 FE
src/views/project/RoundAuditReviewView.vue(Tab1 風險)+ BEassessment_result_app_service.py/ serializerapi/project/serializers/audit_round.py掃出(FR-038 v2 B5 AR,Q2 finding↔︎risk 多對多)
變更紀錄
| 日期 | FR | 變更 |
|---|---|---|
| 2026-07-06 | FR-047 | 自 audit-review.md 拆出:Tab1 風險管理(系統風險 CRUD +
finding 多對多關聯,UC-AR-02)獨立成子頁 |
1. 功能描述
稽核風險管理是 稽核執行頁 的
Tab1「風險」:稽核員把逐控制項判定出的不符合項(not_met
finding)歸納成系統風險(system risk)。每個風險有標題
/ 描述 / 嚴重度(severity)/ 狀態,並勾選由哪幾條 not_met finding
組成(多對多、全量覆寫)。這批風險是 finalize
前置檢查的一部分——每條風險 finalize 前必須至少關聯 1 條 finding(見 _overview §5 坑 6)。風險本體與 Tab0 判定右欄的
not_met 內嵌風險是同一批資料(同
assessment_risks),Tab1 提供全風險的集中管理視圖。
主要使用者:稽核員(auditor)、專案經理(manager,可代行)。
角色速覽(完整定義見 _overview §3;本群主角是 auditor):
| 角色 | 一句話 |
|---|---|
| auditor | 稽核員 — 建立 / 編輯風險與關聯判定的執行者 |
| manager | 專案經理 — 可代行 auditor |
| viewer | 一般參與者 — 只能檢視 |
1.1 功能總覽(本頁全部功能)
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 系統風險列表 | Tab1 呈現本輪全部系統風險(標題 / 描述 / 嚴重度 / 狀態 / 關聯 finding 數) | Tab1 清單 | §6.2 |
| 2 | 建立系統風險 | 標題 / 描述 / 等級 / 來源 not_met 控制項 | Tab1 建立表單 | UC-AR-02 |
| 3 | 編輯 / 刪除風險 | 編輯欄位或刪除(刪除解除 finding 連結) | Tab1 風險卡 | UC-AR-02 |
| 4 | 風險關聯判定(finding) | 一個風險勾選由哪幾條 not_met finding 組成(多對多,全量覆寫) | Tab1 風險卡 sources | UC-AR-02 |
展開成 UC:UC-AR-02(建立系統風險並關聯判定)。判定 / 觀察 / not_met 內嵌風險的輸入端見 稽核判定工作區;頁級 finalize(風險關聯完整性檢查在此把關)見 稽核執行頁 hub。
2. Use Case
角色速覽(完整定義見 _overview §3;本群主角是 auditor):
| 角色 | 一句話 |
|---|---|
| auditor | 稽核員 — 建立 / 編輯風險與關聯判定的執行者 |
| manager | 專案經理 — 可代行 auditor |
| viewer | 一般參與者 — 只能檢視 |
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-AR-02 建立系統風險並關聯判定
| 項目 | 內容 |
|---|---|
| 角色 | auditor(manager 可代行) |
| 前置條件 | 輪次 = auditing;已有 not_met 判定可供關聯 |
| 產出 / 後置條件 | oscal.assessment_risks(severity 存 characterizations
JSONB)+ assessment_finding_risks 關聯;finalize
前每風險至少關聯 1 finding |
3. 權限矩陣
專案層角色基調見 _overview §3。本頁(Tab1 風險)檢查點(FE / BE 對齊,phase guard BE 硬擋):
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 讀 risks | 參與者即可(JWT) | 讀走 writable=False(finalize 後仍可讀) |
assessment_result_app_service.py::list_risks |
| 風險寫入(增 / 改 / 刪 / 關聯) | canEdit = roundStatus==='auditing'(RoundAuditReviewView.vue:49) |
_check_auditor +
assert_round_phase({auditing}) |
assessment_result_app_service.py:111-118、round_guard.py:31 |
判定 / 觀察 寫入的權限見 稽核判定工作區 §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 |
| Finalize 前置:風險皆關聯 | 有風險未關聯 finding →
GRC_RISK_NO_FINDING_LINKED(412) |
assessment_result_app_service.py:809-811 |
finalize 完整前置(含無 pending 判定)與頁級 gate 見 稽核執行頁 §4。
5. UI 設計
實機截圖(STG,亮色模式,2026-07-05,帳號 blsadmin,專案「亞○ CMMC L1 合規」/ auditing 輪次):

狀態呈現:風險 per-按鈕
spinner(savingRisk);風險來源多選對應
notMetControls(state==='not_met' 的 finding),依賴
refreshStats 重算——改完 verdict
未刷新前來源清單可能過時。無 socket,風險增改刪 /
關聯後重取風險清單。
6. API 規格
Envelope:成功 {"code": 1, "data": …}、失敗
{"code": 0, "msg": "…"}。
6.1 總清單(本頁呼叫的 endpoint)
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 載入 | GET /audit-round/{round_uid}/ar/risks |
系統風險清單(含關聯 finding) | §6.2 |
| 風險 | POST /audit-round/{round_uid}/ar/risks |
建立風險 | §6.2 |
| 風險 | PUT/DELETE /ar/risk/{risk_uid} |
編輯 / 刪除風險 | §6.2 |
| 風險關聯 | PUT /ar/risk/{risk_uid}/findings |
關聯 finding(全量覆寫) | §6.2 |
判定 / 觀察端點見 稽核判定工作區 §6;finalize / 匯入 / 專案載入端點見 稽核執行頁 §6。
以下 §6.2 為本頁核心 endpoint 完整規格(欄位逐條對照 serializer / service 組裝碼,出處標在各段末)。
6.2 風險類
[GET] /audit-round/{round_uid}/ar/risks
Response data.risks[](陣列,每項):
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | string | 風險識別 |
| title | string | 標題 |
| severity | string | 嚴重度 |
| description | string | 描述 |
| status | string | 狀態 |
| linked_findings | array | 關聯 finding,每項 {finding_uid, control_id} |
| linked_finding_count | int | 關聯 finding 數 |
[POST] /audit-round/{round_uid}/ar/risks
Request:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| title / description | string | 是 | 風險標題 / 描述 |
| statement | string | 否 | 風險陳述 |
| severity | string | 否 | OneOf
low/medium(預設)/high/critical——存
characterizations JSONB |
| status | string | 否 | OneOf
open(預設)/investigating/remediating/closed |
| remediation | string | 否 | 矯正建議(落 risk.remediations,lifecycle=recommendation) |
PUT /ar/risk/{risk_uid}(UpdateRiskRequest,全欄選填)/
DELETE(解除 finding 連結)同檔。出處:serializer
CreateRiskRequest(audit_round.py:147-162)、service
add_risk(assessment_result_app_service.py:672-686)。
[PUT] /audit-round/{round_uid}/ar/risk/{risk_uid}/findings
風險 ↔︎ finding 關聯(全量覆寫)。Request:
{
"finding_uids": ["string"]
}finding_uids 為 required。雙寫
assessment_finding_risks 關聯表 +
finding.related_risks[](OSCAL 匯出保真)。出處:serializer
LinkRiskFindingsRequest(audit_round.py:182-188)、service
link_risk_findings(assessment_result_app_service.py:763-789)。
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/views/project/RoundAuditReviewView.vue |
主檢視(Tab1 風險 CRUD、關聯 finding、風險來源多選皆在此檔;TabView 容器見 hub §7) |
src/service/BaseService.js |
axios 封裝(AR risks / risk-findings 直接呼叫) |
src/config/api/api.js |
端點常數(AUDIT_ROUND …) |
src/config/locales/i18n/{zh-tw,en}/audit-review.json・round-audit-poam.json |
頁面 i18n(namespace 見 hub §12 坑 6) |
Tab0 判定工作區元件(AuditTreeNav /
AuditControlRef / EvidencePreviewDialog)見 稽核判定工作區
§7。
8. 後端檔案地圖
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 風險列 / 增 / 改 / 刪 | api/project/routes/audit_round_route.py::AuditRoundRisksRoute
/ AuditRoundRiskRoute |
add_risk / update_risk /
delete_risk |
assessment_risks(severity 存 characterizations
JSONB) |
| 風險關聯 finding | 同檔 AuditRoundRiskFindingsRoute |
link_risk_findings(雙寫 finding.related_risks) |
assessment_risks +
assessment_finding_risks |
判定 / 觀察鏈路見 稽核判定工作區 §8;finalize(讀本頁風險做完整性檢查 + 生 POA&M)鏈路見 稽核執行頁 §8。
DDD 提醒:風險寫入 API 都照層級走(route 不碰
DB、app service @transaction、_check_auditor +
assert_round_phase 雙檢查)——與 AP 頁一致的 phase-guard
樣板。
9. DB
9.1 資料表總清單(本頁讀寫的表)
| 表 | 讀/寫 | 說明 | 欄位詳述 |
|---|---|---|---|
| oscal.assessment_risks | 讀/寫 | 系統風險(severity 在 characterizations) | §9.3 |
| oscal.assessment_finding_risks | 讀/寫 | finding ↔︎ risk 多對多 | §9.3 |
| oscal.assessment_findings | 讀 | 關聯來源(not_met finding);雙寫 related_risks
保真 |
見 稽核判定工作區 §9.3 |
| oscal.assessment_remediations | 寫 | 矯正建議(response,remediation 欄落此) | 見 poam.md |
9.2 ER 圖(本頁讀寫範圍)
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)
oscal.assessment_risks — 系統風險
assessment-common risk。owner = ar_result_id /
poam_id。required=[description, statement, status, title, uuid]。
| 欄位 | 型別 | 說明 |
|---|---|---|
| uuid | uuid | 風險身分(= risk_uid) |
| title / description / statement | text | 標題 / 描述 / 風險陳述 |
| status | varchar(40) | open / investigating / remediating / closed |
| characterizations | jsonb | 特徵 facets——風險等級(severity)存這裡,非獨立欄 |
| remediations | jsonb | OSCAL 兜底;抽出真表
assessment_remediations(response,milestone 父層) |
oscal.assessment_finding_risks — finding ↔︎ risk 多對多(FR-038 Q2)
稽核員手動建風險、勾由哪幾條 not_met finding 組成;一風險多 finding、一 finding 多風險。
| 欄位 | 型別 | 說明 |
|---|---|---|
| finding_id | bigint | → assessment_findings(通常 not-satisfied) |
| risk_id | bigint | → assessment_risks |
10. 頁面邏輯與資料對應
關鍵欄位對應(畫面 ↔︎ API):
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 風險列表 | risks |
GET risks data.risks[] |
| 風險嚴重度 | editRisk.severity |
POST/PUT risk severity(存 characterizations) |
| 風險狀態 | editRisk.status |
POST/PUT risk status |
| 風險來源多選 | notMetControls(state==='not_met' 的 finding) |
PUT risk/findings finding_uids |
| 關聯 finding 數 | risk.linked_finding_count |
GET risks linked_finding_count |
儲存流程:風險新建走 POST risks、編輯走
PUT risk/{uid}、刪除走 DELETE risk/{uid};關聯
finding 走 PUT risk/{uid}/findings(全量覆寫,雙寫
finding.related_risks)。增改刪 /
關聯後重取風險清單。風險來源多選 notMetControls 依賴 稽核判定工作區 的 refreshStats
重算。
錯誤對應:BE error envelope → toast;本頁常見碼
GRC_NOT_AUDITOR(403)、GRC_ROUND_PHASE_READONLY(412,過階段);finalize
時的 GRC_RISK_NO_FINDING_LINKED(412)見 稽核執行頁 §10。412 由
axios interceptor → event bus → App.vue toast 統一處理。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| 通知 | 本頁風險增 / 改 / 刪無站內通知 |
| Socket | 本頁無 socket / 輪詢;各動作後手動重取 |
| jedi-* 套件 | jedi-oscal-v2(AR risk repo +
AssessmentResultService)、jedi-auth(JWT) |
| 系統參數 | 無 feature flag |
12. 邊界情況與已知坑
- phase guard BE 硬擋:風險寫入只在 auditing,過階段直接打 API 也回 412(見 _overview §5 坑 2)
- risk severity 存
JSONB:
assessment_risks無 severity 欄,等級在characterizations——比對 / 查詢 severity 時要看 JSONB,不是列舉欄位(_overview §5 坑 5) - finding ↔︎ risk
多對多、全量覆寫:
PUT risk/{uid}/findings是全量覆寫(非增量),漏帶等於解除既有關聯;雙寫assessment_finding_risks關聯表 +finding.related_risks[](OSCAL 匯出保真) - finalize 前置:每風險至少 1 finding:有風險未關聯
finding → finalize 回
GRC_RISK_NO_FINDING_LINKED(412);改風險判定時notMetControls依賴 稽核判定工作區 的refreshStats重算,改完 verdict 未刷新前來源清單可能過時 - 內嵌風險 = 同一批資料:Tab0 判定右欄的 not_met
內嵌風險與 Tab1 風險列表是同
assessment_risks,兩處編輯任一都影響另一(見 稽核判定工作區 §12) - engagement 共用 AR:覆核輪不另開 AR root,append
一筆
ar_results,風險亦掛在該輪 result(_overview §5 坑 7)
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「風險」
- 前置資料:需先在 Tab0 判定出至少一條 not_met finding,才有可關聯的來源
- E2E:
compliance-manager-test/repo(Cucumber + Playwright);以稽核執行/audit-review/風險/risk搜尋 - 相關文件:上層導航 稽核執行頁;稽核判定工作區(Tab0 判定 / not_met 內嵌風險輸入端);FR-038(v2 遷移 B5 AR,Q2 finding↔︎risk 多對多);DB / API 全量見 GAI-SD-02/03