側邊欄選單分組 — 設計說明書(design.md)

欄位 內容
Feature 側邊欄選單分組(menu-group)
Phase Phase 1 — Brainstorm Design
版本 v1.0
日期 2026-05-26
撰寫者 Claude(協作對象:raymond)
上游文件 docs/features/FR-029-2605-menu-group/requirement-detail.md
狀態 待 user review,review 通過後進 Phase 3 implementation-plan

1. 設計目標與背景

1.1 問題

public.ui_routes 共 39 筆,全部 pid=0(一層扁平結構),側邊欄太冗長。

1.2 目標

按業務領域分成 7 個分組,每個分組是可展開父節點,下面掛對應功能入口。具體 7 個分組與包含項目見 requirement-detail.md §2。

1.3 設計原則

  • 資料驅動:所有選單結構靠 DB(ui_routes)+ 既有 /user/web-menu API 即可呈現,FE 不寫死任何項目。
  • 最小改動:DB schema 完全不動,BE 套件只加 1 段剪枝邏輯,主專案 BE 程式碼零改動
  • 向後相容:既有 user 的 role/capabilities 完全不動,看到的選單內容與舊版一致,只是改為分組顯示。
  • 權限友善:分組節點不掛 capability,靠「無子節點自動隱藏」邏輯保持選單乾淨。

2. 整體 Architecture

三層改動:

┌─────────────────────────────────────────────────────────────┐
│  DB (compliance-manager-be)                                  │
│  ───────────────────────────                                 │
│  • 新增 9 筆 ui_routes (7 群組 + 2 個寫死搬遷項)              │
│  • Update 27 筆既有 ui_routes 的 pid / sort                  │
│  • schema 完全不動,只動資料                                  │
└─────────────────────────────────────────────────────────────┘
              ↓ /user/web-menu API
┌─────────────────────────────────────────────────────────────┐
│  BE Package (jedi-auth)                                      │
│  ─────────────────────                                       │
│  • user_service.get_user_web_ui_routes() 末段                │
│    加 prune_empty_groups() 剪枝邏輯                          │
│  • 移除「無 url 且 items 為空」的分組節點                     │
└─────────────────────────────────────────────────────────────┘
              ↓ JSON tree
┌─────────────────────────────────────────────────────────────┐
│  FE (compliance-manager-fe)                                  │
│  ──────────────────────────                                  │
│  • AppMenu.vue: 移除寫死 grcItems 陣列                       │
│  • i18n/zh-tw/menu.json + zh-cn: 補 7 個 group-* key         │
│  • AppMenuItem.vue 不動(既有 t() 邏輯自然 work)            │
└─────────────────────────────────────────────────────────────┘

主專案 BE 程式碼零改動


3. DB Schema 變動

3.1 schema 變更

public.ui_routes 表結構完全不動,不加欄位、不加 constraint。

3.2 Migration 內容

檔案位置scripts/sql/2026-XX-XX-menu-group-restructure.sql(日期由實作時補上)

Step 1:新增 9 筆 ui_routes

-- 7 個分組節點(pid=NULL,無 url,僅作為展開父節點)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-project-task',     NULL, 'pi pi-briefcase',  1, '專案與任務分組',   10
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-project-task');

INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-compliance-audit', NULL, 'pi pi-shield',     1, '合規與稽核分組',   20
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-compliance-audit');

INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-communication',    NULL, 'pi pi-comments',   1, '溝通與互動分組',   30
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-communication');

INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-tenant-org',       NULL, 'pi pi-building',   1, '租戶與組織分組',   40
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-tenant-org');

INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-system-admin',     NULL, 'pi pi-cog',        1, '系統管理分組',     50
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-system-admin');

INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-system-config',    NULL, 'pi pi-sliders-h',  1, '系統設定分組',     60
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-system-config');

INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-ai-analytics',     NULL, 'pi pi-chart-line', 1, 'AI 與分析分組',    70
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-ai-analytics');

