chore: apply cargo fmt across workspace and update docs
- Run cargo fmt on all Rust crates for consistent formatting - Update CLAUDE.md with WASM plugin commands and dev.ps1 instructions - Update wiki: add WASM plugin architecture, rewrite dev environment docs - Minor frontend cleanup (unused imports)
This commit is contained in:
@@ -29,6 +29,7 @@ mod m20260413_000026_create_audit_logs;
|
||||
mod m20260414_000027_fix_unique_indexes_soft_delete;
|
||||
mod m20260414_000028_add_standard_fields_to_tokens;
|
||||
mod m20260414_000029_add_standard_fields_to_process_variables;
|
||||
mod m20260414_000032_fix_settings_unique_index_null;
|
||||
mod m20260415_000030_add_version_to_message_tables;
|
||||
mod m20260416_000031_create_domain_events;
|
||||
|
||||
@@ -69,6 +70,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260414_000029_add_standard_fields_to_process_variables::Migration),
|
||||
Box::new(m20260415_000030_add_version_to_message_tables::Migration),
|
||||
Box::new(m20260416_000031_create_domain_events::Migration),
|
||||
Box::new(m20260414_000032_fix_settings_unique_index_null::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Tenant::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Tenant::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Tenant::Id).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Tenant::Name).string().not_null())
|
||||
.col(
|
||||
ColumnDef::new(Tenant::Code)
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Users::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Users::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.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())
|
||||
|
||||
@@ -25,7 +25,11 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default("password"),
|
||||
)
|
||||
.col(ColumnDef::new(UserCredentials::CredentialData).json().null())
|
||||
.col(
|
||||
ColumnDef::new(UserCredentials::CredentialData)
|
||||
.json()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(UserCredentials::Verified)
|
||||
.boolean()
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Roles::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Roles::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.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())
|
||||
|
||||
@@ -12,7 +12,11 @@ impl MigrationTrait for Migration {
|
||||
.table(RolePermissions::Table)
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(RolePermissions::RoleId).uuid().not_null())
|
||||
.col(ColumnDef::new(RolePermissions::PermissionId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(RolePermissions::PermissionId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(RolePermissions::TenantId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(RolePermissions::CreatedAt)
|
||||
|
||||
@@ -18,7 +18,11 @@ impl MigrationTrait for Migration {
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(DictionaryItems::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(DictionaryItems::DictionaryId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(DictionaryItems::DictionaryId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(DictionaryItems::Label).string().not_null())
|
||||
.col(ColumnDef::new(DictionaryItems::Value).string().not_null())
|
||||
.col(
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Menus::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Menus::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Menus::Id).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Menus::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(Menus::ParentId).uuid().null())
|
||||
.col(ColumnDef::new(Menus::Title).string().not_null())
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Settings::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Settings::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Settings::Id).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Settings::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(Settings::Scope).string().not_null())
|
||||
.col(ColumnDef::new(Settings::ScopeId).uuid().null())
|
||||
|
||||
@@ -17,7 +17,11 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessDefinitions::TenantId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessDefinitions::TenantId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessDefinitions::Name).string().not_null())
|
||||
.col(ColumnDef::new(ProcessDefinitions::Key).string().not_null())
|
||||
.col(
|
||||
@@ -27,7 +31,11 @@ impl MigrationTrait for Migration {
|
||||
.default(1),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessDefinitions::Category).string().null())
|
||||
.col(ColumnDef::new(ProcessDefinitions::Description).text().null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessDefinitions::Description)
|
||||
.text()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessDefinitions::Nodes)
|
||||
.json_binary()
|
||||
@@ -58,8 +66,16 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessDefinitions::CreatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(ProcessDefinitions::UpdatedBy).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessDefinitions::CreatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessDefinitions::UpdatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessDefinitions::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
|
||||
@@ -18,15 +18,27 @@ impl MigrationTrait for Migration {
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessInstances::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(ProcessInstances::DefinitionId).uuid().not_null())
|
||||
.col(ColumnDef::new(ProcessInstances::BusinessKey).string().null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::DefinitionId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::BusinessKey)
|
||||
.string()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::Status)
|
||||
.string()
|
||||
.not_null()
|
||||
.default("running"),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessInstances::StartedBy).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::StartedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::StartedAt)
|
||||
.timestamp_with_time_zone()
|
||||
@@ -50,8 +62,16 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessInstances::CreatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(ProcessInstances::UpdatedBy).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::CreatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::UpdatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessInstances::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Tokens::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Tokens::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Tokens::Id).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Tokens::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(Tokens::InstanceId).uuid().not_null())
|
||||
.col(ColumnDef::new(Tokens::NodeId).string().not_null())
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Tasks::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Tasks::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Tasks::Id).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Tasks::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(Tasks::InstanceId).uuid().not_null())
|
||||
.col(ColumnDef::new(Tasks::TokenId).uuid().not_null())
|
||||
|
||||
@@ -18,7 +18,11 @@ impl MigrationTrait for Migration {
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessVariables::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(ProcessVariables::InstanceId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessVariables::InstanceId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessVariables::Name).string().not_null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessVariables::VarType)
|
||||
@@ -27,8 +31,16 @@ impl MigrationTrait for Migration {
|
||||
.default("string"),
|
||||
)
|
||||
.col(ColumnDef::new(ProcessVariables::ValueString).text().null())
|
||||
.col(ColumnDef::new(ProcessVariables::ValueNumber).double().null())
|
||||
.col(ColumnDef::new(ProcessVariables::ValueBoolean).boolean().null())
|
||||
.col(
|
||||
ColumnDef::new(ProcessVariables::ValueNumber)
|
||||
.double()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessVariables::ValueBoolean)
|
||||
.boolean()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(ProcessVariables::ValueDate)
|
||||
.timestamp_with_time_zone()
|
||||
|
||||
@@ -18,16 +18,8 @@ impl MigrationTrait for Migration {
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(MessageTemplates::TenantId).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::Name)
|
||||
.string()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::Code)
|
||||
.string()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(MessageTemplates::Name).string().not_null())
|
||||
.col(ColumnDef::new(MessageTemplates::Code).string().not_null())
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::Channel)
|
||||
.string()
|
||||
@@ -50,11 +42,31 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default("zh-CN"),
|
||||
)
|
||||
.col(ColumnDef::new(MessageTemplates::CreatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(ColumnDef::new(MessageTemplates::UpdatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(ColumnDef::new(MessageTemplates::CreatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(MessageTemplates::UpdatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(MessageTemplates::DeletedAt).timestamp_with_time_zone().null())
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::CreatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::UpdatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageTemplates::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -11,12 +11,7 @@ impl MigrationTrait for Migration {
|
||||
Table::create()
|
||||
.table(Messages::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(Messages::Id)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Messages::Id).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Messages::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(Messages::TemplateId).uuid().null())
|
||||
.col(ColumnDef::new(Messages::SenderId).uuid().null())
|
||||
@@ -49,26 +44,50 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default(false),
|
||||
)
|
||||
.col(ColumnDef::new(Messages::ReadAt).timestamp_with_time_zone().null())
|
||||
.col(
|
||||
ColumnDef::new(Messages::ReadAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Messages::IsArchived)
|
||||
.boolean()
|
||||
.not_null()
|
||||
.default(false),
|
||||
)
|
||||
.col(ColumnDef::new(Messages::ArchivedAt).timestamp_with_time_zone().null())
|
||||
.col(ColumnDef::new(Messages::SentAt).timestamp_with_time_zone().null())
|
||||
.col(
|
||||
ColumnDef::new(Messages::ArchivedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Messages::SentAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Messages::Status)
|
||||
.string()
|
||||
.not_null()
|
||||
.default("sent"),
|
||||
)
|
||||
.col(ColumnDef::new(Messages::CreatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(ColumnDef::new(Messages::UpdatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(
|
||||
ColumnDef::new(Messages::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Messages::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(Messages::CreatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(Messages::UpdatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(Messages::DeletedAt).timestamp_with_time_zone().null())
|
||||
.col(
|
||||
ColumnDef::new(Messages::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -17,23 +17,63 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(MessageSubscriptions::TenantId).uuid().not_null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::UserId).uuid().not_null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::NotificationTypes).json().null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::ChannelPreferences).json().null())
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::TenantId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::UserId)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::NotificationTypes)
|
||||
.json()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::ChannelPreferences)
|
||||
.json()
|
||||
.null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::DndEnabled)
|
||||
.boolean()
|
||||
.not_null()
|
||||
.default(false),
|
||||
)
|
||||
.col(ColumnDef::new(MessageSubscriptions::DndStart).string().null())
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::DndStart)
|
||||
.string()
|
||||
.null(),
|
||||
)
|
||||
.col(ColumnDef::new(MessageSubscriptions::DndEnd).string().null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::CreatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::UpdatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::CreatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::UpdatedBy).uuid().not_null())
|
||||
.col(ColumnDef::new(MessageSubscriptions::DeletedAt).timestamp_with_time_zone().null())
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::UpdatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::CreatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::UpdatedBy)
|
||||
.uuid()
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(MessageSubscriptions::DeletedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.null(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -26,20 +26,33 @@ impl MigrationTrait for Migration {
|
||||
.col(ColumnDef::new(AuditLogs::NewValue).json().null())
|
||||
.col(ColumnDef::new(AuditLogs::IpAddress).string().null())
|
||||
.col(ColumnDef::new(AuditLogs::UserAgent).text().null())
|
||||
.col(ColumnDef::new(AuditLogs::CreatedAt).timestamp_with_time_zone().not_null())
|
||||
.col(
|
||||
ColumnDef::new(AuditLogs::CreatedAt)
|
||||
.timestamp_with_time_zone()
|
||||
.not_null(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"CREATE INDEX idx_audit_logs_tenant ON audit_logs (tenant_id)".to_string(),
|
||||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
manager
|
||||
.get_connection()
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"CREATE INDEX idx_audit_logs_tenant ON audit_logs (tenant_id)".to_string(),
|
||||
))
|
||||
.await
|
||||
.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"CREATE INDEX idx_audit_logs_resource ON audit_logs (resource_type, resource_id)".to_string(),
|
||||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
manager
|
||||
.get_connection()
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"CREATE INDEX idx_audit_logs_resource ON audit_logs (resource_type, resource_id)"
|
||||
.to_string(),
|
||||
))
|
||||
.await
|
||||
.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
use sea_orm::Statement;
|
||||
use sea_orm::DatabaseBackend;
|
||||
use sea_orm::Statement;
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
/// Recreate unique indexes on roles and permissions to include soft-delete awareness.
|
||||
#[derive(DeriveMigrationName)]
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
/// 修复 settings 表唯一索引:原索引使用 scope_id 列,当 scope_id 为 NULL 时
|
||||
/// PostgreSQL B-tree 不认为两行重复(NULL != NULL),导致可插入重复数据。
|
||||
/// 修复方案:使用 COALESCE(scope_id, '00000000-0000-0000-0000-000000000000')
|
||||
/// 将 NULL 转为固定 UUID,使索引能正确约束 NULL scope_id 的行。
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 删除旧索引
|
||||
manager
|
||||
.get_connection()
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"DROP INDEX IF EXISTS idx_settings_scope_key".to_string(),
|
||||
))
|
||||
.await
|
||||
.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
// 创建新索引,使用 COALESCE 处理 NULL scope_id
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"CREATE UNIQUE INDEX idx_settings_scope_key ON settings (tenant_id, scope, COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'), setting_key) WHERE deleted_at IS NULL".to_string(),
|
||||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
// 清理可能已存在的重复数据(保留每组最新的一条)
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
r#"
|
||||
DELETE FROM settings a USING settings b
|
||||
WHERE a.id < b.id
|
||||
AND a.tenant_id = b.tenant_id
|
||||
AND a.scope = b.scope
|
||||
AND a.setting_key = b.setting_key
|
||||
AND a.deleted_at IS NULL
|
||||
AND b.deleted_at IS NULL
|
||||
AND COALESCE(a.scope_id, '00000000-0000-0000-0000-000000000000') = COALESCE(b.scope_id, '00000000-0000-0000-0000-000000000000')
|
||||
"#.to_string(),
|
||||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 回滚:删除新索引,恢复旧索引
|
||||
manager
|
||||
.get_connection()
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"DROP INDEX IF EXISTS idx_settings_scope_key".to_string(),
|
||||
))
|
||||
.await
|
||||
.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"CREATE UNIQUE INDEX idx_settings_scope_key ON settings (tenant_id, scope, scope_id, setting_key) WHERE deleted_at IS NULL".to_string(),
|
||||
)).await.map_err(|e| DbErr::Custom(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,11 @@ impl MigrationTrait for Migration {
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(Alias::new("tenant_id")).uuid().not_null())
|
||||
.col(ColumnDef::new(Alias::new("event_type")).string_len(200).not_null())
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("event_type"))
|
||||
.string_len(200)
|
||||
.not_null(),
|
||||
)
|
||||
.col(ColumnDef::new(Alias::new("payload")).json().null())
|
||||
.col(ColumnDef::new(Alias::new("correlation_id")).uuid().null())
|
||||
.col(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::Router;
|
||||
use axum::extract::{Extension, FromRef, Query, State};
|
||||
use axum::response::Json;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -35,8 +35,7 @@ where
|
||||
let page_size = params.page_size.unwrap_or(20).min(100);
|
||||
let tenant_id = ctx.tenant_id;
|
||||
|
||||
let mut q = audit_log::Entity::find()
|
||||
.filter(audit_log::Column::TenantId.eq(tenant_id));
|
||||
let mut q = audit_log::Entity::find().filter(audit_log::Column::TenantId.eq(tenant_id));
|
||||
|
||||
if let Some(rt) = ¶ms.resource_type {
|
||||
q = q.filter(audit_log::Column::ResourceType.eq(rt.clone()));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::Router;
|
||||
use axum::extract::State;
|
||||
use axum::response::Json;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
@@ -6,14 +6,9 @@ use utoipa::openapi::OpenApiBuilder;
|
||||
///
|
||||
/// 返回 OpenAPI 3.0 规范 JSON 文档
|
||||
pub async fn openapi_spec() -> Json<Value> {
|
||||
let mut info = utoipa::openapi::Info::new(
|
||||
"ERP Platform API",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
);
|
||||
let mut info = utoipa::openapi::Info::new("ERP Platform API", env!("CARGO_PKG_VERSION"));
|
||||
info.description = Some("ERP 平台底座 REST API 文档".to_string());
|
||||
|
||||
let spec = OpenApiBuilder::new()
|
||||
.info(info)
|
||||
.build();
|
||||
let spec = OpenApiBuilder::new().info(info).build();
|
||||
Json(serde_json::to_value(spec).unwrap_or_default())
|
||||
}
|
||||
|
||||
@@ -7,13 +7,11 @@ mod state;
|
||||
|
||||
/// OpenAPI 规范定义(预留,未来可通过 utoipa derive 合并各模块 schema)。
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
info(
|
||||
title = "ERP Platform API",
|
||||
version = "0.1.0",
|
||||
description = "ERP 平台底座 REST API 文档"
|
||||
)
|
||||
)]
|
||||
#[openapi(info(
|
||||
title = "ERP Platform API",
|
||||
version = "0.1.0",
|
||||
description = "ERP 平台底座 REST API 文档"
|
||||
))]
|
||||
#[allow(dead_code)]
|
||||
struct ApiDoc;
|
||||
|
||||
@@ -38,13 +36,15 @@ async fn main() -> anyhow::Result<()> {
|
||||
// Initialize tracing
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::new(&config.log.level)),
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&config.log.level)),
|
||||
)
|
||||
.json()
|
||||
.init();
|
||||
|
||||
tracing::info!(version = env!("CARGO_PKG_VERSION"), "ERP Server starting...");
|
||||
tracing::info!(
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
"ERP Server starting..."
|
||||
);
|
||||
|
||||
// Connect to database
|
||||
let db = db::connect(&config.database).await?;
|
||||
@@ -116,19 +116,35 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
// Initialize auth module
|
||||
let auth_module = erp_auth::AuthModule::new();
|
||||
tracing::info!(module = auth_module.name(), version = auth_module.version(), "Auth module initialized");
|
||||
tracing::info!(
|
||||
module = auth_module.name(),
|
||||
version = auth_module.version(),
|
||||
"Auth module initialized"
|
||||
);
|
||||
|
||||
// Initialize config module
|
||||
let config_module = erp_config::ConfigModule::new();
|
||||
tracing::info!(module = config_module.name(), version = config_module.version(), "Config module initialized");
|
||||
tracing::info!(
|
||||
module = config_module.name(),
|
||||
version = config_module.version(),
|
||||
"Config module initialized"
|
||||
);
|
||||
|
||||
// Initialize workflow module
|
||||
let workflow_module = erp_workflow::WorkflowModule::new();
|
||||
tracing::info!(module = workflow_module.name(), version = workflow_module.version(), "Workflow module initialized");
|
||||
tracing::info!(
|
||||
module = workflow_module.name(),
|
||||
version = workflow_module.version(),
|
||||
"Workflow module initialized"
|
||||
);
|
||||
|
||||
// Initialize message module
|
||||
let message_module = erp_message::MessageModule::new();
|
||||
tracing::info!(module = message_module.name(), version = message_module.version(), "Message module initialized");
|
||||
tracing::info!(
|
||||
module = message_module.name(),
|
||||
version = message_module.version(),
|
||||
"Message module initialized"
|
||||
);
|
||||
|
||||
// Initialize module registry and register modules
|
||||
let registry = ModuleRegistry::new()
|
||||
@@ -136,7 +152,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
.register(config_module)
|
||||
.register(workflow_module)
|
||||
.register(message_module);
|
||||
tracing::info!(module_count = registry.modules().len(), "Modules registered");
|
||||
tracing::info!(
|
||||
module_count = registry.modules().len(),
|
||||
"Modules registered"
|
||||
);
|
||||
|
||||
// Register event handlers
|
||||
registry.register_handlers(&event_bus);
|
||||
@@ -182,7 +201,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
let public_routes = Router::new()
|
||||
.merge(handlers::health::health_check_router())
|
||||
.merge(erp_auth::AuthModule::public_routes())
|
||||
.route("/docs/openapi.json", axum::routing::get(handlers::openapi::openapi_spec))
|
||||
.route(
|
||||
"/docs/openapi.json",
|
||||
axum::routing::get(handlers::openapi::openapi_spec),
|
||||
)
|
||||
.layer(axum::middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
middleware::rate_limit::rate_limit_by_ip,
|
||||
|
||||
@@ -15,7 +15,8 @@ struct RateLimitResponse {
|
||||
message: String,
|
||||
}
|
||||
|
||||
/// 限流参数。
|
||||
/// 限流参数(预留配置化扩展)。
|
||||
#[allow(dead_code)]
|
||||
pub struct RateLimitConfig {
|
||||
/// 窗口内最大请求数。
|
||||
pub max_requests: u64,
|
||||
@@ -74,11 +75,7 @@ async fn apply_rate_limit(
|
||||
}
|
||||
};
|
||||
|
||||
let count: i64 = match redis::cmd("INCR")
|
||||
.arg(&key)
|
||||
.query_async(&mut conn)
|
||||
.await
|
||||
{
|
||||
let count: i64 = match redis::cmd("INCR").arg(&key).query_async(&mut conn).await {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Redis INCR 失败,跳过限流");
|
||||
|
||||
Reference in New Issue
Block a user