# -*- coding: utf-8 -*- """重新提交上次执行中失败的 31 个任务。 先用 refresh_token 刷新 access_token,再提交执行并轮询等待完成。 """ from __future__ import annotations import json import sys import time from pathlib import Path import requests TOKEN_FILE = Path(__file__).parent / ".monitor_token" BASE = "http://localhost:8000" # refresh_token(7 天有效) REFRESH_TOKEN = ( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." "eyJzdWIiOiIxIiwic2l0ZV9pZCI6Mjc5MDY4NTQxNTQ0MzI2OSwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3NzIyNjM0NjN9." "XYoda5lfxNtTSAGWoLlYhS9cA-hTK9iqK0SqUyn2KV4" ) def refresh_access_token() -> str: """用 refresh_token 换取新的 access_token。""" resp = requests.post( f"{BASE}/api/auth/refresh", json={"refresh_token": REFRESH_TOKEN}, timeout=10, ) if resp.status_code != 200: print(f"❌ 刷新 token 失败: {resp.status_code} {resp.text}") sys.exit(1) data = resp.json() token = data["access_token"] # 缓存到文件 TOKEN_FILE.write_text(token, encoding="utf-8") print(f"✅ access_token 已刷新并缓存") return token # ── 刷新 token ── TOKEN = refresh_access_token() HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"} # 上次失败的 31 个任务 FAILED_TASKS = [ "DWS_ASSISTANT_DAILY", "DWS_ASSISTANT_MONTHLY", "DWS_ASSISTANT_CUSTOMER", "DWS_ASSISTANT_SALARY", "DWS_ASSISTANT_FINANCE", "ODS_SETTLEMENT_RECORDS", "ODS_PAYMENT", "ODS_REFUND", "DWS_BUILD_ORDER_SUMMARY", "DWS_MEMBER_CONSUMPTION", "DWS_MEMBER_VISIT", "ODS_GOODS_CATEGORY", "ODS_STORE_GOODS", "ODS_STORE_GOODS_SALES", "ODS_TENANT_GOODS", "ODS_PLATFORM_COUPON", "ODS_GROUP_PACKAGE", "ODS_GROUP_BUY_REDEMPTION", "ODS_INVENTORY_STOCK", "ODS_INVENTORY_CHANGE", "DWS_GOODS_STOCK_DAILY", "DWS_GOODS_STOCK_WEEKLY", "DWS_GOODS_STOCK_MONTHLY", "DWS_FINANCE_DAILY", "DWS_FINANCE_RECHARGE", "DWS_FINANCE_INCOME_STRUCTURE", "DWS_FINANCE_DISCOUNT_DETAIL", "DWS_WINBACK_INDEX", "DWS_NEWCONV_INDEX", "DWS_RELATION_INDEX", "DWD_LOAD_FROM_ODS", ] config = { "tasks": FAILED_TASKS, "flow": "api_full", "processing_mode": "full_window", "window_mode": "custom", "window_start": "2025-11-01", "window_end": "2026-02-20", "window_split": "month", "window_split_days": 30, "force_full": True, "dry_run": False, "lookback_hours": 24, "overlap_seconds": 600, } print(f"📤 提交 {len(FAILED_TASKS)} 个失败任务重新执行...") print(f" flow=api_full, mode=full_window, 2025-11-01~2026-02-20, 30天切分, force-full") resp = requests.post(f"{BASE}/api/execution/run", headers=HEADERS, json=config, timeout=30) if resp.status_code != 200: print(f"❌ 提交失败: {resp.status_code} {resp.text}") sys.exit(1) data = resp.json() execution_id = data["execution_id"] print(f"✅ 已提交,execution_id={execution_id}") print(f" 轮询等待完成...") poll_interval = 20 max_wait = 1800 elapsed = 0 while elapsed < max_wait: time.sleep(poll_interval) elapsed += poll_interval mm, ss = divmod(elapsed, 60) try: hist_resp = requests.get( f"{BASE}/api/execution/history?limit=5", headers=HEADERS, timeout=15, ) if hist_resp.status_code == 401: print(f" [{mm}m{ss}s] token 过期,刷新中...") TOKEN = refresh_access_token() HEADERS["Authorization"] = f"Bearer {TOKEN}" continue if hist_resp.status_code != 200: print(f" [{mm}m{ss}s] 查询历史失败: {hist_resp.status_code}") continue history = hist_resp.json() target = next((h for h in history if h["id"] == execution_id), None) if target is None: print(f" [{mm}m{ss}s] 执行记录尚未出现...") continue status = target.get("status", "unknown") duration_ms = target.get("duration_ms") duration_str = f"{duration_ms / 1000:.1f}s" if duration_ms else "—" if status in ("success", "failed", "cancelled"): print(f"\n🏁 执行完成: status={status}, 耗时={duration_str}, exit_code={target.get('exit_code')}") # 获取日志摘要 log_resp = requests.get( f"{BASE}/api/execution/{execution_id}/logs", headers=HEADERS, timeout=30, ) if log_resp.status_code == 200: log_data = log_resp.json() output = log_data.get("output_log", "") or "" lines = output.strip().split("\n") print(f"\n--- 日志末尾 60 行 ---") for line in lines[-60:]: print(line) break else: print(f" [{mm}m{ss}s] status={status}") except Exception as e: print(f" [{mm}m{ss}s] 轮询异常: {e}") else: print(f"\n⏰ 超时({max_wait}s),请手动检查 execution_id={execution_id}")