-- 2 個寫死搬遷項(pid 指向 group-project-task)
WITH g AS (SELECT id FROM public.ui_routes WHERE name = 'group-project-task')
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), g.id, 'project-dashboard', '/project/dashboard',  'pi pi-home',         1, '專案儀表板', 10 FROM g
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'project-dashboard');

WITH g AS (SELECT id FROM public.ui_routes WHERE name = 'group-project-task')
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), g.id, 'my-tasks',          '/project/task-manage','pi pi-check-square', 1, '待辦任務',   20 FROM g
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'my-tasks');

Step 2:Update 27 筆既有 ui_routes 的 pid / sort

-- Group 1 (project-task):sort 30/40/50(前面 10/20 給 project-dashboard / my-tasks 用了)
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-project-task'),     sort = 30 WHERE name = 'project-list';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-project-task'),     sort = 40 WHERE name = 'audit-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-project-task'),     sort = 50 WHERE name = 'report-manage';

-- Group 2 (compliance-audit):sort 10/20/30/40
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 10 WHERE name = 'module-frame';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 20 WHERE name = 'compliance-framework-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 30 WHERE name = 'tool-plugin-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 40 WHERE name = 'flow-template-manage';

-- Group 3 (communication):sort 10/20/30/40/50/60
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'),    sort = 10 WHERE name = 'bulletin-list';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'),    sort = 20 WHERE name = 'bulletin-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'),    sort = 30 WHERE name = 'survey-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'),    sort = 40 WHERE name = 'system-feedback-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'),    sort = 50 WHERE name = 'system-feedback-view';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'),    sort = 60 WHERE name = 'issue-integrate-config';

-- Group 4 (tenant-org):sort 10/20/30/40
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'),       sort = 10 WHERE name = 'tenant-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'),       sort = 20 WHERE name = 'department-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'),       sort = 30 WHERE name = 'user-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'),       sort = 40 WHERE name = 'role-manage';

-- Group 5 (system-admin):sort 10/20/30/40/50
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'),     sort = 10 WHERE name = 'system-menu-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'),     sort = 20 WHERE name = 'information-system-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'),     sort = 30 WHERE name = 'device-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'),     sort = 40 WHERE name = 'storage-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'),     sort = 50 WHERE name = 'cloud-integrations';

-- Group 6 (system-config):sort 10/20/30/40
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'),    sort = 10 WHERE name = 'smtp-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'),    sort = 20 WHERE name = 'ldap-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'),    sort = 30 WHERE name = 'notify-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'),    sort = 40 WHERE name = 'user-log';

-- Group 7 (ai-analytics):sort 10
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-ai-analytics'),     sort = 10 WHERE name = 'ai-dashboard';

Step 3:執行注意事項

  • Transaction 包覆:整份 migration 用 BEGIN; ... COMMIT; 包,全成或全敗。
  • 權限帳號:用 cmmgr 跑(避開 RLS 擋寫;cm_app 寫不進系統層級資料)。
  • 註解時間標記:每個語句加 -- N. 說明 (2026-XX-XX) 註解(依 CLAUDE.md 規範)。
  • Idempotent:Step 1 用 WHERE NOT EXISTS 保護;Step 2 的 UPDATE 重複跑無害(目標 pid / sort 固定)。

3.3 未列項目(12 個 enable=0)— 完全不動

ui_routes.name enable 處置
workflow-setup 0 不動 pid(保持 0/NULL)、不動 sort、不動 enable
workflow-view 0 同上
textbook-execution 0 同上
textbook-tested 0 同上
textbook-general 0 同上
resource-version-manage 0 同上
resource-manage 0 同上
cruise-project-manage 0 同上
cruise-feedback-view 0 同上
report-view 0 同上
customer-dashboard-editor 0 同上
user-guide 0 同上

→ BE 過濾 enable=1,這 12 個項目根本不出現在 /user/web-menu 回傳結果。不會變成「pid=0 的孤兒葉節點」與分組節點並列。


4. BE 改動:jedi-auth 套件剪枝邏輯

4.1 改動範圍

檔案~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/jedi_auth/app/service/user_service.py

