From 524c027a4a10f6d675ba0caa29ba61e95475996d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?cls=5F=E5=AE=81=E6=B3=A2=E6=9C=AC=E6=9C=BA?= <908705107@qq.com> Date: Mon, 22 Jun 2026 01:21:01 +0800 Subject: [PATCH] feat: restore core admin modules and harden ai model fallback --- backend-fastapi/ai_platform/api/agent_api.py | 5 +- .../ai_platform/services/agent_service.py | 11 +- .../ai_platform/services/llm_service.py | 5 +- backend-fastapi/core/menu/service.py | 65 ++- backend-fastapi/core/router.py | 2 + web/apps/web-ele/src/locales/index.ts | 12 +- .../src/locales/langs/zh-CN/application.json | 74 +++ .../src/locales/langs/zh-CN/menu-title.json | 1 + web/apps/web-ele/src/router/access.ts | 3 + web/apps/web-ele/src/router/light-menu.ts | 8 + .../src/views/_core/application/index.vue | 513 ++++++++++++++++++ .../modules/application-form-modal.vue | 345 ++++++++++++ .../src/views/_core/org-chart/index.vue | 33 ++ .../_core/org-chart/modules/OrgChartPanel.vue | 344 ++++++++++++ .../views/_core/org-chart/modules/OrgNode.vue | 496 +++++++++++++++++ 15 files changed, 1896 insertions(+), 21 deletions(-) create mode 100644 web/apps/web-ele/src/locales/langs/zh-CN/application.json create mode 100644 web/apps/web-ele/src/views/_core/application/index.vue create mode 100644 web/apps/web-ele/src/views/_core/application/modules/application-form-modal.vue create mode 100644 web/apps/web-ele/src/views/_core/org-chart/index.vue create mode 100644 web/apps/web-ele/src/views/_core/org-chart/modules/OrgChartPanel.vue create mode 100644 web/apps/web-ele/src/views/_core/org-chart/modules/OrgNode.vue diff --git a/backend-fastapi/ai_platform/api/agent_api.py b/backend-fastapi/ai_platform/api/agent_api.py index 9859b9f..afc47f3 100644 --- a/backend-fastapi/ai_platform/api/agent_api.py +++ b/backend-fastapi/ai_platform/api/agent_api.py @@ -16,7 +16,7 @@ from app.base_schema import PaginatedResponse, ResponseModel from utils.context import get_current_user_id_from_context from ai_platform.models import ( Agent, AgentConversation, AgentMessage, - LLMModel, AIWorkflow, + LLMModel, LLMProvider, AIWorkflow, ) from core.application.model import Application from ai_platform.schemas.agent_schema import ( @@ -59,10 +59,13 @@ CODEX_AGENT_PROMPT = """ async def _resolve_default_chat_model_id(db: AsyncSession) -> Optional[str]: result = await db.execute( select(LLMModel) + .join(LLMProvider, LLMProvider.id == LLMModel.provider_id) .where( LLMModel.is_deleted == False, LLMModel.is_active == True, LLMModel.model_type == "chat", + LLMProvider.is_deleted == False, + LLMProvider.is_active == True, ) .order_by(LLMModel.sort.desc(), LLMModel.sys_create_datetime.desc()) ) diff --git a/backend-fastapi/ai_platform/services/agent_service.py b/backend-fastapi/ai_platform/services/agent_service.py index 76bb58e..13e2485 100644 --- a/backend-fastapi/ai_platform/services/agent_service.py +++ b/backend-fastapi/ai_platform/services/agent_service.py @@ -10,7 +10,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from ai_platform.models import ( - Agent, AgentConversation, AgentMessage, AIWorkflow, LLMModel, + Agent, AgentConversation, AgentMessage, AIWorkflow, LLMModel, LLMProvider, ) from .llm_service import LLMService @@ -212,10 +212,14 @@ class AgentService: if agent.model_id: result = await self._db.execute( - select(LLMModel).where( + select(LLMModel) + .join(LLMProvider, LLMProvider.id == LLMModel.provider_id) + .where( LLMModel.id == agent.model_id, LLMModel.is_deleted == False, LLMModel.is_active == True, + LLMProvider.is_deleted == False, + LLMProvider.is_active == True, ) ) if result.scalar_one_or_none(): @@ -223,10 +227,13 @@ class AgentService: result = await self._db.execute( select(LLMModel) + .join(LLMProvider, LLMProvider.id == LLMModel.provider_id) .where( LLMModel.is_deleted == False, LLMModel.is_active == True, LLMModel.model_type == "chat", + LLMProvider.is_deleted == False, + LLMProvider.is_active == True, ) .order_by(LLMModel.sort.desc(), LLMModel.sys_create_datetime.desc()) ) diff --git a/backend-fastapi/ai_platform/services/llm_service.py b/backend-fastapi/ai_platform/services/llm_service.py index 03fbfa7..3c0d90c 100644 --- a/backend-fastapi/ai_platform/services/llm_service.py +++ b/backend-fastapi/ai_platform/services/llm_service.py @@ -44,14 +44,17 @@ class LLMService: if not self._db: raise ValueError("未找到可用的 chat 模型,请先在模型配置中启用一个模型") - from ai_platform.models import LLMModel + from ai_platform.models import LLMModel, LLMProvider result = await self._db.execute( select(LLMModel) + .join(LLMProvider, LLMProvider.id == LLMModel.provider_id) .where( LLMModel.is_deleted == False, LLMModel.is_active == True, LLMModel.model_type == "chat", + LLMProvider.is_deleted == False, + LLMProvider.is_active == True, ) .order_by(LLMModel.sort.desc(), LLMModel.sys_create_datetime.desc()) ) diff --git a/backend-fastapi/core/menu/service.py b/backend-fastapi/core/menu/service.py index a0d36fa..8127b54 100644 --- a/backend-fastapi/core/menu/service.py +++ b/backend-fastapi/core/menu/service.py @@ -17,13 +17,14 @@ from core.menu.schema import MenuCreate, MenuUpdate menu_cache = CacheManager(prefix="menu:") # 缓存key -AI_AGENT_ADMIN_MENU_CACHE_VERSION = "v16" +AI_AGENT_ADMIN_MENU_CACHE_VERSION = "v19" MENU_TREE_CACHE_KEY = f"tree:ai_agent_admin:{AI_AGENT_ADMIN_MENU_CACHE_VERSION}" USER_ROUTE_CACHE_PREFIX = f"user_route:ai_agent_admin:{AI_AGENT_ADMIN_MENU_CACHE_VERSION}:" AI_AGENT_ADMIN_MENU_NAMES = { "AccountSettings", "ControlCenter", + "Application", "AIPlatform", "AIAgent", "AIModelConfig", @@ -39,6 +40,10 @@ AI_AGENT_ADMIN_MENU_NAMES = { "SystemPost", "SystemDict", "SystemFileManager", + "LoginLog", + "OrgNode", + "SystemLoginLog", + "SystemOrgChart", "SystemRole", "SystemConfigManager", "UIConfigManager", @@ -50,6 +55,7 @@ AI_AGENT_ADMIN_MENU_NAMES = { AI_AGENT_ADMIN_VISIBLE_MENU_NAMES = { "ControlCenter", + "Application", "AIPlatform", "AIAgent", "AIModelConfig", @@ -65,6 +71,10 @@ AI_AGENT_ADMIN_VISIBLE_MENU_NAMES = { "SystemPost", "SystemDict", "SystemFileManager", + "LoginLog", + "OrgNode", + "SystemLoginLog", + "SystemOrgChart", "SystemRole", "SystemConfigManager", "UIConfigManager", @@ -82,10 +92,29 @@ AI_AGENT_ADMIN_SYSTEM_CHILD_MENU_NAMES = { "SystemPost", "SystemDict", "SystemFileManager", + "LoginLog", + "OrgNode", + "SystemLoginLog", + "SystemOrgChart", "SystemRole", "UIConfigManager", } +AI_AGENT_ADMIN_AI_CHILD_MENU_NAMES = { + "AIAgent", + "AIModelConfig", + "AIWorkflow", + "AIWorkflowRuns", + "CodexAgentChat", + "KnowledgeBase", +} + +AI_AGENT_ADMIN_MESSAGE_CHILD_MENU_NAMES = { + "AnnouncementList", + "AnnouncementManage", + "MessageList", +} + AI_AGENT_ADMIN_MENU_OVERRIDES = { "SystemConfigManager": { "component": "/_core/system-config/index", @@ -100,6 +129,16 @@ AI_AGENT_ADMIN_MENU_OVERRIDES = { "component": "/_core/file-manager/index", "path": "/system/file-manager", }, + "LoginLog": { + "component": "/_core/login-log/index", + "path": "/system/login-log", + "title": "menu-title.loginLog", + }, + "OrgNode": { + "component": "/_core/org-chart/index", + "path": "/system/org-chart", + "title": "menu-title.orgChart", + }, "UIConfigManager": { "component": "/_core/ui-config/index", "path": "/system/ui-config", @@ -131,19 +170,33 @@ def _normalize_ai_agent_admin_menu(menu: Menu) -> None: def _normalize_ai_agent_admin_parentage(menus: List[Menu]) -> None: - """Attach retained system children back to the lightweight system catalog.""" + """Attach retained children back to the lightweight product catalogs.""" retained_ids = {menu.id for menu in menus} system_menu = next( (menu for menu in menus if menu.name == "SystemManagement"), None, ) + ai_menu = next( + (menu for menu in menus if menu.name == "AIPlatform"), + None, + ) + message_menu = next( + (menu for menu in menus if menu.name == "Message"), + None, + ) for menu in menus: - if not menu.parent_id or menu.parent_id in retained_ids: - continue if system_menu and menu.name in AI_AGENT_ADMIN_SYSTEM_CHILD_MENU_NAMES: menu.parent_id = system_menu.id - else: - menu.parent_id = None + continue + if ai_menu and menu.name in AI_AGENT_ADMIN_AI_CHILD_MENU_NAMES: + menu.parent_id = ai_menu.id + continue + if message_menu and menu.name in AI_AGENT_ADMIN_MESSAGE_CHILD_MENU_NAMES: + menu.parent_id = message_menu.id + continue + if not menu.parent_id or menu.parent_id in retained_ids: + continue + menu.parent_id = None class MenuService(BaseService[Menu, MenuCreate, MenuUpdate]): diff --git a/backend-fastapi/core/router.py b/backend-fastapi/core/router.py index 206b71d..d4430e9 100644 --- a/backend-fastapi/core/router.py +++ b/backend-fastapi/core/router.py @@ -10,6 +10,7 @@ from core.chat.api import router as chat_router from core.dept.api import router as dept_router from core.device.api import router as device_router from core.file_manager.router import router as file_manager_router +from core.login_log.api import router as login_log_router from core.menu.api import router as menu_router from core.message.api import announcement_router from core.message.api import router as message_router @@ -40,6 +41,7 @@ router.include_router(field_permission_router) router.include_router(role_router) router.include_router(user_router) router.include_router(file_manager_router) +router.include_router(login_log_router) router.include_router(message_router) router.include_router(announcement_router) router.include_router(oauth_router) diff --git a/web/apps/web-ele/src/locales/index.ts b/web/apps/web-ele/src/locales/index.ts index cc7736a..93f30eb 100644 --- a/web/apps/web-ele/src/locales/index.ts +++ b/web/apps/web-ele/src/locales/index.ts @@ -29,17 +29,7 @@ const baseModules = import.meta.glob([ './langs/zh-CN/ui.json', ]); -const businessModules = import.meta.glob([ - './langs/zh-CN/ai-platform.json', - './langs/zh-CN/announcement.json', - './langs/zh-CN/dashboard-design.json', - './langs/zh-CN/menu.json', - './langs/zh-CN/message.json', - './langs/zh-CN/permission.json', - './langs/zh-CN/role.json', - './langs/zh-CN/system.json', - './langs/zh-CN/user.json', -]); +const businessModules = import.meta.glob('./langs/zh-CN/*.json'); const baseLocalesMap = loadLocalesMapFromDir( /\.\/langs\/([^/]+)\/(.*)\.json$/, diff --git a/web/apps/web-ele/src/locales/langs/zh-CN/application.json b/web/apps/web-ele/src/locales/langs/zh-CN/application.json new file mode 100644 index 0000000..1195544 --- /dev/null +++ b/web/apps/web-ele/src/locales/langs/zh-CN/application.json @@ -0,0 +1,74 @@ +{ + "title": "应用管理", + "createApp": "创建应用", + "editApp": "编辑应用", + "search": "搜索", + "searchPlaceholder": "搜索应用名称或编码", + "noApps": "暂无应用", + "appName": "应用名称", + "appNamePlaceholder": "请输入应用名称", + "appCode": "应用编码", + "appCodePlaceholder": "请输入应用编码(用于URL路由)", + "appType": "应用类型", + "appTypePlaceholder": "请选择应用类型", + "appDescription": "应用描述", + "appDescriptionPlaceholder": "请输入应用描述", + "appIcon": "应用图标", + "systemMenu": "系统菜单", + "systemMenuPlaceholder": "选择子应用可访问的基础菜单(留空则显示全部)", + "selectSystemMenu": "选择系统菜单", + "save": "保存", + "create": "创建", + "cancel": "取消", + "edit": "编辑", + "publish": "发布", + "enable": "启用", + "disable": "停用", + "delete": "删除", + "publishApp": "发布应用", + "enableApp": "启用应用", + "disableApp": "停用应用", + "confirmPublish": "确认发布", + "confirmEnable": "确认启用", + "confirmDisable": "确认停用", + "publishConfirmMsg": "确定要发布应用「{name}」吗?", + "enableConfirmMsg": "确定要重新启用应用「{name}」吗?", + "publishSuccessMsg": "发布后,用户可以通过以下链接访问该应用:", + "enableSuccessMsg": "启用后,用户可以通过以下链接访问该应用:", + "disableConfirmMsg": "确定要停用应用「{name}」吗?", + "appLink": "应用链接:", + "deleteConfirm": "删除确认", + "deleteConfirmMsg": "确定要删除应用「{name}」吗?", + "confirm": "确定", + "loadFailed": "加载应用列表失败", + "publishSuccess": "发布成功", + "publishFailed": "发布失败", + "enableSuccess": "启用成功", + "enableFailed": "启用失败", + "disableSuccess": "停用成功", + "disableFailed": "停用失败", + "deleteSuccess": "删除成功", + "copySuccess": "链接已复制到剪贴板", + "copyFailed": "复制失败", + "appTypes": { + "mixed": "混合应用", + "form": "表单应用", + "workflow": "流程应用", + "ai": "AI应用", + "dashboard": "数据应用", + "screen": "大屏应用" + }, + "appStatus": { + "draft": "开发中", + "published": "已发布", + "disabled": "已停用" + }, + "validation": { + "nameRequired": "请输入应用名称", + "nameLength": "长度在 2 到 100 个字符", + "codeRequired": "请输入应用编码", + "codePattern": "编码必须以字母开头,只能包含字母、数字、下划线和短横线", + "codeLength": "长度在 2 到 100 个字符", + "typeRequired": "请选择应用类型" + } +} diff --git a/web/apps/web-ele/src/locales/langs/zh-CN/menu-title.json b/web/apps/web-ele/src/locales/langs/zh-CN/menu-title.json index c0ebafe..6bebd86 100644 --- a/web/apps/web-ele/src/locales/langs/zh-CN/menu-title.json +++ b/web/apps/web-ele/src/locales/langs/zh-CN/menu-title.json @@ -23,6 +23,7 @@ "serverMonitoring": "服务器监控", "uiConfig": "界面配置", "loginLog": "登录日志", + "orgChart": "组织架构", "roleManagement": "角色权限", "permissionManagement": "API管理", "menuManagement": "菜单管理", diff --git a/web/apps/web-ele/src/router/access.ts b/web/apps/web-ele/src/router/access.ts index 6a514a4..ee956e2 100644 --- a/web/apps/web-ele/src/router/access.ts +++ b/web/apps/web-ele/src/router/access.ts @@ -175,14 +175,17 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) { '../views/_core/agent-chat/**/*.vue', '../views/_core/announcement/index.vue', '../views/_core/announcement/list.vue', + '../views/_core/application/index.vue', '../views/_core/authentication/login.vue', '../views/_core/dept/index.vue', '../views/_core/dict/index.vue', '../views/_core/fallback/**/*.vue', '../views/_core/file-manager/index.vue', '../views/_core/file-preview/index.vue', + '../views/_core/login-log/index.vue', '../views/_core/menu/index.vue', '../views/_core/message/index.vue', + '../views/_core/org-chart/index.vue', '../views/_core/page-render/index.vue', '../views/_core/permission/index.vue', '../views/_core/post/index.vue', diff --git a/web/apps/web-ele/src/router/light-menu.ts b/web/apps/web-ele/src/router/light-menu.ts index a13b7cd..7ec295c 100644 --- a/web/apps/web-ele/src/router/light-menu.ts +++ b/web/apps/web-ele/src/router/light-menu.ts @@ -1,5 +1,6 @@ export const LIGHT_MENU_NAMES = new Set([ 'AccountSettings', + 'Application', 'ControlCenter', 'AIAgent', 'AIKnowledgeDetail', @@ -14,10 +15,14 @@ export const LIGHT_MENU_NAMES = new Set([ 'MessageCenter', 'MessageList', 'KnowledgeBase', + 'LoginLog', + 'OrgNode', 'SystemDept', 'SystemDict', 'SystemFileManager', + 'SystemLoginLog', 'SystemManagement', + 'SystemOrgChart', 'SystemConfig', 'SystemConfigManager', 'SystemMenu', @@ -38,6 +43,7 @@ const LIGHT_ROUTE_PATH_PREFIXES = [ '/ai-platform/workflow', '/ai-platform/workflow-runs', '/agent-chat', + '/application', '/message/list', '/message/announcement', '/message/announcement-list', @@ -47,6 +53,8 @@ const LIGHT_ROUTE_PATH_PREFIXES = [ '/system/dept', '/system/dict', '/system/file-manager', + '/system/login-log', + '/system/org-chart', '/system/post', '/system/config', '/system/menu', diff --git a/web/apps/web-ele/src/views/_core/application/index.vue b/web/apps/web-ele/src/views/_core/application/index.vue new file mode 100644 index 0000000..a45d539 --- /dev/null +++ b/web/apps/web-ele/src/views/_core/application/index.vue @@ -0,0 +1,513 @@ + + + + + diff --git a/web/apps/web-ele/src/views/_core/application/modules/application-form-modal.vue b/web/apps/web-ele/src/views/_core/application/modules/application-form-modal.vue new file mode 100644 index 0000000..157af66 --- /dev/null +++ b/web/apps/web-ele/src/views/_core/application/modules/application-form-modal.vue @@ -0,0 +1,345 @@ + + + diff --git a/web/apps/web-ele/src/views/_core/org-chart/index.vue b/web/apps/web-ele/src/views/_core/org-chart/index.vue new file mode 100644 index 0000000..a7c6259 --- /dev/null +++ b/web/apps/web-ele/src/views/_core/org-chart/index.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/web/apps/web-ele/src/views/_core/org-chart/modules/OrgChartPanel.vue b/web/apps/web-ele/src/views/_core/org-chart/modules/OrgChartPanel.vue new file mode 100644 index 0000000..03bd483 --- /dev/null +++ b/web/apps/web-ele/src/views/_core/org-chart/modules/OrgChartPanel.vue @@ -0,0 +1,344 @@ + + + + + diff --git a/web/apps/web-ele/src/views/_core/org-chart/modules/OrgNode.vue b/web/apps/web-ele/src/views/_core/org-chart/modules/OrgNode.vue new file mode 100644 index 0000000..4c0539c --- /dev/null +++ b/web/apps/web-ele/src/views/_core/org-chart/modules/OrgNode.vue @@ -0,0 +1,496 @@ + + + + +