巴青农资商城

api_client.py 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # ============================================================
  2. # HTTP 客户端封装 — 统一处理 Token、会话、请求/响应日志
  3. # ============================================================
  4. import requests
  5. import json
  6. import time
  7. from config import API_BASE_URL, REQUEST_TIMEOUT
  8. class ApiClient:
  9. """封装 HTTP 请求,自动管理 Token 和会话"""
  10. def __init__(self, base_url=None):
  11. self.base_url = base_url or API_BASE_URL
  12. self.session = requests.Session()
  13. self.session.headers.update({
  14. "Content-Type": "application/json;charset=utf-8",
  15. "User-Agent": "E2ETest/1.0",
  16. })
  17. self._last_response = None
  18. self._admin_token = None # 管理员 Token(Authorization 头)
  19. self._member_token = None # 会员 Token(具体头部看接口)
  20. self._seller_token = None # 商家 Token
  21. # ---------- 基础请求方法 ----------
  22. def request(self, method, path, **kwargs):
  23. url = f"{self.base_url}{path}" if not path.startswith("http") else path
  24. kwargs.setdefault("timeout", REQUEST_TIMEOUT)
  25. kwargs.setdefault("headers", {})
  26. resp = self.session.request(method, url, **kwargs)
  27. self._last_response = resp
  28. # 打印简略日志
  29. _log_req(method, url, kwargs)
  30. _log_resp(resp)
  31. return resp
  32. def get(self, path, **kwargs):
  33. return self.request("GET", path, **kwargs)
  34. def post(self, path, **kwargs):
  35. return self.request("POST", path, **kwargs)
  36. def put(self, path, **kwargs):
  37. return self.request("PUT", path, **kwargs)
  38. def delete(self, path, **kwargs):
  39. return self.request("DELETE", path, **kwargs)
  40. # ---------- Token 管理 ----------
  41. def set_admin_token(self, token):
  42. self._admin_token = token
  43. self.session.headers["Authorization"] = f"Bearer {token}"
  44. def set_member_token(self, token):
  45. self._member_token = token
  46. # 根据 MemberContext 的实现,会员 Token 也是 Authorization 头
  47. # 但区分会员和管理员,我们用不同的头
  48. self.session.headers["Authorization"] = f"Bearer {token}"
  49. def set_seller_token(self, token):
  50. self._seller_token = token
  51. self.session.headers["Authorization"] = f"Bearer {token}"
  52. def clear_token(self):
  53. self._admin_token = None
  54. self._member_token = None
  55. self._seller_token = None
  56. self.session.headers.pop("Authorization", None)
  57. # ---------- 响应解析 ----------
  58. def parse_json(self):
  59. if self._last_response is None:
  60. return None
  61. try:
  62. return self._last_response.json()
  63. except Exception:
  64. return None
  65. def assert_ok(self, err_msg=None):
  66. """断言接口返回 code == 200(AjaxResult.success)"""
  67. data = self.parse_json()
  68. if data is None:
  69. raise AssertionError(f"{err_msg or '响应不是 JSON'}: {self._last_response.text[:200]}")
  70. code = data.get("code") if isinstance(data, dict) else None
  71. if code != 200:
  72. msg = data.get("msg", "未知错误")
  73. raise AssertionError(f"{err_msg or '接口错误'}: code={code}, msg={msg}")
  74. return data
  75. def extract_data(self):
  76. """从 AjaxResult 中提取 data 字段"""
  77. data = self.assert_ok()
  78. return data.get("data")
  79. # ---------- 工具方法 ----------
  80. def get_captcha(self):
  81. """
  82. 获取登录验证码(若依标准)
  83. 返回 (uuid, math_answer) 或 (None, None)
  84. """
  85. resp = self.get("/captchaImage")
  86. body = self.assert_ok("获取验证码失败")
  87. return body.get("uuid"), None # math 验证码需要 OCR,建议测试环境关闭
  88. # ---------- 日志辅助 ----------
  89. def _log_req(method, url, kwargs):
  90. body = kwargs.get("json") or kwargs.get("data")
  91. snippet = ""
  92. if body:
  93. s = json.dumps(body, ensure_ascii=False)
  94. snippet = f" | body={s[:120]}"
  95. print(f" >>> {method} {url}{snippet}")
  96. def _log_resp(resp):
  97. status = resp.status_code
  98. body = ""
  99. try:
  100. data = resp.json()
  101. if isinstance(data, dict):
  102. code = data.get("code", "")
  103. msg = data.get("msg", "")
  104. body = f"code={code} msg={msg}"
  105. except Exception:
  106. body = resp.text[:80]
  107. print(f" <<< {status} | {body}")