ForcePilot/backend/server/utils/error_response.py
Kris 12ec5af0de feat: 新增全链路追踪、优雅关停支持与统一异常处理,优化渠道查询接口
1. 新增TraceId中间件实现全链路请求追踪
2. 新增在途请求计数器与中间件支持优雅关停
3. 重构全局异常处理器,统一三模块异常响应格式
4. 优化渠道查询接口,新增多维度过滤与模糊匹配
5. 重构channels模块数据库会话管理,避免事务冲突
6. 新增目录搜索建议与目录导出功能
2026-07-07 16:25:47 +08:00

123 lines
3.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.

"""统一错误响应构建器。
定义 ``UnifiedError`` 协议(三模块异常基类都实现此协议),
提供 ``build_error_response()`` 和 ``build_error_response_from_exc()``
统一构建错误响应,确保所有全局异常处理器输出格式一致。
"""
from __future__ import annotations
from typing import Any, Protocol
from fastapi.responses import JSONResponse
from yuxi.utils.trace_context import get_trace_id
class UnifiedError(Protocol):
"""统一异常协议。
三模块异常基类ChannelError / ExternalSystemError / SchedulerError
都实现此协议,允许统一 handler 处理。
字段:
status_code: HTTP 状态码。
error_code: 稳定错误码(字符串,如 "VALIDATION_ERROR")。
message: 人类可读错误信息。
details: 业务字段字典(如 references, retry_after 等)。
trace_id: 链路追踪 ID。
"""
status_code: int
error_code: str
message: str
details: dict[str, Any]
trace_id: str | None
def _extract_retry_after(exc: UnifiedError) -> dict[str, str]:
"""统一提取 Retry-After 响应头。
429/503 且 details 含 retry_after 时设置。
503 且无 retry_after 时设置默认 60 秒ChannelDegradedError 等降级场景)。
channels 的 RateLimitError 已将 retry_after 写入 details通过 to_dict())。
external_systems 的 RateLimitExceededError 已将 retry_after 写入 details。
Args:
exc: 满足 UnifiedError 协议的异常对象。
Returns:
含 Retry-After 头的字典(无需要时为空字典)。
"""
headers: dict[str, str] = {}
if exc.status_code in (429, 503):
retry_after = exc.details.get("retry_after")
if retry_after is not None:
headers["Retry-After"] = str(retry_after)
elif exc.status_code == 503:
# 503 降级场景无明确 retry_after 时设置默认 60 秒
headers["Retry-After"] = "60"
return headers
def build_error_response(
status_code: int,
code: str,
message: str,
details: dict[str, Any] | None = None,
trace_id: str | None = None,
headers: dict[str, str] | None = None,
) -> JSONResponse:
"""构建统一错误响应。
所有全局异常处理器调用此函数,确保响应格式一致。
trace_id 优先使用传入值,其次从 ContextVar 获取。
Args:
status_code: HTTP 状态码。
code: 稳定错误码。
message: 人类可读错误信息。
details: 业务字段字典。
trace_id: 链路追踪 ID为 None 时从 ContextVar 获取)。
headers: 额外响应头(如 Retry-After
Returns:
统一格式的 JSONResponse。
"""
resolved_trace_id = trace_id or get_trace_id()
return JSONResponse(
status_code=status_code,
content={
"success": False,
"error": {
"code": code,
"message": message,
"trace_id": resolved_trace_id,
"details": details or {},
},
},
headers=headers or {},
)
def build_error_response_from_exc(exc: UnifiedError) -> JSONResponse:
"""从统一异常对象构建错误响应(统一入口)。
所有满足 UnifiedError 协议的异常都通过此函数构建响应,
确保 handler 逻辑完全一致,不再有 isinstance 特判。
Args:
exc: 满足 UnifiedError 协议的异常对象。
Returns:
统一格式的 JSONResponse。
"""
headers = _extract_retry_after(exc)
return build_error_response(
status_code=exc.status_code,
code=exc.error_code,
message=exc.message,
details=exc.details,
trace_id=exc.trace_id,
headers=headers,
)