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:
@@ -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),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
109
crates/erp-server/migration/src/m20260411_000002_create_users.rs
Normal file
109
crates/erp-server/migration/src/m20260411_000002_create_users.rs
Normal 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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
106
crates/erp-server/migration/src/m20260411_000005_create_roles.rs
Normal file
106
crates/erp-server/migration/src/m20260411_000005_create_roles.rs
Normal 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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
Reference in New Issue
Block a user