跳轉到

角色明細(Role Detail)

功能群:系統管理|權限模型先讀 功能群總覽 §2|上層:角色

事實基準:2026-10-08 從 FE src/views/access/RoleDetail.vue、src/components/access/PermissionTree.vue、src/composables/usePermissionGrouping.js/usePermissionOwnership.js + jedi-iam role_route.py、role_domain_service.py + BE api/permission_matrix/、core/host_capabilities.py + 本機 DEV DB 掃出。

變更紀錄

日期 FR 變更
2026-10-08 FR-132 新頁:新增/編輯/檢視角色同一頁,權限樹+可見選單即時預覽,取代角色權限管理表單頁

1. 功能描述

角色明細是 角色 列表點進來的頁(route access-role-detail,/access/roles/:uid;新增=access-role-new,/access/roles/new,複製=/access/roles/new?template=<uid>)。新增、編輯、檢視、複製四種進入方式同一頁。左邊是權限樹(勾選本角色有哪些權限),右邊即時預覽「指派此角色的人登入後看得到哪些選單」。

主要使用者:租戶管理員。

角色速覽

角色 在本頁能做什麼
持 iam.role.read 檢視(樹唯讀)
另持 iam.role.update/create 編輯既有角色/建立新角色
另持 iam.user.assign 「指派給人」
內建系統管理員角色 誰來看都唯讀,只能「複製成新角色」「匯出 JSON」

1.1 功能總覽

# 功能 說明 位置 詳述
1 標題與動作 既有角色:名稱(內建帶鎖頭)+「指派給人」「複製成新角色」「匯出 JSON」 頁首 §3
2 名稱、描述 新增模式為表單欄(名稱必填 ≤50、描述 ≤255);編輯模式可改 頁首 —
3 租戶 新增時有子租戶可選才出現;角色只能指派給同租戶的人;換租戶會清空已勾 頁首 §12
4 權限樹(依功能選單) 分組 › 側欄頁 › 子頁 › 權限,三態勾選;每項權限附「此權限涵蓋」說明 左欄 UC-ROLE-01
5 依資源視角 模組 × 資源 × 動作矩陣,進階用 左欄切換 —
6 全部展開/收合、搜尋 搜尋功能或動作 樹工具列 —
7 只給檢視 一鍵只勾該頁「進入此頁所需」那項 每頁列 —
8 從既有角色帶入 新增模式;對話框挑同租戶角色,帶入後各自獨立;已勾過會先問是否覆蓋 樹工具列 UC-ROLE-02
9 可見選單預覽 依分組列全部頁:綠點可見、灰點不可見加「加入」(勾該頁門檻項);原廠專用頁標「原廠管理員專用」不給加 右欄 §10
10 角色資料卡 狀態、最後修改、權限數、使用者數、「檢視指派清單」(帶角色篩選跳 指派) 右欄 —
11 權限規則卡 角色實際存的規則唯讀清單,每條標展開幾項 右欄 —
12 儲存列 列出未儲存的變更(新增/移除哪幾項);「放棄變更」「儲存角色」;新增模式「建立角色」 底部固定 UC-ROLE-01
13 離開確認 有未儲存變更時離開頁面先確認 — —
14 指派給人 開 指派 同一個新增指派對話框,角色已選好;停用的角色不給按 頁首 —

展開成 UC:UC-ROLE-01(勾選並儲存)、UC-ROLE-02(以既有角色為範本新增)。

2. Use Case

角色速覽:同 §1。

UC-ROLE-01 勾選權限並儲存