Methodget_user_web_ui_routes()(line 438~528)

4.2 改動內容

在現有 sort_nodes(roots) 之後新增剪枝 step:

@transaction
def get_user_web_ui_routes(self, user_uid: str, tenant_id: int | None) -> list[dict]:
    """
    取得使用者可檢視的前端路由選單(RBAC,多租戶版)
    ... (既有 docstring 保留)
    """
    # ... (既有邏輯不動:取使用者 capability、build node_map、組樹、sort) ...
    sort_nodes(roots)

    # 新增:剪枝空分組節點(無 url 且 items 為空的純分組節點)
    return self._prune_empty_groups(roots)

def _prune_empty_groups(self, nodes: list[dict]) -> list[dict]:
    """
    遞迴剪枝:移除「無 url 且 items 為空」的分組節點。
    葉節點(有 url)永遠保留;分組節點若還有可見子項也保留。

    使用情境:
    - 某 user 對「租戶與組織」分組下所有功能都無 capability → 該分組整個從選單消失
    - 葉節點 url 為 None(理論上不應發生)也會被當分組處理:實務上葉節點都應有 url
    """
    pruned = []
    for n in nodes:
        if n.get("items"):
            n["items"] = self._prune_empty_groups(n["items"])
        is_group = not n.get("url")
        if is_group and not n.get("items"):
            continue
        pruned.append(n)
    return pruned

4.3 Unit Test 新增

檔案~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/tests/unittest/test_user_service.py

新增 3 個 case(接在現有 test_get_user_web_ui_routes_* 系列後):

  1. test_get_user_web_ui_routes_prune_empty_group

    • 設置:mock get_viewable_routes_by_user_uid 回 1 個分組節點(無 url)+ 0 個子節點
    • 預期:回傳空 list(分組被 prune)
  2. test_get_user_web_ui_routes_keep_group_with_visible_children

    • 設置:mock 回 1 個分組節點 + 2 個對應 pid 的子節點(都有 url)
    • 預期:分組節點保留,items 有 2 筆
  3. test_get_user_web_ui_routes_keep_group_with_partial_visible_children

    • 設置:mock 回 1 個分組節點 + 1 個有 url 的子節點(另一個子節點被 capability 過濾掉,不在 mock 結果內)
    • 預期:分組節點保留,items 有 1 筆

4.4 套件版本 bump

整 feature 完成 + smoke 通過 + user 明確指示後才做:

  • jedi-auth/pyproject.tomlversion = "0.0.X""0.0.X+1"(patch bump,剪枝邏輯屬 bug-fix-style 增量改進)
  • jedi-auth commit + push origin + 推 Nexus
  • 主專案 pyproject.toml 還原 path → pin 新版本 + poetry update jedi-auth

5. FE 改動

5.1 compliance-manager-fe/src/layout/AppMenu.vue

 onMounted(()=> {
-    // GRC / Project module (static — no permission gate needed for demo)
-    const grcItems = [
-        { label: 'project-dashboard',  icon: 'pi pi-home',         to: '/project/dashboard' },
-        // { label: 'project-manage',     icon: 'pi pi-folder-open',  to: '/project/projects' },
-        { label: 'my-tasks',           icon: 'pi pi-check-square', to: '/project/task-manage' },
-    ]
-
-    menu.value = [...grcItems, ...genMenuNode(permissions.value)]
+    menu.value = genMenuNode(permissions.value)
 })

5.2 compliance-manager-fe/src/config/locales/i18n/zh-tw/menu.json

"menu" 物件內新增 7 個 key(建議排在現有同類 key 附近,或集中放在檔案末尾並加註解區隔):

{
    "menu": {
        ...
        "group-project-task":     "專案與任務",
        "group-compliance-audit": "合規與稽核",
        "group-communication":    "溝通與互動",
        "group-tenant-org":       "租戶與組織",
        "group-system-admin":     "系統管理",
        "group-system-config":    "系統設定",
        "group-ai-analytics":     "AI 與分析"
    }
}

5.3 compliance-manager-fe/src/config/locales/i18n/zh-cn/menu.json

