Files
nj/crates/erp-server/migration/src/m20260601_000300_diary_role_seed.rs
iven b320641d9c
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
fix(app): 全链路验证修复 — 编译错误/CORS/迁移/启动脚本
前端修复:
- calendar_page: 移除不存在的 JournalEntry.content getter
- responsive_scaffold: 移除不存在的 notchThickness 参数
- splash_page: SingleTickerProvider → TickerProvider (多 AnimationController)
- profile_page: UserRoleType.name → .code (修复运行时崩溃)
- 导入缺失的 user.dart

后端修复:
- class_service: generate_class_code 取 UUID 后6位(随机部分)避免碰撞
- diary_role_seed: 移除不存在的 id 列,使用复合主键 ON CONFLICT

基础设施:
- config/default.toml: CORS 改为通配符(开发模式)
- scripts/dev.sh: 统一启动脚本(自动清理端口)
- docs/opendesign/: Open Design 设计规格 HTML 原型稿

验证结果: flutter analyze 0 error, cargo test 77/77 通过, 17个页面全部渲染正常
2026-06-02 01:03:58 +08:00

102 lines
4.3 KiB
Rust

// 暖记角色种子 — 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 {
// role_permissions 表主键为 (role_id, permission_id),无 id 列
let sql = format!(
r#"INSERT INTO role_permissions (role_id, permission_id, tenant_id, created_at, updated_at, created_by, updated_by, version)
SELECT 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 (role_id, permission_id) 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(())
}
}