diff --git a/crates/erp-server/migration/src/lib.rs b/crates/erp-server/migration/src/lib.rs index 4372a87..b5a8120 100644 --- a/crates/erp-server/migration/src/lib.rs +++ b/crates/erp-server/migration/src/lib.rs @@ -70,6 +70,7 @@ mod m20260531_000181_create_teacher_profiles; mod m20260531_000182_create_user_settings; mod m20260531_000183_diary_indexes_and_fts; mod m20260531_000184_diary_seed_data; +mod m20260601_000300_diary_role_seed; pub struct Migrator; @@ -146,6 +147,7 @@ impl MigratorTrait for Migrator { Box::new(m20260531_000182_create_user_settings::Migration), Box::new(m20260531_000183_diary_indexes_and_fts::Migration), Box::new(m20260531_000184_diary_seed_data::Migration), + Box::new(m20260601_000300_diary_role_seed::Migration), ] } } diff --git a/crates/erp-server/migration/src/m20260601_000300_diary_role_seed.rs b/crates/erp-server/migration/src/m20260601_000300_diary_role_seed.rs new file mode 100644 index 0000000..8c4acdf --- /dev/null +++ b/crates/erp-server/migration/src/m20260601_000300_diary_role_seed.rs @@ -0,0 +1,100 @@ +// 暖记角色种子 — student/teacher/parent 角色 + 权限分配 + +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 tid = "'00000000-0000-0000-0000-000000000000'::uuid"; + + // 插入 student/teacher/parent 角色 + let roles = [ + ("student", "学生", "学生 — 创建和管理自己的日记", false), + ("teacher", "老师", "老师 — 创建班级、布置主题、点评日记", false), + ("parent", "家长", "家长 — 查看和管理孩子的日记数据", false), + ]; + + for (code, name, desc, is_system) in &roles { + let sys_flag = if *is_system { "true" } else { "false" }; + let sql = format!( + r#"INSERT INTO roles (id, tenant_id, name, code, description, is_system, created_at, updated_at, created_by, updated_by, version) + VALUES (gen_random_uuid(), {tid}, '{name}', '{code}', '{desc}', {sys_flag}, now(), now(), {tid}, {tid}, 1) + ON CONFLICT (tenant_id, code) WHERE deleted_at IS NULL DO NOTHING"#, + ); + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + sql, + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; + } + + // student 权限: diary.journal.create, diary.journal.read + // teacher 权限: diary.journal.create, diary.journal.read, diary.class.manage, diary.topic.assign, diary.comment.write + // parent 权限: diary.journal.read, diary.parent.bind + let role_permissions = [ + ("student", "diary.journal.create"), + ("student", "diary.journal.read"), + ("teacher", "diary.journal.create"), + ("teacher", "diary.journal.read"), + ("teacher", "diary.class.manage"), + ("teacher", "diary.topic.assign"), + ("teacher", "diary.comment.write"), + ("parent", "diary.journal.read"), + ("parent", "diary.parent.bind"), + ]; + + for (role_code, perm_code) in &role_permissions { + let sql = format!( + r#"INSERT INTO role_permissions (id, role_id, permission_id, tenant_id, created_at, updated_at, created_by, updated_by, version) + SELECT gen_random_uuid(), r.id, p.id, r.tenant_id, now(), now(), {tid}, {tid}, 1 + FROM roles r + JOIN permissions p ON p.tenant_id = r.tenant_id + WHERE r.code = '{role_code}' AND r.tenant_id = {tid} AND r.deleted_at IS NULL + AND p.code = '{perm_code}' AND p.deleted_at IS NULL + ON CONFLICT DO NOTHING"#, + ); + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + sql, + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; + } + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let conn = manager.get_connection(); + let tid_str = "WHERE tenant_id = '00000000-0000-0000-0000-000000000000'"; + + // 先删除 role_permissions 引用 + for role_code in &["student", "teacher", "parent"] { + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!( + "DELETE FROM role_permissions WHERE role_id IN (SELECT id FROM roles WHERE code = '{role_code}' {tid_str})" + ), + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; + } + + // 再删除角色 + for role_code in &["student", "teacher", "parent"] { + conn.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!("DELETE FROM roles WHERE code = '{role_code}' {tid_str}"), + )) + .await + .map_err(|e| DbErr::Custom(e.to_string()))?; + } + + Ok(()) + } +}