75 lines
2.0 KiB
Python
75 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class FieldError:
|
|
field: str
|
|
message: str
|
|
code: str = "INVALID_VALUE"
|
|
|
|
|
|
class AppError(Exception):
|
|
def __init__(
|
|
self,
|
|
detail: str,
|
|
*,
|
|
code: str = "APPLICATION_ERROR",
|
|
status_code: int = 400,
|
|
title: str = "请求处理失败",
|
|
errors: list[FieldError] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
) -> None:
|
|
super().__init__(detail)
|
|
self.detail = detail
|
|
self.code = code
|
|
self.status_code = status_code
|
|
self.title = title
|
|
self.errors = errors or []
|
|
self.context = context or {}
|
|
|
|
|
|
class AuthenticationError(AppError):
|
|
def __init__(self, detail: str = "请先登录") -> None:
|
|
super().__init__(detail, code="AUTHENTICATION_REQUIRED", status_code=401, title="需要登录")
|
|
|
|
|
|
class ConflictError(AppError):
|
|
def __init__(self, detail: str, code: str = "RESOURCE_CONFLICT") -> None:
|
|
super().__init__(detail, code=code, status_code=409, title="状态冲突")
|
|
|
|
|
|
class ExternalServiceError(AppError):
|
|
def __init__(self, detail: str, *, service: str, code: str = "UPSTREAM_ERROR") -> None:
|
|
super().__init__(
|
|
detail,
|
|
code=code,
|
|
status_code=502,
|
|
title="外部服务异常",
|
|
context={"service": service},
|
|
)
|
|
|
|
|
|
class NotFoundError(AppError):
|
|
def __init__(self, detail: str, code: str = "NOT_FOUND") -> None:
|
|
super().__init__(detail, code=code, status_code=404, title="未找到")
|
|
|
|
|
|
class ValidationAppError(AppError):
|
|
def __init__(
|
|
self,
|
|
detail: str,
|
|
*,
|
|
code: str = "VALIDATION_ERROR",
|
|
errors: list[FieldError] | None = None,
|
|
) -> None:
|
|
super().__init__(
|
|
detail,
|
|
code=code,
|
|
status_code=422,
|
|
title="配置校验失败",
|
|
errors=errors,
|
|
)
|