---
title: "稽核計畫：左側選單直達入口 — 討論稿 (FR-144)"
brand: "Guidant AI · **FR-144** 稽核計畫入口"
eyebrow: "FR-144 · 討論稿 · 2026-10-17"
h1: "稽核計畫直達入口"
subtitle: "左側選單多一項「稽核計畫」，進去是跟登入者有關的稽核計畫表格，一列一份、點了直達 FR-141 的填寫頁"
lede: "FR-141 把稽核計畫頁重做好了，但要進去得「專案列表 → 專案 → 第幾輪 → 第幾次 → 稽核計畫」四層點；左側既有的「我的稽核」又是從啟動稽核後才列，計畫還在填的那段完全沒入口。本案在左側選單加一項「稽核計畫」，進去是一張表格：一個稽核次一列（同一專案內稽第 1 次、第 2 次、外稽各一列），狀態分段鈕帶筆數、一個搜尋框、欄頭排序、分頁、整列可點直達該份計畫。版式與 FR-142 的「我的稽核」、FR-143 的「改善計畫」入口同一套，三個清單頁長一樣。不加表，一支清單查詢端點＋一筆選單登記。"
chips: [
  {text: "D1～D3 決策者 2026-10-17 已裁", kind: ok},
  {text: "雛形第五輪已審", kind: accent},
  {text: "不動套件、不加表、一支端點", kind: plain},
  {text: "站在 FR-141（feature/FR-139）之上", kind: plain}
]
footer: "FR-144 · 討論稿 · 2026-10-17 · 輸入：決策者 2026-10-17 需求與雛形五輪回饋 · 現況依據：程式碼與 DEV 資料庫（2026-10-17）· 沿革見 git log"
---

> 雛形審過、D1～D3 已裁，下一步落 design.md，本稿不再改寫。

