fix(db): 知识库 V2 菜单迁移 — PL/pgSQL 安全检查 sys_menu 存在性
迁移 168 引用 sys_menu 表但在无基础 ERP 数据的数据库中不存在。 改用 DO $$ block + information_schema 检查,表不存在时安全跳过。
This commit is contained in:
@@ -6,56 +6,65 @@ pub struct Migration;
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl MigrationTrait for Migration {
|
impl MigrationTrait for Migration {
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
// 在"AI 知识库"同级位置添加"知识库 V2"菜单
|
// 安全插入:仅在 sys_menu 表存在且有 ai-knowledge 菜单时添加 V2 菜单
|
||||||
// parent_id 取旧 ai-knowledge 菜单的 parent_id
|
|
||||||
let sql = r#"
|
let sql = r#"
|
||||||
INSERT INTO sys_menu (id, parent_id, name, path, icon, sort, permission, component, is_external, is_cached, status, visible, created_at, updated_at, deleted_at)
|
DO $$
|
||||||
SELECT
|
BEGIN
|
||||||
gen_random_uuid(),
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'sys_menu') THEN
|
||||||
parent_id,
|
INSERT INTO sys_menu (id, parent_id, name, path, icon, sort, permission, component, is_external, is_cached, status, visible, created_at, updated_at, deleted_at)
|
||||||
'知识库 V2',
|
SELECT
|
||||||
'/health/ai-knowledge-v2',
|
gen_random_uuid(),
|
||||||
'DatabaseOutlined',
|
parent_id,
|
||||||
54,
|
'知识库 V2',
|
||||||
'ai.knowledge.list',
|
'/health/ai-knowledge-v2',
|
||||||
'ai/KnowledgeV2Page',
|
'DatabaseOutlined',
|
||||||
false,
|
54,
|
||||||
false,
|
'ai.knowledge.list',
|
||||||
'active',
|
'ai/KnowledgeV2Page',
|
||||||
true,
|
false,
|
||||||
now(),
|
false,
|
||||||
now(),
|
'active',
|
||||||
NULL
|
true,
|
||||||
FROM sys_menu
|
now(),
|
||||||
WHERE path = '/health/ai-knowledge' AND deleted_at IS NULL
|
now(),
|
||||||
LIMIT 1
|
NULL
|
||||||
ON CONFLICT DO NOTHING
|
FROM sys_menu
|
||||||
|
WHERE path = '/health/ai-knowledge' AND deleted_at IS NULL
|
||||||
|
LIMIT 1
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
IF FOUND THEN
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT r.id, m.id
|
||||||
|
FROM sys_role r, sys_menu m
|
||||||
|
WHERE r.code = 'admin' AND r.deleted_at IS NULL
|
||||||
|
AND m.path = '/health/ai-knowledge-v2' AND m.deleted_at IS NULL
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
manager.get_connection().execute_unprepared(sql).await?;
|
manager.get_connection().execute_unprepared(sql).await?;
|
||||||
|
|
||||||
// 绑定到 admin 角色
|
|
||||||
let role_sql = r#"
|
|
||||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
|
||||||
SELECT r.id, m.id
|
|
||||||
FROM sys_role r, sys_menu m
|
|
||||||
WHERE r.code = 'admin' AND r.deleted_at IS NULL
|
|
||||||
AND m.path = '/health/ai-knowledge-v2' AND m.deleted_at IS NULL
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
"#;
|
|
||||||
|
|
||||||
manager
|
|
||||||
.get_connection()
|
|
||||||
.execute_unprepared(role_sql)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
manager
|
manager
|
||||||
.get_connection()
|
.get_connection()
|
||||||
.execute_unprepared("DELETE FROM sys_menu WHERE path = '/health/ai-knowledge-v2'")
|
.execute_unprepared(
|
||||||
|
r#"
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'sys_menu') THEN
|
||||||
|
DELETE FROM sys_menu WHERE path = '/health/ai-knowledge-v2';
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql
|
||||||
|
"#,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user