// erp-diary 错误类型 use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use serde::Serialize; #[derive(Debug, thiserror::Error)] pub enum DiaryError { #[error("日记未找到: {0}")] NotFound(String), #[error("版本冲突: 本地版本 {local}, 服务端版本 {server}")] VersionConflict { local: i32, server: i32 }, #[error("班级码无效")] InvalidClassCode, #[error("班级码已过期")] ClassCodeExpired, #[error("班级码尝试次数过多,请 {lockout_minutes} 分钟后重试")] ClassCodeLocked { lockout_minutes: u32 }, #[error("无权限执行此操作")] Forbidden, #[error("内容安全检查未通过")] ContentSafetyViolation, #[error("同步失败: {0}")] SyncFailed(String), #[error("{0}")] BadRequest(String), #[error("内部错误: {0}")] Internal(String), } #[derive(Serialize)] struct ErrorBody { error: String, message: String, } impl IntoResponse for DiaryError { fn into_response(self) -> Response { let (status, message) = match &self { DiaryError::NotFound(_) => (StatusCode::NOT_FOUND, self.to_string()), DiaryError::VersionConflict { .. } => (StatusCode::CONFLICT, self.to_string()), DiaryError::InvalidClassCode | DiaryError::ClassCodeExpired => { (StatusCode::BAD_REQUEST, self.to_string()) } DiaryError::ClassCodeLocked { .. } => (StatusCode::TOO_MANY_REQUESTS, self.to_string()), DiaryError::Forbidden => (StatusCode::FORBIDDEN, self.to_string()), DiaryError::ContentSafetyViolation => (StatusCode::BAD_REQUEST, self.to_string()), DiaryError::SyncFailed(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), DiaryError::BadRequest(_) => (StatusCode::BAD_REQUEST, self.to_string()), DiaryError::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), }; let body = ErrorBody { error: format!("diary.{}", status.as_u16()), message, }; (status, axum::Json(body)).into_response() } } impl From for DiaryError { fn from(err: sea_orm::DbErr) -> Self { DiaryError::Internal(err.to_string()) } }