對應補簡體版(用語由 FE 側決定,例:「项目与任务 / 合规与稽核 / 沟通与互动 / ...」)。若該 repo 有 sync-translations.js 工具,跑該工具自動產生候選翻譯後再 review。

5.4 i18n 英文版(en/menu.json)— Phase 1 不要求

若有 en/menu.json 且該 locale 啟用中,補英文翻譯(如 "Projects & Tasks" 等);若 en locale 未啟用或未維護,本期不要求補。Phase 3 implementation-plan 階段確認。

5.5 AppMenuItem.vue — 不動

既有 t(\lang.menu.${item.label}`)邏輯天然支援新增的group-*key。<strong>邊界 case</strong>:分組節點to` 為 null 時的 render 行為,需在實作前 manual verify(Phase 5 開工前)— 預期既有 PrimeVue Menu 行為會自動 render 成可展開父節點而非可點擊連結。


6. Edge Cases & 風險

Case 預期行為 風險評估
既有 user(有 role + capabilities) 看到完整 7 分組 + 對應可見葉節點,與舊版內容一致 無 — 葉節點 capability 沒動
新進 user(無 role) 看到「專案儀表板 / 待辦任務」2 項(不掛 capability)+ 沒有任何分組標題(都被 prune) 無 — 符合「2 項 always visible」設計
Admin(super-admin role) 看到完整 7 分組 + 全部葉節點
FE i18n key 漏譯 AppMenuItem 顯示 lang.menu.group-xxx 字串 低 — PR 前 verify zh-tw + zh-cn 都補齊即可
AppMenuItem 對「無 url 父節點」處理 預期 render 為可展開父節點 中 — Phase 5 manual verify 後若不 work 需 wrap 條件判斷
Migration 中斷 DB 處於部分 update 狀態 低 — BEGIN; ... COMMIT; 全成或全敗
既有外部資料引用 sort 數值 sort 改動影響該外部資料 已確認無 — scripts/sql/ grep 過、BE 只用 sort 做排序、不依賴具體數值
FE 早於 BE/DB 部署 /user/web-menu 仍回扁平 tree,FE 拿掉寫死 → user 暫時看不到「專案儀表板 / 待辦任務」 中 — 部署順序強制 BE/DB 先、FE 後
BE 部署但 DB migration 未跑 jedi-auth 剪枝邏輯 wrong tree(扁平 tree 無分組)→ 不影響 無 — 剪枝對扁平 tree 是 no-op
Rollback 需求 移除 9 筆新節點 + 還原 27 筆原 sort 值 中 — 需事先 dump 既有 sort 備份;Rollback SQL 見附錄 C

7. 部署順序

7.1 Dev 階段(feature 開發中)

  1. 確認 working branch 為 feature/menu-group(user 自行切換,Claude 不切 branch)。
  2. 主專案 pyproject.toml:把 jedi-auth 改成 path dependency(此改動不 commit)。
  3. 改 jedi-auth source code(剪枝邏輯 + tests)— jedi-auth 內 commit + push origin(不 bump version、不推 Nexus)。
  4. 寫主專案 SQL migration、FE 檔案。
  5. 跑 migration(cmmgr 帳號)+ BE 重啟 + smoke test:
    • 用一般 user 登入確認看到完整分組
    • 用 admin 登入確認看到 7 分組 + 全部葉節點
    • 用新進無 role user 確認只看到 2 項固定入口
    • zh-tw / zh-cn 切換確認分組標題正確翻譯

7.2 Feature 完成 + smoke 通過後(user 明確下指令才做)

  1. jedi-auth pyproject.toml bump version + commit + push origin。
  2. jedi-auth 推 Nexus。
  3. 主專案 pyproject.toml 還原 path 為 pin 新版本。
  4. 主專案跑 poetry update jedi-auth
  5. 主專案 commit (含 migration + pyproject.toml + poetry.lock)。
  6. FE commit。
  7. user 明確指示後 push。

7.3 正式環境部署

順序強制:BE → DB migration → FE

步驟 動作 為何這個順序
1 部署 BE(含新 jedi-auth) 剪枝邏輯先就緒。即使 DB 還沒跑,扁平 tree 對剪枝是 no-op,不影響舊行為
2 DB migration 既有 user 立刻看到分組結構(BE 已備好剪枝邏輯)
3 部署 FE AppMenu.vue 拿掉寫死 + i18n 翻譯生效

為何 FE 不能先部署:若 FE 先拿掉 grcItems 但 BE/DB 仍回扁平 tree → user 暫時看不到「專案儀表板 / 待辦任務」。


8. 測試

8.1 jedi-auth 套件 unit test

依 §4.3 新增 3 個 case。

8.2 主專案 BE smoke test(manual)

-- 跑完 migration 後驗證
SELECT name, pid, sort FROM public.ui_routes WHERE name LIKE 'group-%' ORDER BY sort;
-- 預期 7 row

SELECT name, pid, sort FROM public.ui_routes WHERE pid IS NOT NULL ORDER BY pid, sort;
-- 預期 29 row(27 既有 + 2 新增搬遷項)

8.3 FE manual E2E(部署前 verify)

Scenario 預期
admin user 登入 看到 7 個分組標題 + 全部葉節點
一般 user 登入 看到對應 role 的可見分組 + 葉節點
新進無 role user 登入 只看到「專案儀表板 / 待辦任務」2 項、沒有任何分組標題
zh-tw 切換到 zh-cn 分組標題顯示簡體
點開可展開分組 顯示底下葉節點清單
點葉節點 正常跳轉到對應路由
點分組節點本身(無 url) 不跳轉,僅 toggle 展開/收合

8.4 重型自動化 E2E — 不需要

本期屬 UI 重組,BE 邏輯改動範圍小(單個套件函式末段 prune),不需要在 compliance-manager-test repo 加 Cucumber/Playwright scenarios。


§11 Implementation Reality(2026-05-26 實作後補記)

§11.1 AppMenuItem.vue 必須修改(與 §5.5 預測不符)

設計預測(§5.5):「AppMenuItem.vue 不動,既有 PrimeVue Menu 行為自然 work」

實際情況:AppMenuItem.vue template 只有單一 <router-link>,沒有遞迴 render sub-items 的區塊。分組結構上線後葉節點完全看不到、分組節點因 router-link :to=undefined 導向首頁。

修正內容(commit 0d1ca8b):

  • 分組節點改用 <a @click.prevent> 取代 <router-link>
  • <ul v-show="isActiveMenu"><app-menu-item> 遞迴區塊

教訓:設計文件標注「需 Phase 5 manual verify」的邊界 case,實作前必須先手動確認,不能交由 subagent 假設。


9. Phase 3 implementation-plan 預計拆 task 結構

供後續 Phase 3 plan 參考(不在本 design.md 詳列實作步驟):

  1. Task A:jedi-auth 套件改動(剪枝邏輯 + 3 unit tests)
  2. Task B:主專案 path dependency dev 模式 setup(不 commit)
  3. Task C:主專案 DB migration SQL 撰寫 + dry-run
  4. Task D:FE AppMenu.vue 改 + i18n menu.json 補 key
  5. Task E:跨 repo smoke test(一般 / admin / 新進 user × zh-tw / zh-cn)
  6. Task F:收尾 — jedi-auth bump + Nexus 推送 + 主專案 pin 還原 + commit

附錄 A:完整對照表(最終 ui_routes 狀態)

Group id(runtime) name url sort source
new group-project-task NULL 10 本期新增
new group-compliance-audit NULL 20 本期新增
new group-communication NULL 30 本期新增
new group-tenant-org NULL 40 本期新增
new group-system-admin NULL 50 本期新增
new group-system-config NULL 60 本期新增
new group-ai-analytics NULL 70 本期新增
1 new project-dashboard /project/dashboard 10 本期搬遷
1 new my-tasks /project/task-manage 20 本期搬遷
1 8 project-list /project/projects 30 既有 update pid/sort
1 37 audit-manage /project/audit-manage 40 既有 update pid/sort
1 15 report-manage /project-summary-report 50 既有 update pid/sort
2 24 module-frame /module-frame/module-frame 10 既有 update pid/sort
2 33 compliance-framework-manage /compliance-framework/compliance-framework-manage 20 既有 update pid/sort
2 25 tool-plugin-manage /plugin/tool-plugin-manage 30 既有 update pid/sort
2 40 flow-template-manage /flow/template-manage 40 既有 update pid/sort
3 6 bulletin-list /bulletin/bulletin-list 10 既有 update pid/sort
3 7 bulletin-manage /bulletin/bulletin-manage 20 既有 update pid/sort
3 11 survey-manage /survey/manage 30 既有 update pid/sort
3 12 system-feedback-manage /feedback/system-feedback-manage 40 既有 update pid/sort
3 13 system-feedback-view /feedback/system-feedback-view 50 既有 update pid/sort
3 32 issue-integrate-config /system/issue-integrate-config 60 既有 update pid/sort
4 31 tenant-manage /tenant/tenant-manage 10 既有 update pid/sort
4 29 department-manage /department/department-manage 20 既有 update pid/sort
4 2 user-manage /auth/user-manage 30 既有 update pid/sort
4 3 role-manage /auth/role-manage 40 既有 update pid/sort
5 21 system-menu-manage /system/system-menu-manage 10 既有 update pid/sort
5 36 information-system-manage /information-system/manage 20 既有 update pid/sort
5 30 device-manage /device/device-manage 30 既有 update pid/sort
5 34 storage-config /system/storage-config 40 既有 update pid/sort
5 38 cloud-integrations /settings/cloud-integrations 50 既有 update pid/sort
6 27 smtp-config /system/smtp-config-manage 10 既有 update pid/sort
6 28 ldap-config /system/ldap-config 20 既有 update pid/sort
6 39 notify-config /system/notify-config 30 既有 update pid/sort
6 4 user-log /log/user-log 40 既有 update pid/sort
7 35 ai-dashboard /dashboard/ai-dashboard 10 既有 update pid/sort

未列項目 12 筆(enable=0、不動):見 §3.3。


附錄 B:既有 sort 值備份 dump 命令(migration 前必跑)

PGPASSWORD='<password>' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
  -At -F',' \
  -c "SELECT id, name, pid, sort FROM public.ui_routes ORDER BY id;" \
  > scripts/sql/backup/2026-XX-XX-ui_routes-pre-menu-group.csv

備份 CSV 用於 rollback 還原。


附錄 C:Rollback SQL

BEGIN;

-- 1. 還原既有 27 筆的 pid / sort(依附錄 B CSV 還原;以下為示意)
-- 實作時用 psql \copy 或寫 generator 從 CSV 產 UPDATE 語句

-- 2. 移除本期新增 9 筆
DELETE FROM public.ui_routes WHERE name IN (
    'group-project-task', 'group-compliance-audit', 'group-communication',
    'group-tenant-org', 'group-system-admin', 'group-system-config', 'group-ai-analytics',
    'project-dashboard', 'my-tasks'
);

COMMIT;

Rollback 限制:jedi-auth 套件的剪枝邏輯若已透過 Nexus 進版部署,rollback DB 後對線上行為無影響(扁平 tree 對剪枝是 no-op,舊行為自然恢復)。FE 若已部署則 rollback FE commit 即可。


附錄 D:相關文件索引

文件 路徑
上游草稿 docs/draft_requirement_spec/menu-group/requirement.md(user 原稿)
上游白話需求 docs/features/FR-029-2605-menu-group/requirement-detail.md
jedi-auth source ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/jedi_auth/app/service/user_service.py
FE AppMenu.vue ~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/layout/AppMenu.vue
FE i18n menu.json ~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/config/locales/i18n/zh-tw/menu.json
權限系統設計 docs/system-design/permission/Permission-System-權限與選單系統設計說明書.md
Feature workflow SOP docs/claude/feature-development-workflow.md