Files
nj/crates/erp-server/migration/src/m20260602_000301_diary_menu_seed.rs
iven 75db6a7eb7
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
fix(server): 修复菜单种子迁移 — 使用动态 tenant 查询替代 nil UUID
2026-06-02 14:13:32 +08:00

152 lines
7.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 暖记菜单种子 — 日记管理侧边栏菜单(班级管理/日记审核/主题管理/贴纸管理)
// 为所有现有租户插入菜单数据
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let conn = manager.get_connection();
let sys_user = "'00000000-0000-0000-0000-000000000000'::uuid";
// 为每个现有租户插入日记管理菜单
// 使用 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)
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
.map_err(|e| DbErr::Custom(e.to_string()))?;
// 2. 子菜单: 班级管理
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)
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
.map_err(|e| DbErr::Custom(e.to_string()))?;
// 3. 子菜单: 日记审核
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)
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
.map_err(|e| DbErr::Custom(e.to_string()))?;
// 4. 子菜单: 主题管理
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)
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
.map_err(|e| DbErr::Custom(e.to_string()))?;
// 5. 子菜单: 贴纸管理
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)
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 角色
// 查找每个租户下的 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()))?;
// 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(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let conn = manager.get_connection();
// 先删除 menu_roles 引用
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 title IN ('日记管理', '班级管理', '日记审核', '主题管理', '贴纸管理')".to_string(),
))
.await
.map_err(|e| DbErr::Custom(e.to_string()))?;
Ok(())
}
}