項目 內容
角色 持 iam.role.update(編輯)或 iam.role.create(新增)
前置條件 非內建系統管理員;授權未到期
產出後置 role_capability_patterns 整組替換成勾選的權限完整名(一項一列)
flowchart TD
  A["勾選/取消權限"] --> B["右欄可見選單即時重算<br/>POST /ui-routes/visible-by-capabilities"]
  B --> C["按 儲存角色 / 建立角色"]
  C --> D{"新增:有名稱且至少勾一項?"}
  D -- 否 --> X0["按鈕停用:填好名稱並至少勾一項"]
  D -- 是 --> E["PUT /role/:uid 或 POST /role<br/>送勾選的權限 id 清單"]
  E --> F{"內建系統管理員?"}
  F -- 是 --> X1["409 AUTH_409019"]
  F -- 否 --> G{"同租戶已有同名角色?"}
  G -- 是 --> X2["409 AUTH_409004"]
  G -- 否 --> H{"新加的權限都在授權照內?"}
  H -- 否 --> X3["403 LICENSE_403004"]
  H -- 是 --> I{"非原廠租戶角色勾了原廠權限?"}
  I -- 是 --> X4["403 GRC_403060"]
  I -- 否 --> OK["整組替換規則<br/>清展開快取"]

畫面不自己合成萬用字元:勾什麼就存什麼,日後新增的權限不會被自動帶進來。萬用字元規則只來自出貨角色與 JSON 匯入。

UC-ROLE-02 以既有角色為範本新增

項目 內容
角色 持 iam.role.create
前置條件 無
產出後置 新角色帶入範本當下的勾選,之後與範本各自獨立;從列表「複製」進來時名稱預填「<原名> 複本」、建在來源角色的租戶

3. 權限矩陣

操作 FE 判定 BE 強制 BE 檢查位置
進入(檢視) access-role-detail 綁 iam.role.read(ALL) iam.role.read jedi-iam role_route.py(RoleRoute.get)
進入(新增) access-role-new 綁 iam.role.create(ALL) — —
權限樹底稿 同上 iam.role.read BE api/permission_matrix/routes/permission_matrix_route.py;jedi-iam /ui-routes、/capabilities/menu
儲存(編輯) v-can iam.role.update;內建隱藏儲存列 iam.role.update+內建擋 role_route.py(RoleRoute.put)→ role_domain_service.py
建立 v-can iam.role.create iam.role.create role_route.py(RoleCreateRoute)
授權照 未授權模組的項反灰不可勾、附原因 新加的項逐一檢查 BE app/auth/service/role_app_service.py(_assert_new_capabilities_licensed)
原廠權限 非原廠租戶角色樹上不出現原廠項 寫入端擋 capabilities.is_platform;GRC_403060
指派給人 v-can iam.user.assign iam.user.assign role_assignment_route.py

4. 狀態機與前置條件

條件 效果 出處
is_system=true 樹唯讀、無儲存列、提示「內建系統管理員擁有全部權限,不能修改;需要調整請複製成新角色」 RoleDetail.vue;BE AUTH_409019
enable=0 「指派給人」不給按(停用的角色指派了也不生效) RoleDetail.vue
有未儲存變更 離開頁面先確認 RoleDetail.vue(leave_confirm)
授權到期唯讀 寫入鈕停用 useLicenseReadonly

4.1 權限樹怎麼長(規則)

三層結構:第一層=側欄分組(ui_routes 的根);第二層=側欄頁;內頁(ui_routes.is_menu=false)預設不上樹,它的權限上移到最近的母頁。例外:ui_routes.tree_page=true 的內頁上樹成側欄頁底下的子頁,權限留在子頁、它底下的內頁再上移進它。目前三支:專案規劃(project-planning)、專案總覽(project-auditor-overview)、稽核計畫(project-ap-list)——三個工作面各有四十幾項權限,壓成一列管理員分不出誰管什麼。

權限歸到哪一頁:

  1. 權限矩陣(GET /permission-matrix)列在哪幾頁就放那幾頁;route_capabilities 綁在哪頁也列在哪頁。
  2. 都沒有時看 capabilities.home_route_name:是頁名就放那頁;__global__ 放最後的「全站功能」組(如 AI 小幫手)。
  3. 還是沒有的落「其他動作」組——守衛測試保證每項都有家,這組正常是空的、不顯示。

歸屬只管排在哪裡,不影響誰看得到哪頁(可見性只看 route_capabilities)。

同一項權限多頁時只列在擁有者頁,其他頁標「本頁另需:<權限>(於<頁>設定)」。擁有者依序判:

  1. route_capabilities 直接綁的頁;
  2. home_route_name 指到的候選頁;
  3. 同資源其他權限最多的頁;
  4. 樹上先出現的頁。

