147 lines
5.3 KiB
Diff
147 lines
5.3 KiB
Diff
diff --git a/python-api/app/core/exceptions.py b/python-api/app/core/exceptions.py
|
|
index d9970d5..837f8d3 100644
|
|
--- a/python-api/app/core/exceptions.py
|
|
+++ b/python-api/app/core/exceptions.py
|
|
@@ -24,9 +24,16 @@ class AppException(HTTPException):
|
|
status_code: int,
|
|
message: str = "操作失败",
|
|
detail: dict | None = None,
|
|
+ *,
|
|
+ error_code: str | None = None,
|
|
):
|
|
- super().__init__(status_code=status_code, detail=detail or {})
|
|
+ body = detail or {}
|
|
+ body["message"] = message
|
|
+ if error_code:
|
|
+ body["error_code"] = error_code
|
|
+ super().__init__(status_code=status_code, detail=body)
|
|
self.message = message
|
|
+ self.error_code = error_code
|
|
|
|
|
|
class NotFoundException(AppException):
|
|
@@ -44,7 +51,7 @@ class ValidationException(AppException):
|
|
|
|
def __init__(self, message: str = "参数验证失败"):
|
|
super().__init__(
|
|
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
message=message,
|
|
)
|
|
|
|
@@ -79,6 +86,17 @@ class BusinessException(AppException):
|
|
)
|
|
|
|
|
|
+class InsufficientPointsException(AppException):
|
|
+ """积分不足"""
|
|
+
|
|
+ def __init__(self, message: str = "积分不足"):
|
|
+ super().__init__(
|
|
+ status_code=status.HTTP_402_PAYMENT_REQUIRED,
|
|
+ message=message,
|
|
+ error_code="insufficient_points",
|
|
+ )
|
|
+
|
|
+
|
|
class ModelUnavailableException(AppException):
|
|
"""AI 模型不可用"""
|
|
|
|
@@ -99,6 +117,50 @@ class TaskFailedException(AppException):
|
|
)
|
|
|
|
|
|
+class PromptNotFoundException(AppException):
|
|
+ """提示词文件不存在"""
|
|
+
|
|
+ def __init__(self, message: str = "未找到提示词"):
|
|
+ super().__init__(
|
|
+ status_code=status.HTTP_404_NOT_FOUND,
|
|
+ message=message,
|
|
+ error_code="prompt_not_found",
|
|
+ )
|
|
+
|
|
+
|
|
+class AIEmptyResponseException(AppException):
|
|
+ """AI 返回内容为空"""
|
|
+
|
|
+ def __init__(self, message: str = "AI 返回内容为空"):
|
|
+ super().__init__(
|
|
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
+ message=message,
|
|
+ error_code="empty_result",
|
|
+ )
|
|
+
|
|
+
|
|
+class AIParseErrorException(AppException):
|
|
+ """AI 返回内容解析失败"""
|
|
+
|
|
+ def __init__(self, message: str = "AI 返回格式解析失败"):
|
|
+ super().__init__(
|
|
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
+ message=message,
|
|
+ error_code="parse_error",
|
|
+ )
|
|
+
|
|
+
|
|
+class AITimeoutException(AppException):
|
|
+ """AI 调用超时"""
|
|
+
|
|
+ def __init__(self, message: str = "AI 请求超时,请稍后重试"):
|
|
+ super().__init__(
|
|
+ status_code=status.HTTP_504_GATEWAY_TIMEOUT,
|
|
+ message=message,
|
|
+ error_code="timeout",
|
|
+ )
|
|
+
|
|
+
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 第三方平台异常(PlatformError 体系)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
@@ -111,14 +173,15 @@ class PlatformErrorType:
|
|
确保前端和网关能够统一处理。
|
|
"""
|
|
|
|
- RATE_LIMIT = "rate_limit" # 429,可重试
|
|
- AUTH_FAILED = "auth_failed" # 401/403,不可重试
|
|
- TIMEOUT = "timeout" # 连接/读取超时,可重试
|
|
- SERVER_ERROR = "server_error" # 第三方 5xx,可重试
|
|
- BAD_REQUEST = "bad_request" # 参数错误,不可重试
|
|
+ RATE_LIMIT = "rate_limit" # 429,可重试
|
|
+ AUTH_FAILED = "auth_failed" # 401/403,不可重试
|
|
+ TIMEOUT = "timeout" # 连接/读取超时,可重试
|
|
+ SERVER_ERROR = "server_error" # 第三方 5xx,可重试
|
|
+ BAD_REQUEST = "bad_request" # 参数错误,不可重试
|
|
QUOTA_EXHAUSTED = "quota_exhausted" # 额度用完,不可重试(或延迟重试)
|
|
- NOT_FOUND = "not_found" # 资源不存在,不可重试
|
|
- UNKNOWN = "unknown" # 兜底
|
|
+ NOT_FOUND = "not_found" # 资源不存在,不可重试
|
|
+ CONTENT_VIOLATION = "content_violation" # 内容安全/审核不通过,不可重试
|
|
+ UNKNOWN = "unknown" # 兜底
|
|
|
|
|
|
class PlatformError(Exception):
|
|
@@ -145,12 +208,14 @@ class PlatformError(Exception):
|
|
retryable: bool = False,
|
|
error_type: str = PlatformErrorType.UNKNOWN,
|
|
status_code: int | None = None,
|
|
+ raw_code: str | None = None,
|
|
):
|
|
super().__init__(message)
|
|
self.platform = platform
|
|
self.retryable = retryable
|
|
self.error_type = error_type
|
|
self.status_code = status_code
|
|
+ self.raw_code = raw_code
|
|
|
|
def to_http_status(self) -> int:
|
|
"""根据 error_type 和 retryable 返回标准 HTTP 状态码"""
|
|
@@ -161,6 +226,7 @@ class PlatformError(Exception):
|
|
PlatformErrorType.AUTH_FAILED: 401,
|
|
PlatformErrorType.BAD_REQUEST: 400,
|
|
PlatformErrorType.NOT_FOUND: 404,
|
|
+ PlatformErrorType.CONTENT_VIOLATION: 400,
|
|
}
|
|
if self.error_type in mapping:
|
|
return mapping[self.error_type]
|