feat(db): add auth schema migrations (10 tables)

- users with partial unique index on (tenant_id, username) WHERE deleted_at IS NULL
- user_credentials, user_tokens with FK cascade
- roles, permissions with composite unique (tenant_id, code)
- role_permissions, user_roles junction tables
- organizations (self-ref tree), departments (tree + org FK), positions
- All tables include standard fields: id, tenant_id, timestamps, soft delete, version
This commit is contained in:
iven
2026-04-11 02:03:23 +08:00
parent 810eef769f
commit d98e0d383c
11 changed files with 1223 additions and 1 deletions

View File

@@ -1,12 +1,34 @@
pub use sea_orm_migration::prelude::*;
mod m20260410_000001_create_tenant;
mod m20260411_000002_create_users;
mod m20260411_000003_create_user_credentials;
mod m20260411_000004_create_user_tokens;
mod m20260411_000005_create_roles;
mod m20260411_000006_create_permissions;
mod m20260411_000007_create_role_permissions;
mod m20260411_000008_create_user_roles;
mod m20260411_000009_create_organizations;
mod m20260411_000010_create_departments;
mod m20260411_000011_create_positions;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(m20260410_000001_create_tenant::Migration)]
vec![
Box::new(m20260410_000001_create_tenant::Migration),
Box::new(m20260411_000002_create_users::Migration),
Box::new(m20260411_000003_create_user_credentials::Migration),
Box::new(m20260411_000004_create_user_tokens::Migration),
Box::new(m20260411_000005_create_roles::Migration),
Box::new(m20260411_000006_create_permissions::Migration),
Box::new(m20260411_000007_create_role_permissions::Migration),
Box::new(m20260411_000008_create_user_roles::Migration),
Box::new(m20260411_000009_create_organizations::Migration),
Box::new(m20260411_000010_create_departments::Migration),
Box::new(m20260411_000011_create_positions::Migration),
]
}
}

View File

