新增以下文档文件: - PROJECT_OVERVIEW.md 项目总览文档 - BACKEND_ARCHITECTURE.md 后端架构文档 - FRONTEND_ARCHITECTURE.md 前端架构文档 - FLOW_ENGINE.md 流程引擎文档 - SERVICES.md 服务层文档 - ERROR_HANDLING.md 错误处理模块文档 文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
1201 lines
31 KiB
Markdown
1201 lines
31 KiB
Markdown
# 路由层文档
|
|
|
|
## 概述
|
|
|
|
路由层是 UdminAI 的 HTTP API 接口层,负责处理客户端请求、参数验证、权限检查、调用服务层业务逻辑,并返回统一格式的响应。基于 Axum 框架构建,提供 RESTful API 接口。
|
|
|
|
## 架构设计
|
|
|
|
### 路由模块结构
|
|
|
|
```
|
|
routes/
|
|
├── mod.rs # 路由模块导出和总路由配置
|
|
├── auth.rs # 认证相关路由
|
|
├── user.rs # 用户管理路由
|
|
├── role.rs # 角色管理路由
|
|
├── permission.rs # 权限管理路由
|
|
├── flow.rs # 流程管理路由
|
|
├── schedule_job.rs # 定时任务路由
|
|
├── system.rs # 系统管理路由
|
|
├── log.rs # 日志查询路由
|
|
├── notification.rs # 通知管理路由
|
|
└── websocket.rs # WebSocket 路由
|
|
```
|
|
|
|
### 设计原则
|
|
|
|
- **RESTful 设计**: 遵循 REST API 设计规范
|
|
- **统一响应格式**: 所有接口返回统一的响应格式
|
|
- **参数验证**: 在路由层进行请求参数验证
|
|
- **权限控制**: 集成权限中间件进行访问控制
|
|
- **错误处理**: 统一的错误处理和响应
|
|
- **文档支持**: 支持 OpenAPI/Swagger 文档生成
|
|
|
|
## 总路由配置 (mod.rs)
|
|
|
|
### 路由树结构
|
|
|
|
```rust
|
|
pub fn create_routes() -> Router {
|
|
Router::new()
|
|
// 认证路由 (无需认证)
|
|
.nest("/auth", auth::routes())
|
|
|
|
// API 路由 (需要认证)
|
|
.nest("/api",
|
|
Router::new()
|
|
.nest("/users", user::routes())
|
|
.nest("/roles", role::routes())
|
|
.nest("/permissions", permission::routes())
|
|
.nest("/flows", flow::routes())
|
|
.nest("/jobs", schedule_job::routes())
|
|
.nest("/system", system::routes())
|
|
.nest("/logs", log::routes())
|
|
.nest("/notifications", notification::routes())
|
|
.layer(AuthMiddleware::new())
|
|
)
|
|
|
|
// WebSocket 路由
|
|
.nest("/ws", websocket::routes())
|
|
|
|
// 静态文件路由
|
|
.nest("/static", static_files::routes())
|
|
|
|
// 健康检查
|
|
.route("/health", get(health_check))
|
|
|
|
// 中间件层
|
|
.layer(CorsLayer::permissive())
|
|
.layer(TraceLayer::new_for_http())
|
|
.layer(CompressionLayer::new())
|
|
}
|
|
```
|
|
|
|
### 中间件配置
|
|
|
|
```rust
|
|
/// 应用中间件
|
|
pub fn apply_middlewares(app: Router) -> Router {
|
|
app
|
|
// 请求追踪
|
|
.layer(TraceLayer::new_for_http())
|
|
|
|
// CORS 支持
|
|
.layer(CorsLayer::new()
|
|
.allow_origin(Any)
|
|
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
|
|
.allow_headers([AUTHORIZATION, CONTENT_TYPE])
|
|
)
|
|
|
|
// 请求压缩
|
|
.layer(CompressionLayer::new())
|
|
|
|
// 请求限流
|
|
.layer(RateLimitLayer::new(100, Duration::from_secs(60)))
|
|
|
|
// 请求日志
|
|
.layer(RequestLogLayer::new())
|
|
}
|
|
```
|
|
|
|
## 认证路由 (auth.rs)
|
|
|
|
### 路由定义
|
|
|
|
```rust
|
|
pub fn routes() -> Router {
|
|
Router::new()
|
|
.route("/login", post(login))
|
|
.route("/logout", post(logout))
|
|
.route("/refresh", post(refresh_token))
|
|
.route("/register", post(register))
|
|
.route("/forgot-password", post(forgot_password))
|
|
.route("/reset-password", post(reset_password))
|
|
.route("/verify-email", post(verify_email))
|
|
}
|
|
```
|
|
|
|
### 接口实现
|
|
|
|
#### 用户登录
|
|
```rust
|
|
/// POST /auth/login
|
|
/// 用户登录接口
|
|
pub async fn login(
|
|
State(app_state): State<AppState>,
|
|
Json(req): Json<LoginReq>,
|
|
) -> Result<Json<ApiResponse<LoginResp>>, AppError> {
|
|
// 参数验证
|
|
req.validate().map_err(|e| AppError::BadRequest(e.to_string()))?;
|
|
|
|
// 用户认证
|
|
let user = user_service::authenticate(
|
|
&app_state.db,
|
|
&req.username,
|
|
&req.password,
|
|
).await?;
|
|
|
|
// 生成 JWT Token
|
|
let token = jwt::generate_token(&user.id, &app_state.jwt_secret)?;
|
|
let refresh_token = jwt::generate_refresh_token(&user.id, &app_state.jwt_secret)?;
|
|
|
|
// 记录登录日志
|
|
log_service::log_operation(
|
|
&app_state.db,
|
|
OperationLog {
|
|
user_id: user.id.clone(),
|
|
operation: "login".to_string(),
|
|
resource: "auth".to_string(),
|
|
resource_id: None,
|
|
details: json!({ "username": req.username }),
|
|
ip_address: extract_client_ip(&req),
|
|
user_agent: extract_user_agent(&req),
|
|
timestamp: now_fixed_offset(),
|
|
},
|
|
).await?;
|
|
|
|
Ok(Json(ApiResponse::success(LoginResp {
|
|
user,
|
|
token,
|
|
refresh_token,
|
|
expires_in: 3600,
|
|
})))
|
|
}
|
|
```
|
|
|
|
#### 请求/响应结构
|
|
|
|
```rust
|
|
#[derive(Debug, Deserialize, Validate)]
|
|
pub struct LoginReq {
|
|
#[validate(length(min = 3, max = 50))]
|
|
pub username: String,
|
|
#[validate(length(min = 6))]
|
|
pub password: String,
|
|
pub remember_me: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct LoginResp {
|
|
pub user: UserDoc,
|
|
pub token: String,
|
|
pub refresh_token: String,
|
|
pub expires_in: u64,
|
|
}
|
|
```
|
|
|
|
## 用户管理路由 (user.rs)
|
|
|
|
### 路由定义
|
|
|
|
```rust
|
|
pub fn routes() -> Router {
|
|
Router::new()
|
|
.route("/", get(list_users).post(create_user))
|
|
.route("/:id", get(get_user).put(update_user).delete(delete_user))
|
|
.route("/:id/roles", get(get_user_roles).put(update_user_roles))
|
|
.route("/:id/permissions", get(get_user_permissions))
|
|
.route("/:id/password", put(change_password))
|
|
.route("/:id/status", put(update_user_status))
|
|
.route("/profile", get(get_current_user).put(update_profile))
|
|
.route("/avatar", post(upload_avatar))
|
|
}
|
|
```
|
|
|
|
### 接口实现
|
|
|
|
#### 用户列表查询
|
|
```rust
|
|
/// GET /api/users
|
|
/// 分页查询用户列表
|
|
pub async fn list_users(
|
|
State(app_state): State<AppState>,
|
|
Query(params): Query<ListUsersQuery>,
|
|
Extension(current_user): Extension<CurrentUser>,
|
|
) -> Result<Json<ApiResponse<PageResp<UserDoc>>>, AppError> {
|
|
// 权限检查
|
|
permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
"user",
|
|
"read",
|
|
).await?;
|
|
|
|
// 构建过滤条件
|
|
let filters = UserFilters {
|
|
username: params.username,
|
|
email: params.email,
|
|
status: params.status,
|
|
role_ids: params.role_ids,
|
|
created_after: params.created_after,
|
|
created_before: params.created_before,
|
|
};
|
|
|
|
// 查询用户列表
|
|
let result = user_service::list_users(
|
|
&app_state.db,
|
|
params.page.unwrap_or(1),
|
|
params.page_size.unwrap_or(20),
|
|
Some(filters),
|
|
).await?;
|
|
|
|
Ok(Json(ApiResponse::success(result)))
|
|
}
|
|
```
|
|
|
|
#### 创建用户
|
|
```rust
|
|
/// POST /api/users
|
|
/// 创建新用户
|
|
pub async fn create_user(
|
|
State(app_state): State<AppState>,
|
|
Extension(current_user): Extension<CurrentUser>,
|
|
Json(req): Json<CreateUserReq>,
|
|
) -> Result<Json<ApiResponse<UserDoc>>, AppError> {
|
|
// 参数验证
|
|
req.validate().map_err(|e| AppError::BadRequest(e.to_string()))?;
|
|
|
|
// 权限检查
|
|
permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
"user",
|
|
"create",
|
|
).await?;
|
|
|
|
// 创建用户
|
|
let user = user_service::create_user(&app_state.db, req).await?;
|
|
|
|
// 记录操作日志
|
|
log_service::log_operation(
|
|
&app_state.db,
|
|
OperationLog {
|
|
user_id: current_user.id,
|
|
operation: "create_user".to_string(),
|
|
resource: "user".to_string(),
|
|
resource_id: Some(user.id.clone()),
|
|
details: json!({ "username": user.username }),
|
|
ip_address: None,
|
|
user_agent: None,
|
|
timestamp: now_fixed_offset(),
|
|
},
|
|
).await?;
|
|
|
|
Ok(Json(ApiResponse::success(user)))
|
|
}
|
|
```
|
|
|
|
### 查询参数结构
|
|
|
|
```rust
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct ListUsersQuery {
|
|
pub page: Option<u64>,
|
|
pub page_size: Option<u64>,
|
|
pub username: Option<String>,
|
|
pub email: Option<String>,
|
|
pub status: Option<UserStatus>,
|
|
pub role_ids: Option<Vec<String>>,
|
|
pub created_after: Option<DateTime<FixedOffset>>,
|
|
pub created_before: Option<DateTime<FixedOffset>>,
|
|
pub sort_by: Option<String>,
|
|
pub sort_order: Option<SortOrder>,
|
|
}
|
|
```
|
|
|
|
## 流程管理路由 (flow.rs)
|
|
|
|
### 路由定义
|
|
|
|
```rust
|
|
pub fn routes() -> Router {
|
|
Router::new()
|
|
.route("/", get(list_flows).post(create_flow))
|
|
.route("/:id", get(get_flow).put(update_flow).delete(delete_flow))
|
|
.route("/:id/versions", get(list_flow_versions).post(create_flow_version))
|
|
.route("/:id/publish", post(publish_flow))
|
|
.route("/:id/execute", post(execute_flow))
|
|
.route("/:id/executions", get(list_flow_executions))
|
|
.route("/executions/:execution_id", get(get_execution_detail))
|
|
.route("/executions/:execution_id/stop", post(stop_execution))
|
|
.route("/categories", get(list_flow_categories))
|
|
.route("/templates", get(list_flow_templates))
|
|
}
|
|
```
|
|
|
|
### 接口实现
|
|
|
|
#### 执行流程
|
|
```rust
|
|
/// POST /api/flows/:id/execute
|
|
/// 执行指定流程
|
|
pub async fn execute_flow(
|
|
State(app_state): State<AppState>,
|
|
Path(flow_id): Path<String>,
|
|
Extension(current_user): Extension<CurrentUser>,
|
|
Json(req): Json<ExecuteFlowReq>,
|
|
) -> Result<Json<ApiResponse<ExecutionResult>>, AppError> {
|
|
// 权限检查
|
|
permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
"flow",
|
|
"execute",
|
|
).await?;
|
|
|
|
// 获取流程信息
|
|
let flow = flow_service::get_flow_by_id(&app_state.db, &flow_id).await?
|
|
.ok_or_else(|| AppError::NotFound("流程不存在".to_string()))?;
|
|
|
|
// 检查流程状态
|
|
if flow.status != FlowStatus::Published {
|
|
return Err(AppError::BadRequest("只能执行已发布的流程".to_string()));
|
|
}
|
|
|
|
// 执行选项
|
|
let options = ExecuteOptions {
|
|
max_steps: req.max_steps,
|
|
timeout_ms: req.timeout_ms,
|
|
parallel: req.parallel.unwrap_or(false),
|
|
stream_events: req.stream_events.unwrap_or(false),
|
|
};
|
|
|
|
// 执行流程
|
|
let result = flow_service::execute_flow(
|
|
&app_state.db,
|
|
&flow_id,
|
|
req.input.unwrap_or(json!({})),
|
|
options,
|
|
).await?;
|
|
|
|
// 记录执行日志
|
|
log_service::log_operation(
|
|
&app_state.db,
|
|
OperationLog {
|
|
user_id: current_user.id,
|
|
operation: "execute_flow".to_string(),
|
|
resource: "flow".to_string(),
|
|
resource_id: Some(flow_id),
|
|
details: json!({
|
|
"execution_id": result.execution_id,
|
|
"status": result.status
|
|
}),
|
|
ip_address: None,
|
|
user_agent: None,
|
|
timestamp: now_fixed_offset(),
|
|
},
|
|
).await?;
|
|
|
|
Ok(Json(ApiResponse::success(result)))
|
|
}
|
|
```
|
|
|
|
#### 请求/响应结构
|
|
|
|
```rust
|
|
#[derive(Debug, Deserialize, Validate)]
|
|
pub struct ExecuteFlowReq {
|
|
pub input: Option<serde_json::Value>,
|
|
pub max_steps: Option<usize>,
|
|
pub timeout_ms: Option<u64>,
|
|
pub parallel: Option<bool>,
|
|
pub stream_events: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ExecutionResult {
|
|
pub execution_id: String,
|
|
pub status: ExecutionStatus,
|
|
pub result: Option<serde_json::Value>,
|
|
pub error: Option<String>,
|
|
pub start_time: DateTime<FixedOffset>,
|
|
pub end_time: Option<DateTime<FixedOffset>>,
|
|
pub duration_ms: Option<u64>,
|
|
}
|
|
```
|
|
|
|
## 定时任务路由 (schedule_job.rs)
|
|
|
|
### 路由定义
|
|
|
|
```rust
|
|
pub fn routes() -> Router {
|
|
Router::new()
|
|
.route("/", get(list_jobs).post(create_job))
|
|
.route("/:id", get(get_job).put(update_job).delete(delete_job))
|
|
.route("/:id/enable", post(enable_job))
|
|
.route("/:id/disable", post(disable_job))
|
|
.route("/:id/trigger", post(trigger_job))
|
|
.route("/:id/executions", get(list_job_executions))
|
|
.route("/executions/:execution_id", get(get_execution_detail))
|
|
.route("/cron/validate", post(validate_cron))
|
|
.route("/cron/next-runs", post(get_next_runs))
|
|
}
|
|
```
|
|
|
|
### 接口实现
|
|
|
|
#### 创建定时任务
|
|
```rust
|
|
/// POST /api/jobs
|
|
/// 创建定时任务
|
|
pub async fn create_job(
|
|
State(app_state): State<AppState>,
|
|
Extension(current_user): Extension<CurrentUser>,
|
|
Json(req): Json<CreateScheduleJobReq>,
|
|
) -> Result<Json<ApiResponse<ScheduleJobDoc>>, AppError> {
|
|
// 参数验证
|
|
req.validate().map_err(|e| AppError::BadRequest(e.to_string()))?;
|
|
|
|
// 权限检查
|
|
permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
"job",
|
|
"create",
|
|
).await?;
|
|
|
|
// 验证 Cron 表达式
|
|
cron::Schedule::from_str(&req.cron_expression)
|
|
.map_err(|_| AppError::BadRequest("无效的 Cron 表达式".to_string()))?;
|
|
|
|
// 创建任务
|
|
let job = schedule_job_service::create_schedule_job(&app_state.db, req).await?;
|
|
|
|
// 注册到调度器
|
|
if job.enabled {
|
|
schedule_job_service::register_job_to_scheduler(
|
|
&app_state.scheduler,
|
|
&job,
|
|
).await?;
|
|
}
|
|
|
|
Ok(Json(ApiResponse::success(job)))
|
|
}
|
|
```
|
|
|
|
#### Cron 表达式验证
|
|
```rust
|
|
/// POST /api/jobs/cron/validate
|
|
/// 验证 Cron 表达式
|
|
pub async fn validate_cron(
|
|
Json(req): Json<ValidateCronReq>,
|
|
) -> Result<Json<ApiResponse<ValidateCronResp>>, AppError> {
|
|
let schedule = cron::Schedule::from_str(&req.cron_expression)
|
|
.map_err(|e| AppError::BadRequest(format!("无效的 Cron 表达式: {}", e)))?;
|
|
|
|
// 计算接下来的执行时间
|
|
let now = Utc::now();
|
|
let next_runs: Vec<DateTime<Utc>> = schedule
|
|
.upcoming(Utc)
|
|
.take(5)
|
|
.collect();
|
|
|
|
Ok(Json(ApiResponse::success(ValidateCronResp {
|
|
valid: true,
|
|
next_runs,
|
|
description: describe_cron(&req.cron_expression),
|
|
})))
|
|
}
|
|
```
|
|
|
|
## WebSocket 路由 (websocket.rs)
|
|
|
|
### 路由定义
|
|
|
|
```rust
|
|
pub fn routes() -> Router {
|
|
Router::new()
|
|
.route("/flow-execution/:execution_id", get(flow_execution_ws))
|
|
.route("/system-monitor", get(system_monitor_ws))
|
|
.route("/notifications", get(notifications_ws))
|
|
.route("/logs", get(logs_ws))
|
|
}
|
|
```
|
|
|
|
### WebSocket 处理
|
|
|
|
#### 流程执行监控
|
|
```rust
|
|
/// GET /ws/flow-execution/:execution_id
|
|
/// 流程执行实时监控
|
|
pub async fn flow_execution_ws(
|
|
ws: WebSocketUpgrade,
|
|
Path(execution_id): Path<String>,
|
|
Extension(current_user): Extension<CurrentUser>,
|
|
State(app_state): State<AppState>,
|
|
) -> Result<Response, AppError> {
|
|
// 权限检查
|
|
permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
"flow",
|
|
"read",
|
|
).await?;
|
|
|
|
Ok(ws.on_upgrade(move |socket| {
|
|
handle_flow_execution_ws(socket, execution_id, current_user, app_state)
|
|
}))
|
|
}
|
|
|
|
async fn handle_flow_execution_ws(
|
|
socket: WebSocket,
|
|
execution_id: String,
|
|
current_user: CurrentUser,
|
|
app_state: AppState,
|
|
) {
|
|
let (mut sender, mut receiver) = socket.split();
|
|
|
|
// 订阅执行事件
|
|
let mut event_stream = app_state.event_bus
|
|
.subscribe(&format!("flow_execution:{}", execution_id))
|
|
.await;
|
|
|
|
// 发送事件到客户端
|
|
tokio::spawn(async move {
|
|
while let Ok(event) = event_stream.recv().await {
|
|
let message = serde_json::to_string(&event).unwrap();
|
|
if sender.send(Message::Text(message)).await.is_err() {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
// 处理客户端消息
|
|
while let Some(msg) = receiver.next().await {
|
|
match msg {
|
|
Ok(Message::Text(text)) => {
|
|
// 处理客户端命令
|
|
if let Ok(command) = serde_json::from_str::<WsCommand>(&text) {
|
|
handle_ws_command(command, &app_state).await;
|
|
}
|
|
}
|
|
Ok(Message::Close(_)) => break,
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 系统管理路由 (system.rs)
|
|
|
|
### 路由定义
|
|
|
|
```rust
|
|
pub fn routes() -> Router {
|
|
Router::new()
|
|
.route("/info", get(get_system_info))
|
|
.route("/status", get(get_system_status))
|
|
.route("/health", get(health_check))
|
|
.route("/metrics", get(get_metrics))
|
|
.route("/config", get(get_system_config).put(update_system_config))
|
|
.route("/cache/clear", post(clear_cache))
|
|
.route("/backup", post(create_backup))
|
|
.route("/restore", post(restore_backup))
|
|
}
|
|
```
|
|
|
|
### 接口实现
|
|
|
|
#### 系统状态监控
|
|
```rust
|
|
/// GET /api/system/status
|
|
/// 获取系统运行状态
|
|
pub async fn get_system_status(
|
|
State(app_state): State<AppState>,
|
|
Extension(current_user): Extension<CurrentUser>,
|
|
) -> Result<Json<ApiResponse<SystemStatus>>, AppError> {
|
|
// 权限检查
|
|
permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
"system",
|
|
"read",
|
|
).await?;
|
|
|
|
// 获取系统状态
|
|
let status = system_service::get_system_status(
|
|
&app_state.db,
|
|
&app_state.redis,
|
|
).await?;
|
|
|
|
Ok(Json(ApiResponse::success(status)))
|
|
}
|
|
```
|
|
|
|
#### 健康检查
|
|
```rust
|
|
/// GET /health
|
|
/// 系统健康检查
|
|
pub async fn health_check(
|
|
State(app_state): State<AppState>,
|
|
) -> Result<Json<HealthCheckResp>, AppError> {
|
|
let mut checks = HashMap::new();
|
|
|
|
// 数据库健康检查
|
|
let db_health = system_service::check_database_health(&app_state.db).await
|
|
.unwrap_or(HealthStatus::Unhealthy);
|
|
checks.insert("database".to_string(), db_health);
|
|
|
|
// Redis 健康检查
|
|
let redis_health = system_service::check_redis_health(&app_state.redis).await
|
|
.unwrap_or(HealthStatus::Unhealthy);
|
|
checks.insert("redis".to_string(), redis_health);
|
|
|
|
// 整体健康状态
|
|
let overall_status = if checks.values().all(|&status| status == HealthStatus::Healthy) {
|
|
HealthStatus::Healthy
|
|
} else {
|
|
HealthStatus::Unhealthy
|
|
};
|
|
|
|
Ok(Json(HealthCheckResp {
|
|
status: overall_status,
|
|
timestamp: Utc::now(),
|
|
checks,
|
|
}))
|
|
}
|
|
```
|
|
|
|
## 响应格式
|
|
|
|
### 统一响应结构
|
|
|
|
```rust
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ApiResponse<T> {
|
|
pub success: bool,
|
|
pub data: Option<T>,
|
|
pub message: String,
|
|
pub code: u32,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
impl<T> ApiResponse<T> {
|
|
pub fn success(data: T) -> Self {
|
|
Self {
|
|
success: true,
|
|
data: Some(data),
|
|
message: "操作成功".to_string(),
|
|
code: 200,
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
|
|
pub fn error(message: String, code: u32) -> ApiResponse<()> {
|
|
ApiResponse {
|
|
success: false,
|
|
data: None,
|
|
message,
|
|
code,
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 分页响应结构
|
|
|
|
```rust
|
|
#[derive(Debug, Serialize)]
|
|
pub struct PageResp<T> {
|
|
pub items: Vec<T>,
|
|
pub total: u64,
|
|
pub page: u64,
|
|
pub page_size: u64,
|
|
pub total_pages: u64,
|
|
pub has_next: bool,
|
|
pub has_prev: bool,
|
|
}
|
|
```
|
|
|
|
## 错误处理
|
|
|
|
### 错误类型定义
|
|
|
|
```rust
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum AppError {
|
|
#[error("请求参数错误: {0}")]
|
|
BadRequest(String),
|
|
|
|
#[error("未授权访问")]
|
|
Unauthorized,
|
|
|
|
#[error("权限不足: {0}")]
|
|
Forbidden(String),
|
|
|
|
#[error("资源不存在: {0}")]
|
|
NotFound(String),
|
|
|
|
#[error("请求冲突: {0}")]
|
|
Conflict(String),
|
|
|
|
#[error("请求过于频繁")]
|
|
TooManyRequests,
|
|
|
|
#[error("内部服务器错误: {0}")]
|
|
InternalServerError(String),
|
|
}
|
|
```
|
|
|
|
### 错误响应处理
|
|
|
|
```rust
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
let (status, code, message) = match self {
|
|
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, 400, msg),
|
|
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, 401, "未授权访问".to_string()),
|
|
AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, 403, msg),
|
|
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, 404, msg),
|
|
AppError::Conflict(msg) => (StatusCode::CONFLICT, 409, msg),
|
|
AppError::TooManyRequests => (StatusCode::TOO_MANY_REQUESTS, 429, "请求过于频繁".to_string()),
|
|
AppError::InternalServerError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, 500, msg),
|
|
};
|
|
|
|
let response = ApiResponse::<()>::error(message, code);
|
|
(status, Json(response)).into_response()
|
|
}
|
|
}
|
|
```
|
|
|
|
## 中间件
|
|
|
|
### 认证中间件
|
|
|
|
```rust
|
|
#[derive(Clone)]
|
|
pub struct AuthMiddleware {
|
|
jwt_secret: String,
|
|
}
|
|
|
|
impl AuthMiddleware {
|
|
pub fn new(jwt_secret: String) -> Self {
|
|
Self { jwt_secret }
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl<S> FromRequestParts<S> for CurrentUser
|
|
where
|
|
S: Send + Sync,
|
|
{
|
|
type Rejection = AppError;
|
|
|
|
async fn from_request_parts(
|
|
parts: &mut Parts,
|
|
state: &S,
|
|
) -> Result<Self, Self::Rejection> {
|
|
// 从请求头获取 Token
|
|
let auth_header = parts.headers
|
|
.get(AUTHORIZATION)
|
|
.and_then(|header| header.to_str().ok())
|
|
.ok_or(AppError::Unauthorized)?;
|
|
|
|
// 验证 Bearer Token 格式
|
|
let token = auth_header
|
|
.strip_prefix("Bearer ")
|
|
.ok_or(AppError::Unauthorized)?;
|
|
|
|
// 验证 JWT Token
|
|
let claims = jwt::verify_token(token, &jwt_secret)
|
|
.map_err(|_| AppError::Unauthorized)?;
|
|
|
|
// 获取用户信息
|
|
let app_state = parts.extensions.get::<AppState>()
|
|
.ok_or(AppError::InternalServerError("应用状态未找到".to_string()))?;
|
|
|
|
let user = user_service::get_user_by_id(&app_state.db, &claims.user_id).await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?
|
|
.ok_or(AppError::Unauthorized)?;
|
|
|
|
Ok(CurrentUser {
|
|
id: user.id,
|
|
username: user.username,
|
|
roles: user.roles,
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
### 权限中间件
|
|
|
|
```rust
|
|
pub struct PermissionMiddleware {
|
|
resource: String,
|
|
action: String,
|
|
}
|
|
|
|
impl PermissionMiddleware {
|
|
pub fn new(resource: &str, action: &str) -> Self {
|
|
Self {
|
|
resource: resource.to_string(),
|
|
action: action.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl<S> FromRequestParts<S> for PermissionGuard
|
|
where
|
|
S: Send + Sync,
|
|
{
|
|
type Rejection = AppError;
|
|
|
|
async fn from_request_parts(
|
|
parts: &mut Parts,
|
|
state: &S,
|
|
) -> Result<Self, Self::Rejection> {
|
|
// 获取当前用户
|
|
let current_user = CurrentUser::from_request_parts(parts, state).await?;
|
|
|
|
// 获取权限要求
|
|
let permission_req = parts.extensions.get::<PermissionRequirement>()
|
|
.ok_or(AppError::InternalServerError("权限要求未设置".to_string()))?;
|
|
|
|
// 检查权限
|
|
let app_state = parts.extensions.get::<AppState>()
|
|
.ok_or(AppError::InternalServerError("应用状态未找到".to_string()))?;
|
|
|
|
let has_permission = permission_service::check_user_permission(
|
|
&app_state.db,
|
|
¤t_user.id,
|
|
&permission_req.resource,
|
|
&permission_req.action,
|
|
).await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
|
|
|
if !has_permission {
|
|
return Err(AppError::Forbidden("权限不足".to_string()));
|
|
}
|
|
|
|
Ok(PermissionGuard { current_user })
|
|
}
|
|
}
|
|
```
|
|
|
|
### 请求日志中间件
|
|
|
|
```rust
|
|
pub struct RequestLogMiddleware;
|
|
|
|
impl<S> Layer<S> for RequestLogMiddleware {
|
|
type Service = RequestLogService<S>;
|
|
|
|
fn layer(&self, inner: S) -> Self::Service {
|
|
RequestLogService { inner }
|
|
}
|
|
}
|
|
|
|
pub struct RequestLogService<S> {
|
|
inner: S,
|
|
}
|
|
|
|
impl<S> Service<Request<Body>> for RequestLogService<S>
|
|
where
|
|
S: Service<Request<Body>, Response = Response> + Clone + Send + 'static,
|
|
S::Future: Send + 'static,
|
|
{
|
|
type Response = S::Response;
|
|
type Error = S::Error;
|
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
|
|
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
self.inner.poll_ready(cx)
|
|
}
|
|
|
|
fn call(&mut self, request: Request<Body>) -> Self::Future {
|
|
let start_time = Instant::now();
|
|
let method = request.method().clone();
|
|
let uri = request.uri().clone();
|
|
let user_agent = request.headers()
|
|
.get(USER_AGENT)
|
|
.and_then(|h| h.to_str().ok())
|
|
.unwrap_or("")
|
|
.to_string();
|
|
|
|
let future = self.inner.call(request);
|
|
|
|
Box::pin(async move {
|
|
let response = future.await?;
|
|
let duration = start_time.elapsed();
|
|
|
|
info!(
|
|
target = "udmin",
|
|
method = %method,
|
|
uri = %uri,
|
|
status = %response.status(),
|
|
duration_ms = %duration.as_millis(),
|
|
user_agent = %user_agent,
|
|
"http.request.completed"
|
|
);
|
|
|
|
Ok(response)
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
## API 文档
|
|
|
|
### OpenAPI 集成
|
|
|
|
```rust
|
|
use utoipa::{OpenApi, ToSchema};
|
|
use utoipa_swagger_ui::SwaggerUi;
|
|
|
|
#[derive(OpenApi)]
|
|
#[openapi(
|
|
paths(
|
|
auth::login,
|
|
auth::logout,
|
|
user::list_users,
|
|
user::create_user,
|
|
flow::execute_flow,
|
|
),
|
|
components(
|
|
schemas(
|
|
LoginReq,
|
|
LoginResp,
|
|
UserDoc,
|
|
CreateUserReq,
|
|
ExecuteFlowReq,
|
|
ExecutionResult,
|
|
)
|
|
),
|
|
tags(
|
|
(name = "auth", description = "认证相关接口"),
|
|
(name = "user", description = "用户管理接口"),
|
|
(name = "flow", description = "流程管理接口"),
|
|
)
|
|
)]
|
|
struct ApiDoc;
|
|
|
|
pub fn create_swagger_routes() -> Router {
|
|
Router::new()
|
|
.merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()))
|
|
}
|
|
```
|
|
|
|
### 接口文档注解
|
|
|
|
```rust
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/auth/login",
|
|
tag = "auth",
|
|
summary = "用户登录",
|
|
description = "用户名密码登录,返回 JWT Token",
|
|
request_body = LoginReq,
|
|
responses(
|
|
(status = 200, description = "登录成功", body = ApiResponse<LoginResp>),
|
|
(status = 400, description = "请求参数错误", body = ApiResponse<()>),
|
|
(status = 401, description = "用户名或密码错误", body = ApiResponse<()>),
|
|
)
|
|
)]
|
|
pub async fn login(
|
|
State(app_state): State<AppState>,
|
|
Json(req): Json<LoginReq>,
|
|
) -> Result<Json<ApiResponse<LoginResp>>, AppError> {
|
|
// 实现代码...
|
|
}
|
|
```
|
|
|
|
## 性能优化
|
|
|
|
### 请求缓存
|
|
|
|
```rust
|
|
pub struct CacheMiddleware {
|
|
redis: RedisConnection,
|
|
ttl: Duration,
|
|
}
|
|
|
|
impl CacheMiddleware {
|
|
pub fn new(redis: RedisConnection, ttl: Duration) -> Self {
|
|
Self { redis, ttl }
|
|
}
|
|
|
|
async fn get_cache_key(&self, request: &Request<Body>) -> String {
|
|
let method = request.method();
|
|
let uri = request.uri();
|
|
let query = uri.query().unwrap_or("");
|
|
format!("api_cache:{}:{}:{}", method, uri.path(), query)
|
|
}
|
|
}
|
|
```
|
|
|
|
### 响应压缩
|
|
|
|
```rust
|
|
pub fn create_compression_layer() -> CompressionLayer {
|
|
CompressionLayer::new()
|
|
.gzip(true)
|
|
.br(true)
|
|
.deflate(true)
|
|
.quality(CompressionLevel::Default)
|
|
}
|
|
```
|
|
|
|
### 请求限流
|
|
|
|
```rust
|
|
pub struct RateLimitMiddleware {
|
|
redis: RedisConnection,
|
|
max_requests: u32,
|
|
window: Duration,
|
|
}
|
|
|
|
impl RateLimitMiddleware {
|
|
pub async fn check_rate_limit(
|
|
&self,
|
|
client_id: &str,
|
|
) -> Result<bool, AppError> {
|
|
let key = format!("rate_limit:{}", client_id);
|
|
let current_count: u32 = self.redis.get(&key).await.unwrap_or(0);
|
|
|
|
if current_count >= self.max_requests {
|
|
return Ok(false);
|
|
}
|
|
|
|
let _: () = self.redis.incr(&key, 1).await?;
|
|
let _: () = self.redis.expire(&key, self.window.as_secs() as usize).await?;
|
|
|
|
Ok(true)
|
|
}
|
|
}
|
|
```
|
|
|
|
## 测试策略
|
|
|
|
### 单元测试
|
|
|
|
```rust
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use axum_test::TestServer;
|
|
|
|
#[tokio::test]
|
|
async fn test_login_success() {
|
|
let app = create_test_app().await;
|
|
let server = TestServer::new(app).unwrap();
|
|
|
|
let response = server
|
|
.post("/auth/login")
|
|
.json(&json!({
|
|
"username": "admin",
|
|
"password": "password123"
|
|
}))
|
|
.await;
|
|
|
|
response.assert_status_ok();
|
|
|
|
let body: ApiResponse<LoginResp> = response.json();
|
|
assert!(body.success);
|
|
assert!(body.data.is_some());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_login_invalid_credentials() {
|
|
let app = create_test_app().await;
|
|
let server = TestServer::new(app).unwrap();
|
|
|
|
let response = server
|
|
.post("/auth/login")
|
|
.json(&json!({
|
|
"username": "admin",
|
|
"password": "wrong_password"
|
|
}))
|
|
.await;
|
|
|
|
response.assert_status(StatusCode::UNAUTHORIZED);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 集成测试
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
async fn test_user_crud_flow() {
|
|
let app = create_test_app().await;
|
|
let server = TestServer::new(app).unwrap();
|
|
|
|
// 登录获取 Token
|
|
let login_response = server
|
|
.post("/auth/login")
|
|
.json(&json!({
|
|
"username": "admin",
|
|
"password": "password123"
|
|
}))
|
|
.await;
|
|
|
|
let login_body: ApiResponse<LoginResp> = login_response.json();
|
|
let token = login_body.data.unwrap().token;
|
|
|
|
// 创建用户
|
|
let create_response = server
|
|
.post("/api/users")
|
|
.add_header(AUTHORIZATION, format!("Bearer {}", token))
|
|
.json(&json!({
|
|
"username": "test_user",
|
|
"password": "password123",
|
|
"email": "test@example.com"
|
|
}))
|
|
.await;
|
|
|
|
create_response.assert_status_ok();
|
|
|
|
let create_body: ApiResponse<UserDoc> = create_response.json();
|
|
let user_id = create_body.data.unwrap().id;
|
|
|
|
// 获取用户
|
|
let get_response = server
|
|
.get(&format!("/api/users/{}", user_id))
|
|
.add_header(AUTHORIZATION, format!("Bearer {}", token))
|
|
.await;
|
|
|
|
get_response.assert_status_ok();
|
|
|
|
// 删除用户
|
|
let delete_response = server
|
|
.delete(&format!("/api/users/{}", user_id))
|
|
.add_header(AUTHORIZATION, format!("Bearer {}", token))
|
|
.await;
|
|
|
|
delete_response.assert_status_ok();
|
|
}
|
|
```
|
|
|
|
## 最佳实践
|
|
|
|
### 路由设计
|
|
|
|
- **RESTful 风格**: 遵循 REST API 设计原则
|
|
- **资源命名**: 使用复数名词表示资源
|
|
- **HTTP 方法**: 正确使用 GET、POST、PUT、DELETE
|
|
- **状态码**: 返回合适的 HTTP 状态码
|
|
|
|
### 参数验证
|
|
|
|
- **输入验证**: 使用 validator 进行参数验证
|
|
- **类型安全**: 使用强类型结构体
|
|
- **错误信息**: 提供清晰的验证错误信息
|
|
- **安全过滤**: 过滤恶意输入
|
|
|
|
### 错误处理
|
|
|
|
- **统一格式**: 使用统一的错误响应格式
|
|
- **错误分类**: 合理分类不同类型的错误
|
|
- **日志记录**: 记录详细的错误日志
|
|
- **用户友好**: 提供用户友好的错误信息
|
|
|
|
### 安全考虑
|
|
|
|
- **认证授权**: 实现完善的认证授权机制
|
|
- **输入验证**: 严格验证所有输入参数
|
|
- **HTTPS**: 在生产环境使用 HTTPS
|
|
- **CORS**: 正确配置 CORS 策略 |