imetting/backend/sql/migrations/upgrade_imeeting_qy_to_late...

1253 lines
47 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

-- iMeeting legacy DB upgrade
-- Goal: upgrade an old schema to the latest application-facing structure
-- Strategy:
-- 1) standardize system tables to sys_* base tables
-- 2) align shared table columns/indexes
-- 3) create missing configuration tables
-- 4) migrate legacy configuration/hot-word data into new tables
-- 5) rebuild menu tree and role permissions needed by the latest backend
-- 6) recreate compatibility views
-- ----------------------------------------------------------------------
-- 1. Rename legacy system tables to sys_* base tables when needed
-- ----------------------------------------------------------------------
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'users' AND table_type = 'BASE TABLE'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'sys_users'
),
'RENAME TABLE users TO sys_users',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'roles' AND table_type = 'BASE TABLE'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'sys_roles'
),
'RENAME TABLE roles TO sys_roles',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'menus' AND table_type = 'BASE TABLE'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'sys_menus'
),
'RENAME TABLE menus TO sys_menus',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'role_menu_permissions' AND table_type = 'BASE TABLE'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions'
),
'RENAME TABLE role_menu_permissions TO sys_role_menu_permissions',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'dict_data' AND table_type = 'BASE TABLE'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'sys_dict_data'
),
'RENAME TABLE dict_data TO sys_dict_data',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'system_parameters' AND table_type = 'BASE TABLE'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'sys_system_parameters'
),
'RENAME TABLE system_parameters TO sys_system_parameters',
'SELECT 1'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ----------------------------------------------------------------------
-- 2. Align existing sys_* tables
-- ----------------------------------------------------------------------
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_users' AND index_name = 'idx_role_id'
),
'SELECT 1',
'ALTER TABLE sys_users ADD KEY idx_role_id (role_id)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_roles' AND index_name = 'uk_role_name'
),
'SELECT 1',
'ALTER TABLE sys_roles ADD UNIQUE KEY uk_role_name (role_name)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND column_name = 'menu_level'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD COLUMN menu_level TINYINT(3) NOT NULL DEFAULT 1 COMMENT ''菜单层级根节点为1'' AFTER parent_id'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND column_name = 'tree_path'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD COLUMN tree_path VARCHAR(255) DEFAULT NULL COMMENT ''树路径(如 /3/6'' AFTER menu_level'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND column_name = 'is_visible'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD COLUMN is_visible TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''是否在侧边菜单显示'' AFTER is_active'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND index_name = 'idx_menu_level'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD KEY idx_menu_level (menu_level)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND index_name = 'idx_tree_path'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD KEY idx_tree_path (tree_path)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND index_name = 'idx_is_visible'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD KEY idx_is_visible (is_visible)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_menus' AND index_name = 'idx_menus_visible_tree'
),
'SELECT 1',
'ALTER TABLE sys_menus ADD KEY idx_menus_visible_tree (is_active, is_visible, parent_id, sort_order, menu_id)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions' AND column_name = 'granted_by'
),
'SELECT 1',
'ALTER TABLE sys_role_menu_permissions ADD COLUMN granted_by INT(11) DEFAULT NULL COMMENT ''授权操作人ID'' AFTER menu_id'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions' AND column_name = 'granted_at'
),
'SELECT 1',
'ALTER TABLE sys_role_menu_permissions ADD COLUMN granted_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT ''授权时间'' AFTER granted_by'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions' AND index_name = 'idx_granted_by'
),
'SELECT 1',
'ALTER TABLE sys_role_menu_permissions ADD KEY idx_granted_by (granted_by)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions' AND index_name = 'idx_granted_at'
),
'SELECT 1',
'ALTER TABLE sys_role_menu_permissions ADD KEY idx_granted_at (granted_at)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions' AND index_name = 'idx_rmp_role'
),
'SELECT 1',
'ALTER TABLE sys_role_menu_permissions ADD KEY idx_rmp_role (role_id)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'sys_role_menu_permissions' AND index_name = 'idx_rmp_menu'
),
'SELECT 1',
'ALTER TABLE sys_role_menu_permissions ADD KEY idx_rmp_menu (menu_id)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ALTER TABLE `terminals`
MODIFY COLUMN `current_user_id` INT(11) DEFAULT NULL COMMENT '终端绑定账号';
-- prompts: add is_system and composite index
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'prompts' AND column_name = 'is_system'
),
'SELECT 1',
'ALTER TABLE prompts ADD COLUMN is_system TINYINT(1) NOT NULL DEFAULT 0 AFTER creator_id'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := (
SELECT IF(
EXISTS (
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'prompts' AND index_name = 'idx_prompts_task_scope_active'
),
'SELECT 1',
'CREATE INDEX idx_prompts_task_scope_active ON prompts (task_type, is_system, creator_id, is_active, is_default)'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
UPDATE prompts
SET is_system = 1
WHERE creator_id = 1;
-- ----------------------------------------------------------------------
-- 3. Create missing tables with latest structure
-- ----------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `sys_system_parameters` (
`param_id` bigint(20) NOT NULL AUTO_INCREMENT,
`param_key` varchar(128) NOT NULL,
`param_name` varchar(255) NOT NULL,
`param_value` text,
`value_type` varchar(32) NOT NULL DEFAULT 'string',
`category` varchar(64) NOT NULL DEFAULT 'system',
`description` varchar(500) DEFAULT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`param_id`),
UNIQUE KEY `uk_param_key` (`param_key`),
KEY `idx_param_category` (`category`),
KEY `idx_param_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `ai_model_configs` (
`model_id` bigint(20) NOT NULL AUTO_INCREMENT,
`model_code` varchar(128) NOT NULL,
`model_name` varchar(255) NOT NULL,
`model_type` varchar(32) NOT NULL,
`provider` varchar(64) DEFAULT NULL,
`endpoint_url` varchar(512) DEFAULT NULL,
`api_key` varchar(512) DEFAULT NULL,
`llm_model_name` varchar(128) DEFAULT NULL,
`llm_timeout` int(11) DEFAULT NULL,
`llm_temperature` decimal(5,2) DEFAULT NULL,
`llm_top_p` decimal(5,2) DEFAULT NULL,
`llm_max_tokens` int(11) DEFAULT NULL,
`llm_system_prompt` text,
`asr_model_name` varchar(128) DEFAULT NULL,
`asr_vocabulary_id` varchar(255) DEFAULT NULL,
`asr_speaker_count` int(11) DEFAULT NULL,
`asr_language_hints` varchar(255) DEFAULT NULL,
`asr_disfluency_removal_enabled` tinyint(1) DEFAULT NULL,
`asr_diarization_enabled` tinyint(1) DEFAULT NULL,
`config_json` json DEFAULT NULL,
`description` varchar(500) DEFAULT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`is_default` tinyint(1) NOT NULL DEFAULT '0',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`model_id`),
UNIQUE KEY `uk_model_code` (`model_code`),
KEY `idx_model_type` (`model_type`),
KEY `idx_model_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `llm_model_config` (
`config_id` bigint(20) NOT NULL AUTO_INCREMENT,
`model_code` varchar(128) NOT NULL,
`model_name` varchar(255) NOT NULL,
`provider` varchar(64) DEFAULT NULL,
`endpoint_url` varchar(512) DEFAULT NULL,
`api_key` varchar(512) DEFAULT NULL,
`llm_model_name` varchar(128) NOT NULL,
`llm_timeout` int(11) NOT NULL DEFAULT '120',
`llm_temperature` decimal(5,2) NOT NULL DEFAULT '0.70',
`llm_top_p` decimal(5,2) NOT NULL DEFAULT '0.90',
`llm_max_tokens` int(11) NOT NULL DEFAULT '2048',
`llm_system_prompt` text,
`description` varchar(500) DEFAULT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`is_default` tinyint(1) NOT NULL DEFAULT '0',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`config_id`),
UNIQUE KEY `uk_llm_model_code` (`model_code`),
KEY `idx_llm_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `audio_model_config` (
`config_id` bigint(20) NOT NULL AUTO_INCREMENT,
`model_code` varchar(128) NOT NULL,
`model_name` varchar(255) NOT NULL,
`audio_scene` varchar(32) NOT NULL,
`provider` varchar(64) DEFAULT NULL,
`endpoint_url` varchar(512) DEFAULT NULL,
`api_key` varchar(512) DEFAULT NULL,
`asr_model_name` varchar(128) DEFAULT NULL,
`asr_vocabulary_id` varchar(255) DEFAULT NULL,
`hot_word_group_id` int(11) DEFAULT NULL COMMENT '关联热词组ID',
`extra_config` json DEFAULT NULL COMMENT '音频模型差异化配置(JSON)',
`asr_speaker_count` int(11) DEFAULT NULL,
`asr_language_hints` varchar(255) DEFAULT NULL,
`asr_disfluency_removal_enabled` tinyint(1) DEFAULT NULL,
`asr_diarization_enabled` tinyint(1) DEFAULT NULL,
`vp_template_text` text,
`vp_duration_seconds` int(11) DEFAULT NULL,
`vp_sample_rate` int(11) DEFAULT NULL,
`vp_channels` int(11) DEFAULT NULL,
`vp_max_size_bytes` bigint(20) DEFAULT NULL,
`description` varchar(500) DEFAULT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`is_default` tinyint(1) NOT NULL DEFAULT '0',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`config_id`),
UNIQUE KEY `uk_audio_model_code` (`model_code`),
KEY `idx_audio_scene` (`audio_scene`),
KEY `idx_audio_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hot_word_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '热词组名称',
`description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '描述',
`vocabulary_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '阿里云 DashScope 词表ID',
`last_sync_time` datetime DEFAULT NULL COMMENT '最后同步时间',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1:启用 0:停用',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='热词组主表';
CREATE TABLE IF NOT EXISTS `hot_word_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) NOT NULL COMMENT '热词组ID',
`text` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '热词内容',
`weight` int(11) NOT NULL DEFAULT '4' COMMENT '权重 1-10',
`lang` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'zh' COMMENT 'zh/en',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1:启用 0:停用',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_group_text` (`group_id`,`text`),
KEY `idx_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='热词条目从表';
CREATE TABLE IF NOT EXISTS `prompt_config` (
`config_id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`task_type` enum('MEETING_TASK','KNOWLEDGE_TASK') NOT NULL,
`prompt_id` int(11) NOT NULL,
`is_enabled` tinyint(1) NOT NULL DEFAULT '1',
`sort_order` int(11) NOT NULL DEFAULT '0',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`config_id`),
UNIQUE KEY `uk_user_task_prompt` (`user_id`,`task_type`,`prompt_id`),
KEY `idx_user_task_order` (`user_id`,`task_type`,`sort_order`),
KEY `idx_prompt_id` (`prompt_id`),
CONSTRAINT `fk_upc_prompt` FOREIGN KEY (`prompt_id`) REFERENCES `prompts` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_upc_user` FOREIGN KEY (`user_id`) REFERENCES `sys_users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sys_user_mcp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`bot_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`bot_secret` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
`last_used_at` datetime DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sys_user_mcp_user_id` (`user_id`),
UNIQUE KEY `uk_sys_user_mcp_bot_id` (`bot_id`),
KEY `idx_sys_user_mcp_status` (`status`),
CONSTRAINT `fk_sys_user_mcp_user` FOREIGN KEY (`user_id`) REFERENCES `sys_users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户MCP接入凭证';
-- ----------------------------------------------------------------------
-- 4. Migrate legacy system config data into new parameter/model tables
-- ----------------------------------------------------------------------
INSERT INTO `sys_system_parameters`
(`param_key`, `param_name`, `param_value`, `value_type`, `category`, `description`, `is_active`)
SELECT
CASE
WHEN d.`dict_code` = 'timeline_pagesize' THEN 'page_size'
WHEN d.`dict_code` = 'branding_app_name' THEN 'app_name'
WHEN d.`dict_code` = 'branding_console_subtitle' THEN 'console_subtitle'
WHEN d.`dict_code` = 'branding_preview_title' THEN 'preview_title'
WHEN d.`dict_code` = 'branding_login_welcome' THEN 'login_welcome'
WHEN d.`dict_code` = 'branding_footer_text' THEN 'footer_text'
ELSE d.`dict_code`
END,
d.`label_cn`,
JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.value')),
'string',
CASE
WHEN d.`dict_code` IN (
'timeline_pagesize',
'page_size',
'branding_app_name',
'app_name',
'branding_console_subtitle',
'console_subtitle',
'branding_preview_title',
'preview_title',
'branding_login_welcome',
'login_welcome',
'branding_footer_text',
'footer_text',
'max_audio_size',
'max_image_size'
) THEN 'public'
ELSE 'system'
END,
CONCAT('migrated from dict_data.system_config(', d.`dict_code`, ')'),
CASE WHEN d.`status` = 1 THEN 1 ELSE 0 END
FROM `sys_dict_data` d
WHERE d.`dict_type` = 'system_config'
AND d.`dict_code` NOT IN ('llm_model', 'voiceprint')
AND JSON_EXTRACT(d.`extension_attr`, '$.value') IS NOT NULL
ON DUPLICATE KEY UPDATE
`param_name` = VALUES(`param_name`),
`param_value` = VALUES(`param_value`),
`category` = VALUES(`category`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`);
INSERT INTO `ai_model_configs`
(`model_code`, `model_name`, `model_type`, `provider`, `endpoint_url`, `api_key`,
`llm_model_name`, `llm_timeout`, `llm_temperature`, `llm_top_p`, `llm_max_tokens`, `llm_system_prompt`,
`config_json`, `description`, `is_active`, `is_default`)
SELECT
'llm_model',
'默认文本模型',
'llm',
'dashscope',
'https://dashscope.aliyuncs.com/compatible-mode/v1',
NULL,
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.model_name')), 'qwen-plus'),
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.time_out')) AS UNSIGNED), 120),
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.temperature')) AS DECIMAL(5,2)), 0.70),
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.top_p')) AS DECIMAL(5,2)), 0.90),
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.max_tokens')) AS UNSIGNED), 2048),
JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.system_prompt')),
d.`extension_attr`,
'migrated from dict_data.system_config.llm_model',
CASE WHEN d.`status` = 1 THEN 1 ELSE 0 END,
1
FROM `sys_dict_data` d
WHERE d.`dict_type` = 'system_config'
AND d.`dict_code` = 'llm_model'
LIMIT 1
ON DUPLICATE KEY UPDATE
`model_name` = VALUES(`model_name`),
`provider` = VALUES(`provider`),
`endpoint_url` = VALUES(`endpoint_url`),
`llm_model_name` = VALUES(`llm_model_name`),
`llm_timeout` = VALUES(`llm_timeout`),
`llm_temperature` = VALUES(`llm_temperature`),
`llm_top_p` = VALUES(`llm_top_p`),
`llm_max_tokens` = VALUES(`llm_max_tokens`),
`llm_system_prompt` = VALUES(`llm_system_prompt`),
`config_json` = VALUES(`config_json`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`),
`is_default` = VALUES(`is_default`);
INSERT INTO `ai_model_configs`
(`model_code`, `model_name`, `model_type`, `provider`, `endpoint_url`, `api_key`,
`asr_model_name`, `asr_vocabulary_id`, `asr_speaker_count`, `asr_language_hints`,
`asr_disfluency_removal_enabled`, `asr_diarization_enabled`,
`config_json`, `description`, `is_active`, `is_default`)
SELECT
'audio_model',
'默认音频识别模型',
'audio',
'dashscope',
'https://dashscope.aliyuncs.com/api/v1/services/audio/asr/transcription',
NULL,
'paraformer-v2',
(
SELECT JSON_UNQUOTE(JSON_EXTRACT(d2.`extension_attr`, '$.value'))
FROM `sys_dict_data` d2
WHERE d2.`dict_type` = 'system_config' AND d2.`dict_code` = 'asr_vocabulary_id'
LIMIT 1
),
10,
'zh, en',
1,
1,
JSON_OBJECT(
'model', 'paraformer-v2',
'vocabulary_id', (
SELECT JSON_UNQUOTE(JSON_EXTRACT(d3.`extension_attr`, '$.value'))
FROM `sys_dict_data` d3
WHERE d3.`dict_type` = 'system_config' AND d3.`dict_code` = 'asr_vocabulary_id'
LIMIT 1
),
'speaker_count', 10,
'language_hints', 'zh, en',
'disfluency_removal_enabled', TRUE,
'diarization_enabled', TRUE
),
'migrated default ASR config',
1,
1
FROM DUAL
ON DUPLICATE KEY UPDATE
`model_name` = VALUES(`model_name`),
`provider` = VALUES(`provider`),
`endpoint_url` = VALUES(`endpoint_url`),
`asr_model_name` = VALUES(`asr_model_name`),
`asr_vocabulary_id` = VALUES(`asr_vocabulary_id`),
`asr_speaker_count` = VALUES(`asr_speaker_count`),
`asr_language_hints` = VALUES(`asr_language_hints`),
`asr_disfluency_removal_enabled` = VALUES(`asr_disfluency_removal_enabled`),
`asr_diarization_enabled` = VALUES(`asr_diarization_enabled`),
`config_json` = VALUES(`config_json`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`),
`is_default` = VALUES(`is_default`);
INSERT INTO `ai_model_configs`
(`model_code`, `model_name`, `model_type`, `provider`, `endpoint_url`, `api_key`,
`config_json`, `description`, `is_active`, `is_default`)
SELECT
'voiceprint_model',
'默认声纹模型',
'audio',
'funasr',
'http://127.0.0.1:10095',
NULL,
d.`extension_attr`,
'migrated from dict_data.system_config.voiceprint',
CASE WHEN d.`status` = 1 THEN 1 ELSE 0 END,
0
FROM `sys_dict_data` d
WHERE d.`dict_type` = 'system_config'
AND d.`dict_code` = 'voiceprint'
LIMIT 1
ON DUPLICATE KEY UPDATE
`model_name` = VALUES(`model_name`),
`provider` = VALUES(`provider`),
`endpoint_url` = VALUES(`endpoint_url`),
`config_json` = VALUES(`config_json`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`),
`is_default` = VALUES(`is_default`);
INSERT INTO `llm_model_config`
(`model_code`, `model_name`, `provider`, `endpoint_url`, `api_key`, `llm_model_name`, `llm_timeout`,
`llm_temperature`, `llm_top_p`, `llm_max_tokens`, `llm_system_prompt`, `description`, `is_active`, `is_default`)
SELECT
`model_code`,
`model_name`,
`provider`,
`endpoint_url`,
`api_key`,
COALESCE(`llm_model_name`, JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.model_name')), 'qwen-plus'),
COALESCE(`llm_timeout`, CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.time_out')) AS UNSIGNED), 120),
COALESCE(`llm_temperature`, CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.temperature')) AS DECIMAL(5,2)), 0.70),
COALESCE(`llm_top_p`, CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.top_p')) AS DECIMAL(5,2)), 0.90),
COALESCE(`llm_max_tokens`, CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.max_tokens')) AS UNSIGNED), 2048),
COALESCE(`llm_system_prompt`, JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.system_prompt'))),
`description`,
`is_active`,
`is_default`
FROM `ai_model_configs`
WHERE `model_type` = 'llm'
ON DUPLICATE KEY UPDATE
`model_name` = VALUES(`model_name`),
`provider` = VALUES(`provider`),
`endpoint_url` = VALUES(`endpoint_url`),
`api_key` = VALUES(`api_key`),
`llm_model_name` = VALUES(`llm_model_name`),
`llm_timeout` = VALUES(`llm_timeout`),
`llm_temperature` = VALUES(`llm_temperature`),
`llm_top_p` = VALUES(`llm_top_p`),
`llm_max_tokens` = VALUES(`llm_max_tokens`),
`llm_system_prompt` = VALUES(`llm_system_prompt`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`),
`is_default` = VALUES(`is_default`);
INSERT INTO `audio_model_config`
(`model_code`, `model_name`, `audio_scene`, `provider`, `endpoint_url`, `api_key`, `asr_model_name`, `asr_vocabulary_id`,
`asr_speaker_count`, `asr_language_hints`, `asr_disfluency_removal_enabled`, `asr_diarization_enabled`,
`description`, `is_active`, `is_default`)
SELECT
`model_code`,
`model_name`,
'asr',
`provider`,
`endpoint_url`,
`api_key`,
COALESCE(`asr_model_name`, JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.model')), 'paraformer-v2'),
COALESCE(`asr_vocabulary_id`, JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.vocabulary_id'))),
COALESCE(`asr_speaker_count`, CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.speaker_count')) AS UNSIGNED), 10),
COALESCE(`asr_language_hints`, 'zh, en'),
COALESCE(`asr_disfluency_removal_enabled`, 1),
COALESCE(`asr_diarization_enabled`, 1),
`description`,
`is_active`,
`is_default`
FROM `ai_model_configs`
WHERE `model_code` = 'audio_model'
ON DUPLICATE KEY UPDATE
`model_name` = VALUES(`model_name`),
`provider` = VALUES(`provider`),
`endpoint_url` = VALUES(`endpoint_url`),
`api_key` = VALUES(`api_key`),
`asr_model_name` = VALUES(`asr_model_name`),
`asr_vocabulary_id` = VALUES(`asr_vocabulary_id`),
`asr_speaker_count` = VALUES(`asr_speaker_count`),
`asr_language_hints` = VALUES(`asr_language_hints`),
`asr_disfluency_removal_enabled` = VALUES(`asr_disfluency_removal_enabled`),
`asr_diarization_enabled` = VALUES(`asr_diarization_enabled`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`),
`is_default` = VALUES(`is_default`);
INSERT INTO `audio_model_config`
(`model_code`, `model_name`, `audio_scene`, `provider`, `endpoint_url`, `api_key`,
`vp_template_text`, `vp_duration_seconds`, `vp_sample_rate`, `vp_channels`, `vp_max_size_bytes`,
`description`, `is_active`, `is_default`)
SELECT
`model_code`,
`model_name`,
'voiceprint',
`provider`,
`endpoint_url`,
`api_key`,
JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.template_text')),
CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.duration_seconds')) AS UNSIGNED),
CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.sample_rate')) AS UNSIGNED),
CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.channels')) AS UNSIGNED),
CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.voiceprint_max_size')) AS UNSIGNED),
`description`,
`is_active`,
`is_default`
FROM `ai_model_configs`
WHERE `model_code` = 'voiceprint_model'
ON DUPLICATE KEY UPDATE
`model_name` = VALUES(`model_name`),
`provider` = VALUES(`provider`),
`endpoint_url` = VALUES(`endpoint_url`),
`api_key` = VALUES(`api_key`),
`vp_template_text` = VALUES(`vp_template_text`),
`vp_duration_seconds` = VALUES(`vp_duration_seconds`),
`vp_sample_rate` = VALUES(`vp_sample_rate`),
`vp_channels` = VALUES(`vp_channels`),
`vp_max_size_bytes` = VALUES(`vp_max_size_bytes`),
`description` = VALUES(`description`),
`is_active` = VALUES(`is_active`),
`is_default` = VALUES(`is_default`);
-- Backfill extra_config JSON for audio model configs
UPDATE `audio_model_config`
SET `extra_config` = CASE
WHEN `audio_scene` = 'asr' THEN JSON_OBJECT(
'model', `asr_model_name`,
'vocabulary_id', `asr_vocabulary_id`,
'speaker_count', `asr_speaker_count`,
'language_hints', `asr_language_hints`,
'disfluency_removal_enabled', `asr_disfluency_removal_enabled`,
'diarization_enabled', `asr_diarization_enabled`
)
WHEN `audio_scene` = 'voiceprint' THEN JSON_OBJECT(
'model', `model_name`,
'template_text', `vp_template_text`,
'duration_seconds', `vp_duration_seconds`,
'sample_rate', `vp_sample_rate`,
'channels', `vp_channels`,
'max_size_bytes', `vp_max_size_bytes`
)
ELSE JSON_OBJECT()
END
WHERE `extra_config` IS NULL;
-- ----------------------------------------------------------------------
-- 5. Hot-word migration: old hot_words -> group/item
-- ----------------------------------------------------------------------
INSERT INTO `hot_word_group` (`name`, `description`, `status`)
SELECT '默认热词组', '从旧 hot_words 表迁移的热词', 1
FROM DUAL
WHERE EXISTS (SELECT 1 FROM `hot_words` LIMIT 1)
AND NOT EXISTS (SELECT 1 FROM `hot_word_group` WHERE `name` = '默认热词组');
UPDATE `hot_word_group` g
JOIN (
SELECT JSON_UNQUOTE(JSON_EXTRACT(`extension_attr`, '$.value')) AS vocab_id
FROM `sys_dict_data`
WHERE `dict_type` = 'system_config' AND `dict_code` = 'asr_vocabulary_id'
LIMIT 1
) p ON 1 = 1
SET g.`vocabulary_id` = p.`vocab_id`,
g.`last_sync_time` = COALESCE(g.`last_sync_time`, NOW())
WHERE g.`name` = '默认热词组'
AND (g.`vocabulary_id` IS NULL OR g.`vocabulary_id` = '');
INSERT INTO `hot_word_item`
(`group_id`, `text`, `weight`, `lang`, `status`, `create_time`, `update_time`)
SELECT
g.`id`,
hw.`text`,
hw.`weight`,
hw.`lang`,
hw.`status`,
hw.`create_time`,
hw.`update_time`
FROM `hot_words` hw
JOIN `hot_word_group` g ON g.`name` = '默认热词组'
LEFT JOIN `hot_word_item` i
ON i.`group_id` = g.`id`
AND i.`text` = hw.`text`
WHERE i.`id` IS NULL;
UPDATE `audio_model_config` a
JOIN `hot_word_group` g ON g.`name` = '默认热词组'
SET a.`hot_word_group_id` = g.`id`
WHERE a.`audio_scene` = 'asr'
AND a.`hot_word_group_id` IS NULL
AND a.`asr_vocabulary_id` IS NOT NULL
AND a.`asr_vocabulary_id` <> '';
-- ----------------------------------------------------------------------
-- 6. Rebuild menu tree for the latest backend
-- ----------------------------------------------------------------------
-- Reuse legacy root rows when possible
SET @has_dashboard := (SELECT COUNT(*) FROM `sys_menus` WHERE `menu_code` = 'dashboard');
UPDATE `sys_menus`
SET
`menu_code` = 'dashboard',
`menu_name` = 'Dashboard',
`menu_icon` = 'DashboardOutlined',
`menu_url` = '/dashboard',
`menu_type` = 'link',
`parent_id` = NULL,
`sort_order` = 1,
`is_active` = 1,
`is_visible` = 1,
`description` = '管理员桌面'
WHERE `menu_code` = 'account_settings'
AND @has_dashboard = 0;
SET @has_desktop := (SELECT COUNT(*) FROM `sys_menus` WHERE `menu_code` = 'desktop');
UPDATE `sys_menus`
SET
`menu_code` = 'desktop',
`menu_name` = 'Desktop',
`menu_icon` = 'DesktopOutlined',
`menu_url` = '/dashboard',
`menu_type` = 'link',
`parent_id` = NULL,
`sort_order` = 2,
`is_active` = 1,
`is_visible` = 1,
`description` = '普通用户桌面'
WHERE `menu_code` = 'logout'
AND @has_desktop = 0;
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
VALUES
('dashboard', 'Dashboard', 'DashboardOutlined', '/dashboard', 'link', NULL, 1, NULL, 1, 1, 1, '管理员桌面')
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
VALUES
('desktop', 'Desktop', 'DesktopOutlined', '/dashboard', 'link', NULL, 1, NULL, 2, 1, 1, '普通用户桌面')
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
VALUES
('platform_admin', '平台管理', 'Shield', '/admin/management/hot-word-management', 'link', NULL, 1, NULL, 3, 1, 1, '平台管理员后台'),
('system_management', '系统管理', 'Setting', '/admin/management/user-management', 'link', NULL, 1, NULL, 4, 1, 1, '系统基础配置管理(用户、权限、字典、参数)'),
('meeting_manage', '会议管理', 'CalendarOutlined', '/meetings/center', 'link', NULL, 1, NULL, 2, 1, 1, '普通用户会议菜单')
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'prompt_management', '提示词库', 'BookText', '/prompt-management', 'link',
p.`menu_id`, 2, NULL, 3, 1, 1, '管理AI提示词模版'
FROM `sys_menus` p
WHERE p.`menu_code` = 'platform_admin'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'hot_word_management', '热词管理', 'Text', '/admin/management/hot-word-management', 'link',
p.`menu_id`, 2, NULL, 1, 1, 1, 'ASR 热词与同步'
FROM `sys_menus` p
WHERE p.`menu_code` = 'platform_admin'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'model_management', '模型管理', 'Appstore', '/admin/management/model-management', 'link',
p.`menu_id`, 2, NULL, 2, 1, 1, '音频/LLM模型配置管理'
FROM `sys_menus` p
WHERE p.`menu_code` = 'platform_admin'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'client_management', '客户端管理', 'Smartphone', '/admin/management/client-management', 'link',
p.`menu_id`, 2, NULL, 5, 1, 1, '版本、下载地址、发布状态'
FROM `sys_menus` p
WHERE p.`menu_code` = 'platform_admin'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'external_app_management', '外部应用管理', 'AppWindow', '/admin/management/external-app-management', 'link',
p.`menu_id`, 2, NULL, 6, 1, 1, '外部系统入口与图标配置'
FROM `sys_menus` p
WHERE p.`menu_code` = 'platform_admin'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'terminal_management', '终端管理', 'Monitor', '/admin/management/terminal-management', 'link',
p.`menu_id`, 2, NULL, 7, 1, 1, '专用设备、激活和绑定状态'
FROM `sys_menus` p
WHERE p.`menu_code` = 'platform_admin'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'user_management', '用户管理', 'Users', '/admin/management/user-management', 'link',
p.`menu_id`, 2, NULL, 1, 1, 1, '账号、角色、密码重置'
FROM `sys_menus` p
WHERE p.`menu_code` = 'system_management'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'permission_management', '权限管理', 'KeyRound', '/admin/management/permission-management', 'link',
p.`menu_id`, 2, NULL, 2, 1, 1, '菜单与角色授权矩阵'
FROM `sys_menus` p
WHERE p.`menu_code` = 'system_management'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'dict_management', '字典管理', 'BookMarked', '/admin/management/dict-management', 'link',
p.`menu_id`, 2, NULL, 3, 1, 1, '码表、平台类型、扩展属性'
FROM `sys_menus` p
WHERE p.`menu_code` = 'system_management'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'parameter_management', '参数管理', 'Setting', '/admin/management/parameter-management', 'link',
p.`menu_id`, 2, NULL, 8, 1, 1, '系统参数管理'
FROM `sys_menus` p
WHERE p.`menu_code` = 'system_management'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'permission_menu_tree', '菜单树维护', 'AppstoreAdd', '/admin/management/permission-management', 'link',
p.`menu_id`, 3, NULL, 20, 1, 0, '权限管理中的菜单树维护入口(隐藏于侧栏)'
FROM `sys_menus` p
WHERE p.`menu_code` = 'permission_management'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'meeting_center', '会议中心', 'CalendarOutlined', '/meetings/center', 'link',
p.`menu_id`, 2, NULL, 1, 1, 1, '普通用户会议中心'
FROM `sys_menus` p
WHERE p.`menu_code` = 'meeting_manage'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'prompt_config', '提示词配置', 'Book', '/prompt-config', 'link',
p.`menu_id`, 2, NULL, 2, 1, 1, '用户可配置启用提示词与排序'
FROM `sys_menus` p
WHERE p.`menu_code` = 'meeting_manage'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
INSERT INTO `sys_menus`
(`menu_code`, `menu_name`, `menu_icon`, `menu_url`, `menu_type`, `parent_id`, `menu_level`, `tree_path`, `sort_order`, `is_active`, `is_visible`, `description`)
SELECT
'personal_prompt_library', '个人提示词仓库', 'ReadOutlined', '/personal-prompts', 'link',
p.`menu_id`, 2, NULL, 3, 0, 1, '普通用户个人提示词仓库'
FROM `sys_menus` p
WHERE p.`menu_code` = 'meeting_manage'
ON DUPLICATE KEY UPDATE
`menu_name` = VALUES(`menu_name`),
`menu_icon` = VALUES(`menu_icon`),
`menu_url` = VALUES(`menu_url`),
`menu_type` = VALUES(`menu_type`),
`parent_id` = VALUES(`parent_id`),
`sort_order` = VALUES(`sort_order`),
`is_active` = VALUES(`is_active`),
`is_visible` = VALUES(`is_visible`),
`description` = VALUES(`description`);
-- Menu tree metadata (supports current depth 3)
UPDATE `sys_menus`
SET `menu_level` = 1,
`tree_path` = CONCAT('/', `menu_id`)
WHERE `parent_id` IS NULL;
UPDATE `sys_menus` c
JOIN `sys_menus` p ON c.`parent_id` = p.`menu_id`
SET c.`menu_level` = p.`menu_level` + 1,
c.`tree_path` = CONCAT(p.`tree_path`, '/', c.`menu_id`);
UPDATE `sys_menus` c
JOIN `sys_menus` p ON c.`parent_id` = p.`menu_id`
SET c.`menu_level` = p.`menu_level` + 1,
c.`tree_path` = CONCAT(p.`tree_path`, '/', c.`menu_id`);
-- ----------------------------------------------------------------------
-- 7. Align role-menu permissions for latest menu model
-- ----------------------------------------------------------------------
-- Keep existing role-menu permissions unchanged.
-- The upgrade must preserve current grants instead of rebuilding defaults,
-- so repeated execution will not auto-complete or reset menu permissions.
-- ----------------------------------------------------------------------
-- 8. Recreate compatibility views
-- ----------------------------------------------------------------------
DROP VIEW IF EXISTS `users`;
DROP VIEW IF EXISTS `roles`;
DROP VIEW IF EXISTS `menus`;
DROP VIEW IF EXISTS `role_menu_permissions`;
DROP VIEW IF EXISTS `dict_data`;
DROP VIEW IF EXISTS `system_parameters`;
CREATE VIEW `users` AS SELECT * FROM `sys_users`;
CREATE VIEW `roles` AS SELECT * FROM `sys_roles`;
CREATE VIEW `menus` AS SELECT * FROM `sys_menus`;
CREATE VIEW `role_menu_permissions` AS SELECT * FROM `sys_role_menu_permissions`;
CREATE VIEW `dict_data` AS SELECT * FROM `sys_dict_data`;
CREATE VIEW `system_parameters` AS SELECT * FROM `sys_system_parameters`;