新增以下文档文件: - PROJECT_OVERVIEW.md 项目总览文档 - BACKEND_ARCHITECTURE.md 后端架构文档 - FRONTEND_ARCHITECTURE.md 前端架构文档 - FLOW_ENGINE.md 流程引擎文档 - SERVICES.md 服务层文档 - ERROR_HANDLING.md 错误处理模块文档 文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
1650 lines
42 KiB
Markdown
1650 lines
42 KiB
Markdown
# 数据模型文档
|
||
|
||
## 概述
|
||
|
||
数据模型层是 UdminAI 的数据持久化核心,基于 SeaORM 框架实现,提供类型安全的数据库操作接口。包含用户管理、权限控制、流程管理、定时任务、系统日志等核心业务实体。
|
||
|
||
## 架构设计
|
||
|
||
### 模型模块结构
|
||
|
||
```
|
||
models/
|
||
├── mod.rs # 模型模块导出
|
||
├── user.rs # 用户模型
|
||
├── role.rs # 角色模型
|
||
├── permission.rs # 权限模型
|
||
├── user_role.rs # 用户角色关联模型
|
||
├── role_permission.rs # 角色权限关联模型
|
||
├── flow.rs # 流程模型
|
||
├── flow_version.rs # 流程版本模型
|
||
├── flow_execution.rs # 流程执行记录模型
|
||
├── schedule_job.rs # 定时任务模型
|
||
├── job_execution.rs # 任务执行记录模型
|
||
├── system_config.rs # 系统配置模型
|
||
├── operation_log.rs # 操作日志模型
|
||
├── system_log.rs # 系统日志模型
|
||
├── notification.rs # 通知模型
|
||
└── notification_template.rs # 通知模板模型
|
||
```
|
||
|
||
### 设计原则
|
||
|
||
- **实体完整性**: 每个实体都有完整的字段定义
|
||
- **关系映射**: 正确定义实体间的关联关系
|
||
- **类型安全**: 使用强类型定义所有字段
|
||
- **索引优化**: 为查询字段添加合适的索引
|
||
- **软删除**: 重要数据支持软删除机制
|
||
- **审计字段**: 包含创建时间、更新时间等审计字段
|
||
|
||
## 用户模型 (user.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
use sea_orm::entity::prelude::*;
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "users")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
#[sea_orm(unique)]
|
||
pub username: String,
|
||
|
||
pub password_hash: String,
|
||
|
||
#[sea_orm(unique)]
|
||
pub email: Option<String>,
|
||
|
||
pub display_name: Option<String>,
|
||
|
||
pub avatar: Option<String>,
|
||
|
||
pub status: UserStatus,
|
||
|
||
pub last_login_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub login_count: i32,
|
||
|
||
pub is_deleted: bool,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(has_many = "super::user_role::Entity")]
|
||
UserRoles,
|
||
|
||
#[sea_orm(has_many = "super::flow::Entity")]
|
||
Flows,
|
||
|
||
#[sea_orm(has_many = "super::schedule_job::Entity")]
|
||
ScheduleJobs,
|
||
|
||
#[sea_orm(has_many = "super::operation_log::Entity")]
|
||
OperationLogs,
|
||
}
|
||
|
||
impl Related<super::role::Entity> for Entity {
|
||
fn to() -> RelationDef {
|
||
super::user_role::Relation::Role.def()
|
||
}
|
||
|
||
fn via() -> Option<RelationDef> {
|
||
Some(super::user_role::Relation::User.def().rev())
|
||
}
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 用户状态枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "user_status")]
|
||
pub enum UserStatus {
|
||
#[sea_orm(string_value = "active")]
|
||
Active,
|
||
|
||
#[sea_orm(string_value = "inactive")]
|
||
Inactive,
|
||
|
||
#[sea_orm(string_value = "suspended")]
|
||
Suspended,
|
||
|
||
#[sea_orm(string_value = "deleted")]
|
||
Deleted,
|
||
}
|
||
```
|
||
|
||
### 数据库迁移
|
||
|
||
```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> {
|
||
manager
|
||
.create_table(
|
||
Table::create()
|
||
.table(User::Table)
|
||
.if_not_exists()
|
||
.col(
|
||
ColumnDef::new(User::Id)
|
||
.string_len(32)
|
||
.not_null()
|
||
.primary_key(),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::Username)
|
||
.string_len(50)
|
||
.not_null()
|
||
.unique_key(),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::PasswordHash)
|
||
.string_len(255)
|
||
.not_null(),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::Email)
|
||
.string_len(100)
|
||
.unique_key(),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::DisplayName)
|
||
.string_len(100),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::Avatar)
|
||
.string_len(255),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::Status)
|
||
.enumeration(
|
||
Alias::new("user_status"),
|
||
["active", "inactive", "suspended", "deleted"],
|
||
)
|
||
.not_null()
|
||
.default("active"),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::LastLoginAt)
|
||
.timestamp_with_time_zone(),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::LoginCount)
|
||
.integer()
|
||
.not_null()
|
||
.default(0),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::IsDeleted)
|
||
.boolean()
|
||
.not_null()
|
||
.default(false),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::CreatedAt)
|
||
.timestamp_with_time_zone()
|
||
.not_null()
|
||
.default(Expr::current_timestamp()),
|
||
)
|
||
.col(
|
||
ColumnDef::new(User::UpdatedAt)
|
||
.timestamp_with_time_zone()
|
||
.not_null()
|
||
.default(Expr::current_timestamp()),
|
||
)
|
||
.to_owned(),
|
||
)
|
||
.await?;
|
||
|
||
// 创建索引
|
||
manager
|
||
.create_index(
|
||
Index::create()
|
||
.if_not_exists()
|
||
.name("idx_users_email")
|
||
.table(User::Table)
|
||
.col(User::Email)
|
||
.to_owned(),
|
||
)
|
||
.await?;
|
||
|
||
manager
|
||
.create_index(
|
||
Index::create()
|
||
.if_not_exists()
|
||
.name("idx_users_status")
|
||
.table(User::Table)
|
||
.col(User::Status)
|
||
.to_owned(),
|
||
)
|
||
.await?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||
manager
|
||
.drop_table(Table::drop().table(User::Table).to_owned())
|
||
.await
|
||
}
|
||
}
|
||
```
|
||
|
||
## 角色模型 (role.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "roles")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
#[sea_orm(unique)]
|
||
pub name: String,
|
||
|
||
pub description: Option<String>,
|
||
|
||
pub is_system: bool,
|
||
|
||
pub sort_order: i32,
|
||
|
||
pub is_deleted: bool,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(has_many = "super::user_role::Entity")]
|
||
UserRoles,
|
||
|
||
#[sea_orm(has_many = "super::role_permission::Entity")]
|
||
RolePermissions,
|
||
}
|
||
|
||
impl Related<super::user::Entity> for Entity {
|
||
fn to() -> RelationDef {
|
||
super::user_role::Relation::User.def()
|
||
}
|
||
|
||
fn via() -> Option<RelationDef> {
|
||
Some(super::user_role::Relation::Role.def().rev())
|
||
}
|
||
}
|
||
|
||
impl Related<super::permission::Entity> for Entity {
|
||
fn to() -> RelationDef {
|
||
super::role_permission::Relation::Permission.def()
|
||
}
|
||
|
||
fn via() -> Option<RelationDef> {
|
||
Some(super::role_permission::Relation::Role.def().rev())
|
||
}
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
## 权限模型 (permission.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "permissions")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub name: String,
|
||
|
||
pub resource: String,
|
||
|
||
pub action: String,
|
||
|
||
pub description: Option<String>,
|
||
|
||
pub parent_id: Option<String>,
|
||
|
||
pub sort_order: i32,
|
||
|
||
pub is_system: bool,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(has_many = "super::role_permission::Entity")]
|
||
RolePermissions,
|
||
|
||
#[sea_orm(
|
||
belongs_to = "Entity",
|
||
from = "Column::ParentId",
|
||
to = "Column::Id"
|
||
)]
|
||
Parent,
|
||
|
||
#[sea_orm(has_many = "Entity")]
|
||
Children,
|
||
}
|
||
|
||
impl Related<super::role::Entity> for Entity {
|
||
fn to() -> RelationDef {
|
||
super::role_permission::Relation::Role.def()
|
||
}
|
||
|
||
fn via() -> Option<RelationDef> {
|
||
Some(super::role_permission::Relation::Permission.def().rev())
|
||
}
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
## 流程模型 (flow.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "flows")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub name: String,
|
||
|
||
pub description: Option<String>,
|
||
|
||
pub category: String,
|
||
|
||
pub status: FlowStatus,
|
||
|
||
pub version: String,
|
||
|
||
pub design: Json,
|
||
|
||
pub input_schema: Option<Json>,
|
||
|
||
pub output_schema: Option<Json>,
|
||
|
||
pub tags: Option<Json>,
|
||
|
||
pub created_by: String,
|
||
|
||
pub updated_by: Option<String>,
|
||
|
||
pub published_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub is_deleted: bool,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::user::Entity",
|
||
from = "Column::CreatedBy",
|
||
to = "super::user::Column::Id"
|
||
)]
|
||
Creator,
|
||
|
||
#[sea_orm(
|
||
belongs_to = "super::user::Entity",
|
||
from = "Column::UpdatedBy",
|
||
to = "super::user::Column::Id"
|
||
)]
|
||
Updater,
|
||
|
||
#[sea_orm(has_many = "super::flow_version::Entity")]
|
||
FlowVersions,
|
||
|
||
#[sea_orm(has_many = "super::flow_execution::Entity")]
|
||
FlowExecutions,
|
||
|
||
#[sea_orm(has_many = "super::schedule_job::Entity")]
|
||
ScheduleJobs,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 流程状态枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "flow_status")]
|
||
pub enum FlowStatus {
|
||
#[sea_orm(string_value = "draft")]
|
||
Draft,
|
||
|
||
#[sea_orm(string_value = "published")]
|
||
Published,
|
||
|
||
#[sea_orm(string_value = "archived")]
|
||
Archived,
|
||
|
||
#[sea_orm(string_value = "deleted")]
|
||
Deleted,
|
||
}
|
||
```
|
||
|
||
## 流程执行记录模型 (flow_execution.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "flow_executions")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub flow_id: String,
|
||
|
||
pub flow_version: String,
|
||
|
||
pub status: ExecutionStatus,
|
||
|
||
pub input: Option<Json>,
|
||
|
||
pub output: Option<Json>,
|
||
|
||
pub error: Option<String>,
|
||
|
||
pub context: Option<Json>,
|
||
|
||
pub execution_log: Option<Json>,
|
||
|
||
pub started_by: Option<String>,
|
||
|
||
pub trigger_type: TriggerType,
|
||
|
||
pub trigger_source: Option<String>,
|
||
|
||
pub start_time: DateTimeWithTimeZone,
|
||
|
||
pub end_time: Option<DateTimeWithTimeZone>,
|
||
|
||
pub duration_ms: Option<i64>,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::flow::Entity",
|
||
from = "Column::FlowId",
|
||
to = "super::flow::Column::Id"
|
||
)]
|
||
Flow,
|
||
|
||
#[sea_orm(
|
||
belongs_to = "super::user::Entity",
|
||
from = "Column::StartedBy",
|
||
to = "super::user::Column::Id"
|
||
)]
|
||
User,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 执行状态枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "execution_status")]
|
||
pub enum ExecutionStatus {
|
||
#[sea_orm(string_value = "pending")]
|
||
Pending,
|
||
|
||
#[sea_orm(string_value = "running")]
|
||
Running,
|
||
|
||
#[sea_orm(string_value = "completed")]
|
||
Completed,
|
||
|
||
#[sea_orm(string_value = "failed")]
|
||
Failed,
|
||
|
||
#[sea_orm(string_value = "cancelled")]
|
||
Cancelled,
|
||
|
||
#[sea_orm(string_value = "timeout")]
|
||
Timeout,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "trigger_type")]
|
||
pub enum TriggerType {
|
||
#[sea_orm(string_value = "manual")]
|
||
Manual,
|
||
|
||
#[sea_orm(string_value = "schedule")]
|
||
Schedule,
|
||
|
||
#[sea_orm(string_value = "webhook")]
|
||
Webhook,
|
||
|
||
#[sea_orm(string_value = "api")]
|
||
Api,
|
||
|
||
#[sea_orm(string_value = "event")]
|
||
Event,
|
||
}
|
||
```
|
||
|
||
## 定时任务模型 (schedule_job.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "schedule_jobs")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub name: String,
|
||
|
||
pub description: Option<String>,
|
||
|
||
pub cron_expression: String,
|
||
|
||
pub timezone: String,
|
||
|
||
pub job_type: JobType,
|
||
|
||
pub target_id: String,
|
||
|
||
pub target_config: Option<Json>,
|
||
|
||
pub enabled: bool,
|
||
|
||
pub max_retries: i32,
|
||
|
||
pub retry_interval: i32,
|
||
|
||
pub timeout_seconds: Option<i32>,
|
||
|
||
pub last_run_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub next_run_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub run_count: i64,
|
||
|
||
pub success_count: i64,
|
||
|
||
pub failure_count: i64,
|
||
|
||
pub created_by: String,
|
||
|
||
pub updated_by: Option<String>,
|
||
|
||
pub is_deleted: bool,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::user::Entity",
|
||
from = "Column::CreatedBy",
|
||
to = "super::user::Column::Id"
|
||
)]
|
||
Creator,
|
||
|
||
#[sea_orm(
|
||
belongs_to = "super::flow::Entity",
|
||
from = "Column::TargetId",
|
||
to = "super::flow::Column::Id"
|
||
)]
|
||
Flow,
|
||
|
||
#[sea_orm(has_many = "super::job_execution::Entity")]
|
||
JobExecutions,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 任务类型枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "job_type")]
|
||
pub enum JobType {
|
||
#[sea_orm(string_value = "flow")]
|
||
Flow,
|
||
|
||
#[sea_orm(string_value = "script")]
|
||
Script,
|
||
|
||
#[sea_orm(string_value = "http")]
|
||
Http,
|
||
|
||
#[sea_orm(string_value = "command")]
|
||
Command,
|
||
}
|
||
```
|
||
|
||
## 任务执行记录模型 (job_execution.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "job_executions")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub job_id: String,
|
||
|
||
pub status: JobExecutionStatus,
|
||
|
||
pub trigger_type: JobTriggerType,
|
||
|
||
pub start_time: DateTimeWithTimeZone,
|
||
|
||
pub end_time: Option<DateTimeWithTimeZone>,
|
||
|
||
pub duration_ms: Option<i64>,
|
||
|
||
pub output: Option<Json>,
|
||
|
||
pub error: Option<String>,
|
||
|
||
pub retry_count: i32,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::schedule_job::Entity",
|
||
from = "Column::JobId",
|
||
to = "super::schedule_job::Column::Id"
|
||
)]
|
||
ScheduleJob,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 执行状态枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "job_execution_status")]
|
||
pub enum JobExecutionStatus {
|
||
#[sea_orm(string_value = "running")]
|
||
Running,
|
||
|
||
#[sea_orm(string_value = "completed")]
|
||
Completed,
|
||
|
||
#[sea_orm(string_value = "failed")]
|
||
Failed,
|
||
|
||
#[sea_orm(string_value = "timeout")]
|
||
Timeout,
|
||
|
||
#[sea_orm(string_value = "cancelled")]
|
||
Cancelled,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "job_trigger_type")]
|
||
pub enum JobTriggerType {
|
||
#[sea_orm(string_value = "schedule")]
|
||
Schedule,
|
||
|
||
#[sea_orm(string_value = "manual")]
|
||
Manual,
|
||
|
||
#[sea_orm(string_value = "retry")]
|
||
Retry,
|
||
}
|
||
```
|
||
|
||
## 操作日志模型 (operation_log.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "operation_logs")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub user_id: String,
|
||
|
||
pub operation: String,
|
||
|
||
pub resource: String,
|
||
|
||
pub resource_id: Option<String>,
|
||
|
||
pub details: Option<Json>,
|
||
|
||
pub ip_address: Option<String>,
|
||
|
||
pub user_agent: Option<String>,
|
||
|
||
pub status: OperationStatus,
|
||
|
||
pub error: Option<String>,
|
||
|
||
pub duration_ms: Option<i64>,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::user::Entity",
|
||
from = "Column::UserId",
|
||
to = "super::user::Column::Id"
|
||
)]
|
||
User,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 操作状态枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "operation_status")]
|
||
pub enum OperationStatus {
|
||
#[sea_orm(string_value = "success")]
|
||
Success,
|
||
|
||
#[sea_orm(string_value = "failed")]
|
||
Failed,
|
||
|
||
#[sea_orm(string_value = "partial")]
|
||
Partial,
|
||
}
|
||
```
|
||
|
||
## 系统日志模型 (system_log.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "system_logs")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub level: LogLevel,
|
||
|
||
pub source: String,
|
||
|
||
pub event_type: String,
|
||
|
||
pub message: String,
|
||
|
||
pub details: Option<Json>,
|
||
|
||
pub trace_id: Option<String>,
|
||
|
||
pub span_id: Option<String>,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 日志级别枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "log_level")]
|
||
pub enum LogLevel {
|
||
#[sea_orm(string_value = "trace")]
|
||
Trace,
|
||
|
||
#[sea_orm(string_value = "debug")]
|
||
Debug,
|
||
|
||
#[sea_orm(string_value = "info")]
|
||
Info,
|
||
|
||
#[sea_orm(string_value = "warn")]
|
||
Warn,
|
||
|
||
#[sea_orm(string_value = "error")]
|
||
Error,
|
||
}
|
||
```
|
||
|
||
## 通知模型 (notification.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "notifications")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub title: String,
|
||
|
||
pub content: String,
|
||
|
||
pub notification_type: NotificationType,
|
||
|
||
pub channel: NotificationChannel,
|
||
|
||
pub recipient_id: String,
|
||
|
||
pub recipient_type: RecipientType,
|
||
|
||
pub status: NotificationStatus,
|
||
|
||
pub priority: NotificationPriority,
|
||
|
||
pub template_id: Option<String>,
|
||
|
||
pub template_data: Option<Json>,
|
||
|
||
pub scheduled_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub sent_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub read_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub error: Option<String>,
|
||
|
||
pub retry_count: i32,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::notification_template::Entity",
|
||
from = "Column::TemplateId",
|
||
to = "super::notification_template::Column::Id"
|
||
)]
|
||
Template,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 通知相关枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "notification_type")]
|
||
pub enum NotificationType {
|
||
#[sea_orm(string_value = "system")]
|
||
System,
|
||
|
||
#[sea_orm(string_value = "flow_execution")]
|
||
FlowExecution,
|
||
|
||
#[sea_orm(string_value = "job_execution")]
|
||
JobExecution,
|
||
|
||
#[sea_orm(string_value = "user_action")]
|
||
UserAction,
|
||
|
||
#[sea_orm(string_value = "alert")]
|
||
Alert,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "notification_channel")]
|
||
pub enum NotificationChannel {
|
||
#[sea_orm(string_value = "system")]
|
||
System,
|
||
|
||
#[sea_orm(string_value = "email")]
|
||
Email,
|
||
|
||
#[sea_orm(string_value = "sms")]
|
||
Sms,
|
||
|
||
#[sea_orm(string_value = "webhook")]
|
||
Webhook,
|
||
|
||
#[sea_orm(string_value = "push")]
|
||
Push,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "notification_status")]
|
||
pub enum NotificationStatus {
|
||
#[sea_orm(string_value = "pending")]
|
||
Pending,
|
||
|
||
#[sea_orm(string_value = "sent")]
|
||
Sent,
|
||
|
||
#[sea_orm(string_value = "failed")]
|
||
Failed,
|
||
|
||
#[sea_orm(string_value = "cancelled")]
|
||
Cancelled,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "notification_priority")]
|
||
pub enum NotificationPriority {
|
||
#[sea_orm(string_value = "low")]
|
||
Low,
|
||
|
||
#[sea_orm(string_value = "normal")]
|
||
Normal,
|
||
|
||
#[sea_orm(string_value = "high")]
|
||
High,
|
||
|
||
#[sea_orm(string_value = "urgent")]
|
||
Urgent,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "recipient_type")]
|
||
pub enum RecipientType {
|
||
#[sea_orm(string_value = "user")]
|
||
User,
|
||
|
||
#[sea_orm(string_value = "role")]
|
||
Role,
|
||
|
||
#[sea_orm(string_value = "group")]
|
||
Group,
|
||
|
||
#[sea_orm(string_value = "external")]
|
||
External,
|
||
}
|
||
```
|
||
|
||
## 系统配置模型 (system_config.rs)
|
||
|
||
### 实体定义
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "system_configs")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
#[sea_orm(unique)]
|
||
pub key: String,
|
||
|
||
pub value: Json,
|
||
|
||
pub description: Option<String>,
|
||
|
||
pub config_type: ConfigType,
|
||
|
||
pub is_encrypted: bool,
|
||
|
||
pub is_system: bool,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
|
||
pub updated_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 配置类型枚举
|
||
|
||
```rust
|
||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
|
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "config_type")]
|
||
pub enum ConfigType {
|
||
#[sea_orm(string_value = "string")]
|
||
String,
|
||
|
||
#[sea_orm(string_value = "number")]
|
||
Number,
|
||
|
||
#[sea_orm(string_value = "boolean")]
|
||
Boolean,
|
||
|
||
#[sea_orm(string_value = "json")]
|
||
Json,
|
||
|
||
#[sea_orm(string_value = "array")]
|
||
Array,
|
||
}
|
||
```
|
||
|
||
## 关联表模型
|
||
|
||
### 用户角色关联 (user_role.rs)
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "user_roles")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub user_id: String,
|
||
|
||
pub role_id: String,
|
||
|
||
pub assigned_by: String,
|
||
|
||
pub assigned_at: DateTimeWithTimeZone,
|
||
|
||
pub expires_at: Option<DateTimeWithTimeZone>,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::user::Entity",
|
||
from = "Column::UserId",
|
||
to = "super::user::Column::Id"
|
||
)]
|
||
User,
|
||
|
||
#[sea_orm(
|
||
belongs_to = "super::role::Entity",
|
||
from = "Column::RoleId",
|
||
to = "super::role::Column::Id"
|
||
)]
|
||
Role,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
### 角色权限关联 (role_permission.rs)
|
||
|
||
```rust
|
||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||
#[sea_orm(table_name = "role_permissions")]
|
||
pub struct Model {
|
||
#[sea_orm(primary_key, auto_increment = false)]
|
||
pub id: String,
|
||
|
||
pub role_id: String,
|
||
|
||
pub permission_id: String,
|
||
|
||
pub granted_by: String,
|
||
|
||
pub granted_at: DateTimeWithTimeZone,
|
||
|
||
pub created_at: DateTimeWithTimeZone,
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||
pub enum Relation {
|
||
#[sea_orm(
|
||
belongs_to = "super::role::Entity",
|
||
from = "Column::RoleId",
|
||
to = "super::role::Column::Id"
|
||
)]
|
||
Role,
|
||
|
||
#[sea_orm(
|
||
belongs_to = "super::permission::Entity",
|
||
from = "Column::PermissionId",
|
||
to = "super::permission::Column::Id"
|
||
)]
|
||
Permission,
|
||
}
|
||
|
||
impl ActiveModelBehavior for ActiveModel {}
|
||
```
|
||
|
||
## 数据库连接和配置
|
||
|
||
### 数据库连接
|
||
|
||
```rust
|
||
use sea_orm::*;
|
||
|
||
pub async fn establish_connection(database_url: &str) -> Result<DatabaseConnection, DbErr> {
|
||
let mut opt = ConnectOptions::new(database_url.to_owned());
|
||
opt.max_connections(100)
|
||
.min_connections(5)
|
||
.connect_timeout(Duration::from_secs(8))
|
||
.acquire_timeout(Duration::from_secs(8))
|
||
.idle_timeout(Duration::from_secs(8))
|
||
.max_lifetime(Duration::from_secs(8))
|
||
.sqlx_logging(true)
|
||
.sqlx_logging_level(log::LevelFilter::Info);
|
||
|
||
Database::connect(opt).await
|
||
}
|
||
```
|
||
|
||
### 迁移管理
|
||
|
||
```rust
|
||
use sea_orm_migration::prelude::*;
|
||
|
||
pub struct Migrator;
|
||
|
||
#[async_trait::async_trait]
|
||
impl MigratorTrait for Migrator {
|
||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||
vec![
|
||
Box::new(m20220101_000001_create_users_table::Migration),
|
||
Box::new(m20220101_000002_create_roles_table::Migration),
|
||
Box::new(m20220101_000003_create_permissions_table::Migration),
|
||
Box::new(m20220101_000004_create_user_roles_table::Migration),
|
||
Box::new(m20220101_000005_create_role_permissions_table::Migration),
|
||
Box::new(m20220101_000006_create_flows_table::Migration),
|
||
Box::new(m20220101_000007_create_flow_versions_table::Migration),
|
||
Box::new(m20220101_000008_create_flow_executions_table::Migration),
|
||
Box::new(m20220101_000009_create_schedule_jobs_table::Migration),
|
||
Box::new(m20220101_000010_create_job_executions_table::Migration),
|
||
Box::new(m20220101_000011_create_operation_logs_table::Migration),
|
||
Box::new(m20220101_000012_create_system_logs_table::Migration),
|
||
Box::new(m20220101_000013_create_notifications_table::Migration),
|
||
Box::new(m20220101_000014_create_notification_templates_table::Migration),
|
||
Box::new(m20220101_000015_create_system_configs_table::Migration),
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
## 查询构建器
|
||
|
||
### 用户查询示例
|
||
|
||
```rust
|
||
use sea_orm::*;
|
||
|
||
/// 查询活跃用户列表
|
||
pub async fn find_active_users(
|
||
db: &DatabaseConnection,
|
||
page: u64,
|
||
page_size: u64,
|
||
) -> Result<(Vec<user::Model>, u64), DbErr> {
|
||
let paginator = user::Entity::find()
|
||
.filter(user::Column::Status.eq(UserStatus::Active))
|
||
.filter(user::Column::IsDeleted.eq(false))
|
||
.order_by_desc(user::Column::CreatedAt)
|
||
.paginate(db, page_size);
|
||
|
||
let total = paginator.num_items().await?;
|
||
let users = paginator.fetch_page(page - 1).await?;
|
||
|
||
Ok((users, total))
|
||
}
|
||
|
||
/// 根据用户名查询用户
|
||
pub async fn find_user_by_username(
|
||
db: &DatabaseConnection,
|
||
username: &str,
|
||
) -> Result<Option<user::Model>, DbErr> {
|
||
user::Entity::find()
|
||
.filter(user::Column::Username.eq(username))
|
||
.filter(user::Column::IsDeleted.eq(false))
|
||
.one(db)
|
||
.await
|
||
}
|
||
|
||
/// 查询用户及其角色
|
||
pub async fn find_user_with_roles(
|
||
db: &DatabaseConnection,
|
||
user_id: &str,
|
||
) -> Result<Option<(user::Model, Vec<role::Model>)>, DbErr> {
|
||
user::Entity::find_by_id(user_id)
|
||
.find_with_related(role::Entity)
|
||
.all(db)
|
||
.await
|
||
.map(|results| {
|
||
results.into_iter().next().map(|(user, roles)| (user, roles))
|
||
})
|
||
}
|
||
```
|
||
|
||
### 流程查询示例
|
||
|
||
```rust
|
||
/// 查询已发布的流程
|
||
pub async fn find_published_flows(
|
||
db: &DatabaseConnection,
|
||
category: Option<&str>,
|
||
) -> Result<Vec<flow::Model>, DbErr> {
|
||
let mut query = flow::Entity::find()
|
||
.filter(flow::Column::Status.eq(FlowStatus::Published))
|
||
.filter(flow::Column::IsDeleted.eq(false));
|
||
|
||
if let Some(cat) = category {
|
||
query = query.filter(flow::Column::Category.eq(cat));
|
||
}
|
||
|
||
query.order_by_desc(flow::Column::UpdatedAt).all(db).await
|
||
}
|
||
|
||
/// 查询流程执行历史
|
||
pub async fn find_flow_executions(
|
||
db: &DatabaseConnection,
|
||
flow_id: &str,
|
||
status: Option<ExecutionStatus>,
|
||
page: u64,
|
||
page_size: u64,
|
||
) -> Result<(Vec<flow_execution::Model>, u64), DbErr> {
|
||
let mut query = flow_execution::Entity::find()
|
||
.filter(flow_execution::Column::FlowId.eq(flow_id));
|
||
|
||
if let Some(s) = status {
|
||
query = query.filter(flow_execution::Column::Status.eq(s));
|
||
}
|
||
|
||
let paginator = query
|
||
.order_by_desc(flow_execution::Column::StartTime)
|
||
.paginate(db, page_size);
|
||
|
||
let total = paginator.num_items().await?;
|
||
let executions = paginator.fetch_page(page - 1).await?;
|
||
|
||
Ok((executions, total))
|
||
}
|
||
```
|
||
|
||
## 事务处理
|
||
|
||
### 事务示例
|
||
|
||
```rust
|
||
use sea_orm::*;
|
||
|
||
/// 创建用户并分配角色(事务)
|
||
pub async fn create_user_with_roles_tx(
|
||
db: &DatabaseConnection,
|
||
user_data: user::ActiveModel,
|
||
role_ids: Vec<String>,
|
||
) -> Result<user::Model, DbErr> {
|
||
let txn = db.begin().await?;
|
||
|
||
// 创建用户
|
||
let user = user_data.insert(&txn).await?;
|
||
|
||
// 分配角色
|
||
for role_id in role_ids {
|
||
let user_role = user_role::ActiveModel {
|
||
id: Set(crate::utils::generate_id()),
|
||
user_id: Set(user.id.clone()),
|
||
role_id: Set(role_id),
|
||
assigned_by: Set("system".to_string()),
|
||
assigned_at: Set(crate::utils::now_fixed_offset()),
|
||
expires_at: NotSet,
|
||
created_at: Set(crate::utils::now_fixed_offset()),
|
||
};
|
||
user_role.insert(&txn).await?;
|
||
}
|
||
|
||
txn.commit().await?;
|
||
Ok(user)
|
||
}
|
||
|
||
/// 更新流程状态(事务)
|
||
pub async fn publish_flow_tx(
|
||
db: &DatabaseConnection,
|
||
flow_id: &str,
|
||
user_id: &str,
|
||
) -> Result<flow::Model, DbErr> {
|
||
let txn = db.begin().await?;
|
||
|
||
// 更新流程状态
|
||
let flow = flow::Entity::find_by_id(flow_id)
|
||
.one(&txn)
|
||
.await?
|
||
.ok_or(DbErr::RecordNotFound("流程不存在".to_string()))?;
|
||
|
||
let mut flow: flow::ActiveModel = flow.into();
|
||
flow.status = Set(FlowStatus::Published);
|
||
flow.published_at = Set(Some(crate::utils::now_fixed_offset()));
|
||
flow.updated_by = Set(Some(user_id.to_string()));
|
||
flow.updated_at = Set(crate::utils::now_fixed_offset());
|
||
|
||
let updated_flow = flow.update(&txn).await?;
|
||
|
||
// 记录操作日志
|
||
let log = operation_log::ActiveModel {
|
||
id: Set(crate::utils::generate_id()),
|
||
user_id: Set(user_id.to_string()),
|
||
operation: Set("publish_flow".to_string()),
|
||
resource: Set("flow".to_string()),
|
||
resource_id: Set(Some(flow_id.to_string())),
|
||
details: Set(Some(serde_json::json!({
|
||
"flow_name": updated_flow.name,
|
||
"version": updated_flow.version
|
||
}))),
|
||
ip_address: NotSet,
|
||
user_agent: NotSet,
|
||
status: Set(OperationStatus::Success),
|
||
error: NotSet,
|
||
duration_ms: NotSet,
|
||
created_at: Set(crate::utils::now_fixed_offset()),
|
||
};
|
||
log.insert(&txn).await?;
|
||
|
||
txn.commit().await?;
|
||
Ok(updated_flow)
|
||
}
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
### 索引策略
|
||
|
||
```sql
|
||
-- 用户表索引
|
||
CREATE INDEX idx_users_username ON users(username);
|
||
CREATE INDEX idx_users_email ON users(email);
|
||
CREATE INDEX idx_users_status ON users(status);
|
||
CREATE INDEX idx_users_created_at ON users(created_at);
|
||
|
||
-- 流程表索引
|
||
CREATE INDEX idx_flows_status ON flows(status);
|
||
CREATE INDEX idx_flows_category ON flows(category);
|
||
CREATE INDEX idx_flows_created_by ON flows(created_by);
|
||
CREATE INDEX idx_flows_updated_at ON flows(updated_at);
|
||
|
||
-- 流程执行表索引
|
||
CREATE INDEX idx_flow_executions_flow_id ON flow_executions(flow_id);
|
||
CREATE INDEX idx_flow_executions_status ON flow_executions(status);
|
||
CREATE INDEX idx_flow_executions_start_time ON flow_executions(start_time);
|
||
|
||
-- 定时任务表索引
|
||
CREATE INDEX idx_schedule_jobs_enabled ON schedule_jobs(enabled);
|
||
CREATE INDEX idx_schedule_jobs_next_run_at ON schedule_jobs(next_run_at);
|
||
CREATE INDEX idx_schedule_jobs_created_by ON schedule_jobs(created_by);
|
||
|
||
-- 操作日志表索引
|
||
CREATE INDEX idx_operation_logs_user_id ON operation_logs(user_id);
|
||
CREATE INDEX idx_operation_logs_resource ON operation_logs(resource);
|
||
CREATE INDEX idx_operation_logs_created_at ON operation_logs(created_at);
|
||
|
||
-- 系统日志表索引
|
||
CREATE INDEX idx_system_logs_level ON system_logs(level);
|
||
CREATE INDEX idx_system_logs_source ON system_logs(source);
|
||
CREATE INDEX idx_system_logs_created_at ON system_logs(created_at);
|
||
```
|
||
|
||
### 查询优化
|
||
|
||
```rust
|
||
/// 使用预加载优化查询
|
||
pub async fn find_users_with_roles_optimized(
|
||
db: &DatabaseConnection,
|
||
page: u64,
|
||
page_size: u64,
|
||
) -> Result<Vec<(user::Model, Vec<role::Model>)>, DbErr> {
|
||
user::Entity::find()
|
||
.filter(user::Column::Status.eq(UserStatus::Active))
|
||
.filter(user::Column::IsDeleted.eq(false))
|
||
.find_with_related(role::Entity)
|
||
.paginate(db, page_size)
|
||
.fetch_page(page - 1)
|
||
.await
|
||
}
|
||
|
||
/// 使用原生 SQL 优化复杂查询
|
||
pub async fn get_user_statistics(
|
||
db: &DatabaseConnection,
|
||
) -> Result<UserStatistics, DbErr> {
|
||
let result = db
|
||
.query_one(Statement::from_sql_and_values(
|
||
DbBackend::Postgres,
|
||
r#"
|
||
SELECT
|
||
COUNT(*) as total_users,
|
||
COUNT(CASE WHEN status = 'active' THEN 1 END) as active_users,
|
||
COUNT(CASE WHEN status = 'inactive' THEN 1 END) as inactive_users,
|
||
COUNT(CASE WHEN last_login_at > NOW() - INTERVAL '30 days' THEN 1 END) as recent_active_users
|
||
FROM users
|
||
WHERE is_deleted = false
|
||
"#,
|
||
vec![],
|
||
))
|
||
.await?
|
||
.ok_or(DbErr::RecordNotFound("统计数据不存在".to_string()))?;
|
||
|
||
Ok(UserStatistics {
|
||
total_users: result.try_get("", "total_users")?,
|
||
active_users: result.try_get("", "active_users")?,
|
||
inactive_users: result.try_get("", "inactive_users")?,
|
||
recent_active_users: result.try_get("", "recent_active_users")?,
|
||
})
|
||
}
|
||
```
|
||
|
||
## 数据验证
|
||
|
||
### 模型验证
|
||
|
||
```rust
|
||
use validator::{Validate, ValidationError};
|
||
|
||
#[derive(Debug, Validate)]
|
||
pub struct CreateUserRequest {
|
||
#[validate(length(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间"))]
|
||
pub username: String,
|
||
|
||
#[validate(length(min = 6, message = "密码长度至少6个字符"))]
|
||
pub password: String,
|
||
|
||
#[validate(email(message = "邮箱格式不正确"))]
|
||
pub email: Option<String>,
|
||
|
||
#[validate(length(max = 100, message = "显示名称不能超过100个字符"))]
|
||
pub display_name: Option<String>,
|
||
}
|
||
|
||
/// 自定义验证函数
|
||
fn validate_cron_expression(cron: &str) -> Result<(), ValidationError> {
|
||
cron::Schedule::from_str(cron)
|
||
.map_err(|_| ValidationError::new("invalid_cron_expression"))?;
|
||
Ok(())
|
||
}
|
||
|
||
#[derive(Debug, Validate)]
|
||
pub struct CreateScheduleJobRequest {
|
||
#[validate(length(min = 1, max = 100))]
|
||
pub name: String,
|
||
|
||
#[validate(custom = "validate_cron_expression")]
|
||
pub cron_expression: String,
|
||
|
||
#[validate(range(min = 0, max = 10))]
|
||
pub max_retries: i32,
|
||
}
|
||
```
|
||
|
||
## 测试支持
|
||
|
||
### 测试数据库设置
|
||
|
||
```rust
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use sea_orm::*;
|
||
|
||
async fn setup_test_db() -> DatabaseConnection {
|
||
let db = Database::connect("sqlite::memory:").await.unwrap();
|
||
|
||
// 运行迁移
|
||
Migrator::up(&db, None).await.unwrap();
|
||
|
||
db
|
||
}
|
||
|
||
async fn create_test_user(db: &DatabaseConnection) -> user::Model {
|
||
let user = user::ActiveModel {
|
||
id: Set(crate::utils::generate_id()),
|
||
username: Set("test_user".to_string()),
|
||
password_hash: Set("hashed_password".to_string()),
|
||
email: Set(Some("test@example.com".to_string())),
|
||
display_name: Set(Some("Test User".to_string())),
|
||
avatar: NotSet,
|
||
status: Set(UserStatus::Active),
|
||
last_login_at: NotSet,
|
||
login_count: Set(0),
|
||
is_deleted: Set(false),
|
||
created_at: Set(crate::utils::now_fixed_offset()),
|
||
updated_at: Set(crate::utils::now_fixed_offset()),
|
||
};
|
||
|
||
user.insert(db).await.unwrap()
|
||
}
|
||
|
||
#[tokio::test]
|
||
async fn test_create_user() {
|
||
let db = setup_test_db().await;
|
||
let user = create_test_user(&db).await;
|
||
|
||
assert_eq!(user.username, "test_user");
|
||
assert_eq!(user.status, UserStatus::Active);
|
||
assert!(!user.is_deleted);
|
||
}
|
||
|
||
#[tokio::test]
|
||
async fn test_user_role_relationship() {
|
||
let db = setup_test_db().await;
|
||
|
||
// 创建用户和角色
|
||
let user = create_test_user(&db).await;
|
||
let role = create_test_role(&db).await;
|
||
|
||
// 创建用户角色关联
|
||
let user_role = user_role::ActiveModel {
|
||
id: Set(crate::utils::generate_id()),
|
||
user_id: Set(user.id.clone()),
|
||
role_id: Set(role.id.clone()),
|
||
assigned_by: Set("system".to_string()),
|
||
assigned_at: Set(crate::utils::now_fixed_offset()),
|
||
expires_at: NotSet,
|
||
created_at: Set(crate::utils::now_fixed_offset()),
|
||
};
|
||
user_role.insert(&db).await.unwrap();
|
||
|
||
// 验证关联关系
|
||
let user_with_roles = user::Entity::find_by_id(&user.id)
|
||
.find_with_related(role::Entity)
|
||
.all(&db)
|
||
.await
|
||
.unwrap();
|
||
|
||
assert_eq!(user_with_roles.len(), 1);
|
||
assert_eq!(user_with_roles[0].1.len(), 1);
|
||
assert_eq!(user_with_roles[0].1[0].id, role.id);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 最佳实践
|
||
|
||
### 模型设计
|
||
|
||
- **主键设计**: 使用字符串类型的 UUID 作为主键
|
||
- **外键约束**: 正确定义外键关系和级联操作
|
||
- **索引优化**: 为查询字段添加合适的索引
|
||
- **软删除**: 重要数据使用软删除而非物理删除
|
||
- **审计字段**: 包含创建时间、更新时间等审计信息
|
||
|
||
### 查询优化
|
||
|
||
- **预加载**: 使用 `find_with_related` 避免 N+1 查询问题
|
||
- **分页查询**: 使用 `paginate` 进行分页查询
|
||
- **索引使用**: 确保查询条件使用了合适的索引
|
||
- **原生 SQL**: 复杂查询使用原生 SQL 优化性能
|
||
|
||
### 事务管理
|
||
|
||
- **原子性**: 确保相关操作在同一事务中执行
|
||
- **一致性**: 维护数据的完整性约束
|
||
- **隔离性**: 避免并发事务的相互干扰
|
||
- **持久性**: 确保提交的事务持久保存
|
||
|
||
### 错误处理
|
||
|
||
- **类型安全**: 使用 `Result<T, DbErr>` 处理数据库错误
|
||
- **错误分类**: 区分不同类型的数据库错误
|
||
- **错误恢复**: 提供合适的错误恢复机制
|
||
- **日志记录**: 记录详细的错误信息用于调试
|
||
|
||
### 缓存策略
|
||
|
||
- **查询缓存**: 缓存频繁查询的结果
|
||
- **实体缓存**: 缓存常用的实体对象
|
||
- **失效策略**: 合理的缓存失效机制
|
||
- **一致性**: 保证缓存与数据库的一致性
|
||
|
||
## 总结
|
||
|
||
数据模型层是 UdminAI 系统的数据基础,通过 SeaORM 提供了类型安全、高性能的数据访问接口。模型设计遵循了关系型数据库的最佳实践,包括合理的索引设计、事务管理、数据验证和性能优化。
|
||
|
||
主要特点:
|
||
|
||
- **类型安全**: 使用 Rust 的类型系统确保数据安全
|
||
- **关系映射**: 正确定义实体间的关联关系
|
||
- **性能优化**: 通过索引和查询优化提升性能
|
||
- **事务支持**: 提供完整的事务管理机制
|
||
- **测试友好**: 支持单元测试和集成测试
|
||
- **扩展性**: 易于扩展和维护的模型设计
|
||
|
||
通过这套完整的数据模型设计,UdminAI 能够高效、安全地管理用户数据、流程数据、任务数据和系统数据,为上层业务逻辑提供可靠的数据支撑。 |