Files

144 lines
5.2 KiB
Python
Raw Permalink 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.
# =============================================================================
# 企微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),
)