新增以下文档文件: - PROJECT_OVERVIEW.md 项目总览文档 - BACKEND_ARCHITECTURE.md 后端架构文档 - FRONTEND_ARCHITECTURE.md 前端架构文档 - FLOW_ENGINE.md 流程引擎文档 - SERVICES.md 服务层文档 - ERROR_HANDLING.md 错误处理模块文档 文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
42 KiB
42 KiB
数据模型文档
概述
数据模型层是 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 能够高效、安全地管理用户数据、流程数据、任务数据和系统数据,为上层业务逻辑提供可靠的数据支撑。