from logging import exception from typing import Optional, Dict, Any import time import threading import requests from models import LoginResponse from config.ini_manager import ini_manager class BaseHttpClient: """基础HTTP客户端 - 纯网络请求功能""" def __init__(self): """初始化基础HTTP客户端""" self._session = requests.Session() self._settings = None def request(self, method: str, url: str, data: Dict[str, Any] = None, headers: Dict[str, str] = None, timeout: int = None, retries: int = 0, retry_interval: float = 1.0, **kwargs) -> Dict[str, Any]: """ 发送HTTP请求,支持重试机制 Args: method: HTTP方法 url: 请求URL data: 请求数据 headers: 请求头 timeout: 超时时间 retries: 重试次数(可选,默认使用配置中的重试次数) retry_interval: 重试间隔(秒,默认1秒) **kwargs: 其他参数 Returns: Dict[str, Any]: 响应数据 """ if headers is None: headers = {} # 重试逻辑 for attempt in range(retries + 1): try: if method.upper() == 'GET': response = self._session.get( url, headers=headers, timeout=timeout, **kwargs ) elif method.upper() == 'POST': response = self._session.post( url, json=data, headers=headers, timeout=timeout, **kwargs ) else: raise ValueError(f"不支持的HTTP方法: {method}") # 检查响应状态 response.raise_for_status() # 解析JSON响应 return response.json() except exception as e: # 如果是最后一次尝试,直接抛出异常 if attempt == retries: print(f"请求失败(第{attempt + 1}次尝试): {e}") raise # 打印重试信息 print(f"请求失败(第{attempt + 1}次尝试),{retry_interval}秒后重试: {e}") # 等待重试间隔 time.sleep(retry_interval) def get(self, url: str, headers: Dict[str, str] = None, timeout: int = None, retries: int = 0, retry_interval: float = 1.0, **kwargs) -> Dict[str, Any]: """ GET请求 Args: url: 请求URL headers: 请求头 timeout: 超时时间 **kwargs: 其他参数 Returns: Dict[str, Any]: 响应数据 """ return self.request('GET', url, headers=headers, timeout=timeout, retries=retries, retry_interval=retry_interval, **kwargs) def post(self, url: str, data: Dict[str, Any] = None, headers: Dict[str, str] = None, timeout: int = None, retries: int = 0, retry_interval: float = 1.0, **kwargs) -> Dict[str, Any]: """ POST请求 Args: url: 请求URL data: 请求数据 headers: 请求头 timeout: 超时时间 retries: 重试次数(可选,默认使用配置中的重试次数) retry_interval: 重试间隔(秒,默认1秒) **kwargs: 其他参数 Returns: Dict[str, Any]: 响应数据 """ return self.request('POST', url, data=data, headers=headers, timeout=timeout, retries=retries, retry_interval=retry_interval, **kwargs) class ApiHttpClient(BaseHttpClient): """API客户端 - 业务API调用,整合认证和单例功能""" def __init__(self): """初始化API客户端""" """初始化API客户端""" super().__init__() # 认证缓存 self._auth_cache = { 'app_id': None, 'expire_time': None, 'sign_token': None, 'zr_jwt': None } self._cache_lock = threading.RLock() @property def settings(self): """获取配置对象,由子类实现""" if self._settings is None: self._settings = ini_manager return self._settings def login(self) -> bool: """ 用户登录获取AppID Args: url: 登录URL(可选,默认使用配置中的URL) login_model: 登录请求模型(可选,默认使用配置中的模型) Returns: bool: 登录是否成功 """ url = self.settings.api_login_url login_model = self.settings.api_login_model print("开始登录...") try: # 发送登录请求 response_data = self.request( method='POST', url=url, data=login_model, timeout=self.settings.api_timeout ) # 解析登录响应 login_response = LoginResponse(**response_data) if login_response.Code != 200: error_msg = login_response.Message or "登录失败" print(f"获取AppID失败: {error_msg}") return False # 更新认证缓存 with self._cache_lock: self._auth_cache.update({ 'app_id': login_response.app_id, 'expire_time': login_response.expire_time, 'sign_token': login_response.sign_token, 'zr_jwt': login_response.zr_jwt }) print(f"成功获取AppID: {self._auth_cache['app_id']}") print(f"过期时间: {self._auth_cache['expire_time']}") return True except Exception as e: print(f"登录过程中出现异常: {e}") self._clear_auth_cache() return False def is_app_id_valid(self) -> bool: """检查AppID是否有效""" with self._cache_lock: expire_time = self._auth_cache.get('expire_time') if not expire_time: return False # 检查是否过期(提前12小时过期,避免临界情况) try: expire_timestamp = time.mktime(time.strptime(expire_time, '%Y-%m-%d %H:%M:%S')) is_valid = time.time() < expire_timestamp - self.settings.api_auth_timeout if not is_valid: print("认证信息已过期") return is_valid except (ValueError, TypeError): print("日期格式不正确") return False def _get_auth_headers(self) -> Dict[str, str]: """获取认证头信息""" with self._cache_lock: app_id = self._auth_cache.get('app_id') headers = { 'AppID': app_id, 'Content-Type': 'application/json' } return headers def get(self, url: str, auth: bool = True, **kwargs) -> Dict[str, Any]: """ GET请求(支持认证检查) Args: url: 请求URL auth: 是否需要认证 timeout: 超时时间 retries: 重试次数 retry_interval: 重试间隔 **kwargs: 其他参数 Returns: Dict[str, Any]: 响应数据 """ if auth: if not self.is_app_id_valid(): self.login() if not self.is_app_id_valid(): raise Exception("登录失败,无法获取有效AppID") auth_headers = self._get_auth_headers() return self.request(method='GET', url=url, headers=auth_headers, timeout=self.settings.api_timeout, retries=self.settings.api_max_retries, retry_interval=self.settings.api_retry_interval, **kwargs) else: return self.request(method='GET', url=url, timeout=self.settings.api_timeout, retries=self.settings.api_max_retries, retry_interval=self.settings.api_retry_interval, **kwargs) def post(self, url: str, data: Dict[str, Any] = None, auth: bool = True,**kwargs) -> Dict[str, Any]: """ POST请求(支持认证检查) Args: url: 请求URL data: 请求数据 auth: 是否需要认证 timeout: 超时时间 retries: 重试次数 retry_interval: 重试间隔 **kwargs: 其他参数 Returns: Dict[str, Any]: 响应数据 """ if auth: if not self.is_app_id_valid(): self.login() if not self.is_app_id_valid(): raise Exception("登录失败,无法获取有效AppID") auth_headers = self._get_auth_headers() return self.request(method='POST', url=url, data=data, headers=auth_headers, timeout=self.settings.api_timeout, retries=self.settings.api_max_retries, retry_interval=self.settings.api_retry_interval, **kwargs) else: return self.request(method='POST', url=url, data=data, timeout=self.settings.api_timeout, retries=self.settings.api_max_retries, retry_interval=self.settings.api_retry_interval, **kwargs) def _clear_auth_cache(self): """清除认证缓存""" with self._cache_lock: self._auth_cache.clear() # 重新初始化必要的键 self._auth_cache.update({ 'app_id': None, 'expire_time': None, 'sign_token': None, 'zr_jwt': None }) print("认证缓存已清除") api_http_client = ApiHttpClient()