diff --git a/backend/app/main.py b/backend/app/main.py index 52ed3cc..a82986e 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -3,7 +3,17 @@ from pathlib import Path # 添加项目根目录到 Python 路径 current_file = Path(__file__).resolve() +package_dir = current_file.parent project_root = current_file.parent.parent + +# When this file is executed as `python app/main.py`, Python automatically puts +# `/.../backend/app` on sys.path. That shadows the third-party `mcp` package +# with our local `app/mcp` package and breaks `from mcp.server.fastmcp import FastMCP`. +try: + sys.path.remove(str(package_dir)) +except ValueError: + pass + if str(project_root) not in sys.path: sys.path.insert(0, str(project_root)) diff --git a/backend/app/services/admin_settings_service.py b/backend/app/services/admin_settings_service.py index 3dba0dd..03a01d2 100644 --- a/backend/app/services/admin_settings_service.py +++ b/backend/app/services/admin_settings_service.py @@ -17,6 +17,9 @@ def _validate_parameter_request(request): if not param_key: return "参数键不能为空" + if param_key in SystemConfigService.DEPRECATED_BRANDING_PARAMETER_KEYS: + return f"{param_key} 已废弃,现已改为前端固定文案,不再支持配置" + if param_key == SystemConfigService.TOKEN_EXPIRE_DAYS: if request.category != "system": return "token_expire_days 必须归类为 system" @@ -173,7 +176,10 @@ def list_parameters(category: str | None = None, keyword: str | None = None): query += " ORDER BY category ASC, param_key ASC" cursor.execute(query, tuple(params)) - rows = cursor.fetchall() + rows = [ + row for row in cursor.fetchall() + if row["param_key"] not in SystemConfigService.DEPRECATED_BRANDING_PARAMETER_KEYS + ] return create_api_response( code="200", message="获取参数列表成功", @@ -185,6 +191,9 @@ def list_parameters(category: str | None = None, keyword: str | None = None): def get_parameter(param_key: str): try: + if param_key in SystemConfigService.DEPRECATED_BRANDING_PARAMETER_KEYS: + return create_api_response(code="404", message="参数不存在") + SystemConfigService.ensure_builtin_parameters() with get_db_connection() as conn: cursor = conn.cursor(dictionary=True) diff --git a/backend/app/services/system_config_service.py b/backend/app/services/system_config_service.py index 1a000f6..753ad6f 100644 --- a/backend/app/services/system_config_service.py +++ b/backend/app/services/system_config_service.py @@ -21,10 +21,12 @@ class SystemConfigService: # 品牌配置 APP_NAME = 'app_name' - CONSOLE_SUBTITLE = 'console_subtitle' - PREVIEW_TITLE = 'preview_title' - LOGIN_WELCOME = 'login_welcome' - FOOTER_TEXT = 'footer_text' + DEPRECATED_BRANDING_PARAMETER_KEYS = frozenset({ + 'console_subtitle', + 'preview_title', + 'login_welcome', + 'footer_text', + }) # 声纹配置 VOICEPRINT_TEMPLATE_TEXT = 'voiceprint_template_text' @@ -97,42 +99,6 @@ class SystemConfigService: "description": "前端应用标题。", "is_active": 1, }, - { - "param_key": CONSOLE_SUBTITLE, - "param_name": "控制台副标题", - "param_value": "智能会议协作平台", - "value_type": "string", - "category": PUBLIC_CATEGORY, - "description": "登录后控制台副标题。", - "is_active": 1, - }, - { - "param_key": PREVIEW_TITLE, - "param_name": "会议预览标题", - "param_value": "会议预览", - "value_type": "string", - "category": PUBLIC_CATEGORY, - "description": "会议预览页标题。", - "is_active": 1, - }, - { - "param_key": LOGIN_WELCOME, - "param_name": "登录欢迎语", - "param_value": "欢迎登录 iMeeting,请输入您的账号信息。", - "value_type": "string", - "category": PUBLIC_CATEGORY, - "description": "登录页欢迎语。", - "is_active": 1, - }, - { - "param_key": FOOTER_TEXT, - "param_name": "页脚文案", - "param_value": "©2026 iMeeting", - "value_type": "string", - "category": PUBLIC_CATEGORY, - "description": "系统页脚文案。", - "is_active": 1, - }, ] @classmethod @@ -703,10 +669,6 @@ class SystemConfigService: public_configs = cls.get_configs_by_category(cls.PUBLIC_CATEGORY) required_keys = [ cls.APP_NAME, - cls.CONSOLE_SUBTITLE, - cls.PREVIEW_TITLE, - cls.LOGIN_WELCOME, - cls.FOOTER_TEXT, cls.PAGE_SIZE, cls.MAX_AUDIO_SIZE, cls.MAX_IMAGE_SIZE, @@ -720,12 +682,7 @@ class SystemConfigService: max_image_size_mb = cls.get_max_image_size() return { - **public_configs, "app_name": str(public_configs[cls.APP_NAME]).strip(), - "console_subtitle": str(public_configs[cls.CONSOLE_SUBTITLE]).strip(), - "preview_title": str(public_configs[cls.PREVIEW_TITLE]).strip(), - "login_welcome": str(public_configs[cls.LOGIN_WELCOME]).strip(), - "footer_text": str(public_configs[cls.FOOTER_TEXT]).strip(), "page_size": str(page_size), "PAGE_SIZE": page_size, "max_audio_size": str(max_audio_size_mb), diff --git a/backend/sql/imeeting-seed-latest.sql b/backend/sql/imeeting-seed-latest.sql index 54a4889..90a0c5c 100644 --- a/backend/sql/imeeting-seed-latest.sql +++ b/backend/sql/imeeting-seed-latest.sql @@ -160,11 +160,7 @@ VALUES ('page_size', '系统分页大小', '10', 'number', 'public', '系统通用分页数量。', 1, NOW(), NOW()), ('max_audio_size', '音频上传大小限制', '100', 'number', 'public', '音频上传大小限制,单位:MB。', 1, NOW(), NOW()), ('max_image_size', '图片上传大小限制', '10', 'number', 'public', '图片上传大小限制,单位:MB。', 1, NOW(), NOW()), - ('app_name', '系统名称', 'iMeeting', 'string', 'public', '前端应用标题。', 1, NOW(), NOW()), - ('console_subtitle', '控制台副标题', '智能会议协作平台', 'string', 'public', '登录后控制台副标题。', 1, NOW(), NOW()), - ('preview_title', '会议预览标题', '会议预览', 'string', 'public', '会议预览页标题。', 1, NOW(), NOW()), - ('login_welcome', '登录欢迎语', '欢迎登录 iMeeting,请输入您的账号信息。', 'string', 'public', '登录页欢迎语。', 1, NOW(), NOW()), - ('footer_text', '页脚文案', '©2026 iMeeting', 'string', 'public', '系统页脚文案。', 1, NOW(), NOW()) + ('app_name', '系统名称', 'iMeeting', 'string', 'public', '前端应用标题。', 1, NOW(), NOW()) ON DUPLICATE KEY UPDATE `param_name` = VALUES(`param_name`), `param_value` = VALUES(`param_value`), diff --git a/frontend/src/config/systemManagementModules.jsx b/frontend/src/config/systemManagementModules.jsx index e56903e..9addbd4 100644 --- a/frontend/src/config/systemManagementModules.jsx +++ b/frontend/src/config/systemManagementModules.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { lazy } from 'react'; import { UserOutlined, KeyOutlined, @@ -10,7 +10,7 @@ import { DeploymentUnitOutlined, } from '@ant-design/icons'; -export const SYSTEM_MANAGEMENT_OVERVIEW_LOADER = () => import('../pages/admin/SystemManagementOverview'); +export const SYSTEM_MANAGEMENT_OVERVIEW_COMPONENT = lazy(() => import('../pages/admin/SystemManagementOverview')); export const SYSTEM_MANAGEMENT_MODULES = [ { @@ -19,7 +19,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '账号、角色、密码重置', path: '/admin/management/user-management', icon: , - loader: () => import('../pages/admin/UserManagement'), + component: lazy(() => import('../pages/admin/UserManagement')), group: '基础治理', }, { @@ -28,7 +28,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '菜单与角色授权矩阵', path: '/admin/management/permission-management', icon: , - loader: () => import('../pages/admin/PermissionManagement'), + component: lazy(() => import('../pages/admin/PermissionManagement')), group: '基础治理', }, { @@ -37,7 +37,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '码表、平台类型、扩展属性', path: '/admin/management/dict-management', icon: , - loader: () => import('../pages/admin/DictManagement'), + component: lazy(() => import('../pages/admin/DictManagement')), group: '基础治理', }, { @@ -46,7 +46,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: 'ASR 热词与同步', path: '/admin/management/hot-word-management', icon: , - loader: () => import('../pages/admin/HotWordManagement'), + component: lazy(() => import('../pages/admin/HotWordManagement')), group: '基础治理', }, { @@ -55,7 +55,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '系统参数统一配置', path: '/admin/management/parameter-management', icon: , - loader: () => import('../pages/admin/ParameterManagement'), + component: lazy(() => import('../pages/admin/ParameterManagement')), group: '平台配置', }, { @@ -64,7 +64,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '音频模型与 LLM 模型配置', path: '/admin/management/model-management', icon: , - loader: () => import('../pages/admin/ModelManagement'), + component: lazy(() => import('../pages/admin/ModelManagement')), group: '平台配置', }, { @@ -73,7 +73,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '版本、下载地址、发布状态', path: '/admin/management/client-management', icon: , - loader: () => import('../pages/ClientManagement'), + component: lazy(() => import('../pages/ClientManagement')), group: '设备与应用', }, { @@ -82,7 +82,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '外部系统入口与图标配置', path: '/admin/management/external-app-management', icon: , - loader: () => import('../pages/admin/ExternalAppManagement'), + component: lazy(() => import('../pages/admin/ExternalAppManagement')), group: '设备与应用', }, { @@ -91,7 +91,7 @@ export const SYSTEM_MANAGEMENT_MODULES = [ desc: '专用设备、激活和绑定状态', path: '/admin/management/terminal-management', icon: , - loader: () => import('../pages/admin/TerminalManagement'), + component: lazy(() => import('../pages/admin/TerminalManagement')), group: '设备与应用', }, ]; diff --git a/frontend/src/pages/AdminManagement.jsx b/frontend/src/pages/AdminManagement.jsx index 2cdfe6f..49d7ef2 100644 --- a/frontend/src/pages/AdminManagement.jsx +++ b/frontend/src/pages/AdminManagement.jsx @@ -1,9 +1,9 @@ -import React, { Suspense, lazy, useMemo } from 'react'; +import React, { Suspense, useMemo } from 'react'; import { Alert } from 'antd'; import { Navigate, useParams } from 'react-router-dom'; import { DEFAULT_SYSTEM_MANAGEMENT_MODULE, - SYSTEM_MANAGEMENT_OVERVIEW_LOADER, + SYSTEM_MANAGEMENT_OVERVIEW_COMPONENT, SYSTEM_MANAGEMENT_MODULE_MAP, } from '../config/systemManagementModules'; @@ -13,17 +13,11 @@ const AdminManagement = () => { const currentModule = useMemo(() => ( currentModuleKey === DEFAULT_SYSTEM_MANAGEMENT_MODULE - ? { loader: SYSTEM_MANAGEMENT_OVERVIEW_LOADER } + ? { component: SYSTEM_MANAGEMENT_OVERVIEW_COMPONENT } : SYSTEM_MANAGEMENT_MODULE_MAP[currentModuleKey] ), [currentModuleKey]); - const CurrentComponent = useMemo(() => { - if (!currentModule?.loader) { - return null; - } - - return lazy(currentModule.loader); - }, [currentModule]); + const CurrentComponent = currentModule?.component || null; if (!moduleKey) { return ; @@ -42,7 +36,7 @@ const AdminManagement = () => { return ( }> - + ); }; diff --git a/frontend/src/pages/admin/ParameterManagement.jsx b/frontend/src/pages/admin/ParameterManagement.jsx index 37c258b..8402677 100644 --- a/frontend/src/pages/admin/ParameterManagement.jsx +++ b/frontend/src/pages/admin/ParameterManagement.jsx @@ -26,10 +26,7 @@ const PUBLIC_PARAM_KEYS = new Set([ 'app_name', 'page_size', 'max_audio_size', - 'preview_title', - 'login_welcome', - 'footer_text', - 'console_subtitle', + 'max_image_size', ]); const ParameterManagement = () => { diff --git a/frontend/src/utils/configService.js b/frontend/src/utils/configService.js index 7421085..17127a2 100644 --- a/frontend/src/utils/configService.js +++ b/frontend/src/utils/configService.js @@ -3,12 +3,13 @@ import { buildApiUrl, API_ENDPOINTS } from '../config/api'; const PUBLIC_CONFIG_CACHE_KEY = 'imeeting_public_config_cache'; const PUBLIC_CONFIG_CACHE_TTL = 5 * 60 * 1000; +const FIXED_BRANDING = { + console_subtitle: '智能会议协作平台', + preview_title: '会议预览', + login_welcome: '欢迎登录,请输入您的账号信息。', +}; const REQUIRED_PUBLIC_CONFIG_KEYS = [ 'app_name', - 'console_subtitle', - 'preview_title', - 'login_welcome', - 'footer_text', 'PAGE_SIZE', 'MAX_FILE_SIZE', 'MAX_IMAGE_SIZE', @@ -50,10 +51,6 @@ const validatePublicConfig = (configs) => { return { ...configs, app_name: String(configs.app_name).trim(), - console_subtitle: String(configs.console_subtitle).trim(), - preview_title: String(configs.preview_title).trim(), - login_welcome: String(configs.login_welcome).trim(), - footer_text: String(configs.footer_text).trim(), PAGE_SIZE: pageSize, page_size: String(pageSize), MAX_FILE_SIZE: maxFileSize, @@ -115,12 +112,13 @@ class ConfigService { } buildBrandingConfig(configs) { + const appName = String(configs?.app_name || 'iMeeting').trim() || 'iMeeting'; return { - app_name: configs.app_name, - console_subtitle: configs.console_subtitle, - preview_title: configs.preview_title, - login_welcome: configs.login_welcome, - footer_text: configs.footer_text, + app_name: appName, + console_subtitle: FIXED_BRANDING.console_subtitle, + preview_title: FIXED_BRANDING.preview_title, + login_welcome: FIXED_BRANDING.login_welcome, + footer_text: `©${new Date().getFullYear()} ${appName}`, }; }