ForcePilot/backend/package/yuxi/channels/contract/errors/base.py
Kris 742299cb07 chore: 批量清理报告相关代码并完成多项功能迭代
本次提交包含多维度代码优化与功能增强:
1.  移除报告模块冗余导入与枚举,清理报表相关代码
2.  新增扫码登录支持方法与飞书适配器适配
3.  完善异常日志与健康检查信息
4.  扩展目录、配对管理、能力查询等接口
5.  优化出站管道与事务提交后钩子逻辑
6.  修复飞书消息解析与响应空值问题
7.  重构配置更新与服务账号创建逻辑
8.  统一传输错误分类契约与错误基类扩展
2026-07-06 20:49:35 +08:00

102 lines
4.4 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.

"""错误基类定义。
定义所有契约层错误的根类型 ``Error``,提供统一的 ``error_code`` / ``message`` /
``trace_id`` / ``category_hint`` 字段与 ``to_dict()`` 序列化方法。所有跨边界
错误 **必须** 继承自 ``Error``禁止原生异常穿透至核心层INV-7 / FF-ERR-01
"""
from __future__ import annotations
from abc import ABC
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
# 仅用于类型注解:``from __future__ import annotations`` 使注解在运行时
# 为字符串TYPE_CHECKING 块不会执行,避免与 ``errors.transport`` 的循环
# 导入transport 模块运行时依赖 ``Error`` 基类)。
from yuxi.channels.contract.errors.transport import TransportErrorCategory
class Error(Exception, ABC):
"""错误基类。
所有契约层错误的根类型。子类通过类变量 ``error_code`` 声明稳定的错误码,
便于驱动适配器映射为外部协议响应HTTP 状态码、错误码)。
继承 ``Exception`` 以支持 ``raise`` 语句,继承 ``ABC`` 以保留抽象基类
语义(禁止直接实例化 ``Error``)。
字段:
error_code: 稳定错误码(由子类以类变量覆盖)。
message: 人类可读错误信息。
trace_id: 调用链路追踪 ID由调用方显式注入用于跨链路关联INV-10
未注入时为空字符串,契约层不再从 ContextVar 反向解析,以避免
违反 §6.1(契约层禁止依赖任何实现层)。
category_hint: 传输分类建议F-06。插件构造 Error 时显式声明期望的
传输分类(``auth_expired`` / ``rate_limited`` / ``transient`` /
``permanent``),供翻译器读取以消除对 ``cause`` 字符串匹配的依赖。
默认 ``None``:旧插件不设置时翻译器走 isinstance 兜底逻辑,保持
向后兼容。
属性:
status_code: HTTP 状态码,由 ``error_code`` 查 ``CHANNEL_ERROR_STATUS_MAP``
得出,未命中时默认 500。
details: 业务字段字典,从 ``to_dict()`` 提取并排除 ``error_code`` /
``message`` / ``trace_id``,子类覆写 ``to_dict()`` 后自动生效。
"""
error_code: str = "ERROR"
def __init__(
self,
message: str,
*,
trace_id: str | None = None,
category_hint: TransportErrorCategory | None = None,
) -> None:
super().__init__(message)
self.message = message
# trace_id 由调用方显式注入契约层禁止依赖实现层§6.1
# 因此不再从 ContextVar 反向解析。adapter/model 层抛错时由其
# 自行注入当前 trace_idP1-5/P1-6。统一接受 ``str | None``
# ``None`` 归一化为空字符串,便于子类与 ``UnifiedError`` 协议
# ``trace_id: str | None``)一致。
self.trace_id = trace_id or ""
# category_hint 是"建议"而非"命令":翻译器可根据业务需要覆盖。
# 默认 None 保持向后兼容,旧插件不设置时翻译器走 isinstance 兜底。
self.category_hint = category_hint
def to_dict(self) -> dict[str, Any]:
"""序列化为字典,便于跨层传递、日志记录与 API 响应。"""
return {
"error_code": self.error_code,
"message": self.message,
"trace_id": self.trace_id,
}
@property
def status_code(self) -> int:
"""HTTP 状态码:由 error_code 查表得出。
查询 ``CHANNEL_ERROR_STATUS_MAP``,未命中时返回 500。使用延迟 import
以避免核心层与状态码映射表之间的循环依赖。
"""
from yuxi.channels.contract.errors._status_map import (
CHANNEL_ERROR_STATUS_MAP,
)
return CHANNEL_ERROR_STATUS_MAP.get(self.error_code, 500)
@property
def details(self) -> dict[str, Any]:
"""业务字段:从 to_dict() 提取,排除 error_code/message/trace_id。
子类覆写的 to_dict() 自动生效业务字段resource/id/retry_after_ms 等)
自动进入 details。
"""
full_dict = self.to_dict()
return {k: v for k, v in full_dict.items() if k not in ("error_code", "message", "trace_id")}
def __str__(self) -> str:
return f"[{self.error_code}] {self.message}"