只列一次是因為勾選以權限 id 共用,列兩次勾一處另一處會連動,管理員會當成兩項不同權限。依此規則:任務執行面(檢視/修改任務、任務留言、任務證據)歸專案總覽;建立/刪除/指派/匯入任務歸專案規劃;稽核寫入權限歸稽核計畫,「我的稽核」頁只留檢視稽核。

其他列示規則:

  • 進入該頁所需那項(route_capabilities 的 ALL 規則,每頁至多一項)標「進入此頁所需」。
  • 空殼權限(沒綁頁、沒功能點、後端沒守)不列,「依資源」視角也不列。
  • 內頁只列自己的進入門檻與母頁沒列的項;同一母頁下多支內頁同門檻併成一列。
  • 名稱一律走 i18n(選單 lang.menu.<name>、權限 lang.capability.<模組>.<資源>.<動作>)。

5. UI 設計

兩欄:左 8/12 權限樹(工具列:依功能選單/依資源切換、搜尋、全部展開收合;新增模式另有「從既有角色帶入」),底部固定儲存列;右 4/12 三張卡:可見選單(不收合)、角色資料、權限規則(可收合;新增模式只有可見選單卡)。

📸 實機截圖待補(亮色模式):編輯模式整頁(含未儲存變更列)、新增模式、內建系統管理員唯讀、依資源視角、從既有角色帶入對話框。

6. API 規格

6.1 總清單

分類 Method + Path 說明 守門
明細 GET /api/1.0/role/<uid> 角色+capability_patterns iam.role.read
建立 POST /api/1.0/role 見 §6.2 iam.role.create
更新 PUT /api/1.0/role/<uid> 同上 iam.role.update
停用/啟用 PUT /api/1.0/role/status/<uid> 內建回 409 iam.role.update
統計 POST /api/1.0/roles/stats 權限數、每條規則展開幾項 iam.role.read
可見選單 POST /api/1.0/ui-routes/visible-by-capabilities 給權限名清單或角色 uid,回可見頁;與側欄 /user/web-menu 同一條判定(含授權照過濾) iam.role.read
樹底稿 GET /api/1.0/ui-routes、GET /api/1.0/capabilities/menu、GET /api/1.0/permission-matrix 頁樹、權限清單、頁 › 功能點 › 權限對照(隨版靜態、帶 ETag) iam.role.read
範本下拉 POST /api/1.0/roles 從既有角色帶入 iam.role.read
匯出 POST /api/1.0/roles/export 見 角色 §6 iam.role.export

6.2 建立/更新(RoleRequest)

{
  "name": "稽核員(北區)",
  "description": "北區稽核",
  "tenant_id": 102,
  "enable": 1,
  "capabilities": [12, 15, 88]
}
欄位 型別 說明
name string 必填;同租戶不可重名(409 AUTH_409004)
description string
tenant_id int 角色所屬租戶
capabilities list[int] 勾選的權限 id;後端逐項轉完整名、整組替換 role_capability_patterns

出處:jedi-iam api/serializers/role.py(RoleRequest)、domain/service/role_domain_service.py(_write_patterns)。

7. 前端檔案地圖(compliance-manager-fe/)

檔案 角色
src/views/access/RoleDetail.vue 頁面本體
src/views/access/RoleDetailHeader.vue、RoleInfoFields.vue 頁首、名稱描述
src/views/access/RoleSaveBar.vue 底部儲存列
src/views/access/RoleTemplateDialog.vue 從既有角色帶入
src/views/access/RoleInfoCard.vue、RolePatternCard.vue 右欄卡片
src/components/access/PermissionTree.vue、PermissionMenuTree.vue、PermissionResourceMatrix.vue、TriStateCheckbox.vue 權限樹兩種視角
src/components/access/VisibleMenuPreview.vue 可見選單預覽
src/composables/usePermissionGrouping.js、usePermissionOwnership.js、usePermissionMatrix.js 樹的分組、擁有者、矩陣(純函式)

8. 後端檔案地圖

