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

1241 lines
46 KiB
MySQL
Raw Normal View History

2026-04-03 07:00:06 +00:00
-- 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
d.`dict_code`,
d.`label_cn`,
JSON_UNQUOTE(JSON_EXTRACT(d.`extension_attr`, '$.value')),
'string',
'system',
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`),
`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
-- ----------------------------------------------------------------------
-- Admin gets all active menus
INSERT IGNORE INTO `sys_role_menu_permissions` (`role_id`, `menu_id`, `granted_by`, `granted_at`)
SELECT 1, m.`menu_id`, 1, NOW()
FROM `sys_menus` m
WHERE m.`is_active` = 1;
-- Normal user only gets desktop + meeting menus + prompt config
DELETE p
FROM `sys_role_menu_permissions` p
JOIN `sys_menus` m ON m.`menu_id` = p.`menu_id`
WHERE p.`role_id` = 2
AND m.`menu_code` NOT IN ('desktop', 'meeting_manage', 'meeting_center', 'prompt_config');
INSERT IGNORE INTO `sys_role_menu_permissions` (`role_id`, `menu_id`, `granted_by`, `granted_at`)
SELECT 2, m.`menu_id`, 1, NOW()
FROM `sys_menus` m
WHERE m.`menu_code` IN ('desktop', 'meeting_manage', 'meeting_center', 'prompt_config')
AND m.`is_active` = 1;
-- ----------------------------------------------------------------------
-- 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`;