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

1650 lines
42 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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