From a025245c8f210dcb7b9264d9b49b6cee6d1b5a08 Mon Sep 17 00:00:00 2001 From: oliver Date: Mon, 11 May 2026 18:38:08 +0800 Subject: [PATCH] Show UME sync delete counts in API and UME UI Parse UmeSyncJob.details_json in GET /v1/ume/sync/status for deleted_inventory_ne, deleted_inventory_holders, deleted_stale_current_alarms. Add table columns on UmePage and types. Co-authored-by: Cursor --- netx_api/main.py | 31 +++++++++++++++++++++++++++++++ web/src/pages/UmePage.tsx | 8 +++++++- web/src/types.ts | 5 +++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/netx_api/main.py b/netx_api/main.py index 3297be3..10052b7 100644 --- a/netx_api/main.py +++ b/netx_api/main.py @@ -496,6 +496,33 @@ def ume_sync(payload: dict[str, Any] | None = None, db: Session = Depends(get_db return out +def _ume_sync_job_details_counts(row: UmeSyncJob) -> dict[str, int]: + """Parse ``details_json`` from sync jobs (inventory / alarms) for delete-reconcile stats.""" + out = {"deleted_inventory_ne": 0, "deleted_inventory_holders": 0, "deleted_current_alarms": 0} + raw = str(getattr(row, "details_json", "") or "").strip() + if not raw: + return out + try: + obj = json.loads(raw) + except Exception: + return out + if not isinstance(obj, dict): + return out + try: + out["deleted_inventory_ne"] = max(0, int(obj.get("deleted_inventory_ne") or 0)) + except Exception: + pass + try: + out["deleted_inventory_holders"] = max(0, int(obj.get("deleted_inventory_holders") or 0)) + except Exception: + pass + try: + out["deleted_current_alarms"] = max(0, int(obj.get("deleted_stale_current_alarms") or 0)) + except Exception: + pass + return out + + @app.get("/v1/ume/sync/status") def ume_sync_status( page: int = Query(default=1, ge=1), @@ -513,6 +540,7 @@ def ume_sync_status( items = [] latest_by_domain: dict[str, dict[str, Any]] = {} for r in rows: + del_counts = _ume_sync_job_details_counts(r) item = { "id": int(r.id), "domain": str(r.domain or ""), @@ -521,6 +549,9 @@ def ume_sync_status( "pulled_count": int(r.pulled_count or 0), "inserted_count": int(r.inserted_count or 0), "updated_count": int(r.updated_count or 0), + "deleted_inventory_ne": int(del_counts["deleted_inventory_ne"]), + "deleted_inventory_holders": int(del_counts["deleted_inventory_holders"]), + "deleted_current_alarms": int(del_counts["deleted_current_alarms"]), "error_message": str(r.error_message or ""), "started_at": (_ensure_utc(r.started_at) or datetime.now(timezone.utc)).isoformat(), "ended_at": (_ensure_utc(r.ended_at).isoformat() if r.ended_at else None), diff --git a/web/src/pages/UmePage.tsx b/web/src/pages/UmePage.tsx index 9d0a2e8..32acb48 100644 --- a/web/src/pages/UmePage.tsx +++ b/web/src/pages/UmePage.tsx @@ -241,6 +241,9 @@ export function UmePage() { pulled inserted updated + 删网元 + 删机框 + 删当前告警 started_at ended_at error @@ -255,6 +258,9 @@ export function UmePage() { {x.pulled_count} {x.inserted_count} {x.updated_count} + {Number(x.deleted_inventory_ne ?? 0)} + {Number(x.deleted_inventory_holders ?? 0)} + {Number(x.deleted_current_alarms ?? 0)} {formatSystemTime(x.started_at)} {x.ended_at ? formatSystemTime(x.ended_at) : "-"} {x.error_message || "-"} @@ -262,7 +268,7 @@ export function UmePage() { ))} {!syncStatusQuery.isLoading && (syncStatusQuery.data?.items || []).length === 0 && ( - 暂无同步记录 + 暂无同步记录 )} diff --git a/web/src/types.ts b/web/src/types.ts index edf20d6..29407bf 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -82,6 +82,11 @@ export type UmeSyncJobItem = { pulled_count: number; inserted_count: number; updated_count: number; + /** Reconcile deletes after full inventory snapshot (netx ume_sync_service). */ + deleted_inventory_ne?: number; + deleted_inventory_holders?: number; + /** Reconcile deletes for current alarms after full snapshot. */ + deleted_current_alarms?: number; error_message?: string; started_at: string; ended_at?: string | null;