216 lines
10 KiB
Python
216 lines
10 KiB
Python
# -*- 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()
|