feat(ai): 扩展 AiError 支持配额/缓存/知识库/队列/配置错误变体
新增 QuotaExhausted→429, CacheError/KnowledgeError/QueueError/ConfigError→500
This commit is contained in:
@@ -31,6 +31,21 @@ pub enum AiError {
|
|||||||
|
|
||||||
#[error("数据库错误: {0}")]
|
#[error("数据库错误: {0}")]
|
||||||
DbError(String),
|
DbError(String),
|
||||||
|
|
||||||
|
#[error("AI 配额已耗尽: {reason}")]
|
||||||
|
QuotaExhausted { tenant_id: uuid::Uuid, reason: String },
|
||||||
|
|
||||||
|
#[error("缓存错误: {0}")]
|
||||||
|
CacheError(String),
|
||||||
|
|
||||||
|
#[error("知识库错误: {0}")]
|
||||||
|
KnowledgeError(String),
|
||||||
|
|
||||||
|
#[error("分析队列错误: {0}")]
|
||||||
|
QueueError(String),
|
||||||
|
|
||||||
|
#[error("AI 配置错误: {0}")]
|
||||||
|
ConfigError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AiError> for AppError {
|
impl From<AiError> for AppError {
|
||||||
@@ -43,6 +58,7 @@ impl From<AiError> for AppError {
|
|||||||
AppError::Internal(format!("AI 提供商 {p} 不可用"))
|
AppError::Internal(format!("AI 提供商 {p} 不可用"))
|
||||||
}
|
}
|
||||||
AiError::RateLimitExceeded => AppError::TooManyRequests,
|
AiError::RateLimitExceeded => AppError::TooManyRequests,
|
||||||
|
AiError::QuotaExhausted { .. } => AppError::TooManyRequests,
|
||||||
AiError::VersionMismatch => AppError::VersionMismatch,
|
AiError::VersionMismatch => AppError::VersionMismatch,
|
||||||
AiError::DbError(msg) => AppError::Internal(msg),
|
AiError::DbError(msg) => AppError::Internal(msg),
|
||||||
other => AppError::Internal(other.to_string()),
|
other => AppError::Internal(other.to_string()),
|
||||||
@@ -161,4 +177,54 @@ mod tests {
|
|||||||
other => panic!("期望 AppError::Internal,得到 {:?}", other),
|
other => panic!("期望 AppError::Internal,得到 {:?}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quota_exhausted_maps_to_429() {
|
||||||
|
let err = AiError::QuotaExhausted {
|
||||||
|
tenant_id: uuid::Uuid::now_v7(),
|
||||||
|
reason: "monthly budget".into(),
|
||||||
|
};
|
||||||
|
let app: AppError = err.into();
|
||||||
|
assert!(matches!(app, AppError::TooManyRequests));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cache_error_maps_to_internal() {
|
||||||
|
let err = AiError::CacheError("redis timeout".into());
|
||||||
|
let app: AppError = err.into();
|
||||||
|
match app {
|
||||||
|
AppError::Internal(msg) => assert!(msg.contains("redis timeout")),
|
||||||
|
other => panic!("期望 AppError::Internal,得到 {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn knowledge_error_maps_to_internal() {
|
||||||
|
let err = AiError::KnowledgeError("rule not found".into());
|
||||||
|
let app: AppError = err.into();
|
||||||
|
match app {
|
||||||
|
AppError::Internal(msg) => assert!(msg.contains("rule not found")),
|
||||||
|
other => panic!("期望 AppError::Internal,得到 {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn queue_error_maps_to_internal() {
|
||||||
|
let err = AiError::QueueError("queue full".into());
|
||||||
|
let app: AppError = err.into();
|
||||||
|
match app {
|
||||||
|
AppError::Internal(msg) => assert!(msg.contains("queue full")),
|
||||||
|
other => panic!("期望 AppError::Internal,得到 {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_error_maps_to_internal() {
|
||||||
|
let err = AiError::ConfigError("missing provider".into());
|
||||||
|
let app: AppError = err.into();
|
||||||
|
match app {
|
||||||
|
AppError::Internal(msg) => assert!(msg.contains("missing provider")),
|
||||||
|
other => panic!("期望 AppError::Internal,得到 {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user