144 lines
5.2 KiB
Python
144 lines
5.2 KiB
Python
|
|
# =============================================================================
|
|||
|
|
# 企微IT智能服务台 — 统一响应格式工具
|
|||
|
|
# =============================================================================
|
|||
|
|
# 说明:定义所有 API 的统一响应格式和异常处理
|
|||
|
|
# 格式:{code: 0, data: {}, message: "success"}
|
|||
|
|
# code=0 表示成功,非0表示错误(1000+通用/2000+企微/3000+业务)
|
|||
|
|
# =============================================================================
|
|||
|
|
|
|||
|
|
from typing import Any, Dict, Optional
|
|||
|
|
|
|||
|
|
from fastapi import Request
|
|||
|
|
from fastapi.responses import JSONResponse
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
# 统一响应函数
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
def success_response(data: Any = None, message: str = "success") -> Dict[str, Any]:
|
|||
|
|
"""构建成功响应。
|
|||
|
|
|
|||
|
|
所有 API 成功时都应使用此函数返回统一格式。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
data: 业务数据(可以是字典、列表、None等)
|
|||
|
|
message: 成功消息(默认 "success")
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Dict[str, Any]: 统一格式的响应字典
|
|||
|
|
示例: {"code": 0, "data": {...}, "message": "success"}
|
|||
|
|
"""
|
|||
|
|
return {
|
|||
|
|
"code": 0,
|
|||
|
|
"data": data,
|
|||
|
|
"message": message,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def error_response(code: int, message: str, data: Any = None) -> Dict[str, Any]:
|
|||
|
|
"""构建错误响应。
|
|||
|
|
|
|||
|
|
所有 API 错误时都应使用此函数返回统一格式。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
code: 错误码(1000+通用/2000+企微/3000+业务)
|
|||
|
|
message: 错误消息
|
|||
|
|
data: 附加数据(可选,如验证错误详情)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Dict[str, Any]: 统一格式的错误响应字典
|
|||
|
|
示例: {"code": 1001, "data": null, "message": "参数错误"}
|
|||
|
|
"""
|
|||
|
|
return {
|
|||
|
|
"code": code,
|
|||
|
|
"data": data,
|
|||
|
|
"message": message,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
# 业务异常类
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
class AppException(Exception):
|
|||
|
|
"""业务异常基类。
|
|||
|
|
|
|||
|
|
在业务逻辑中抛出此异常,全局异常处理器会自动转换为统一响应格式。
|
|||
|
|
避免在每个路由函数中重复写 try/except 和响应构造代码。
|
|||
|
|
|
|||
|
|
Attributes:
|
|||
|
|
code: 错误码
|
|||
|
|
message: 错误消息
|
|||
|
|
data: 附加数据
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, code: int, message: str, data: Any = None):
|
|||
|
|
"""初始化业务异常。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
code: 错误码
|
|||
|
|
message: 错误消息
|
|||
|
|
data: 附加数据
|
|||
|
|
"""
|
|||
|
|
self.code = code
|
|||
|
|
self.message = message
|
|||
|
|
self.data = data
|
|||
|
|
super().__init__(self.message)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
# 预定义错误常量
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
# 错误码规范:
|
|||
|
|
# 0 = 成功
|
|||
|
|
# 1000+ = 通用错误(参数错误、未授权等)
|
|||
|
|
# 2000+ = 企微 API 错误
|
|||
|
|
# 3000+ = 业务逻辑错误
|
|||
|
|
|
|||
|
|
# --- 通用错误 (1000+) ---
|
|||
|
|
ERR_PARAMS = AppException(1001, "参数错误")
|
|||
|
|
ERR_UNAUTHORIZED = AppException(1002, "未授权")
|
|||
|
|
ERR_NOT_FOUND = AppException(1003, "资源不存在")
|
|||
|
|
ERR_FORBIDDEN = AppException(1004, "无权限访问")
|
|||
|
|
ERR_INTERNAL = AppException(1005, "服务器内部错误")
|
|||
|
|
|
|||
|
|
# --- 企微 API 错误 (2000+) ---
|
|||
|
|
ERR_WECOM_TOKEN = AppException(2001, "企微 access_token 获取失败")
|
|||
|
|
ERR_WECOM_SEND = AppException(2002, "企微消息发送失败")
|
|||
|
|
ERR_WECOM_DECRYPT = AppException(2003, "企微消息解密失败")
|
|||
|
|
ERR_WECOM_ENCRYPT = AppException(2004, "企微消息加密失败")
|
|||
|
|
ERR_WECOM_VERIFY = AppException(2005, "企微回调签名验证失败")
|
|||
|
|
ERR_WECOM_USER_INFO = AppException(2006, "企微用户信息获取失败")
|
|||
|
|
|
|||
|
|
# --- 业务逻辑错误 (3000+) ---
|
|||
|
|
ERR_AGENT_OFFLINE = AppException(3001, "坐席不在线")
|
|||
|
|
ERR_CONVERSATION_RESOLVED = AppException(3002, "会话已结单")
|
|||
|
|
ERR_CONVERSATION_NOT_FOUND = AppException(3003, "会话不存在")
|
|||
|
|
ERR_AGENT_NOT_FOUND = AppException(3004, "坐席不存在")
|
|||
|
|
ERR_AGENT_BUSY = AppException(3005, "坐席已满负荷,无法接单")
|
|||
|
|
ERR_DUPLICATE_ASSIGN = AppException(3006, "会话已分配坐席")
|
|||
|
|
ERR_GRAB_NO_AGENT = AppException(3011, "该会话尚未分配坐席,请使用接单功能")
|
|||
|
|
ERR_GRAB_SELF = AppException(3012, "不能接手自己的会话")
|
|||
|
|
ERR_GRAB_NOT_SERVING = AppException(3013, "只能接手服务中的会话")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
# 全局异常处理器
|
|||
|
|
# --------------------------------------------------------------------------
|
|||
|
|
async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
|
|||
|
|
"""AppException 全局异常处理器。
|
|||
|
|
|
|||
|
|
当业务逻辑抛出 AppException 时,FastAPI 自动调用此处理器,
|
|||
|
|
将异常转换为统一响应格式返回给前端。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
request: 请求对象(FastAPI 自动传入)
|
|||
|
|
exc: 业务异常对象(FastAPI 自动传入)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
JSONResponse: 统一格式的错误响应
|
|||
|
|
"""
|
|||
|
|
return JSONResponse(
|
|||
|
|
status_code=200, # 业务错误仍返回 HTTP 200,通过 code 区分
|
|||
|
|
content=error_response(exc.code, exc.message, exc.data),
|
|||
|
|
)
|