""" API客户端 """ import requests from typing import Dict, Any, Optional from src.utils.logger import get_logger from src.config.constants import API_BASE_URL, API_TIMEOUT, SOFTWARE_CODE, ENCRYPT_ENABLED from src.api.encrypt import encryptor logger = get_logger(__name__) class APIClient: """API客户端""" def __init__(self, base_url: str = API_BASE_URL): self.base_url = base_url.rstrip('/') self.session = requests.Session() self.session.timeout = API_TIMEOUT def _request(self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]: """发送请求""" url = f"{self.base_url}{endpoint}" # 准备请求数据 request_data = data or {} request_data['software_code'] = SOFTWARE_CODE # 确保公钥已加载 self.ensure_public_key_loaded() # 加密数据 encrypted_data = encryptor.encrypt(request_data) # 请求头 request_headers = { 'Content-Type': 'application/json', **(headers or {}) } try: response = self.session.request( method=method, url=url, json={'encrypted': encrypted_data}, headers=request_headers ) response.raise_for_status() return response.json() except requests.exceptions.Timeout: logger.error(f"API请求超时: {url}") raise Exception("请求超时,请检查网络连接") except requests.exceptions.ConnectionError: logger.error(f"API连接失败: {url}") raise Exception("无法连接到服务器,请检查网络连接") except requests.exceptions.HTTPError as e: logger.error(f"API HTTP错误: {e.response.status_code} - {url}") try: error_data = e.response.json() error_msg = error_data.get("msg", f"请求失败: {e.response.status_code}") except: error_msg = f"请求失败: {e.response.status_code}" raise Exception(error_msg) except requests.exceptions.RequestException as e: logger.error(f"API请求失败: {e} - {url}") raise Exception(f"请求失败: {str(e)}") def register(self, username: str, password: str, email: str, card_code: str, machine_code: str) -> Dict[str, Any]: """用户注册""" return self._request('POST', '/api/user/register', { 'username': username, 'password': password, 'email': email, 'card_code': card_code, 'machine_code': machine_code }) def login(self, username: str, password: str, machine_code: str) -> Dict[str, Any]: """用户登录""" return self._request('POST', '/api/user/login', { 'username': username, 'password': password, 'machine_code': machine_code }) def unbind(self, username: str, password: str) -> Dict[str, Any]: """机器解绑""" return self._request('POST', '/api/user/unbind', { 'username': username, 'password': password }) def heartbeat(self, user_id: int) -> Dict[str, Any]: """心跳检测""" return self._request('POST', '/api/user/heartbeat', { 'user_id': user_id }) def activate_card(self, user_id: int, card_code: str) -> Dict[str, Any]: """激活卡密(充值)""" return self._request('POST', '/api/user/activate-card', { 'user_id': user_id, 'card_code': card_code }) def get_recharge_records(self, user_id: int) -> Dict[str, Any]: """获取充值记录""" # GET请求需要特殊处理,因为需要传递加密数据 url = f"{self.base_url}/api/user/recharge-records" # 准备请求数据 request_data = { 'user_id': user_id, 'software_code': SOFTWARE_CODE } # 加密数据 encrypted_data = encryptor.encrypt(request_data) try: response = self.session.get( url, params={'encrypted': encrypted_data}, timeout=API_TIMEOUT ) response.raise_for_status() return response.json() except requests.exceptions.Timeout: logger.error(f"API请求超时: {url}") raise Exception("请求超时,请检查网络连接") except requests.exceptions.ConnectionError: logger.error(f"API连接失败: {url}") raise Exception("无法连接到服务器,请检查网络连接") except requests.exceptions.HTTPError as e: logger.error(f"API HTTP错误: {e.response.status_code} - {url}") try: error_data = e.response.json() error_msg = error_data.get("msg", f"请求失败: {e.response.status_code}") except: error_msg = f"请求失败: {e.response.status_code}" raise Exception(error_msg) except requests.exceptions.RequestException as e: logger.error(f"API请求失败: {e} - {url}") raise Exception(f"请求失败: {str(e)}") def get_public_key(self) -> Dict[str, Any]: """获取公钥""" # 心跳接口不需要加密 url = f"{self.base_url}/api/keys/public" response = self.session.get(url, timeout=API_TIMEOUT) response.raise_for_status() return response.json() def refresh_public_key(self) -> bool: """主动刷新公钥""" if not ENCRYPT_ENABLED: return True try: result = self.get_public_key() if result.get("code") == 200: data = result.get("data", {}) public_key = data.get("public_key") if public_key: encryptor.load_public_key(public_key) logger.info("公钥刷新成功") return True raise ValueError("公钥数据为空") raise ValueError(result.get("msg", "获取公钥失败")) except Exception as e: logger.error(f"刷新公钥失败: {e}") raise Exception("获取公钥失败,请稍后重试") def ensure_public_key_loaded(self): """确保公钥已加载(必要时自动刷新)""" if not ENCRYPT_ENABLED: return if encryptor.public_key: return self.refresh_public_key() def get_announcement(self, page: int = 1, page_size: int = 10, keyword: Optional[str] = None) -> Dict[str, Any]: """获取公告(支持分页与检索)""" # 公告接口不需要加密,使用查询参数 url = f"{self.base_url}/api/announcement" params: Dict[str, Any] = { "page": page, "page_size": page_size, } if keyword: params["keyword"] = keyword response = self.session.get(url, params=params, timeout=API_TIMEOUT) response.raise_for_status() return response.json() def get_steam_accounts(self, user_id: int) -> Dict[str, Any]: """获取Steam账号列表""" url = f"{self.base_url}/api/user/steam-accounts" # 准备请求数据 request_data = { 'user_id': user_id, 'software_code': SOFTWARE_CODE } # 加密数据 encrypted_data = encryptor.encrypt(request_data) try: response = self.session.get( url, params={'encrypted': encrypted_data}, timeout=API_TIMEOUT ) response.raise_for_status() return response.json() except requests.exceptions.Timeout: logger.error(f"API请求超时: {url}") raise Exception("请求超时,请检查网络连接") except requests.exceptions.ConnectionError: logger.error(f"API连接失败: {url}") raise Exception("无法连接到服务器,请检查网络连接") except requests.exceptions.HTTPError as e: logger.error(f"API HTTP错误: {e.response.status_code} - {url}") try: error_data = e.response.json() error_msg = error_data.get("msg", f"请求失败: {e.response.status_code}") except: error_msg = f"请求失败: {e.response.status_code}" raise Exception(error_msg) except requests.exceptions.RequestException as e: logger.error(f"API请求失败: {e} - {url}") raise Exception(f"请求失败: {str(e)}") def create_steam_account(self, user_id: int, steam_username: str, shared_secret: str, identity_secret: str, steam_id: Optional[str] = None) -> Dict[str, Any]: """添加Steam账号""" return self._request('POST', '/api/user/steam-accounts', { 'user_id': user_id, 'steam_username': steam_username, 'steam_id': steam_id, 'shared_secret': shared_secret, 'identity_secret': identity_secret }) def update_steam_account(self, user_id: int, account_id: int, steam_username: Optional[str] = None, shared_secret: Optional[str] = None, identity_secret: Optional[str] = None, steam_id: Optional[str] = None, is_active: Optional[int] = None) -> Dict[str, Any]: """更新Steam账号""" data = {'user_id': user_id} if steam_username is not None: data['steam_username'] = steam_username if shared_secret is not None: data['shared_secret'] = shared_secret if identity_secret is not None: data['identity_secret'] = identity_secret if steam_id is not None: data['steam_id'] = steam_id if is_active is not None: data['is_active'] = is_active return self._request('PUT', f'/api/user/steam-accounts/{account_id}', data) def delete_steam_account(self, user_id: int, account_id: int) -> Dict[str, Any]: """删除Steam账号""" # DELETE请求需要特殊处理 url = f"{self.base_url}/api/user/steam-accounts/{account_id}" # 准备请求数据 request_data = { 'user_id': user_id, 'software_code': SOFTWARE_CODE } # 加密数据 encrypted_data = encryptor.encrypt(request_data) try: response = self.session.delete( url, json={'encrypted': encrypted_data}, timeout=API_TIMEOUT ) response.raise_for_status() return response.json() except requests.exceptions.Timeout: logger.error(f"API请求超时: {url}") raise Exception("请求超时,请检查网络连接") except requests.exceptions.ConnectionError: logger.error(f"API连接失败: {url}") raise Exception("无法连接到服务器,请检查网络连接") except requests.exceptions.HTTPError as e: logger.error(f"API HTTP错误: {e.response.status_code} - {url}") try: error_data = e.response.json() error_msg = error_data.get("msg", f"请求失败: {e.response.status_code}") except: error_msg = f"请求失败: {e.response.status_code}" raise Exception(error_msg) except requests.exceptions.RequestException as e: logger.error(f"API请求失败: {e} - {url}") raise Exception(f"请求失败: {str(e)}") # 全局API客户端实例 api_client = APIClient()