170 lines
5.0 KiB
Python
170 lines
5.0 KiB
Python
# -*- 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}")
|