From 847ed4539a61cccc2fa4bc8f942f94442647daa9 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 2 Oct 2026 21:51:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E8=BF=90=E8=A1=8C=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E7=9A=84=E5=81=87=E8=80=97=E6=97=B6=E3=80=81=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E3=80=8C=E4=B8=8B=E6=AC=A1=E8=BF=90=E8=A1=8C=E3=80=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=88=90=E8=BF=87=E5=8E=BB=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两处显示问题(来自线上页面核对): 1. 耗时列:被重启打断的轮次,finished_at 是 cleanup_orphan_runs 在重启后补上的, 于是「耗时」把停机时间也算进去了(线上出现过 235s / 687s 这种假数字)。 改为只有真正扫描过数据的轮次才计算耗时,其余显示 '-' —— 一轮就报错的 (比如历史上的 SQL 语法错误)同理。 2. 下次运行:两个作业串行跑在同一个调度线程里,前一个耗时长时后一个会排队, next_run_at 因此会停在「已经过了」的时刻;直接显示一个过去时间看起来像故障。 改为:未启用 → 「未启用」;已过期 → 「排队中(<时间> 到期,等待前序作业让出 线程)」,未到期照常显示。 --- web/frontend/src/pages/AutomationPage.tsx | 33 +++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/web/frontend/src/pages/AutomationPage.tsx b/web/frontend/src/pages/AutomationPage.tsx index 4024b43..7224cb3 100644 --- a/web/frontend/src/pages/AutomationPage.tsx +++ b/web/frontend/src/pages/AutomationPage.tsx @@ -1,3 +1,4 @@ +import type { ReactNode } from "react"; import { useCallback, useEffect, useState } from "react"; import { Alert, @@ -34,6 +35,28 @@ const STATUS_TAG: Record = { skipped: { color: "default", text: "已跳过" }, }; +/** + * 下次运行时间:两个作业串行跑在同一个调度线程里,前一个作业耗时长时, + * 后一个会排队等待 —— 于是 next_run_at 会停在「已经过了」的时刻。 + * 直接显示一个过去时间看起来像故障,这里标明「排队中」。 + */ +function nextRunLabel(job: AutomationJobItem): ReactNode { + if (!job.next_run_at) { + return 未启用; + } + const next = new Date(job.next_run_at).getTime(); + if (!Number.isFinite(next)) return formatTime(job.next_run_at); + if (next > Date.now()) return formatTime(job.next_run_at); + return ( + + 排队中{" "} + + ({formatTime(job.next_run_at)} 到期,等待前序作业让出线程) + + + ); +} + function secondsToHours(seconds: number): number { return Math.round((seconds / 3600) * 10) / 10; } @@ -191,7 +214,13 @@ export default function AutomationPage() { { title: "耗时", width: 90, - render: (_, row) => formatDuration(row.started_at, row.finished_at), + render: (_, row) => + // 没实际扫描过的轮次没有真实耗时:被中断的记录 finished_at 是重启后 + // 补上的(见 cleanup_orphan_runs),差值里混入了停机时间,是个假数字; + // 一轮就报错的(如 SQL 语法错误)同理。 + row.scanned > 0 && row.finished_at + ? formatDuration(row.started_at, row.finished_at) + : "-", }, { title: "说明", dataIndex: "message", ellipsis: true }, ]; @@ -256,7 +285,7 @@ export default function AutomationPage() { {formatTime(job.last_run_at)} - {formatTime(job.next_run_at)} + {nextRunLabel(job)} {job.last_status ? (