@@ -171,145 +171,6 @@ class SystemJobDBOperator(BaseDBOperator):
|
||||
row["detail_json"] = {}
|
||||
return rows
|
||||
|
||||
@staticmethod
|
||||
def _clean_job_keys(job_keys: List[str]) -> List[str]:
|
||||
"""清洗批量查询用的任务 key 列表。
|
||||
|
||||
设计说明:
|
||||
1. 后台列表页会一次性请求多个任务的历史摘要,必须先去掉空值和重复值;
|
||||
2. 统一在 DB Operator 层做清洗,避免上层每个调用方都重复写一遍;
|
||||
3. 保持输入顺序,便于后续排查时能和原始列表一一对应。
|
||||
"""
|
||||
clean_keys: List[str] = []
|
||||
seen = set()
|
||||
for item in job_keys or []:
|
||||
key = str(item or "").strip()
|
||||
if not key or key in seen:
|
||||
continue
|
||||
clean_keys.append(key)
|
||||
seen.add(key)
|
||||
return clean_keys
|
||||
|
||||
def get_latest_logs_map(self, job_keys: List[str]) -> Dict[str, Dict[str, Any]]:
|
||||
"""批量读取每个任务最新一条执行日志。"""
|
||||
clean_keys = self._clean_job_keys(job_keys)
|
||||
if not clean_keys:
|
||||
return {}
|
||||
|
||||
placeholders = ",".join(["%s"] * len(clean_keys))
|
||||
sql = f"""
|
||||
SELECT l.*
|
||||
FROM t_system_job_logs l
|
||||
INNER JOIN (
|
||||
SELECT job_key, MAX(id) AS max_id
|
||||
FROM t_system_job_logs
|
||||
WHERE job_key IN ({placeholders})
|
||||
GROUP BY job_key
|
||||
) t ON l.id = t.max_id
|
||||
"""
|
||||
rows = self.execute_query(sql, tuple(clean_keys)) or []
|
||||
result: Dict[str, Dict[str, Any]] = {}
|
||||
for row in rows:
|
||||
detail = row.get("detail_json")
|
||||
if isinstance(detail, str):
|
||||
try:
|
||||
row["detail_json"] = json.loads(detail)
|
||||
except json.JSONDecodeError:
|
||||
row["detail_json"] = {}
|
||||
elif detail is None:
|
||||
row["detail_json"] = {}
|
||||
|
||||
job_key = str(row.get("job_key") or "").strip()
|
||||
if job_key:
|
||||
result[job_key] = row
|
||||
return result
|
||||
|
||||
def get_job_history_summary_map(self, job_keys: List[str]) -> Dict[str, Dict[str, Any]]:
|
||||
"""批量汇总系统任务的执行历史摘要。
|
||||
|
||||
返回字段覆盖后台最常用的问题定位视角:
|
||||
1. 最近成功时间,便于判断任务是否长期没有跑通;
|
||||
2. 最近失败时间与失败摘要,便于列表页直接看到异常原因;
|
||||
3. 累计成功/失败/总执行次数,便于粗看任务稳定性。
|
||||
"""
|
||||
clean_keys = self._clean_job_keys(job_keys)
|
||||
if not clean_keys:
|
||||
return {}
|
||||
|
||||
placeholders = ",".join(["%s"] * len(clean_keys))
|
||||
summary_sql = f"""
|
||||
SELECT
|
||||
job_key,
|
||||
MAX(CASE WHEN status = 'success' THEN triggered_at ELSE NULL END) AS latest_success_at,
|
||||
MAX(CASE WHEN status = 'failed' THEN triggered_at ELSE NULL END) AS latest_failed_at,
|
||||
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS success_count,
|
||||
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS fail_count,
|
||||
COUNT(*) AS total_count
|
||||
FROM t_system_job_logs
|
||||
WHERE job_key IN ({placeholders})
|
||||
GROUP BY job_key
|
||||
"""
|
||||
latest_failed_sql = f"""
|
||||
SELECT l.*
|
||||
FROM t_system_job_logs l
|
||||
INNER JOIN (
|
||||
SELECT job_key, MAX(id) AS max_id
|
||||
FROM t_system_job_logs
|
||||
WHERE status = 'failed' AND job_key IN ({placeholders})
|
||||
GROUP BY job_key
|
||||
) t ON l.id = t.max_id
|
||||
"""
|
||||
|
||||
summary_rows = self.execute_query(summary_sql, tuple(clean_keys)) or []
|
||||
latest_failed_rows = self.execute_query(latest_failed_sql, tuple(clean_keys)) or []
|
||||
|
||||
result: Dict[str, Dict[str, Any]] = {}
|
||||
for row in summary_rows:
|
||||
job_key = str(row.get("job_key") or "").strip()
|
||||
if not job_key:
|
||||
continue
|
||||
result[job_key] = {
|
||||
"latest_success_at": row.get("latest_success_at"),
|
||||
"latest_failed_at": row.get("latest_failed_at"),
|
||||
"latest_failure_summary": "",
|
||||
"latest_failure_detail": {},
|
||||
"history_success_count": int(row.get("success_count") or 0),
|
||||
"history_fail_count": int(row.get("fail_count") or 0),
|
||||
"history_total_count": int(row.get("total_count") or 0),
|
||||
}
|
||||
|
||||
for row in latest_failed_rows:
|
||||
job_key = str(row.get("job_key") or "").strip()
|
||||
if not job_key:
|
||||
continue
|
||||
|
||||
detail = row.get("detail_json")
|
||||
if isinstance(detail, str):
|
||||
try:
|
||||
detail = json.loads(detail)
|
||||
except json.JSONDecodeError:
|
||||
detail = {}
|
||||
elif detail is None:
|
||||
detail = {}
|
||||
|
||||
history = result.setdefault(
|
||||
job_key,
|
||||
{
|
||||
"latest_success_at": None,
|
||||
"latest_failed_at": row.get("triggered_at"),
|
||||
"latest_failure_summary": "",
|
||||
"latest_failure_detail": {},
|
||||
"history_success_count": 0,
|
||||
"history_fail_count": 0,
|
||||
"history_total_count": 0,
|
||||
},
|
||||
)
|
||||
history["latest_failed_at"] = row.get("triggered_at")
|
||||
history["latest_failure_summary"] = str(row.get("summary") or "").strip()
|
||||
history["latest_failure_detail"] = detail or {}
|
||||
|
||||
return result
|
||||
|
||||
def get_latest_log_time(self, job_key: str) -> Optional[datetime]:
|
||||
"""获取任务最新一次执行日志时间。"""
|
||||
row = self.execute_query(
|
||||
|
||||
Reference in New Issue
Block a user