@@ -0,0 +1,109 @@
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> {
manager
.create_table(
Table::create()
.table(Users::Table)
.if_not_exists()
.col(
ColumnDef::new(Users::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(Users::TenantId).uuid().not_null())
.col(ColumnDef::new(Users::Username).string().not_null())
.col(ColumnDef::new(Users::Email).string().null())
.col(ColumnDef::new(Users::Phone).string().null())
.col(ColumnDef::new(Users::DisplayName).string().null())
.col(ColumnDef::new(Users::AvatarUrl).string().null())
.col(
ColumnDef::new(Users::Status)
.string()
.not_null()
.default("active"),
)
.col(
ColumnDef::new(Users::LastLoginAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Users::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(Users::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(Users::CreatedBy).uuid().not_null())
.col(ColumnDef::new(Users::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(Users::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Users::Version)
.integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_users_tenant_id")
.table(Users::Table)
.col(Users::TenantId)
.to_owned(),
)
.await?;
manager.get_connection().execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
"CREATE UNIQUE INDEX idx_users_tenant_username ON users (tenant_id, username) WHERE deleted_at IS NULL".to_string(),
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Users::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Users {
Table,
Id,
TenantId,
Username,
Email,
Phone,
DisplayName,
AvatarUrl,
Status,
LastLoginAt,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}

View File

@@ -0,0 +1,123 @@
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> {
manager
.create_table(
Table::create()
.table(UserCredentials::Table)
.if_not_exists()
.col(
ColumnDef::new(UserCredentials::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(UserCredentials::TenantId).uuid().not_null())
.col(ColumnDef::new(UserCredentials::UserId).uuid().not_null())
.col(
ColumnDef::new(UserCredentials::CredentialType)
.string()
.not_null()
.default("password"),
)
.col(ColumnDef::new(UserCredentials::CredentialData).json().null())
.col(
ColumnDef::new(UserCredentials::Verified)
.boolean()
.not_null()
.default(false),
)
.col(
ColumnDef::new(UserCredentials::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(UserCredentials::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(UserCredentials::CreatedBy).uuid().not_null())
.col(ColumnDef::new(UserCredentials::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(UserCredentials::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(UserCredentials::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_user_credentials_user_id")
.from(UserCredentials::Table, UserCredentials::UserId)
.to(Users::Table, Users::Id)
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_credentials_tenant_id")
.table(UserCredentials::Table)
.col(UserCredentials::TenantId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_credentials_user_id")
.table(UserCredentials::Table)
.col(UserCredentials::UserId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(UserCredentials::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum UserCredentials {
Table,
Id,
TenantId,
UserId,
CredentialType,
CredentialData,
Verified,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}
#[derive(DeriveIden)]
enum Users {
Table,
Id,
}

View File

@@ -0,0 +1,140 @@
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> {
manager
.create_table(
Table::create()
.table(UserTokens::Table)
.if_not_exists()
.col(
ColumnDef::new(UserTokens::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(UserTokens::TenantId).uuid().not_null())
.col(ColumnDef::new(UserTokens::UserId).uuid().not_null())
.col(
ColumnDef::new(UserTokens::TokenHash)
.string()
.not_null()
.unique_key(),
)
.col(ColumnDef::new(UserTokens::TokenType).string().not_null())
.col(
ColumnDef::new(UserTokens::ExpiresAt)
.timestamp_with_time_zone()
.not_null(),
)
.col(
ColumnDef::new(UserTokens::RevokedAt)
.timestamp_with_time_zone()
.null(),
)
.col(ColumnDef::new(UserTokens::DeviceInfo).string().null())
.col(
ColumnDef::new(UserTokens::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(UserTokens::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(UserTokens::CreatedBy).uuid().not_null())
.col(ColumnDef::new(UserTokens::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(UserTokens::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(UserTokens::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_user_tokens_user_id")
.from(UserTokens::Table, UserTokens::UserId)
.to(Users::Table, Users::Id)
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_tokens_tenant_id")
.table(UserTokens::Table)
.col(UserTokens::TenantId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_tokens_user_id")
.table(UserTokens::Table)
.col(UserTokens::UserId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_tokens_token_hash")
.table(UserTokens::Table)
.col(UserTokens::TokenHash)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(UserTokens::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum UserTokens {
Table,
Id,
TenantId,
UserId,
TokenHash,
TokenType,
ExpiresAt,
RevokedAt,
DeviceInfo,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}
#[derive(DeriveIden)]
enum Users {
Table,
Id,
}

View File

@@ -0,0 +1,106 @@
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> {
manager
.create_table(
Table::create()
.table(Roles::Table)
.if_not_exists()
.col(
ColumnDef::new(Roles::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(Roles::TenantId).uuid().not_null())
.col(ColumnDef::new(Roles::Name).string().not_null())
.col(ColumnDef::new(Roles::Code).string().not_null())
.col(ColumnDef::new(Roles::Description).text().null())
.col(
ColumnDef::new(Roles::IsSystem)
.boolean()
.not_null()
.default(false),
)
.col(
ColumnDef::new(Roles::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(Roles::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(Roles::CreatedBy).uuid().not_null())
.col(ColumnDef::new(Roles::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(Roles::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Roles::Version)
.integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_roles_tenant_id")
.table(Roles::Table)
.col(Roles::TenantId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_roles_tenant_code")
.table(Roles::Table)
.col(Roles::TenantId)
.col(Roles::Code)
.unique()
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Roles::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Roles {
Table,
Id,
TenantId,
Name,
Code,
Description,
IsSystem,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}

View File

@@ -0,0 +1,103 @@
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> {
manager
.create_table(
Table::create()
.table(Permissions::Table)
.if_not_exists()
.col(
ColumnDef::new(Permissions::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(Permissions::TenantId).uuid().not_null())
.col(ColumnDef::new(Permissions::Code).string().not_null())
.col(ColumnDef::new(Permissions::Name).string().not_null())
.col(ColumnDef::new(Permissions::Resource).string().not_null())
.col(ColumnDef::new(Permissions::Action).string().not_null())
.col(ColumnDef::new(Permissions::Description).text().null())
.col(
ColumnDef::new(Permissions::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(Permissions::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(Permissions::CreatedBy).uuid().not_null())
.col(ColumnDef::new(Permissions::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(Permissions::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Permissions::Version)
.integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_permissions_tenant_id")
.table(Permissions::Table)
.col(Permissions::TenantId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_permissions_tenant_code")
.table(Permissions::Table)
.col(Permissions::TenantId)
.col(Permissions::Code)
.unique()
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Permissions::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Permissions {
Table,
Id,
TenantId,
Code,
Name,
Resource,
Action,
Description,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}

View File

@@ -0,0 +1,116 @@
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> {
manager
.create_table(
Table::create()
.table(RolePermissions::Table)
.if_not_exists()
.col(
ColumnDef::new(RolePermissions::RoleId)
.uuid()
.not_null()
.primary_key(),
)
.col(
ColumnDef::new(RolePermissions::PermissionId)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(RolePermissions::TenantId).uuid().not_null())
.col(
ColumnDef::new(RolePermissions::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(RolePermissions::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(RolePermissions::CreatedBy).uuid().not_null())
.col(ColumnDef::new(RolePermissions::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(RolePermissions::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(RolePermissions::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_role_permissions_role_id")
.from(RolePermissions::Table, RolePermissions::RoleId)
.to(Roles::Table, Roles::Id)
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_role_permissions_permission_id")
.from(RolePermissions::Table, RolePermissions::PermissionId)
.to(Permissions::Table, Permissions::Id)
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_role_permissions_tenant_id")
.table(RolePermissions::Table)
.col(RolePermissions::TenantId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(RolePermissions::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum RolePermissions {
Table,
RoleId,
PermissionId,
TenantId,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}
#[derive(DeriveIden)]
enum Roles {
Table,
Id,
}
#[derive(DeriveIden)]
enum Permissions {
Table,
Id,
}

View File

@@ -0,0 +1,116 @@
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> {
manager
.create_table(
Table::create()
.table(UserRoles::Table)
.if_not_exists()
.col(
ColumnDef::new(UserRoles::UserId)
.uuid()
.not_null()
.primary_key(),
)
.col(
ColumnDef::new(UserRoles::RoleId)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(UserRoles::TenantId).uuid().not_null())
.col(
ColumnDef::new(UserRoles::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(UserRoles::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(UserRoles::CreatedBy).uuid().not_null())
.col(ColumnDef::new(UserRoles::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(UserRoles::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(UserRoles::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_user_roles_user_id")
.from(UserRoles::Table, UserRoles::UserId)
.to(Users::Table, Users::Id)
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_user_roles_role_id")
.from(UserRoles::Table, UserRoles::RoleId)
.to(Roles::Table, Roles::Id)
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_roles_tenant_id")
.table(UserRoles::Table)
.col(UserRoles::TenantId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(UserRoles::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum UserRoles {
Table,
UserId,
RoleId,
TenantId,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}
#[derive(DeriveIden)]
enum Users {
Table,
Id,
}
#[derive(DeriveIden)]
enum Roles {
Table,
Id,
}

View File

@@ -0,0 +1,116 @@
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> {
manager
.create_table(
Table::create()
.table(Organizations::Table)
.if_not_exists()
.col(
ColumnDef::new(Organizations::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(Organizations::TenantId).uuid().not_null())
.col(ColumnDef::new(Organizations::Name).string().not_null())
.col(ColumnDef::new(Organizations::Code).string().null())
.col(ColumnDef::new(Organizations::ParentId).uuid().null())
.col(ColumnDef::new(Organizations::Path).string().null())
.col(
ColumnDef::new(Organizations::Level)
.integer()
.not_null()
.default(0),
)
.col(
ColumnDef::new(Organizations::SortOrder)
.integer()
.not_null()
.default(0),
)
.col(
ColumnDef::new(Organizations::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(Organizations::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(Organizations::CreatedBy).uuid().not_null())
.col(ColumnDef::new(Organizations::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(Organizations::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Organizations::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_organizations_parent_id")
.from(Organizations::Table, Organizations::ParentId)
.to(Organizations::Table, Organizations::Id)
.on_delete(ForeignKeyAction::Restrict)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_organizations_tenant_id")
.table(Organizations::Table)
.col(Organizations::TenantId)
.to_owned(),
)
.await?;
manager.get_connection().execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
"CREATE UNIQUE INDEX idx_organizations_tenant_code ON organizations (tenant_id, code) WHERE code IS NOT NULL".to_string(),
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Organizations::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Organizations {
Table,
Id,
TenantId,
Name,
Code,
ParentId,
Path,
Level,
SortOrder,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}

View File

@@ -0,0 +1,146 @@
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> {
manager
.create_table(
Table::create()
.table(Departments::Table)
.if_not_exists()
.col(
ColumnDef::new(Departments::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(Departments::TenantId).uuid().not_null())
.col(ColumnDef::new(Departments::OrgId).uuid().not_null())
.col(ColumnDef::new(Departments::Name).string().not_null())
.col(ColumnDef::new(Departments::Code).string().null())
.col(ColumnDef::new(Departments::ParentId).uuid().null())
.col(ColumnDef::new(Departments::ManagerId).uuid().null())
.col(ColumnDef::new(Departments::Path).string().null())
.col(
ColumnDef::new(Departments::SortOrder)
.integer()
.not_null()
.default(0),
)
.col(
ColumnDef::new(Departments::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(Departments::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(Departments::CreatedBy).uuid().not_null())
.col(ColumnDef::new(Departments::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(Departments::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Departments::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_departments_org_id")
.from(Departments::Table, Departments::OrgId)
.to(Organizations::Table, Organizations::Id)
.on_delete(ForeignKeyAction::Restrict)
.to_owned(),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_departments_parent_id")
.from(Departments::Table, Departments::ParentId)
.to(Departments::Table, Departments::Id)
.on_delete(ForeignKeyAction::Restrict)
.to_owned(),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_departments_manager_id")
.from(Departments::Table, Departments::ManagerId)
.to(Users::Table, Users::Id)
.on_delete(ForeignKeyAction::SetNull)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_departments_tenant_id")
.table(Departments::Table)
.col(Departments::TenantId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_departments_org_id")
.table(Departments::Table)
.col(Departments::OrgId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Departments::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Departments {
Table,
Id,
TenantId,
OrgId,
Name,
Code,
ParentId,
ManagerId,
Path,
SortOrder,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}
#[derive(DeriveIden)]
enum Organizations {
Table,
Id,
}
#[derive(DeriveIden)]
enum Users {
Table,
Id,
}

View File

@@ -0,0 +1,125 @@
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> {
manager
.create_table(
Table::create()
.table(Positions::Table)
.if_not_exists()
.col(
ColumnDef::new(Positions::Id)
.uuid()
.not_null()
.primary_key(),
)
.col(ColumnDef::new(Positions::TenantId).uuid().not_null())
.col(ColumnDef::new(Positions::DeptId).uuid().not_null())
.col(ColumnDef::new(Positions::Name).string().not_null())
.col(ColumnDef::new(Positions::Code).string().null())
.col(
ColumnDef::new(Positions::Level)
.integer()
.not_null()
.default(0),
)
.col(
ColumnDef::new(Positions::SortOrder)
.integer()
.not_null()
.default(0),
)
.col(
ColumnDef::new(Positions::CreatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(
ColumnDef::new(Positions::UpdatedAt)
.timestamp_with_time_zone()
.not_null()
.default(Expr::current_timestamp()),
)
.col(ColumnDef::new(Positions::CreatedBy).uuid().not_null())
.col(ColumnDef::new(Positions::UpdatedBy).uuid().not_null())
.col(
ColumnDef::new(Positions::DeletedAt)
.timestamp_with_time_zone()
.null(),
)
.col(
ColumnDef::new(Positions::Version)
.integer()
.not_null()
.default(1),
)
.foreign_key(
&mut ForeignKey::create()
.name("fk_positions_dept_id")
.from(Positions::Table, Positions::DeptId)
.to(Departments::Table, Departments::Id)
.on_delete(ForeignKeyAction::Restrict)
.to_owned(),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_positions_tenant_id")
.table(Positions::Table)
.col(Positions::TenantId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_positions_dept_id")
.table(Positions::Table)
.col(Positions::DeptId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Positions::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Positions {
Table,
Id,
TenantId,
DeptId,
Name,
Code,
Level,
SortOrder,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
DeletedAt,
Version,
}
#[derive(DeriveIden)]
enum Departments {
Table,
Id,
}