Files
udmin/docs/MODELS.md
ayou a3f2f99a68 docs: 添加项目文档包括总览、架构、流程引擎和服务层
新增以下文档文件:
- PROJECT_OVERVIEW.md 项目总览文档
- BACKEND_ARCHITECTURE.md 后端架构文档
- FRONTEND_ARCHITECTURE.md 前端架构文档
- FLOW_ENGINE.md 流程引擎文档
- SERVICES.md 服务层文档
- ERROR_HANDLING.md 错误处理模块文档

文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
2025-09-24 20:21:45 +08:00

42 KiB
Raw Blame History

数据模型文档

概述

数据模型层是 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)

实体定义

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 {}

用户状态枚举

#[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,
}

数据库迁移

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)

实体定义

#[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)

实体定义

#[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)

实体定义

#[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 {}

流程状态枚举

#[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)

实体定义

#[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 {}

执行状态枚举

#[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)

实体定义

#[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 {}

任务类型枚举

#[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)

实体定义

#[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 {}

执行状态枚举

#[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)

实体定义

#[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 {}

操作状态枚举

#[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)

实体定义

#[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 {}

日志级别枚举

#[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)

实体定义

#[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 {}

通知相关枚举

#[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)

实体定义

#[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 {}

配置类型枚举

#[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)

#[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)

#[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 {}

数据库连接和配置

数据库连接

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
}

迁移管理

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),
        ]
    }
}

查询构建器

用户查询示例

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))
        })
}

流程查询示例

/// 查询已发布的流程
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))
}

事务处理

事务示例

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)
}

性能优化

索引策略

-- 用户表索引
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);

查询优化

/// 使用预加载优化查询
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")?,
    })
}

数据验证

模型验证

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,
}

测试支持

测试数据库设置

#[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 能够高效、安全地管理用户数据、流程数据、任务数据和系统数据,为上层业务逻辑提供可靠的数据支撑。