diff --git a/deploy/fba/seed_iaop_menus.py b/deploy/fba/seed_iaop_menus.py index 57e382c..a138e5c 100644 --- a/deploy/fba/seed_iaop_menus.py +++ b/deploy/fba/seed_iaop_menus.py @@ -31,32 +31,40 @@ import urllib.error import urllib.parse import urllib.request -# ---- iAOP 模块注册表(镜像 web/shared/session.js 的 MODULES,顺序即菜单顺序) ---- +# ---- iAOP 模块注册表 v2(Epic #163 原生重构:type=1 原生菜单,component→views/iaop/*) ---- +# path=/iaop/,component=iaop//index(与 apps/web-antdv-next/src/views/iaop/* 对应); +# icon 为 iconify 名;users 不注册(FBA 系统管理承载)、mobile 暂不注册(见 B9)。 MODULES = [ - {"key": "cockpit", "title": "配置化驾驶舱", "icon": "🚀", "path": "/cockpit/"}, - {"key": "chat", "title": "对话助手", "icon": "💬", "path": "/chat/assistant.html"}, - {"key": "studio", "title": "模板配置台", "icon": "🧩", "path": "/studio/", "children": [ - {"key": "import", "title": "点位导入"}, - {"key": "hyper", "title": "模型超参"}, - {"key": "layout", "title": "驾驶舱编排"}, - {"key": "version", "title": "版本发布"}, + {"key": "cockpit", "title": "配置化驾驶舱", "icon": "mdi:view-dashboard", + "path": "/iaop/cockpit", "component": "iaop/cockpit/index"}, + {"key": "chat", "title": "对话助手", "icon": "mdi:chat-processing-outline", + "path": "/iaop/chat", "component": "iaop/chat/index"}, + {"key": "studio", "title": "模板配置台", "icon": "mdi:widgets-outline", + "path": "/iaop/studio", "component": "iaop/studio/index", "children": [ + {"key": "import", "title": "点位导入", "component": "iaop/studio/import"}, + {"key": "hyper", "title": "模型超参", "component": "iaop/studio/hyper"}, + {"key": "layout", "title": "驾驶舱编排", "component": "iaop/studio/layout"}, + {"key": "version", "title": "版本发布", "component": "iaop/studio/version"}, ]}, - {"key": "admin", "title": "管理控制台", "icon": "⚙️", "path": "/admin/", "children": [ - {"key": "models", "title": "模型管理"}, - {"key": "kb", "title": "知识库管理"}, - {"key": "alerts", "title": "告警确认"}, - {"key": "audit", "title": "审计查询"}, + {"key": "admin", "title": "管理控制台", "icon": "mdi:cog-outline", + "path": "/iaop/admin", "component": "iaop/admin/index", "children": [ + {"key": "models", "title": "模型管理", "component": "iaop/admin/models"}, + {"key": "kb", "title": "知识库管理", "component": "iaop/admin/kb"}, + {"key": "alerts", "title": "告警确认", "component": "iaop/admin/alerts"}, + {"key": "audit", "title": "审计查询", "component": "iaop/admin/audit"}, ]}, - {"key": "users", "title": "用户与角色", "icon": "👤", "path": "/auth/users.html"}, - {"key": "mobile", "title": "移动端驾驶舱", "icon": "📱", "path": "/mobile/"}, ] -# 角色 → 可见模块(PRD 8.2;admin 通常为 is_superuser,无需分配,看到全部) +# 角色 → 可见 iAOP 模块(PRD 8.2;admin 角色为新增业务管理员,非超管) ROLE_MENUS = { - "viewer": ["cockpit", "chat", "mobile"], - "engineer": ["cockpit", "chat", "mobile", "studio"], + "viewer": ["cockpit", "chat"], + "engineer": ["cockpit", "chat", "studio"], + "admin": ["cockpit", "chat", "studio", "admin"], } +# admin 角色额外分配:FBA 原生管理菜单(存在即分配) +FBA_ADMIN_MENUS = ["system", "monitor", "log", "scheduler"] + class FbaClient: def __init__(self, base: str, username: str, password: str): @@ -133,21 +141,26 @@ def main(): # ---- 1. 播种菜单(幂等:按 name 查重) -------------------------------- existing = flatten_menu_names(cli.get("/sys/menus")) - created, skipped = 0, 0 + created, updated = 0, 0 - def ensure_menu(name, title, path, mtype, sort, parent_id=None): - nonlocal created, skipped - if name in existing: - skipped += 1 - return existing[name] + def upsert_menu(name, title, path, mtype, sort, parent_id=None, + component=None, icon=None, perms=None): + nonlocal created, updated body = { "title": title, "name": name, "path": path, - "parent_id": parent_id, "sort": sort, "icon": None, + "parent_id": parent_id, "sort": sort, "icon": icon, "type": mtype, # 0目录 1菜单 - "component": None, "perms": f"iaop:{name}", + "component": component, "perms": perms or f"iaop:{name}", "status": 1, "display": 1, "cache": 0, - "link": None, "remark": "iAOP 模块(seed_iaop_menus.py)", + "link": None, "remark": "iAOP 原生菜单(seed_iaop_menus.py v2)", } + if name in existing: + # 幂等:已存在(含 #161 旧版 type=0/iframe 菜单)→ PUT 更新关键字段 + mid = existing[name] + cli.put(f"/sys/menus/{mid}", body) + updated += 1 + print(f"[upd] 更新菜单 {name}({title})") + return mid cli.post("/sys/menus", body) created += 1 # 创建后重新拉取以拿到 id(接口不回传 id) @@ -159,13 +172,17 @@ def main(): for i, m in enumerate(MODULES): children = m.get("children") if children: - pid = ensure_menu(m["key"], m["title"], m["path"], 0, i) + pid = upsert_menu(m["key"], m["title"], m["path"], 0, i, + component=m.get("component"), icon=m.get("icon")) for j, c in enumerate(children): - ensure_menu(c["key"], c["title"], f"{m['path']}#{c['key']}", - 1, j, parent_id=pid) + upsert_menu(c["key"], c["title"], + f"{m['path']}/{c['key']}", 1, j, + parent_id=pid, component=c.get("component"), + icon=c.get("icon")) else: - ensure_menu(m["key"], m["title"], m["path"], 1, i) - print(f"[ok] 菜单:新建 {created},已存在跳过 {skipped}") + upsert_menu(m["key"], m["title"], m["path"], 1, i, + component=m.get("component"), icon=m.get("icon")) + print(f"[ok] 菜单:新建 {created},更新 {updated}") # ---- 2. 播种角色并分配菜单 ------------------------------------------- menu_ids = flatten_menu_names(cli.get("/sys/menus")) @@ -195,6 +212,11 @@ def main(): ids = [] for k in mod_keys: ids.extend(module_menu_ids(k)) + # admin 角色额外分配 FBA 原生管理菜单(system/monitor/log/scheduler 等) + if role_name == "admin": + for fba_name in FBA_ADMIN_MENUS: + if fba_name in menu_ids: + ids.append(menu_ids[fba_name]) cli.put(f"/sys/roles/{rid}/menus", {"menus": ids}) print(f"[ok] 角色 {role_name} 菜单:{', '.join(mod_keys)}({len(ids)} 项)")