request_utils.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import requests
  2. import time
  3. from typing import Optional
  4. from app.core.config import settings
  5. from app.core.exceptions import (
  6. UnauthorizedException,
  7. ForbiddenException,
  8. BusinessException,
  9. InternalServerException,
  10. ErrorMessage,
  11. StatusCode
  12. )
  13. class request_service:
  14. def __init__(self):
  15. """初始化请求服务"""
  16. self.base_url = settings.JAVA_API_BASE_URL
  17. self.session = requests.Session()
  18. self.session.timeout = 80 # 80秒超时
  19. self.max_retries = 3 # 最大重试次数
  20. self.retry_delay = 1 # 重试延迟(秒)
  21. def request(self, method: str, url: str, auth: bool, authorization: Optional[str] = None,
  22. source: Optional[str] = None, version: Optional[str] = None, **kwargs) -> any:
  23. """
  24. 发送请求的核心方法
  25. 参数:
  26. method: HTTP方法 (GET/POST等)
  27. url: 请求路径
  28. auth: 是否需要认证
  29. authorization: Authorization token
  30. source: Source 头部,默认为 'customer'
  31. version: Version 头部,默认为 'V6'
  32. **kwargs: 其他请求参数
  33. 返回:
  34. 响应数据或文件对象
  35. 异常:
  36. 抛出请求或业务逻辑相关的异常
  37. """
  38. # 1. 准备请求头
  39. headers = self._prepare_headers(auth, authorization, source, version)
  40. # 2. 处理GET请求的数组参数
  41. if method.upper() == 'GET' and 'params' in kwargs:
  42. kwargs['params'] = self._process_array_params(kwargs['params'])
  43. # 3. 设置请求头
  44. kwargs['headers'] = headers
  45. # 4. 发送请求(带重试机制)
  46. response = self._send_request_with_retry(method, url, **kwargs)
  47. # 5. 处理响应
  48. return self._handle_response(response)
  49. def _prepare_headers(self, auth: bool, authorization: Optional[str] = None,
  50. source: Optional[str] = None, version: Optional[str] = None) -> 'dict[str, str]':
  51. """准备请求头"""
  52. headers = {
  53. 'Source': source or 'customer',
  54. 'Content-Type': 'application/json',
  55. 'Version': version or 'V6'
  56. }
  57. if auth and authorization:
  58. headers['Authorization'] = authorization
  59. return headers
  60. def _process_array_params(self, params: 'dict[str, any]') -> 'dict[str, any]':
  61. """处理GET请求中的数组参数"""
  62. processed = {}
  63. for key, value in params.items():
  64. if isinstance(value, list):
  65. processed[key] = ','.join(map(str, value))
  66. else:
  67. processed[key] = value
  68. return processed
  69. def _handle_response(self, response: requests.Response) -> any:
  70. """处理响应"""
  71. # 处理文件类型的响应
  72. if response.headers.get('requesttype') == 'file':
  73. return response
  74. try:
  75. res = response.json()
  76. except ValueError:
  77. raise InternalServerException(detail=ErrorMessage.INVALID_RESPONSE)
  78. if not res:
  79. return None
  80. # 处理业务逻辑错误
  81. if res.get('code') != 200:
  82. self._handle_business_error(res)
  83. return res.get('data')
  84. def _handle_business_error(self, response_data: 'dict[str, any]'):
  85. """处理业务逻辑错误,直接使用Java服务返回的code和message"""
  86. error_code = response_data.get('code')
  87. error_msg = response_data.get('message', '未知系统错误')
  88. # 可接受的错误码,直接返回(不抛出异常)
  89. if error_code in (-1, 0):
  90. return
  91. # 根据错误码映射HTTP状态码,但保留原始的code和message
  92. # 600, 601 -> 401 (未授权)
  93. if error_code in (600, 601):
  94. raise UnauthorizedException(
  95. detail=error_msg,
  96. code=error_code
  97. )
  98. # 403或权限相关 -> 403 (禁止访问)
  99. elif error_code == 403 or "无该货主权限" in error_msg or "无权限" in error_msg or "没有权限" in error_msg:
  100. raise ForbiddenException(
  101. detail=error_msg,
  102. code=error_code if error_code else StatusCode.FORBIDDEN
  103. )
  104. else:
  105. # 其他所有错误(包括1001等),直接使用原始code和message
  106. # HTTP状态码使用400,但响应体中的code保留原始值
  107. raise BusinessException(
  108. detail=error_msg,
  109. status_code=400,
  110. code=error_code
  111. )
  112. def _send_request_with_retry(self, method: str, url: str, **kwargs) -> requests.Response:
  113. """带重试机制的请求发送,打印完整请求数据"""
  114. last_exception = None
  115. full_url = f"{self.base_url}{url}"
  116. for attempt in range(self.max_retries):
  117. try:
  118. # 获取headers并确保它是字典
  119. headers = kwargs.get('headers', {})
  120. if headers is None:
  121. headers = {}
  122. safe_headers = dict(headers) # 更安全的方式创建副本
  123. if 'Authorization' in safe_headers:
  124. safe_headers['Authorization'] = 'Bearer ******'
  125. # 发送请求
  126. response = self.session.request(method, full_url, **kwargs)
  127. return response
  128. except (requests.exceptions.RequestException,
  129. requests.exceptions.Timeout) as e:
  130. last_exception = e
  131. if attempt < self.max_retries - 1:
  132. time.sleep(self.retry_delay)
  133. raise InternalServerException(
  134. detail=str(last_exception) if last_exception else ErrorMessage.REQUEST_FAILED
  135. )