From c15ea00a23842c0714b7d8758ff75820613323c7 Mon Sep 17 00:00:00 2001 From: oliver Date: Tue, 22 Sep 2026 15:58:32 +0800 Subject: [PATCH] Show collect start/end times on biz-state task and batch lists. Helps tune schedule interval from last-run window and batch duration. Co-authored-by: Cursor --- netx_api/biz_state/service.py | 3 ++ web/src/i18n/en.ts | 2 ++ web/src/i18n/zh.ts | 2 ++ web/src/pages/network/BizStatePage.tsx | 45 +++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/netx_api/biz_state/service.py b/netx_api/biz_state/service.py index 224914d..817f43b 100644 --- a/netx_api/biz_state/service.py +++ b/netx_api/biz_state/service.py @@ -428,6 +428,9 @@ def list_tasks(db: Session, *, purpose: str | None = None) -> list[dict[str, Any "interval_sec": t.interval_sec, "collect_running": bool(t.collect_running), "last_error": t.last_error, + "last_collect_started_at": t.last_collect_started_at.isoformat() + "Z" + if t.last_collect_started_at + else None, "last_collect_ended_at": t.last_collect_ended_at.isoformat() + "Z" if t.last_collect_ended_at else None, diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index bbc31aa..83bbe5e 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -295,6 +295,8 @@ const en = { colNe: "NE", colStatus: "Status", colLast: "Last collect", + colStarted: "Started", + colEnded: "Ended", colTime: "Time", colRows: "Rows", colActions: "Actions", diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index c3c5544..5014268 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -295,6 +295,8 @@ const zh = { colNe: "网元", colStatus: "状态", colLast: "最近采集", + colStarted: "开始时间", + colEnded: "结束时间", colTime: "时间", colRows: "行数", colActions: "操作", diff --git a/web/src/pages/network/BizStatePage.tsx b/web/src/pages/network/BizStatePage.tsx index f3be2bb..4cf8709 100644 --- a/web/src/pages/network/BizStatePage.tsx +++ b/web/src/pages/network/BizStatePage.tsx @@ -50,6 +50,7 @@ type TaskRow = { status: string; collect_running: boolean; last_error: string; + last_collect_started_at?: string | null; last_collect_ended_at?: string | null; interval_sec?: number; }; @@ -82,6 +83,7 @@ type BatchRow = { row_count: number; command_count: number; started_at?: string | null; + ended_at?: string | null; is_baseline?: boolean; protected?: boolean; protect_reasons?: string[]; @@ -169,6 +171,21 @@ function fmtTime(v?: string | null) { return formatSystemTime(v) || v; } +function fmtDuration(started?: string | null, ended?: string | null) { + if (!started || !ended) return ""; + const a = Date.parse(started); + const b = Date.parse(ended); + if (!Number.isFinite(a) || !Number.isFinite(b) || b < a) return ""; + const sec = Math.round((b - a) / 1000); + if (sec < 60) return `${sec}s`; + const m = Math.floor(sec / 60); + const s = sec % 60; + if (m < 60) return s ? `${m}m${s}s` : `${m}m`; + const h = Math.floor(m / 60); + const rm = m % 60; + return rm ? `${h}h${rm}m` : `${h}h`; +} + function cellText(v: unknown): string { if (v === null || v === undefined) return ""; if (typeof v === "object") return JSON.stringify(v); @@ -1056,7 +1073,8 @@ export function BizStatePage() { {t("bizState.colInterval")} {t("bizState.scheduleEnabled")} {t("bizState.colStatus")} - {t("bizState.colLast")} + {t("bizState.colStarted")} + {t("bizState.colEnded")} {t("bizState.colActions")} @@ -1119,7 +1137,15 @@ export function BizStatePage() { )} - {fmtTime(row.last_collect_ended_at)} + {fmtTime(row.last_collect_started_at)} + + {fmtTime(row.last_collect_ended_at)} + {fmtDuration(row.last_collect_started_at, row.last_collect_ended_at) ? ( +
+ {fmtDuration(row.last_collect_started_at, row.last_collect_ended_at)} +
+ ) : null} +