階段推進橫幅(FlowPhaseBanner)
共用元件|出現在多個稽核輪次生命週期頁的頂部(規劃 / 總覽 / 稽核計畫填寫 / 覆核 / 缺失改善)
事實基準:2026-07-19 從 FE / BE code 掃出(FR-050 階段回退補充);endpoint 對
routes.json、欄位對 marshmallow serializer 逐條核
變更紀錄
| 日期 | FR | 變更 |
|---|---|---|
| 2026-07-06 | FR-047 | 從 project-planning.md 抽出成共用元件 spec |
| 2026-07-19 | FR-050 | 新增「返回上一階段」回退功能:manager only + reason 必填
Dialog(白名單
ap_authoring/audit/poam
三階段可退,planning
為鏈頭無上一步不開放);新增階段歷程時間軸展開區(advance/rollback
雙色列表);新增
stage/rollback、stage/transitions 兩
endpoint |
1. 功能描述
FlowPhaseBanner 是稽核輪次階段推進的共用 UI
元件(FlowPhaseBanner.vue),固定在生命週期頁頂部。它把當前輪次的
BPMN 流程狀態呈現成一條階段軸(規劃 → 稽核計畫填寫 →
稽核 →
缺失改善),標出目前階段、各階段完成狀態、以及「推進到下一階段」的前置條件與可否推進。推進時呼叫後端
stage/advance,由 handler 執行副作用(如凍結
SSP、開新輪)後再驅動 BPMN 前進——副作用與 BPMN 前進在同一
transaction,失敗全 rollback。
因為同一條輪次狀態機貫穿多個頁面,此橫幅在各頁重複出現、行為一致,故抽成單一元件 spec;各頁只引用本頁、不重複描述機制(見 §1.2)。輪次 7 態狀態機(含轉換圖)見 _overview §2。
FR-050(2026-07-19)新增「返回上一階段」回退能力:manager
可在 ap_authoring / audit / poam
三個階段將輪次退回相鄰上一步(planning
是鏈頭無上一步,不開放)。回退會觸發副作用復原(frozen SSP / AP 草稿 /
POA&M 標記 superseded,不物理刪)+ BPMN job
復原(revert_job)+ round.status 回寫,同一
transaction 失敗全 rollback;每次 advance 或 rollback
都落一筆到「階段歷程時間軸」供稽核追溯。
主要使用者:該 stage 的
main_roles(BPMN 範本定義,多為 auditor / manager);專案
manager 可全域代行推進、亦是回退唯一允許角色。
角色速覽(完整定義見 專案管理 _overview §3):
| 角色 | 一句話 |
|---|---|
| manager | 專案經理 — 可全域代行推進任一 stage(含
force);回退唯一允許角色 |
| main_roles | 該 stage BPMN 定義的負責角色 — 前置通過才可推進 |
| 參與者 | 其餘 — 只看得到進度,不能推進、不能回退 |
1.2 嵌入此元件的頁面(各頁只引用、不重複描述)
| 頁面 | 用途 |
|---|---|
| 專案規劃頁 | planning 階段主工作區頂部 |
| 專案總覽頁 | 輪次總覽頁頂部 |
| 稽核計畫填寫 | AP 撰寫階段推進 |
| 稽核覆核 | 稽核 / 覆核階段(含 review 型 decision) |
| POA&M 缺失改善 | 缺失改善階段推進 / finalize |
各頁的「階段推進」描述一律指回本頁;頁內只保留「本頁對應哪個 stage、推進後去哪」的一句話上下文。
2. Use Case
流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-FB-01 推進階段
| 項目 | 內容 |
|---|---|
| 角色 | 該 stage 的 main_roles;manager 可全域代行 |
| 前置條件 | stage 的 precondition handler 通過(如任務執行完成度);banner 顯示
user_can_advance |
| 產出 / 後置條件 | handler 副作用先行(同 transaction),成功則 BPMN 前進一個
UserTask、project_audit_rounds.status 同步;失敗全
rollback |
UC-FB-02 返回上一階段(回退,FR-050)
| 項目 | 內容 |
|---|---|
| 角色 | manager only(無 main_roles 代行概念,不同於推進) |
| 前置條件 | reason 非空白字串;round.status 非
closed;round.status
落在回退邊界白名單(audit_planning/auditing/remediation);registry
查得到對應 handler |
| 產出 / 後置條件 | handler 副作用復原(SSP/AP/POA&M 標
superseded,不物理刪)→ 落
round_stage_transitions(direction=rollback)→ BPMN
revert_job 復原 → round.status 回寫上一階段 →
_sync_project_status;同一 transaction,任何一步失敗全
rollback |
3. 權限矩陣
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 讀階段資訊 | 參與者即可 | 隨頁面權限 | stage_advance_service.get_current_stage_info |
| 推進階段 | banner user_can_advance |
stage main_roles(manager 代行)→ 不符
GRC_403050(GRC_STAGE_ROLE_FORBIDDEN);force
非 manager → GRC_403002 |
app/flow_engine/service/stage_advance_service.py:286-304 |
| 前置不過 | 按鈕禁用帶原因 | precondition handler → 不過
GRC_412021(GRC_STAGE_PRECONDITION_FAILED) |
同上(_run_precondition,約
:307-314) |
| 讀階段歷程時間軸 | 參與者即可(同讀階段資訊) | 隨頁面權限 | audit_round_app_service.list_stage_transitions |
| 回退階段(FR-050) | banner isManager && canRollback(FE 白名單鏡像
BE,見 §12 坑1) |
manager only → 非 manager
GRC_NOT_MANAGER(403);reason 空 →
GRC_400096;closed 輪次或狀態不在白名單 →
GRC_412047;handler 未註冊 → GRC_400097 |
app/flow_engine/service/stage_rollback_service.py:90-133 |
4. 狀態機與前置條件
輪次 7 態狀態機見 _overview §2。本元件相關 gate:
| 條件 | 效果 | 出處 |
|---|---|---|
| stage precondition handler 通過 | banner 顯示可推進、按鈕啟用 | stage_advance_service(precondition_key →
handler) |
| precondition 不過 | 按鈕禁用 + precondition.reason_i18n_key
顯示原因;強推(force)→ GRC_412021 |
同上 |
| review 型 stage | 推進必帶 decision(approve/reject);reject 需
comment |
stage/advance request |
| 使用者非 main_roles 且非 manager | 無推進按鈕 | user_can_advance=false |
round.status ∈
{audit_planning, auditing, remediation} |
該 stage 顯示「返回上一階段」按鈕(manager
可見);planning 為鏈頭無按鈕 |
stage_rollback_service.py
_ROLLBACK_BOUNDARIES(FE 鏡像白名單見
FlowPhaseBanner.vue
ROLLBACK_ELIGIBLE_STAGE_CODES) |
round.status == closed |
回退入口不顯示;API 直打也擋 | FE:無
banner(isFlowClosed);BE:GRC_412047 |
| 回退 reason 空白 | 送出鈕 disabled(FE);BE 再擋一次 | FE !rollbackReason.trim();BE
GRC_400096 |
5. UI 設計
版面骨架(元件解剖 + 代表性 API + 嵌入頁面,區塊可點):
📸 實機截圖(亮色模式):橫幅特寫見 專案規劃頁 §5 的
planning-banner.png(規劃→稽核計畫填寫→稽核→缺失改善 階段軸)。
狀態呈現:階段軸的每個點依 progress[].status(done /
current / pending)上色;current_stage 為 null
時(流程未開始或已結束)不顯示推進區、整個 Banner
不渲染(isFlowClosed);無 socket,推進 /
回退後由呼叫端重載頁面資料。
FR-050 回退
UI(2026-07-19):isManager && canRollback
為真時,動作區顯示「返回上一階段」按鈕(canRollback 依 FE
白名單 ap_authoring/audit/poam
判斷,鏡像 BE _ROLLBACK_BOUNDARIES,見 §12 坑1);點擊開
Dialog 要求填 reason(未填送出鈕 disabled),確認鈕
severity="danger"。時間軸展開鈕(pi-history
icon-only)永遠顯示,點擊切換 StageTransitionTimeline.vue
展開區——垂直清單依 direction 上色(advance 藍
pi-arrow-right / rollback 橘
pi-replay),rollback 項額外顯示 reason 與
operator_nickname。
6. API 規格
Envelope:成功 {"status": true, "data": …}、失敗
{"error_code": "…", "msg": "…"}。
6.1 總清單
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 階段 | GET
/project/{uid}/audit-round/{round_uid}/stage/info |
當前階段資訊 + 前置條件 + 進度點 | §6.2 |
| 階段 | POST
/project/{uid}/audit-round/{round_uid}/stage/advance |
推進階段(handler 副作用 + BPMN 前進,atomic) | §6.2 |
| 回退(FR-050) | POST
/project/{uid}/audit-round/{round_uid}/stage/rollback |
返回上一階段(manager only、reason 必填、handler 副作用復原 + BPMN 復原,atomic) | §6.3 |
| 回退(FR-050) | GET
/project/{uid}/audit-round/{round_uid}/stage/transitions |
階段歷程時間軸(advance+rollback,依 created_at 升冪) | §6.3 |
6.2 核心 endpoint
[GET] /project/{project_uid}/audit-round/{round_uid}/stage/info
Response data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| current_stage | object|null | {code, name_i18n, kind, complete_button_label_i18n, reject_button_label_i18n, complete_handler_key, precondition_key, main_roles[]};null
= 流程未開始或已結束 |
| user_can_advance / user_role | bool / string | 當前使用者可否推進、其角色 |
| precondition | object|null | {passed: bool, reason_i18n_key, context} —
不通過時按鈕禁用並顯示原因 |
| progress[] | array | 進度點:{code, name_i18n, status: done/current/pending} |
| workflow_execution_uid / workflow_status / job_id | string|null | BPMN 狀態 |
出處:api/flow_engine/serializers/stage_advance.py:56-66
[POST] /project/{project_uid}/audit-round/{round_uid}/stage/advance
Request:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| force | bool | 否 | 強制推進(限 manager) |
| decision | string | 否 | approve / reject——review 型 stage
必填 |
| comment | string | 否 | reject 時必填,寫入 job_execution_comments |
| ctx | dict | 否 | handler 上下文 |
Response data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| advanced | bool | 是否成功推進 |
| handler_result | — | handler 執行結果 |
| current_stage_info | object | 推進後的新 stage info |
出處:同檔 :8-27
6.3 回退 endpoint(FR-050)
[POST] /project/{project_uid}/audit-round/{round_uid}/stage/rollback
Request:
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| reason | string | 是 | 回退原因(min length 1,前後端雙重必填檢查) |
| ctx | dict | 否 | 額外 context(如 user_nickname,route 層自動塞入) |
Response data:
| 欄位 | 型別 | 說明 |
|---|---|---|
| rolled_back | bool | 是否成功回退 |
| handler_result | dict|null | rollback handler
執行結果(AuditRoundAppService.rollback_to_* 回傳的輪次
DTO) |
錯誤:
| Error Code | HTTP | 情境 |
|---|---|---|
(沿用 GRC_NOT_MANAGER) |
403 | 非 manager |
GRC_400096(GRC_ROUND_ROLLBACK_REASON_REQUIRED) |
400 | reason 空白 |
GRC_412047(GRC_ROUND_ROLLBACK_NOT_ALLOWED) |
412 | round.status == closed,或狀態不在回退邊界白名單(audit_planning/auditing/remediation)——route
進來的請求兩者皆走此碼(stage_rollback_service.py 兩個獨立
if) |
GRC_412046(GRC_ROUND_ROLLBACK_INVALID_STATE) |
412 | app service 層 rollback_to_*
的狀態複驗分支(防併發競態雙保險)——單線程請求在 route 層即被
GRC_412047 擋下,正常流程打不到此碼 |
GRC_400097(GRC_ROUND_ROLLBACK_HANDLER_NOT_REGISTERED) |
400 | registry 查無對應 handler(理論上不應發生,防呆) |
出處:app/flow_engine/service/stage_rollback_service.py:72-168;error
code 定義 common/code/grc_error_code.py:338-341
[GET] /project/{project_uid}/audit-round/{round_uid}/stage/transitions
Response data:陣列,依 created_at
升冪排序
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | string | 該筆歷程 uid |
| from_stage / to_stage | string | stage_objects.code(非 i18n dict,FE
自行對照顯示名稱) |
| direction | string | advance(推進)|rollback(退回) |
| operator | string | login_name |
| operator_nickname | string|null | 顯示用暱稱快照 |
| reason | string|null | rollback 必有;advance 恆 null |
| created_at | datetime |
出處:app/grc/service/audit_round_app_service.py:696-717(list_stage_transitions);serializer
api/flow_engine/serializers/stage_rollback.py
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/components/grc/FlowPhaseBanner.vue |
本元件(階段軸 + 前置顯示 + 推進 / 退回鈕 + 回退按鈕與 Dialog + 時間軸展開切換;各生命週期頁 import) |
src/components/grc/StageTransitionTimeline.vue |
階段歷程時間軸子元件(FR-050,純展示,caller-driven:entries 由
Banner 透過 StageService.getStageTransitions
取得後傳入) |
src/composables/useStageInfo.js |
stageInfo 狀態管理 + advance /
rollback 方法封裝 |
src/service/StageService.js |
呼叫 stage/info / stage/advance /
stage/rollback(FR-050)/
stage/transitions(FR-050) |
8. 後端檔案地圖(本元件核心鏈路)
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 階段資訊 / 推進 | api/flow_engine/routes/stage_advance_route.py |
app/flow_engine/service/stage_advance_service.py(get_current_stage_info
/ advance_stage) |
BPMN 當前 UserTask + compliance.stage_objects + handler
registry(app/grc/service/oscal_stage_handlers.py) |
| 回退(FR-050) | api/flow_engine/routes/stage_rollback_route.py(StageRollbackResource
/ RoundStageTransitionsResource) |
app/flow_engine/service/stage_rollback_service.py(StageRollbackService.rollback_stage)+
app/grc/service/audit_round_app_service.py(rollback_to_planning
/ rollback_to_audit_planning /
rollback_to_auditing /
list_stage_transitions) |
rollback handler
registry(app/grc/service/oscal_stage_rollback_handlers.py)+
BPMN
WorkflowExecutionService.revert_job(jedi-flow-engine,bypass_round_frozen_gate=True)+
compliance.round_stage_transitions /
compliance.round_rollback_supersessions |
DDD 提醒:推進 / 回退的 handler 副作用(凍結 SSP、開輪、標記 superseded 等)與 BPMN 前進/復原在同一 transaction,任何一步失敗整筆 rollback——維護 handler 時不可把副作用抽到 transaction 外。
9. DB
9.1 資料表總清單
| 表 | 讀寫 | 說明 | 詳述 |
|---|---|---|---|
| compliance.stage_objects | 讀 | 階段行為定義(precondition / handler / main_roles / label i18n key) | §9.3 |
| compliance.project_audit_rounds | 讀 / 寫(status) | 推進 / 回退後同步輪次狀態 | 規劃頁 §9.3 |
| compliance.job_execution_comments | 寫(review reject) | reject 時寫入 comment | GAI-SD-03 |
| compliance.round_stage_transitions | 寫 / 讀 | 階段歷程時間軸(FR-050,advance+rollback 皆落表) | §9.3 |
| compliance.round_rollback_supersessions | 寫 | 回退時 SSP/AP/POA&M 作廢標記(FR-050,不物理刪) | §9.3 |
9.3 核心表欄位
compliance.stage_objects — 階段行為定義
每個 BPMN stage 對應一列,定義該階段的前置條件 handler、完成
handler、負責角色與按鈕文案 i18n key。stage/info 的
current_stage 大部分欄位即取自此表。
| 欄位 | 型別 | 說明 |
|---|---|---|
| code | varchar | stage 代碼(對應 BPMN UserTask) |
| name_i18n / *_button_label_i18n | varchar | 顯示文案 i18n key |
| kind | varchar | stage 導頁型別(routed=依 route_pattern
導頁 / stateful=站內完成);「review 型
stage」是另一軸,判斷依 code == 'review',非
kind |
| precondition_key / complete_handler_key | varchar | 前置條件 / 完成 handler 的 registry key |
| default_main_roles | jsonb | 該階段的 fallback 主要角色清單;實際生效的
main_roles(API 回傳)= BPMN UserTask 的
main_role property override 優先,無 override 才 fallback
此欄位(stage_advance_service.py:_resolve_main_roles) |
欄位以
db_schema.json為準;stage_objects 為行為配置表,實際 handler 實作在app/grc/service/oscal_stage_handlers.py的 registry。
compliance.round_stage_transitions — 階段歷程時間軸(FR-050)
advance / rollback 皆落表;round_id soft-ref
project_audit_rounds.id(不建 FK
constraint,比照母表軟隔離慣例,見 migration
scripts/sql/2026-07-19-fr050-round-stage-transitions.sql)。
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(36) | 唯一識別 |
| round_id | bigint | → project_audit_rounds.id(soft ref) |
| from_stage / to_stage | varchar(50) | stage_objects.code |
| direction | varchar(10) | advance | rollback(DB CHECK 限定) |
| operator / operator_nickname | varchar(255) | login_name / 顯示暱稱快照 |
| reason | text | rollback 必填(DB CHECK 強制非空白);advance 恆 NULL |
| created_at | timestamptz |
compliance.round_rollback_supersessions — 回退作廢標記(FR-050)
design §4.4 裁決選 B:本專案側標記表,不動 jedi-oscal-v2
PublishStatus 列舉;SSP/AP/POA&M 三種 entity
統一走本表,不物理刪、不改動套件層狀態欄。讀取封裝在
RoundRollbackSupersessionDomainService。
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(36) | 唯一識別 |
| entity_type | varchar(20) | ssp | ap | poam(DB CHECK
限定) |
| entity_id | bigint | 依 entity_type 指向對應表 id(soft ref) |
| round_id | bigint | → project_audit_rounds.id(哪一輪回退觸發的標記) |
| reason | text | 回退原因快照(與 round_stage_transitions.reason
同值,去正規化) |
| operator | varchar(255) | login_name |
| superseded_at | timestamptz |
10. 頁面邏輯與資料對應
| 畫面元素 | FE state | API 欄位 |
|---|---|---|
| 階段軸各點 | stage.progress |
progress[].{code, name_i18n, status} |
| 目前階段標題 | stage.current_stage |
current_stage.name_i18n |
| 推進按鈕啟用 | canAdvance |
user_can_advance &&
precondition.passed |
| 禁用原因 | preconditionReason |
precondition.reason_i18n_key |
| 退回按鈕(review 型) | 依 current_stage.kind |
reject_button_label_i18n |
| 返回上一階段按鈕(FR-050) | isManager && canRollback |
user_role === 'manager' &&
current_stage.code ∈ FE 白名單 |
| 時間軸展開內容(FR-050) | transitions(StageTransitionTimeline
entries) |
GET stage/transitions 回傳陣列 |
推進流程:按推進 → (review 型帶
decision/comment)→ POST
stage/advance → handler 副作用 + BPMN 前進(atomic)→ 回新
current_stage_info,呼叫端重載頁面。
回退流程(FR-050):按「返回上一階段」→ Dialog 填
reason → POST stage/rollback → handler 副作用復原 + 落
round_stage_transitions + BPMN revert_job
復原(atomic)→ 重載
stage/info;transitionsStale 旗標由推進 /
回退共同標記,時間軸展開中即時重抓。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| 交易性 | handler 副作用先於 BPMN 推進 / 復原,同一 transaction,失敗全 rollback |
| BPMN | 推進 = jedi-flow-engine 驅動 BPMN 引擎前進一個
UserTask;回退(FR-050)= BE 層
WorkflowExecutionService.revert_job 驅動 BPMN 復原(含
task_survey EDITING 復原副作用),呼叫時傳
bypass_round_frozen_gate=True 繞過「規劃期才可退回」凍結
gate |
| socket | 無(推進 / 回退後呼叫端重載) |
| 依賴 | jedi-flow-engine(BPMN)、compliance.stage_objects(行為配置)、handler
registry(含 FR-050 rollback handler
分支)、compliance.round_stage_transitions /
compliance.round_rollback_supersessions |
12. 邊界情況與已知坑
- 副作用與 BPMN 前進同 transaction:handler(凍結 SSP / 開輪)失敗會連 BPMN 前進一起 rollback——不可把副作用移出交易,否則會出現「BPMN 前進了但副作用沒做」的不一致。
- review 型 stage 必帶
decision:
kind=review的 stage 推進若沒帶decision(approve/reject)會被擋;reject 另需comment(寫job_execution_comments)。 - manager 全域代行 + force:manager 可對非自己
main_roles 的 stage
推進(
force);審計上留意「誰推進了不屬於自己角色的階段」。 - current_stage 為 null 的兩種情境:流程未開始 vs
已結束都回 null,UI 需靠
progress[]或輪次 status 區分,別只看 current_stage。 - FE 回退白名單是鏡像判斷,非 API
回傳旗標(FR-050):
stage/info沒有回傳「這個 stage 可否回退」的欄位,FEROLLBACK_ELIGIBLE_STAGE_CODES = ['ap_authoring', 'audit', 'poam']是手動鏡像 BE_ROLLBACK_BOUNDARIES(key 是round.status)——兩者維護時必須同步改,BE 白名單加減邊界時別漏改 FE 常數,否則會出現「按鈕顯示但一按就 412」或「符合條件卻不顯示」的落差。 - 只允許退回相鄰上一步,跨階段靠連按:v1
三條回退邊界(
audit_planning→planning/auditing→audit_planning/remediation→auditing)都只退一步,不支援一次跳兩級;要退更早的階段須連續操作,每次各留一筆round_stage_transitions。 - 回退鐵則:不物理刪,一律作廢標記:SSP/AP/POA&M
回退時標記
superseded(
round_rollback_supersessions)而非刪除,重推進會產生新的 snapshot/草稿;已填的 AR 矩陣在auditing→audit_planning回退時保留可見(非作廢,因內容仍要可編輯續填),與 SSP/AP/POA&M 的作廢語意不同,勿混淆。 - 回退繞過凍結 gate:
revert_job呼叫傳bypass_round_frozen_gate=True,僅限本回退路徑;既有其他呼叫點保持False不受影響(維護時不可把這個 flag 誤用到非回退情境)。
13. 開發與驗證
- 跑起來:起 BE + FE;登入見
.env/ 部署文件測試帳號 - 導航:任一嵌入頁(規劃 / 總覽 / 稽核計畫 / 覆核 / 缺失改善)頂部即見橫幅
- 前置資料:需一個有 BPMN 流程綁定的輪次;測不同 stage 的前置 handler(如任務完成度未達 → 按鈕禁用帶原因)、測 review 型的 approve/reject
- 回退測試(FR-050):manager 帳號在
ap_authoring/audit/poam三階段各測一次回退(reason 必填、成功後時間軸多一筆 rollback 條目);非 manager 帳號確認按鈕不顯示;closed輪次確認整個 Banner 不顯示;推進/回退交替操作確認時間軸即時刷新 - 相關:輪次狀態機見 _overview §2;各嵌入頁見
§1.2;stage handler registry
app/grc/service/oscal_stage_handlers.py;rollback handler registryapp/grc/service/oscal_stage_rollback_handlers.py;FR-050 implementation-plan.md / design.md