**狀態**：討論稿（決策已裁，待落 design）　｜　**日期**：2026-10-17　｜　**雛形**：[prototype/index.html](prototype/index.html) → [稽核計畫清單](prototype/my-audit-plans.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）。

## 變更紀錄 {#changelog nav="變更紀錄"}

| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-17 | 初稿：需求白話、現況盤點、D1～D3（已裁）、資料來源、拆卡、雛形五輪回饋全部反映 | — |

---

## 分工概述（30 秒版） {#overview nav="概述"}

整案一句話：**左側選單多一項「稽核計畫」，點進去一張表格列跟我有關的每一份稽核計畫，點列直達 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 打端點驗資料。

```{.mermaid cap="圖 1 — 使用者路徑：現在四層 vs 本案一層"}
%%{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
```

---

## 需求 {#need nav="需求"}

### 決策者原話（2026-10-17）

> 目前稽核計畫都只能從專案進入，很繞。我想直接增加一個入口在左側選單，進去就是看到跟自己相關的稽核計畫清單，一樣可以連到稽核計畫頁面。
> [— 決策者（2026-10-17）]{.attr}

雛形五輪回饋（全部已反映）：**不要卡片式清單，用表格，要可搜尋排序**；**風格要跟 FR-142「我的稽核」統一，不要一個功能設計一次**；**進度不要進度條、用百分比，進度條要滑鼠才看得到**；**只列查核項目的進度，計畫概要不用**；**一個欄位不要塞太多資訊，頂多多一行小字**；**名稱把「我的」拿掉，就叫「稽核計畫」**；**日期給完整的，有時間的帶時間，太長可以換行**。

### 白話複述（決策者 2026-10-17 已確認）

**為什麼要做**：FR-141 把稽核計畫頁做好了，但要進去得點四層。左側「我的稽核」是給判定用的，列的是啟動稽核以後的輪次，計畫還在填的那個階段不在上面。

**做出來長什麼樣**：左側選單「待辦任務」底下多一項「稽核計畫」，旁邊一個數字是「待填寫」有幾份。進去是表格，一列一份稽核計畫（一個稽核次一份）。上面一排狀態分段鈕帶筆數（待填寫／稽核中／矯正中／待覆核／已結案／全部，預設停「待填寫」），右邊一個搜尋框。欄位：專案（小字框架）、稽核次、狀態、填寫進度（百分比）、填寫期限（待填寫時標逾期或剩幾天）、稽核日期（起迄各一行帶時間）、稽核人員（自己加粗、多的收 +N）、專案經理、最後更新。點列直達該份計畫；最右一顆圖示：待填寫＝填寫、其他＝檢視、外稽未指派稽核人員且我是專案經理＝去指派。

**「跟我有關」怎麼判**：看專案參與者表。我在該專案是稽核人員或專案經理 → 列出；是覆核者 → 也列出但只能檢視；viewer 不列。

**怎麼存**：不加表。資料全在既有的稽核次、稽核計畫、參與者三張表，加一支清單查詢。

**不做什麼**：不動 FR-141 的稽核計畫頁；不碰「我的稽核」頁；不做批次操作。

---

## 現況盤點 {#inventory nav="盤點"}

| 元件 | 現況 | 本案動作 |
|---|---|---|
| 左側選單 `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～D3（決策者 2026-10-17 已裁） {#decisions nav="決策"}

::: {.callout .decided}
**D1 選單名稱與位置：「稽核計畫」，放「待辦任務」底下、「專案列表」上面**

選了會怎樣：群組「專案與任務」變成專案儀表板／待辦任務／稽核計畫／專案列表／我的稽核。名稱不帶「我的」。FR-143 的「改善計畫」入口定案後排在「我的稽核」底下，三個入口並列：稽核計畫（填）→ 我的稽核（判）→ 改善計畫（排）。

[**定案理由**：與現有「我的稽核」分開而非併成頁籤——兩頁對象不同（填計畫的是顧問、判定的是稽核人員，常同一人但階段不同），併一頁要再猜。被排除：把「我的稽核」改成兩個頁籤。]{.rec}
:::

::: {.callout .decided}
**D2 規劃中（還沒走到填寫站）的稽核次不列**

選了會怎樣：清單從輪次走到「稽核計畫填寫」站起才出現（稽核次狀態 `audit_planning` 以後）。輪次還在 `planning` 的那些點進去是空的，不列。

[**定案理由**：與「我的稽核」同一條白名單邏輯（它從 auditing 起）；列了點進去空白會被當成壞掉。被排除：列出但標「尚未開始」。]{.rec}
:::

::: {.callout .decided}
**D3 外稽還沒指派稽核人員時，專案經理那列按「去指派」跳專案規劃頁**

選了會怎樣：稽核次已到填寫站但計畫概要還沒有稽核人員，且登入者是該專案 manager → 稽核人員欄顯示「未指派」橘字、最右圖示變「去指派」，點了到 `project-planning-round`。登入者只是 auditor → 該列不出現（他還沒被指派）。

[**定案理由**：外稽的稽核人員多是外部顧問，先由 PM 指派才有人填；清單上直接看得到「這份還沒人」比到專案裡才發現好。]{.rec}
:::

### 雛形回饋定下的版面規則（不另開 D，照做）

| 規則 | 內容 |
|---|---|
| 表格不用卡片 | 一列一份；狀態分段鈕帶筆數；一個搜尋框搜專案、框架、稽核人員、專案經理、稽核性質；欄頭排序（點三次：升、降、取消）；每頁 20、分頁 |
| 與 FR-142「我的稽核」同版式 | 置中 80rem、表頭灰底小字、專案名下一行小字、最右一顆圖示鈕、整列可點、分段鈕與搜尋框位置相同。雛形直接共用 FR-142 的 `proto.css` 與 `proto.js` |
| 進度用百分比不用進度條 | 「75%」直接印在格子裡，100% 綠字、0% 淡字；分子分母放 title 滑過才看 |
| 只列查核項目進度 | 有填方法的評估目標數／總數；計畫概要六段不列 |
| 一格一件事，最多一行小字 | 專案（小字框架）、稽核次、狀態、進度、期限（小字逾期標籤，只在待填寫）、稽核日期（起、迄各一行帶時間）、稽核人員、專案經理、最後更新（只日期）各自一欄；不列「我的角色」欄——稽核人員欄或專案經理欄哪個有我的名字（加粗）就知道 |
| 日期完整 | 日期全寫 YYYY-MM-DD；有時間的帶 HH:MM；太長換行不截 |

---

## 資料來源與清單查詢 {#model nav="資料"}

不加表。一支讀模型 `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")`，與「我的稽核」同。

::: {.callout .warn}
**陷阱：分段鈕的筆數要全狀態一次回**

分段鈕上每個狀態的數字是「不套狀態篩選、只套搜尋」的筆數。後端分頁若只回目前狀態那頁，數字就算不出來。端點 meta 多回一個 `status_counts`（一句 `GROUP BY status`），FE 不要自己數。
:::

---

## 端到端流程 {#flow nav="流程"}

```{.mermaid cap="圖 2 — 顧問從登入到填完一份計畫"}
%%{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%
```

---

## 拆卡草案 {#cards nav="拆卡"}

| 卡 | 做什麼 | 動哪裡 | 依賴 | 驗收 |
|---|---|---|---|---|
| **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 收口一起。

---

## 不做清單 {#out nav="不做"}

- 不動 FR-141 稽核計畫頁本身、不動「我的稽核」頁與其端點。
- 不做批次操作（批次送審、批次匯出 Excel）。
- 不列規劃中的輪次（D2）。
- 不用稽核人員 party 判歸屬（沒有帳號對應；要做要先在 party 加 user 連結，那是 FR-141 的延伸，不在本案）。
- 不做「依框架篩選」下拉，交給搜尋框（與 FR-142 做法一致）。

---

## 坑與陷阱 {#pitfalls nav="坑"}

| # | 坑 | 怎麼避 |
|---|---|---|
| 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」 |
