import time import jwt import requests from typing import Any, Dict, Optional # 配置参数 CONFIG = { # 认证配置 'app_id': "xxxxx", # 请替换为实际的 app_id(页面-配置管理-项目信息-APPID) 'access_token': 'xxxxx', # 请替换为实际的 access_token(页面-配置管理-项目信息-APPKEY) 'token_ttl_seconds': 120, # 业务参数 'username': 'xxxx', # 企微ID 'idstr': '1448989451370153984', # # 记录的 ID,比如 url 为:https://perfsight.qq.com/singleUserDetail?id=1360168518203637760&os=pc&projId=3545&env=djI%3D&from=click 可通过 conditionalSearch 接口获取 performance_id 填入 'enable_event_kv': True, # 请求细节 'request_id': '{username}_detailApi', # API URLs - 国内环境 'detail_url': 'https://api.perfsight.qq.com/openapi/single/detail', 'getkv_url': 'https://api.perfsight.qq.com/openapi/single/getKv', # API URLs - 海外环境(新加坡) # 'detail_url': 'https://api.perfsight.wetest.net/openapi/single/detail', # 'getkv_url': 'https://api.perfsight.wetest.net/openapi/single/getKv', # 网络选项 'timeout_seconds': 60, 'post_detail_sleep_seconds': 0.2, # detail 成功后等待再拉 KV } def _generate_jwt_token(app_id: str, access_token: str, ttl_seconds: int) -> str: """生成JWT token""" jwt_headers = { 'alg': 'HS256', 'typ': 'JWT' } exp = int(time.time() + ttl_seconds) payload = { 'exp': exp, 'app_id': app_id } token = jwt.encode(payload=payload, key=access_token, algorithm='HS256', headers=jwt_headers) # pyjwt>=2.x 返回 str,<2.x 返回 bytes,这里统一转 str if isinstance(token, bytes): token = token.decode('utf-8') return token def call_detail(config: Dict[str, Any]) -> Dict[str, Any]: """调用 detail 接口""" token = _generate_jwt_token( app_id=config['app_id'], access_token=config['access_token'], ttl_seconds=config['token_ttl_seconds'], ) params = { 'username': config['username'], 'requestid': config['request_id'], 'token': token, 'Idstr': config['idstr'], } headers = {'Content-Type': 'application/json'} response = requests.post( config['detail_url'], json=params, headers=headers, timeout=config['timeout_seconds'] ) response.raise_for_status() try: result = response.json() print("Detail Response:", result) return result except ValueError: raise RuntimeError('detail 接口返回非 JSON 响应') def call_get_kv(config: Dict[str, Any]) -> Dict[str, Any]: """调用 getKv 接口""" params = { 'idstr': config['idstr'], 'enableEventKv': config['enable_event_kv'], } headers = {'Content-Type': 'application/json'} response = requests.post( config['getkv_url'], json=params, headers=headers, timeout=config['timeout_seconds'] ) response.raise_for_status() try: return response.json() except ValueError: raise RuntimeError('getKv 接口返回非 JSON 响应') def request_get_kv(custom_config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: """ 统一入口:先调用 detail 接口,再调用 getKV 接口(KV 的拼装依赖 detail 接口的解析) 返回 getKV 的 JSON 结果 """ config = {**CONFIG, **(custom_config or {})} try: detail_json = call_detail(config) except Exception as exc: raise RuntimeError(f"调用 detail 失败: {exc}") from exc # 可选的短暂等待,确保后端准备就绪 time.sleep(config['post_detail_sleep_seconds']) try: kv_json = call_get_kv(config) except Exception as exc: raise RuntimeError(f"调用 getKv 失败: {exc}") from exc # 返回 getKV 的结果;必要时也可同时返回 detail_json return kv_json if __name__ == '__main__': try: result = request_get_kv() print("Final Result:", result) except Exception as e: print(f"Error: {e}")