鏈路 Route Service 底層
角色 CRUD jedi-iam api/routes/role_route.py BE app/auth/service/role_app_service.py(授權照守門、審計暱稱)→ jedi-iam role_domain_service.py shipped_role_writer_impl.py 寫規則表
權限矩陣 BE api/permission_matrix/routes/permission_matrix_route.py core/permission_matrix.py 隨版靜態資料
權限宣告 — BE core/host_capabilities.py、jedi-* 各套件宣告 乾淨安裝與每次升級照宣告寫 capabilities(含 home_route_name)

守衛測試 test/test_fr132_capability_home_route.py 擋「沒綁頁又沒填歸屬頁」與「填了不存在的頁名」。

9. DB

表 讀寫 說明
public.roles 讀/寫 角色本體
public.role_capability_patterns 讀/寫(整組替換) 判定唯一來源
public.capabilities 讀 權限目錄
public.ui_routes 讀 頁樹(is_menu、tree_page)
public.route_capabilities 讀 進頁門檻(ALL/ANY)

role_capabilities 已退役、不再寫入;判定與角色頁只讀規則表。

public.roles(FR-132 相關欄位)

欄位 型別 說明
is_system boolean 內建系統管理員:每租戶至多一個,不可刪不可改,規則固定 *.*.*
is_template boolean 出貨範例:安裝時帶入、客戶可改可刪
is_admin integer 管理員旗標

public.role_capability_patterns

欄位 型別 說明
role_id integer 角色
pattern varchar 萬用字元規則,例 grc.project.*、*.*.read、*.*.*;畫面勾選則存完整名

public.capabilities(FR-132 相關欄位)

欄位 說明
name 三段名 <模組>.<資源>.<動作>
module 第一段,「依資源」視角第一層
is_platform 原廠層,只有原廠租戶角色可持有
license_module 授權照模組鍵
legacy_name 改名前舊名(匯入舊檔對照用)
enforce_mode enforce(不放行即 403)/log_only;208 項目前全為 enforce
home_route_name 角色樹歸屬頁(ui_routes.name 或 __global__)

public.ui_routes.tree_page:角色樹上當子頁顯示的內頁,只對 is_menu=false 有意義。

10. 頁面邏輯與資料對應

sequenceDiagram
  participant P as 角色明細
  participant BE
  P->>BE: GET /ui-routes、/capabilities/menu、/permission-matrix
  alt 編輯/檢視
    P->>BE: GET /role/:uid
    P->>BE: POST /roles/stats
  else 複製
    P->>BE: GET /role/:template(帶入勾選)
  end
  P->>P: groupPermissions(歸頁、擁有者、上移內頁)
  loop 每次勾選
    P->>BE: POST /ui-routes/visible-by-capabilities
  end
畫面元素 FE state API 欄位
勾選 selected(權限 id Set) capabilities[]
權限規則卡 role.capability_patterns GET /role/:uid
每條展開幾項 stats.pattern_counts /roles/stats
角色資料卡權限數 沒改動時用展開後數字(含星號展開);有改動以勾選為準 /roles/stats capability_count

11. 背景行為與外部依賴

類型 內容
快取 儲存後清規則展開快取;權限矩陣用 ETag
生效時機 角色權限變動後,持有者下一次請求即依新規則判定;側欄選單在重新整理或重新登入後更新
socket/排程 無

12. 邊界情況與已知坑

  1. 換租戶清空勾選:原廠權限只屬原廠租戶,換租戶後清掉已勾的,以免帶入不屬於新租戶的項。
  2. 含星號規則的角色被畫面編輯:畫面以展開後的具體項呈現;儲存會把規則整組換成勾選的完整名,星號規則不再保留,日後新增的權限也就不會自動帶入。
  3. 未授權模組:已勾但授權照未含的項維持設定、反灰顯示「購買後會自動恢復」;新勾則不允許。
  4. 選單可見與能不能操作是兩回事:可見只看進頁門檻;頁內按鈕另依各自權限隱藏。

13. 開發與驗證

  • 導航:存取管理 › 角色 → 點角色名稱,或「新增角色」。
  • 相關:角色、指派、使用手冊:存取管理;設計 docs/features/FR-132-2610-access-management/design.md §6.6;權限清單 permission-matrix.json。