From 75db6a7eb77d7744ac92513cc3fbbfd03a40fe2d Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 2 Jun 2026 14:13:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(server):=20=E4=BF=AE=E5=A4=8D=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=A7=8D=E5=AD=90=E8=BF=81=E7=A7=BB=20=E2=80=94=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=8A=A8=E6=80=81=20tenant=20=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=9B=BF=E4=BB=A3=20nil=20UUID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/m20260602_000301_diary_menu_seed.rs | 195 +++++++----------- 1 file changed, 69 insertions(+), 126 deletions(-) diff --git a/crates/erp-server/migration/src/m20260602_000301_diary_menu_seed.rs b/crates/erp-server/migration/src/m20260602_000301_diary_menu_seed.rs index 8cd330d..2555917 100644 --- a/crates/erp-server/migration/src/m20260602_000301_diary_menu_seed.rs +++ b/crates/erp-server/migration/src/m20260602_000301_diary_menu_seed.rs @@ -1,4 +1,5 @@ // 暖记菜单种子 — 日记管理侧边栏菜单(班级管理/日记审核/主题管理/贴纸管理) +// 为所有现有租户插入菜单数据 use sea_orm_migration::prelude::*; @@ -9,27 +10,20 @@ pub struct Migration; impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { let conn = manager.get_connection(); - let tid = "'00000000-0000-0000-0000-000000000000'::uuid"; + let sys_user = "'00000000-0000-0000-0000-000000000000'::uuid"; - // 1. 创建"日记管理"顶级目录菜单 + // 为每个现有租户插入日记管理菜单 + // 使用 gen_random_uuid() 为每个租户生成唯一 ID,避免跨租户 ID 冲突 + + // 1. 创建"日记管理"顶级目录菜单(每个租户一条) conn.execute(sea_orm::Statement::from_string( sea_orm::DatabaseBackend::Postgres, format!( r#"INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order, visible, menu_type, permission, created_at, updated_at, created_by, updated_by, deleted_at, version) - VALUES ( - 'a0000001-0000-0000-0000-000000000001'::uuid, - {tid}, - NULL, - '日记管理', - NULL, - 'BookOutlined', - 50, - true, - 'directory', - NULL, - now(), now(), {tid}, {tid}, NULL, 1 - ) - ON CONFLICT (id) DO NOTHING"#, + SELECT gen_random_uuid(), t.id, NULL, '日记管理', NULL, 'BookOutlined', 50, true, 'directory', NULL, + now(), now(), {sys_user}, {sys_user}, NULL, 1 + FROM tenant t WHERE t.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, ), )) .await @@ -40,20 +34,11 @@ impl MigrationTrait for Migration { sea_orm::DatabaseBackend::Postgres, format!( r#"INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order, visible, menu_type, permission, created_at, updated_at, created_by, updated_by, deleted_at, version) - VALUES ( - 'a0000001-0000-0000-0000-000000000010'::uuid, - {tid}, - 'a0000001-0000-0000-0000-000000000001'::uuid, - '班级管理', - '/diary/classes', - 'TeamOutlined', - 1, - true, - 'menu', - 'diary.class.manage', - now(), now(), {tid}, {tid}, NULL, 1 - ) - ON CONFLICT (id) DO NOTHING"#, + SELECT gen_random_uuid(), m.tenant_id, m.id, '班级管理', '/diary/classes', 'TeamOutlined', 1, true, 'menu', 'diary.class.manage', + now(), now(), {sys_user}, {sys_user}, NULL, 1 + FROM menus m + WHERE m.title = '日记管理' AND m.menu_type = 'directory' AND m.parent_id IS NULL AND m.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, ), )) .await @@ -64,20 +49,11 @@ impl MigrationTrait for Migration { sea_orm::DatabaseBackend::Postgres, format!( r#"INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order, visible, menu_type, permission, created_at, updated_at, created_by, updated_by, deleted_at, version) - VALUES ( - 'a0000001-0000-0000-0000-000000000020'::uuid, - {tid}, - 'a0000001-0000-0000-0000-000000000001'::uuid, - '日记审核', - '/diary/journals', - 'FileSearchOutlined', - 2, - true, - 'menu', - 'diary.journal.read', - now(), now(), {tid}, {tid}, NULL, 1 - ) - ON CONFLICT (id) DO NOTHING"#, + SELECT gen_random_uuid(), m.tenant_id, m.id, '日记审核', '/diary/journals', 'FileSearchOutlined', 2, true, 'menu', 'diary.journal.read', + now(), now(), {sys_user}, {sys_user}, NULL, 1 + FROM menus m + WHERE m.title = '日记管理' AND m.menu_type = 'directory' AND m.parent_id IS NULL AND m.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, ), )) .await @@ -88,20 +64,11 @@ impl MigrationTrait for Migration { sea_orm::DatabaseBackend::Postgres, format!( r#"INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order, visible, menu_type, permission, created_at, updated_at, created_by, updated_by, deleted_at, version) - VALUES ( - 'a0000001-0000-0000-0000-000000000030'::uuid, - {tid}, - 'a0000001-0000-0000-0000-000000000001'::uuid, - '主题管理', - '/diary/topics', - 'ScheduleOutlined', - 3, - true, - 'menu', - 'diary.topic.assign', - now(), now(), {tid}, {tid}, NULL, 1 - ) - ON CONFLICT (id) DO NOTHING"#, + SELECT gen_random_uuid(), m.tenant_id, m.id, '主题管理', '/diary/topics', 'ScheduleOutlined', 3, true, 'menu', 'diary.topic.assign', + now(), now(), {sys_user}, {sys_user}, NULL, 1 + FROM menus m + WHERE m.title = '日记管理' AND m.menu_type = 'directory' AND m.parent_id IS NULL AND m.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, ), )) .await @@ -112,64 +79,48 @@ impl MigrationTrait for Migration { sea_orm::DatabaseBackend::Postgres, format!( r#"INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order, visible, menu_type, permission, created_at, updated_at, created_by, updated_by, deleted_at, version) - VALUES ( - 'a0000001-0000-0000-0000-000000000040'::uuid, - {tid}, - 'a0000001-0000-0000-0000-000000000001'::uuid, - '贴纸管理', - '/diary/stickers', - 'SmileOutlined', - 4, - true, - 'menu', - 'diary.journal.read', - now(), now(), {tid}, {tid}, NULL, 1 - ) - ON CONFLICT (id) DO NOTHING"#, + SELECT gen_random_uuid(), m.tenant_id, m.id, '贴纸管理', '/diary/stickers', 'SmileOutlined', 4, true, 'menu', 'diary.journal.read', + now(), now(), {sys_user}, {sys_user}, NULL, 1 + FROM menus m + WHERE m.title = '日记管理' AND m.menu_type = 'directory' AND m.parent_id IS NULL AND m.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, ), )) .await .map_err(|e| DbErr::Custom(e.to_string()))?; // 6. 将日记管理目录及其子菜单关联到 admin 角色 - let menu_ids = [ - "a0000001-0000-0000-0000-000000000001", // 日记管理 (directory) - "a0000001-0000-0000-0000-000000000010", // 班级管理 - "a0000001-0000-0000-0000-000000000020", // 日记审核 - "a0000001-0000-0000-0000-000000000030", // 主题管理 - "a0000001-0000-0000-0000-000000000040", // 贴纸管理 - ]; + // 查找每个租户下的 admin 角色,并关联该租户下的所有日记菜单 + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!( + r#"INSERT INTO menu_roles (id, menu_id, role_id, tenant_id, created_at, updated_at, created_by, updated_by, version) + SELECT gen_random_uuid(), m.id, r.id, m.tenant_id, now(), now(), {sys_user}, {sys_user}, 1 + FROM menus m + JOIN roles r ON r.tenant_id = m.tenant_id AND r.code = 'admin' AND r.deleted_at IS NULL + WHERE m.title IN ('日记管理', '班级管理', '日记审核', '主题管理', '贴纸管理') + AND m.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, + ), + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; - for mid in &menu_ids { - conn.execute(sea_orm::Statement::from_string( - sea_orm::DatabaseBackend::Postgres, - format!( - r#"INSERT INTO menu_roles (id, menu_id, role_id, tenant_id, created_at, updated_at, created_by, updated_by, version) - SELECT gen_random_uuid(), '{mid}'::uuid, r.id, r.tenant_id, now(), now(), {tid}, {tid}, 1 - FROM roles r - WHERE r.code = 'admin' AND r.tenant_id = {tid} AND r.deleted_at IS NULL - ON CONFLICT DO NOTHING"#, - ), - )) - .await - .map_err(|e| DbErr::Custom(e.to_string()))?; - } - - // 7. 也将菜单关联到 teacher 角色 - for mid in &menu_ids { - conn.execute(sea_orm::Statement::from_string( - sea_orm::DatabaseBackend::Postgres, - format!( - r#"INSERT INTO menu_roles (id, menu_id, role_id, tenant_id, created_at, updated_at, created_by, updated_by, version) - SELECT gen_random_uuid(), '{mid}'::uuid, r.id, r.tenant_id, now(), now(), {tid}, {tid}, 1 - FROM roles r - WHERE r.code = 'teacher' AND r.tenant_id = {tid} AND r.deleted_at IS NULL - ON CONFLICT DO NOTHING"#, - ), - )) - .await - .map_err(|e| DbErr::Custom(e.to_string()))?; - } + // 7. 也关联到 teacher 角色 + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!( + r#"INSERT INTO menu_roles (id, menu_id, role_id, tenant_id, created_at, updated_at, created_by, updated_by, version) + SELECT gen_random_uuid(), m.id, r.id, m.tenant_id, now(), now(), {sys_user}, {sys_user}, 1 + FROM menus m + JOIN roles r ON r.tenant_id = m.tenant_id AND r.code = 'teacher' AND r.deleted_at IS NULL + WHERE m.title IN ('日记管理', '班级管理', '日记审核', '主题管理', '贴纸管理') + AND m.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, + ), + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; Ok(()) } @@ -178,27 +129,19 @@ impl MigrationTrait for Migration { let conn = manager.get_connection(); // 先删除 menu_roles 引用 - let menu_ids = [ - "a0000001-0000-0000-0000-000000000001", - "a0000001-0000-0000-0000-000000000010", - "a0000001-0000-0000-0000-000000000020", - "a0000001-0000-0000-0000-000000000030", - "a0000001-0000-0000-0000-000000000040", - ]; - - for mid in &menu_ids { - conn.execute(sea_orm::Statement::from_string( - sea_orm::DatabaseBackend::Postgres, - format!("DELETE FROM menu_roles WHERE menu_id = '{mid}'::uuid"), - )) - .await - .map_err(|e| DbErr::Custom(e.to_string()))?; - } + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + "DELETE FROM menu_roles WHERE menu_id IN ( + SELECT id FROM menus WHERE title IN ('日记管理', '班级管理', '日记审核', '主题管理', '贴纸管理') + )".to_string(), + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; // 删除菜单 (子菜单会被 CASCADE 自动删除) conn.execute(sea_orm::Statement::from_string( sea_orm::DatabaseBackend::Postgres, - "DELETE FROM menus WHERE id = 'a0000001-0000-0000-0000-000000000001'::uuid".to_string(), + "DELETE FROM menus WHERE title IN ('日记管理', '班级管理', '日记审核', '主题管理', '贴纸管理')".to_string(), )) .await .map_err(|e| DbErr::Custom(e.to_string()))?;