# -*- coding: utf-8 -*- import requests import time import json # 配置部分 COOKIES = { 'auth_token': 's%3A.9uztgExtmqUJXHCi00hv9SGq6eVYSvH%2BxQSwrox1Yls', 'fx-lang': 'zh_cn', 'tenantId': 'agndqbuttb7ipfciraxcokgqyu', 'AGL_USER_ID': 'a50da526-dd43-4a78-ace1-ba810a6f2168', '_ga': 'GA1.1.626243428.1772260541', '_clck': 'y7ldwu%5E2%5Eg3y%5E0%5E2250', 'sensorsdata2015jssdkcross': '%7B%22distinct_id%22%3A%2257956c24ceedab0c48c17b4e%22%2C%22first_id%22%3A%2219b6dd1a10c148a-063e3a033b10ed-4c657b58-2073600-19b6dd1a10d145b%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E8%87%AA%E7%84%B6%E6%90%9C%E7%B4%A2%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC%22%2C%22%24latest_referrer%22%3A%22https%3A%2F%2Fcn.bing.com%2F%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTliNmRkMWExMGMxNDhhLTA2M2UzYTAzM2IxMGVkLTRjNjU3YjU4LTIwNzM2MDAtMTliNmRkMWExMGQxNDViIiwiJGlkZW50aXR5X2xvZ2luX2lkIjoiNTc5NTZjMjRjZWVkYWIwYzQ4YzE3YjRlIn0%3D%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2257956c24ceedab0c48c17b4e%22%7D%7D', '_ga_JTDW9M3LHZ': 'GS2.1.s1772266909$o3$g0$t1772266909$j60$l0$h0', '_csrf': 's%3A_1U5JMsnCD3RTwmCER9JqFb4.mo773pACR2hpC5wQ5xRGTuHdG4IFBizJgZutwkTfRhc', 'Hm_lvt_de47dd1629940fe88b02865de93dd9fe': '1771984077,1772162048,1772241582,1772414152', 'Hm_lpvt_de47dd1629940fe88b02865de93dd9fe': '1772414152', 'HMACCOUNT': 'A6A0585E8C70051D', 'GSuvNKHqfvX2r6v7P8HkZv2bow': 's%3ANSFxWNnsV8OLC9fbqrhhSe7MujIfdZRJ.tMYAhg8UajUy6BrCqfYcElJt5PZXAPx5IFCMAzFhC0g', 'JDY_SID': 's%3AUdb7kb2OqRccqGoceHeVLZk8x0WSlLt7.ZWuCEChyCcI5HYSTmXSbFVsmMaAx9Lplm3%2F%2FJ4B%2Biuo', 'acw_tc': '743e76f717725247327573805ec920bcf336c7da350308d821feb726945021', } HEADERS = { 'accept': 'application/json, text/plain, */*', 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'content-type': 'application/json', 'origin': 'https://www.jiandaoyun.com', 'priority': 'u=1, i', 'referer': 'https://www.jiandaoyun.com/dashboard/app/675b900991ad2491c69389ca/settings', 'sec-ch-ua': '"Not:A-Brand";v="99", "Microsoft Edge";v="145", "Chromium";v="145"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0', 'x-csrf-token': 'bNv86JCo-fWdbOodYigjN6C8IUfIrvt211k4', 'x-jdy-ver': '10.17.4', 'x-request-id': 'ed87c6c8-7a8c-4444-b487-5d1b37c653e7', # 'cookie': 'auth_token=s%3A.9uztgExtmqUJXHCi00hv9SGq6eVYSvH%2BxQSwrox1Yls; fx-lang=zh_cn; tenantId=agndqbuttb7ipfciraxcokgqyu; AGL_USER_ID=a50da526-dd43-4a78-ace1-ba810a6f2168; _ga=GA1.1.626243428.1772260541; _clck=y7ldwu%5E2%5Eg3y%5E0%5E2250; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2257956c24ceedab0c48c17b4e%22%2C%22first_id%22%3A%2219b6dd1a10c148a-063e3a033b10ed-4c657b58-2073600-19b6dd1a10d145b%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E8%87%AA%E7%84%B6%E6%90%9C%E7%B4%A2%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC%22%2C%22%24latest_referrer%22%3A%22https%3A%2F%2Fcn.bing.com%2F%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTliNmRkMWExMGMxNDhhLTA2M2UzYTAzM2IxMGVkLTRjNjU3YjU4LTIwNzM2MDAtMTliNmRkMWExMGQxNDViIiwiJGlkZW50aXR5X2xvZ2luX2lkIjoiNTc5NTZjMjRjZWVkYWIwYzQ4YzE3YjRlIn0%3D%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2257956c24ceedab0c48c17b4e%22%7D%7D; _ga_JTDW9M3LHZ=GS2.1.s1772266909$o3$g0$t1772266909$j60$l0$h0; _csrf=s%3A_1U5JMsnCD3RTwmCER9JqFb4.mo773pACR2hpC5wQ5xRGTuHdG4IFBizJgZutwkTfRhc; Hm_lvt_de47dd1629940fe88b02865de93dd9fe=1771984077,1772162048,1772241582,1772414152; Hm_lpvt_de47dd1629940fe88b02865de93dd9fe=1772414152; HMACCOUNT=A6A0585E8C70051D; GSuvNKHqfvX2r6v7P8HkZv2bow=s%3ANSFxWNnsV8OLC9fbqrhhSe7MujIfdZRJ.tMYAhg8UajUy6BrCqfYcElJt5PZXAPx5IFCMAzFhC0g; JDY_SID=s%3AUdb7kb2OqRccqGoceHeVLZk8x0WSlLt7.ZWuCEChyCcI5HYSTmXSbFVsmMaAx9Lplm3%2F%2FJ4B%2Biuo; acw_tc=743e76f717725247327573805ec920bcf336c7da350308d821feb726945021', } BASE_URL = "https://dingtalk.jiandaoyun.com" APP_ID = '675b900991ad2491c69389ca' PROCESS_ID = "694b9e1a5ef7b2983fe1f922" # 智能助手流程id NODE_ID = 3 # 根据原代码固定为4# 出現問題的結點 def check_response(resp_or_data, step_name): """通用响应校验函数(兼容 Response / 已解析的 JSON 数据)""" if hasattr(resp_or_data, "status_code") and hasattr(resp_or_data, "json"): response = resp_or_data if response.status_code != 200: print(f"[❌ {step_name}] 请求失败,状态码: {response.status_code}") print(f" 响应内容: {response.text[:200]}...") return None try: data = response.json() except ValueError: print(f"[❌ {step_name}] 响应不是有效的 JSON 格式") return None else: # 允许直接传入 response.json() 的结果(dict/list/...) data = resp_or_data # 检查常见错误结构:尽量只在“明确失败”时返回 None,避免误伤正常返回 if isinstance(data, dict): if data.get("error"): msg = data.get("message") or data.get("msg") or data.get("error") or "未知错误" print(f"[❌ {step_name}] 业务逻辑错误: {msg}") return None if data.get("success") is False: msg = data.get("message") or data.get("msg") or "success=false" print(f"[❌ {step_name}] 业务逻辑错误: {msg}") return None if "code" in data: code = data.get("code") # 常见成功码:0/200;其他情况仅当明确不是成功码才判失败 if code not in (None, 0, 200, "0", "200", True): msg = data.get("message") or data.get("msg") or data.get("error") or f"code={code}" print(f"[❌ {step_name}] 业务逻辑错误: {msg}") return None return data def main(): print(f"[*] 开始执行流程,目标应用: {APP_ID}, 流程: {PROCESS_ID}") # 1. 轮询查询日志列表并依次处理 limit = 20 # 每次最多处理多少条 success_count = 0 fail_count = 0 total_instances = 0 while True: log_payload = { 'appId': APP_ID, 'process_id': PROCESS_ID, 'skip': 0, # 每次都从第 0 条开始查最新的失败记录 'limit': limit, 'filter': [], 'status': 3, # 假设 3 代表某种特定状态(如失败/待处理) } print(f"[*] 正在获取流程日志列表,limit={limit} ...") resp_logs = requests.post( f'{BASE_URL}/automation/apis/process/logs', cookies=COOKIES, headers=HEADERS, json=log_payload, ) logs_data = check_response(resp_logs, "获取日志列表") if not logs_data: break print(logs_data) # 兼容不同返回字段命名:有的环境是 "logs",有的是 "log" instance_list = logs_data.get("log") or logs_data.get("logs") or [] if not instance_list: print("[⚠️] 未找到符合条件的流程实例,任务结束。") break batch_count = len(instance_list) total_instances += batch_count print(f"[✅] 本批获取 {batch_count} 条实例记录,开始逐个处理...") for idx, item in enumerate(instance_list, start=1): instance_id = item.get("instance_id") if not instance_id: print(f"[⚠️] 第 {idx} 条记录缺少 instance_id,跳过。") continue print(f"\n--- 处理实例 {idx}: {instance_id} ---") # 2. 获取节点日志 (获取 execution_id 和 service_id) node_log_payload = { 'appId': APP_ID, 'process_id': PROCESS_ID, 'instance_id': instance_id, 'node_id': NODE_ID, } resp_node = requests.post( f'{BASE_URL}/automation/apis/process/node_logs', cookies=COOKIES, headers=HEADERS, json=node_log_payload, ) node_data = check_response(resp_node, "获取节点日志") if not node_data: print(f"[❌] 实例 {instance_id} 获取节点日志失败,跳过重试。") fail_count += 1 continue execution_id = node_data.get('execution_id') service_id = node_data.get('service_id') if not execution_id or not service_id: print(f"[❌] 实例 {instance_id} 返回数据中缺少 execution_id 或 service_id。") print(f" 返回数据: {json.dumps(node_data)}") fail_count += 1 continue print(f" [ℹ️] 获取到执行ID: {execution_id}, 服务ID: {service_id}") # 3. 执行重试 (Retry) retry_payload = { 'appId': APP_ID, 'process_id': PROCESS_ID, 'instance_id': instance_id, 'execution_id': execution_id, 'service_id': service_id, } resp_retry = requests.post( f'{BASE_URL}/automation/apis/process/retry_node', cookies=COOKIES, headers=HEADERS, json=retry_payload, ) retry_data = check_response(resp_retry, "执行重试") if retry_data: print(f"[✅] 实例 {instance_id} 重试请求发送成功。") success_count += 1 else: print(f"[❌] 实例 {instance_id} 重试请求失败。") fail_count += 1 # 可选:添加短暂延时,避免触发频率限制 time.sleep(0.5) # 不使用 skip 分页,而是重新查询最新的失败记录,直到接口不再返回失败实例为止 print("\n" + "=" * 30) print(f"[🏁] 任务完成。总计实例数: {total_instances}, 成功重试: {success_count}, 失败: {fail_count}") if __name__ == "__main__": try: main() except Exception as e: print(f"[💥] 程序发生未捕获的异常: {e}") import traceback traceback.print_exc()