FR-144 · 討論稿 · 2026-10-17
左側選單多一項「稽核計畫」,進去是跟登入者有關的稽核計畫表格,一列一份、點了直達 FR-141 的填寫頁
FR-141 把稽核計畫頁重做好了,但要進去得「專案列表 → 專案 → 第幾輪 → 第幾次 → 稽核計畫」四層點;左側既有的「我的稽核」又是從啟動稽核後才列,計畫還在填的那段完全沒入口。本案在左側選單加一項「稽核計畫」,進去是一張表格:一個稽核次一列(同一專案內稽第 1 次、第 2 次、外稽各一列),狀態分段鈕帶筆數、一個搜尋框、欄頭排序、分頁、整列可點直達該份計畫。版式與 FR-142 的「我的稽核」、FR-143 的「改善計畫」入口同一套,三個清單頁長一樣。不加表,一支清單查詢端點+一筆選單登記。
雛形審過、D1~D3 已裁,下一步落 design.md,本稿不再改寫。
狀態:討論稿(決策已裁,待落 design) | 日期:2026-10-17 | 雛形:prototype/index.html → 稽核計畫清單
路徑慣例:BE 路徑相對 compliance-manager-be/;FE 路徑相對 compliance-manager-fe/;pkg/ 代表 ~/Projects/Jedicogy/module/jedi-python-package/<套件>/<套件底線名>/。三個 repo 都在 feature/FR-139(FR-141 已併入)。
名詞:稽核次=FR-140 的 compliance.audit_round_passes,一輪裡每做一次稽核一筆(內稽第 1 次、第 2 次、外稽第 1 次),每筆各自一份稽核計畫;稽核計畫(AP)=FR-141 的計畫概要+查核項目規劃兩頁籤;評估目標(AO)=控制項底下的檢查項目;填寫站=流程範本的「稽核計畫填寫」站,輪次狀態 audit_planning;參與者=compliance.project_participants,一人在一專案的角色(manager/auditor/reviewer/viewer)。
| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-17 | 初稿:需求白話、現況盤點、D1~D3(已裁)、資料來源、拆卡、雛形五輪回饋全部反映 | — |
整案一句話:左側選單多一項「稽核計畫」,點進去一張表格列跟我有關的每一份稽核計畫,點列直達 FR-141 的填寫頁。
| 棒 | 做什麼 | 產出 | 完成怎麼判定(決策者親手做什麼、看到什麼) |
|---|---|---|---|
| 0 | 討論稿+雛形 | 本稿、prototype/ |
已審五輪(表格、同 FR-142 版式、百分比、一格一事、名稱與日期) |
| 1 | 清單端點+選單登記 | 新讀模型、新端點、ui_routes seed、權限綁定 |
用顧問帳號打端點,回的列數=DEV 裡該人是 auditor 或 manager、且輪次已到填寫站以後的稽核次數;用 viewer 帳號打到 403 |
| 2 | 清單頁 | FE 新頁、路由、api 常數、i18n、側邊選單徽章 | 登入顧問帳號,左側選單看到「稽核計畫」帶數字;進去對照雛形操作:分段鈕切狀態、搜尋、點欄頭排序、分頁、點列跳到 FR-141 頁且麵包屑顯示對的稽核次;專案經理帳號看到外稽未指派那列按「去指派」跳專案規劃頁 |
依賴:1 → 2。兩棒可同一人接續做;最先看得到畫面的是棒 2,棒 1 做完先用 Swagger 打端點驗資料。
%%{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 TB
subgraph now[現在:四層]
direction LR
A[專案列表] --> B[專案] --> C[第幾輪] --> D[第幾次] --> E[稽核計畫]
end
subgraph new[本案:一層]
direction LR
M[左側選單:稽核計畫] --> T[表格:一列一份] --> E2[稽核計畫]
end
目前稽核計畫都只能從專案進入,很繞。我想直接增加一個入口在左側選單,進去就是看到跟自己相關的稽核計畫清單,一樣可以連到稽核計畫頁面。 — 決策者(2026-10-17)
雛形五輪回饋(全部已反映):不要卡片式清單,用表格,要可搜尋排序;風格要跟 FR-142「我的稽核」統一,不要一個功能設計一次;進度不要進度條、用百分比,進度條要滑鼠才看得到;只列查核項目的進度,計畫概要不用;一個欄位不要塞太多資訊,頂多多一行小字;名稱把「我的」拿掉,就叫「稽核計畫」;日期給完整的,有時間的帶時間,太長可以換行。
為什麼要做:FR-141 把稽核計畫頁做好了,但要進去得點四層。左側「我的稽核」是給判定用的,列的是啟動稽核以後的輪次,計畫還在填的那個階段不在上面。
做出來長什麼樣:左側選單「待辦任務」底下多一項「稽核計畫」,旁邊一個數字是「待填寫」有幾份。進去是表格,一列一份稽核計畫(一個稽核次一份)。上面一排狀態分段鈕帶筆數(待填寫/稽核中/矯正中/待覆核/已結案/全部,預設停「待填寫」),右邊一個搜尋框。欄位:專案(小字框架)、稽核次、狀態、填寫進度(百分比)、填寫期限(待填寫時標逾期或剩幾天)、稽核日期(起迄各一行帶時間)、稽核人員(自己加粗、多的收 +N)、專案經理、最後更新。點列直達該份計畫;最右一顆圖示:待填寫=填寫、其他=檢視、外稽未指派稽核人員且我是專案經理=去指派。
「跟我有關」怎麼判:看專案參與者表。我在該專案是稽核人員或專案經理 → 列出;是覆核者 → 也列出但只能檢視;viewer 不列。
怎麼存:不加表。資料全在既有的稽核次、稽核計畫、參與者三張表,加一支清單查詢。
不做什麼:不動 FR-141 的稽核計畫頁;不碰「我的稽核」頁;不做批次操作。
| 元件 | 現況 | 本案動作 |
|---|---|---|
左側選單 public.ui_routes |
群組 group-project-task(id 42)下:專案儀表板 sort 10、待辦任務 20、專案列表 30、我的稽核 40。選單項靠 route_capabilities 綁能力點,「我的稽核」綁 grc.audit.read |
seed 一筆 audit-plan-list,url /project/audit-plans,sort 25(待辦任務與專案列表之間),綁 grc.audit.read。先例 scripts/sql/2026-10-15-fr139-version-history-routes.sql |
「我的稽核」清單 POST /grc/audits/my/list |
端點在套件 jedi-compliance-audit,實作在主專案 app/readmodel/service/auditor_dashboard_service.py → infra/readmodel/audit/auditor_dashboard_query.py。條件:參與者 role in (auditor, manager)、輪次狀態 in (auditing, remediation, closed)、一輪一列 |
不改它。本案另寫一支同形狀的讀模型,條件換成「稽核次」粒度、狀態從 audit_planning 起 |
稽核次 compliance.audit_round_passes |
欄位 round_id、pass_no、audit_nature(internal/external)、nature_seq、status、assessment_plan_id |
清單主表,一筆一列 |
輪次 compliance.project_audit_rounds |
status、current_pass_id、round_no |
取輪號;狀態分段鈕用稽核次的 status |
計畫概要 oscal.ap_terms_and_conditions、oscal.ap_tasks |
FR-141 A1:六段文字一列一段;單一 task 的 timing.within-date-range 存稽核起迄日期時間 |
讀稽核日期 |
逐 AO oscal.ap_assessment_activities |
FR-141 B1:一條 AO 一個 activity,steps[].props[method] 放方法 |
算填寫進度:有方法的 activity 數/該計畫 AO 總數 |
稽核人員 oscal.parties |
person 型、props responsible-role=assessor,掛在 AP 的 metadata 下;只有姓名與 email,沒有連到登入帳號 |
顯示名單用;不能拿來判「跟我有關」 |
| 填寫期限 | 流程範本站的完成期限,走 workflow_execution 與 job 的 deadline(FR-140 卡 10 已有「列稽核次」API 帶 props) |
讀目前停在填寫站那張 job 的期限;沒有就顯示「—」 |
FE 「我的稽核」頁 src/views/project/MyAuditsView.vue |
卡片式,245 行;FR-142 雛形已提案改表格 | 本案新頁 AuditPlanListView.vue,版式照 FR-142 prototype/my-audits.html;FR-142 改版時兩頁抽共用表格殼 |
| FE 路由 | project-ap-authoring-round 帶 ?pass=<uid> 切非目前稽核次 |
點列導到 {name:'project-ap-authoring-round', params:{id, roundUid}, query:{pass}},目前稽核次不帶 pass |
| 側邊選單徽章 | 現有選單項沒有數字徽章機制 | 棒 2 加:選單載入時打清單端點的 count(只算待填寫);做法照 FR-143 D9 的「改善計畫」入口,兩案共用一個徽章元件 |
D1 選單名稱與位置:「稽核計畫」,放「待辦任務」底下、「專案列表」上面
選了會怎樣:群組「專案與任務」變成專案儀表板/待辦任務/稽核計畫/專案列表/我的稽核。名稱不帶「我的」。FR-143 的「改善計畫」入口定案後排在「我的稽核」底下,三個入口並列:稽核計畫(填)→ 我的稽核(判)→ 改善計畫(排)。
定案理由:與現有「我的稽核」分開而非併成頁籤——兩頁對象不同(填計畫的是顧問、判定的是稽核人員,常同一人但階段不同),併一頁要再猜。被排除:把「我的稽核」改成兩個頁籤。
D2 規劃中(還沒走到填寫站)的稽核次不列
選了會怎樣:清單從輪次走到「稽核計畫填寫」站起才出現(稽核次狀態 audit_planning 以後)。輪次還在 planning 的那些點進去是空的,不列。
定案理由:與「我的稽核」同一條白名單邏輯(它從 auditing 起);列了點進去空白會被當成壞掉。被排除:列出但標「尚未開始」。
D3 外稽還沒指派稽核人員時,專案經理那列按「去指派」跳專案規劃頁
選了會怎樣:稽核次已到填寫站但計畫概要還沒有稽核人員,且登入者是該專案 manager → 稽核人員欄顯示「未指派」橘字、最右圖示變「去指派」,點了到 project-planning-round。登入者只是 auditor → 該列不出現(他還沒被指派)。
定案理由:外稽的稽核人員多是外部顧問,先由 PM 指派才有人填;清單上直接看得到「這份還沒人」比到專案裡才發現好。
| 規則 | 內容 |
|---|---|
| 表格不用卡片 | 一列一份;狀態分段鈕帶筆數;一個搜尋框搜專案、框架、稽核人員、專案經理、稽核性質;欄頭排序(點三次:升、降、取消);每頁 20、分頁 |
| 與 FR-142「我的稽核」同版式 | 置中 80rem、表頭灰底小字、專案名下一行小字、最右一顆圖示鈕、整列可點、分段鈕與搜尋框位置相同。雛形直接共用 FR-142 的 proto.css 與 proto.js |
| 進度用百分比不用進度條 | 「75%」直接印在格子裡,100% 綠字、0% 淡字;分子分母放 title 滑過才看 |
| 只列查核項目進度 | 有填方法的評估目標數/總數;計畫概要六段不列 |
| 一格一件事,最多一行小字 | 專案(小字框架)、稽核次、狀態、進度、期限(小字逾期標籤,只在待填寫)、稽核日期(起、迄各一行帶時間)、稽核人員、專案經理、最後更新(只日期)各自一欄;不列「我的角色」欄——稽核人員欄或專案經理欄哪個有我的名字(加粗)就知道 |
| 日期完整 | 日期全寫 YYYY-MM-DD;有時間的帶 HH:MM;太長換行不截 |
不加表。一支讀模型 infra/readmodel/audit/audit_plan_list_query.py,照 auditor_dashboard_query.py 的寫法(raw SQL、回 list[dict]、不外流 row),一句 SQL 把每列要的東西算完:
| 欄 | 來源 |
|---|---|
| 專案、框架 | compliance.projects(name、框架走既有專案框架欄) |
| 稽核次 | audit_round_passes.pass_no / audit_nature / nature_seq+project_audit_rounds.round_no |
| 狀態 | audit_round_passes.status;白名單 audit_planning, auditing, remediation, pending_reverify, closed(D2) |
| 填寫進度 | 子查詢:該 AP 的 ap_assessment_activities 中 steps 有 method 的筆數/ap_reviewed_controls.control_objective_selections 的 AO 總數。沒有 AP(外稽未開始)回 null → 顯示「尚未建立」 |
| 填寫期限 | 該稽核次停在填寫站的 job deadline;沒有回 null |
| 稽核日期 | ap_tasks.timing.within-date-range.start / end |
| 稽核人員 | 該 AP metadata 下 props responsible-role=assessor 的 person party 名單;空陣列=未指派 |
| 專案經理 | 參與者 role=manager 的 user nickname(可能多位,取全部) |
| 最後更新 | audit_round_passes.updated_at 與 AP 相關表 updated_at 取大 |
| 我的角色 | 參與者表中登入者的 role 集合(FE 用來決定按鈕,不顯示成欄) |
| 可編輯 | 稽核次狀態=audit_planning 且 是目前稽核次 且 我是 auditor 或 manager |
「跟我有關」只看參與者表(project_participants.user_id = 登入者 and role in (auditor, manager, reviewer))。稽核人員 party 沒有帳號對應,不能用。
端點:POST /audit-plans/my/list(主專案 api/project/routes/,不進套件;body {filters:{status,keyword}, page, size, sort},照「待辦任務」的後端分頁回 data + meta);GET /audit-plans/my/count 回待填寫筆數給徽章。守門:@capability_required("grc.audit.read")+@require_license("audit"),與「我的稽核」同。
陷阱:分段鈕的筆數要全狀態一次回
分段鈕上每個狀態的數字是「不套狀態篩選、只套搜尋」的筆數。後端分頁若只回目前狀態那頁,數字就算不出來。端點 meta 多回一個 status_counts(一句 GROUP BY status),FE 不要自己數。
%%{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'}}}%%
sequenceDiagram
participant U as 顧問
participant M as 左側選單
participant L as 稽核計畫清單
participant P as FR-141 稽核計畫頁
U->>M: 登入,看到「稽核計畫 3」
U->>L: 點進去(預設停「待填寫」)
L->>L: 搜尋/排序/切狀態(後端分頁)
U->>P: 點一列(帶 roundUid+pass)
P->>P: 填計畫概要與查核項目,儲存
U->>L: 回清單,該列百分比上升、填完 100%
| 卡 | 做什麼 | 動哪裡 | 依賴 | 驗收 |
|---|---|---|---|---|
| 1 清單端點+選單登記 | 讀模型、app service、route、schema;ui_routes seed(sort 25、綁 grc.audit.read);count 端點 |
BE 新檔 infra/readmodel/audit/audit_plan_list_query.py、app/readmodel/service/audit_plan_list_service.py、route、serializer、scripts/sql/2026-10-xx-fr144-audit-plan-list-route.sql |
— | 概述棒 1 |
| 2 清單頁 | 新頁、路由 /project/audit-plans、api 常數、i18n(zh-tw/en 的 menu 與新頁)、選單徽章 |
FE 新檔 src/views/project/AuditPlanListView.vue、路由、api.js、locales、側邊選單元件 |
1 | 概述棒 2 |
出貨基線:卡 1 的 ui_routes seed 是主線 phase=seed migration,寫完回報「出貨基線待重產」,隨 FR-140/141 收口一起。
| # | 坑 | 怎麼避 |
|---|---|---|
| 1 | 「我的稽核」是一輪一列,本案是一稽核次一列;兩頁的列數會對不上 | 這是刻意的。清單上稽核次欄寫明「第 1 輪 · 內稽第 2 次」 |
| 2 | 外稽未開始時沒有 AP(assessment_plan_id NULL),進度、稽核日期、稽核人員全 null |
SQL 一律 LEFT JOIN;FE 顯示「尚未建立」與「未指派」 |
| 3 | 點列帶錯 pass:目前稽核次帶 ?pass= 會讓 FR-141 頁進「檢視過去那次」唯讀模式 |
目前稽核次(is_current)不帶 pass,其他才帶 |
| 4 | 側邊選單徽章每次切頁都打一次 count 端點 | 只在選單載入與從清單頁離開時打;count SQL 只數待填寫、不 join AP |
| 5 | route_capabilities 漏綁 |
症狀是所有人都看不到選單項且無錯誤。migration 末段照先例加 DO 區塊驗「恰一顆 ALL」 |