ForcePilot/backend/package/yuxi/channels/contract/errors/server.py
Kris b88c0ae29e feat(channels): 批量新增多渠道网关限界上下文基础代码与契约
新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
2026-07-02 03:22:12 +08:00

172 lines
4.8 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 TimeoutError(ServerError):
"""超时错误。
操作超时时抛出HTTP 504。在 ``server`` 模块内覆盖 Python 内置
``TimeoutError``,使用时通过模块路径或显式导入避免歧义。
"""
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