实体: - journal_entry: 日记核心表 (心情/天气/标签/版本) - journal_element: 日记元素 (文字/图片/贴纸/手写/胶带) - handwriting_stroke: 手写笔画 (独立大字段表) - school_class: 班级 (6位码/过期控制) - class_member: 班级成员 (复合PK) - topic_assignment: 主题布置 - comment: 老师点评 - sticker_pack + sticker: 贴纸包和贴纸 - template: 日记模板 - achievement + user_achievement: 成就系统 - parent_child_binding: 家长-孩子绑定 (PIPL) - teacher_profile: 老师档案 - user_settings: 用户设置 迁移 (000170-000184): - 15 个建表迁移 + 索引 + RLS 策略 + 种子数据 - 所有表含 tenant_id 多租户隔离 - 软删除 + 乐观锁版本号 - 外键级联删除 - 暖记权限注册到基座 permissions 表 验证: cargo check 通过, 425 个测试全通过
100 lines
5.7 KiB
Rust
100 lines
5.7 KiB
Rust
// 暖记种子数据 — 默认成就 + 基础贴纸包 + 暖记权限
|
||
|
||
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();
|
||
|
||
// 默认租户和系统用户 UUID(内嵌 SQL,避免类型转换问题)
|
||
let tid = "'00000000-0000-0000-0000-000000000000'::uuid";
|
||
|
||
// 插入默认成就
|
||
let achievements = [
|
||
("first_diary", "初出茅庐", "写下第一篇日记", "📝", "writing", r#"{"type":"diary_count","threshold":1}"#, 10),
|
||
("diary_10", "小有成就", "累计写 10 篇日记", "✏️", "writing", r#"{"type":"diary_count","threshold":10}"#, 20),
|
||
("diary_50", "笔下生花", "累计写 50 篇日记", "🌸", "writing", r#"{"type":"diary_count","threshold":50}"#, 30),
|
||
("diary_100", "日记达人", "累计写 100 篇日记", "🏆", "writing", r#"{"type":"diary_count","threshold":100}"#, 40),
|
||
("streak_3", "三日不间断", "连续 3 天写日记", "🔥", "writing", r#"{"type":"streak","threshold":3}"#, 50),
|
||
("streak_7", "一周坚持", "连续 7 天写日记", "⭐", "writing", r#"{"type":"streak","threshold":7}"#, 60),
|
||
("streak_30", "月度冠军", "连续 30 天写日记", "👑", "writing", r#"{"type":"streak","threshold":30}"#, 70),
|
||
("first_share", "分享快乐", "第一次分享日记到班级", "💝", "social", r#"{"type":"share_count","threshold":1}"#, 80),
|
||
("comment_received", "获得鼓励", "第一次收到老师点评", "💌", "social", r#"{"type":"comment_received","threshold":1}"#, 90),
|
||
("all_moods", "情绪彩虹", "使用过所有 5 种心情", "🌈", "collection", r#"{"type":"mood_variety","threshold":5}"#, 100),
|
||
];
|
||
|
||
for (code, name, desc, icon, category, condition, sort) in &achievements {
|
||
let sql = format!(
|
||
r#"INSERT INTO achievements (id, tenant_id, code, name, description, icon, category, condition, sort_order, created_at, updated_at, created_by, updated_by, version)
|
||
VALUES (gen_random_uuid(), {tid}, '{code}', '{name}', '{desc}', '{icon}', '{category}', '{condition}'::jsonb, {sort}, now(), now(), {tid}, {tid}, 1)"#,
|
||
);
|
||
conn.execute(sea_orm::Statement::from_string(
|
||
sea_orm::DatabaseBackend::Postgres,
|
||
sql,
|
||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||
}
|
||
|
||
// 插入基础贴纸包
|
||
conn.execute(sea_orm::Statement::from_string(
|
||
sea_orm::DatabaseBackend::Postgres,
|
||
format!(
|
||
r#"INSERT INTO sticker_packs (id, tenant_id, name, description, is_free, price, category, created_at, updated_at, created_by, updated_by, version)
|
||
VALUES (gen_random_uuid(), {tid}, '基础贴纸', '暖记默认贴纸包,包含常用表情和装饰', true, 0, 'basic', now(), now(), {tid}, {tid}, 1)"#,
|
||
),
|
||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||
|
||
// 插入暖记权限到 permissions 表 (resource + action 模式)
|
||
let diary_permissions = [
|
||
("diary.journal.create", "创建日记", "journal", "create", "允许创建日记条目"),
|
||
("diary.journal.read", "查看日记", "journal", "read", "允许查看日记条目"),
|
||
("diary.journal.update", "编辑日记", "journal", "update", "允许编辑日记条目"),
|
||
("diary.journal.delete", "删除日记", "journal", "delete", "允许删除日记条目"),
|
||
("diary.class.manage", "管理班级", "class", "manage", "允许创建和管理班级"),
|
||
("diary.topic.assign", "布置主题", "topic", "assign", "允许老师布置日记主题"),
|
||
("diary.comment.write", "写评语", "comment", "write", "允许老师点评日记"),
|
||
("diary.parent.bind", "家长绑定", "parent", "bind", "允许家长绑定孩子账号"),
|
||
];
|
||
|
||
for (code, name, resource, action, desc) in &diary_permissions {
|
||
let sql = format!(
|
||
r#"INSERT INTO permissions (id, tenant_id, code, name, resource, action, description, created_at, updated_at, created_by, updated_by, version)
|
||
VALUES (gen_random_uuid(), {tid}, '{code}', '{name}', '{resource}', '{action}', '{desc}', 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()))?;
|
||
}
|
||
|
||
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'";
|
||
|
||
conn.execute(sea_orm::Statement::from_string(
|
||
sea_orm::DatabaseBackend::Postgres,
|
||
format!("DELETE FROM achievements {tid_str}"),
|
||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||
|
||
conn.execute(sea_orm::Statement::from_string(
|
||
sea_orm::DatabaseBackend::Postgres,
|
||
format!("DELETE FROM sticker_packs {tid_str}"),
|
||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||
|
||
conn.execute(sea_orm::Statement::from_string(
|
||
sea_orm::DatabaseBackend::Postgres,
|
||
format!("DELETE FROM permissions WHERE code LIKE 'diary.%' AND tenant_id = '00000000-0000-0000-0000-000000000000'"),
|
||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||
|
||
Ok(())
|
||
}
|
||
}
|