ForcePilot/backend/package/yuxi/channels/contract/errors/server.py
Kris 00092c818e chore: 批量代码优化与规范完善
本次提交包含多项代码优化与规范修正:
1. 文档与注释优化:修正注释术语、补充注解与FR编号
2. 代码格式调整:统一空格、换行与缩进规范
3. 类型与接口完善:补充__all__导出、修正返回类型注解
4. 错误处理增强:新增领域错误类与校验逻辑
5. 依赖与导入调整:修复路径引用、统一时区导入
6. 协议与契约更新:完善接口文档与一致性注解
2026-07-03 19:18:13 +08:00

213 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""服务端错误类型定义。
定义 ``ServerError`` 抽象基类及其 4 个具体子类,表示由服务端引起的错误
5xx 语义):内部错误、依赖故障、超时、未实现。``ServerError`` 额外携带
``cause`` 字段记录原始错误,便于追踪。
"""
from __future__ import annotations
from typing import Any
from yuxi.channels.contract.errors.base import Error
class ServerError(Error):
"""服务端错误抽象基类。
表示由服务端引起的错误5xx 语义)。额外携带 ``cause`` 字段记录原始错误,
便于跨链路追踪。子类包括 ``InternalError`` / ``DependencyError`` /
``TimeoutError``。
"""
error_code = "SERVER_ERROR"
def __init__(
self,
message: str,
*,
trace_id: str | None = None,
cause: Error | Exception | None = None,
) -> None:
super().__init__(message, trace_id=trace_id)
self.cause = cause
def to_dict(self) -> dict[str, Any]:
data = super().to_dict()
if self.cause is None:
data["cause"] = None
elif isinstance(self.cause, Error):
data["cause"] = self.cause.to_dict()
else:
# 原生 Exception 无 to_dict(),仅记录类型与信息以便追踪
data["cause"] = {
"type": type(self.cause).__name__,
"message": str(self.cause),
}
return data
class InternalError(ServerError):
"""内部错误。
服务端内部异常导致的错误HTTP 500。``cause`` 可选:当原始错误为
``Error`` 子类时传入以保留结构化错误链;当原始错误为原生 ``Exception``
时可不传 ``cause``,通过 ``message`` 携带异常信息。
"""
error_code = "INTERNAL"
def __init__(
self,
cause: Error | Exception | None = None,
*,
trace_id: str | None = None,
message: str | None = None,
) -> None:
super().__init__(
message or "Internal error",
trace_id=trace_id,
cause=cause,
)
class DependencyError(ServerError):
"""依赖故障错误。
外部依赖如数据库、Redis、第三方服务故障时抛出HTTP 502
"""
error_code = "DEPENDENCY"
def __init__(
self,
dep: str,
cause: Error | Exception,
*,
trace_id: str | None = None,
) -> None:
super().__init__(
f"Dependency {dep} failed",
trace_id=trace_id,
cause=cause,
)
self.dep = dep
def to_dict(self) -> dict[str, Any]:
data = super().to_dict()
data["dep"] = self.dep
return data
class OperationTimeoutError(ServerError):
"""操作超时错误。
操作超时时抛出HTTP 504error_code=``TIMEOUT``)。命名为
``OperationTimeoutError`` 以避免与 Python 内置 ``TimeoutError`` 冲突,
遵循 ``PermissionDeniedError`` 的同一命名约定。
"""
error_code = "TIMEOUT"
def __init__(
self,
timeout_ms: int,
*,
message: str | None = None,
trace_id: str | None = None,
) -> None:
"""初始化超时错误。
Args:
timeout_ms: 超时毫秒数,写入 ``details`` 供客户端参考。
message: 自定义错误信息。为 ``None`` 时使用默认模板
``"Operation timed out after {timeout_ms}ms"``。
trace_id: 调用链路追踪 ID用于跨链路关联。
"""
super().__init__(
message or f"Operation timed out after {timeout_ms}ms",
trace_id=trace_id,
)
self.timeout_ms = timeout_ms
def to_dict(self) -> dict[str, Any]:
data = super().to_dict()
data["timeout_ms"] = self.timeout_ms
return data
class NotImplementedError(ServerError):
"""未实现错误。
请求的操作尚未实现时抛出HTTP 501。用于标记尚未落地的功能分支
避免静默返回空结果掩盖功能缺失。
"""
error_code = "NOT_IMPLEMENTED"
def __init__(
self,
operation: str,
*,
message: str | None = None,
trace_id: str | None = None,
) -> None:
"""初始化未实现错误。
Args:
operation: 未实现的操作名称,写入 ``details`` 供客户端参考。
message: 自定义错误信息。为 ``None`` 时使用默认模板
``"Operation not implemented: {operation}"``。
trace_id: 调用链路追踪 ID用于跨链路关联。
"""
super().__init__(
message or f"Operation not implemented: {operation}",
trace_id=trace_id,
)
self.operation = operation
def to_dict(self) -> dict[str, Any]:
data = super().to_dict()
data["operation"] = self.operation
return data
class ServiceAccountCreationError(InternalError):
"""服务账号创建失败错误。
为渠道账户自动创建服务账号(``User.user_type='service'``)失败时抛出
HTTP 500。``details`` 包含 ``channel_type``、``account_id`` 与
``reason``,便于定位创建失败的根因。
适配器层抛出时应使用 ``raise ServiceAccountCreationError(...) from exc``
保留原始异常 traceback原始异常通过 ``cause`` 参数传递至
``InternalError.cause`` 字段。
"""
error_code = "CHANNEL_SERVICE_ACCOUNT_CREATE_FAILED"
def __init__(
self,
channel_type: str,
account_id: str,
reason: str,
*,
cause: Error | Exception | None = None,
trace_id: str | None = None,
) -> None:
super().__init__(
cause=cause,
trace_id=trace_id,
message=f"failed to create service account for {channel_type}:{account_id}: {reason}",
)
self.channel_type = channel_type
self.account_id = account_id
self.reason = reason
def to_dict(self) -> dict[str, Any]:
data = super().to_dict()
data["channel_type"] = self.channel_type
data["account_id"] = self.account_id
data["reason"] = self.reason
return data