mirror of
https://github.com/hansjone/netx.git
synced 2026-10-10 12:40:44 +08:00
Unify compare search via live source lookup; stratify success samples.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
dd4195cdbc
commit
b8c0f96982
8 changed files with 546 additions and 40 deletions
|
|
@ -509,14 +509,16 @@ const en = {
|
|||
sheetPending: "Pending",
|
||||
ranWithDuration: "Compare finished ({{s}}s)",
|
||||
unchangedNotStored: "Success rows were counted but not stored. Set “Store success rows” to sample and re-run for spot-check.",
|
||||
unchangedSampleHint: "{{total}} success rows total; browsing a sample of {{listed}} (hydrated from source tables for cutover spot-check). Pass rate uses all {{total}}.",
|
||||
unchangedSampleHint: "{{total}} success rows total; browsing a stratified sample of {{listed}}. Search any route for live source lookup. Pass rate uses all {{total}}.",
|
||||
liveSearchHint: "Search: live verdict from source batches (tabs only filter kind). Works for fail, success, and added.",
|
||||
liveSearchTruncatedHint: "Search results truncated — narrow the query (full prefix / neighbor).",
|
||||
storeUnchanged: "Store success rows",
|
||||
storeUnchangedAuto: "Auto (full if small / sample 5k + hydrate if large)",
|
||||
storeUnchangedSample: "Sample only (up to 5k keys + hydrate)",
|
||||
storeUnchangedKeys: "All success keys (full browse; large write still slow)",
|
||||
storeUnchangedAlways: "Always full JSON (slow on huge sheets)",
|
||||
storeUnchangedNever: "Never (counts only)",
|
||||
storeUnchangedHint: "Cutover focuses on fails and pass rate; success defaults to a sample. Pass rate always uses the full success count.",
|
||||
storeUnchangedHint: "Cutover focuses on fails and pass rate; success defaults to a stratified sample. Type a filter to look up any route from source tables. Pass rate uses the full success count.",
|
||||
saveJob: "Save config",
|
||||
jobSaved: "Job config saved",
|
||||
jobDeleted: "Compare job deleted",
|
||||
|
|
@ -543,7 +545,7 @@ const en = {
|
|||
pickRun: "Select run…",
|
||||
runCount: "{{n}} runs",
|
||||
resultEmpty: "No matching diff rows",
|
||||
resultFilterPh: "Filter key / values…",
|
||||
resultFilterPh: "Search prefix / neighbor / RD… (live source lookup)",
|
||||
kindAll: "All",
|
||||
kindDiff: "Fail",
|
||||
kindAdded: "Added",
|
||||
|
|
@ -575,7 +577,7 @@ const en = {
|
|||
filterFailField: "Failed compare field",
|
||||
filterFailFieldAll: "Any failed field",
|
||||
resultEmpty: "No matching rows",
|
||||
resultFilterPh: "Filter identity / values…",
|
||||
resultFilterPh: "Search prefix / neighbor / RD… (live source lookup)",
|
||||
diffCount: "{{n}} failed",
|
||||
passRate: "Pass rate",
|
||||
passOk: "All passed",
|
||||
|
|
|
|||
|
|
@ -508,14 +508,16 @@ const zh = {
|
|||
sheetPending: "等待中",
|
||||
ranWithDuration: "比对完成(耗时 {{s}} 秒)",
|
||||
unchangedNotStored: "成功行仅统计数量未落库。可在任务配置将「成功行保存」改为抽样后重新比对(抽查用)。",
|
||||
unchangedSampleHint: "成功共 {{total}} 条,明细抽样 {{listed}} 条(从原表补全显示,割接抽查用)。通过率按全部 {{total}} 计。",
|
||||
unchangedSampleHint: "成功共 {{total}} 条,明细抽样 {{listed}} 条(分层抽查)。要查任意路由请输入筛选条件——将按原表即时判定。通过率按全部 {{total}} 计。",
|
||||
liveSearchHint: "搜索:原表即时判定(页签只过滤种类)。失败/成功/新增均可查到。",
|
||||
liveSearchTruncatedHint: "搜索结果已截断,请收窄条件(如完整前缀 / neighbor)。",
|
||||
storeUnchanged: "成功行保存",
|
||||
storeUnchangedAuto: "自动(小表全量 / 大表抽样 5000+原表补全)",
|
||||
storeUnchangedSample: "仅抽样(最多 5000,身份键+原表补全)",
|
||||
storeUnchangedKeys: "全量身份键(可翻完全部成功,大表写入仍较久)",
|
||||
storeUnchangedAlways: "始终全量 JSON(大表很慢,慎用)",
|
||||
storeUnchangedNever: "不保存(只计数量)",
|
||||
storeUnchangedHint: "割接优先看失败与通过率;成功默认抽样抽查。通过率始终按全部成功计数。",
|
||||
storeUnchangedHint: "割接优先看失败与通过率;成功默认分层抽样。输入筛选可回查原表(不依赖抽样)。通过率按全部成功计数。",
|
||||
saveJob: "保存配置",
|
||||
jobSaved: "任务配置已保存",
|
||||
jobDeleted: "比对任务已删除",
|
||||
|
|
@ -542,7 +544,7 @@ const zh = {
|
|||
pickRun: "选择比对记录…",
|
||||
runCount: "{{n}} 次",
|
||||
resultEmpty: "无匹配失败/结果行",
|
||||
resultFilterPh: "筛选身份 / 字段值…",
|
||||
resultFilterPh: "搜索前缀 / neighbor / RD…(回查原表)",
|
||||
kindAll: "全部",
|
||||
kindDiff: "失败",
|
||||
kindAdded: "新增",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import { jobChipColor, NmStatusChip } from "./nmChips";
|
|||
type PageTab = "templates" | "jobs";
|
||||
type JobDetailTab = "config" | "runs" | "result";
|
||||
type KindFilter = "diff" | "all" | "added" | "removed" | "changed" | "unchanged";
|
||||
type DiffListSource = "stored" | "live" | "";
|
||||
type CreateJobStep = 0 | 1 | 2 | 3;
|
||||
const CREATE_JOB_STEPS = 4;
|
||||
|
||||
|
|
@ -847,6 +848,8 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
const [resultTotal, setResultTotal] = useState(0);
|
||||
const [pagedDiffs, setPagedDiffs] = useState<DiffRow[]>([]);
|
||||
const [diffsLoading, setDiffsLoading] = useState(false);
|
||||
const [diffsSource, setDiffsSource] = useState<DiffListSource>("");
|
||||
const [diffsTruncated, setDiffsTruncated] = useState(false);
|
||||
const boardRef = useRef<HTMLDivElement | null>(null);
|
||||
const tableScrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const tableScrollPosRef = useRef({ top: 0, left: 0 });
|
||||
|
|
@ -1069,6 +1072,8 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
if (!runId || !mid || jobDetailTab !== "result" || sheetStillRunning) {
|
||||
setPagedDiffs([]);
|
||||
setResultTotal(0);
|
||||
setDiffsSource("");
|
||||
setDiffsTruncated(false);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
|
|
@ -1086,6 +1091,10 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
if (cancelled) return;
|
||||
setPagedDiffs((res.items || []) as DiffRow[]);
|
||||
setResultTotal(Number(res.total || 0));
|
||||
setDiffsSource(
|
||||
String((res as any).source || "").toLowerCase() === "live" ? "live" : "stored",
|
||||
);
|
||||
setDiffsTruncated(Boolean((res as any).truncated));
|
||||
const pages = Math.max(
|
||||
1,
|
||||
Math.ceil(Number(res.total || 0) / Number(res.page_size || resultPageSize)),
|
||||
|
|
@ -1257,8 +1266,15 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
String(activeSheetCard?.status || ""),
|
||||
);
|
||||
const activeAdded = Number(activeSheetCard?.added || 0);
|
||||
const activeRemoved = Number(activeSheetCard?.removed || 0);
|
||||
const activeChanged = Number(activeSheetCard?.changed || 0);
|
||||
const showFailCol =
|
||||
kindFilter === "diff" || kindFilter === "all" || kindFilter === "added";
|
||||
kindFilter === "diff" ||
|
||||
kindFilter === "all" ||
|
||||
kindFilter === "added" ||
|
||||
kindFilter === "removed" ||
|
||||
kindFilter === "changed";
|
||||
const isLiveSearch = diffsSource === "live" && Boolean(debouncedResultKw.trim());
|
||||
const resultEmptyColSpan =
|
||||
1 +
|
||||
(showFailCol ? 1 : 0) +
|
||||
|
|
@ -3504,6 +3520,8 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
{(
|
||||
[
|
||||
["diff", activeFail, "diff"],
|
||||
["removed", activeRemoved, "removed"],
|
||||
["changed", activeChanged, "changed"],
|
||||
["added", activeAdded, "added"],
|
||||
["unchanged", activeSuccess, "unchanged"],
|
||||
["all", null, "all"],
|
||||
|
|
@ -3523,7 +3541,11 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
? t("bizCompare.kindAll")
|
||||
: id === "added"
|
||||
? t("bizCompare.kindAddedShort")
|
||||
: t("bizCompare.kindSuccess")}
|
||||
: id === "removed"
|
||||
? t("bizCompare.kindRemovedShort")
|
||||
: id === "changed"
|
||||
? t("bizCompare.kindChangedShort")
|
||||
: t("bizCompare.kindSuccess")}
|
||||
{n !== null ? (
|
||||
<>
|
||||
{" "}
|
||||
|
|
@ -3543,11 +3565,31 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
{diffsLoading ? "…" : `${pagedDiffs.length}/${resultTotal}`}
|
||||
</span>
|
||||
</div>
|
||||
{kindFilter === "unchanged" &&
|
||||
{isLiveSearch ? (
|
||||
<p className="muted bm-hint" style={{ margin: "4px 0 0" }}>
|
||||
{diffsTruncated
|
||||
? t("bizCompare.liveSearchTruncatedHint")
|
||||
: t("bizCompare.liveSearchHint")}
|
||||
</p>
|
||||
) : null}
|
||||
{!isLiveSearch &&
|
||||
kindFilter === "unchanged" &&
|
||||
(runDetail?.summary?.unchanged_truncated ||
|
||||
(Number(runDetail?.summary?.unchanged || 0) >
|
||||
Number(runDetail?.summary?.unchanged_listed || 0) &&
|
||||
Number(runDetail?.summary?.unchanged_listed || 0) > 0)) ? (
|
||||
(Number(
|
||||
(activeRunSheet?.summary as any)?.unchanged ||
|
||||
runDetail?.summary?.unchanged ||
|
||||
0,
|
||||
) >
|
||||
Number(
|
||||
(activeRunSheet?.summary as any)?.unchanged_listed ??
|
||||
runDetail?.summary?.unchanged_listed ??
|
||||
0,
|
||||
) &&
|
||||
Number(
|
||||
(activeRunSheet?.summary as any)?.unchanged_listed ??
|
||||
runDetail?.summary?.unchanged_listed ??
|
||||
0,
|
||||
) > 0)) ? (
|
||||
<p className="muted bm-hint" style={{ margin: "4px 0 0" }}>
|
||||
{t("bizCompare.unchangedSampleHint", {
|
||||
listed: String(
|
||||
|
|
|
|||
|
|
@ -2185,6 +2185,10 @@ export const bizCompareListRunDiffs = (params: {
|
|||
page_size: number;
|
||||
metric_id: string;
|
||||
items: Record<string, unknown>[];
|
||||
source?: string;
|
||||
truncated?: boolean;
|
||||
live_before_matched?: number;
|
||||
live_after_matched?: number;
|
||||
}>(`/v1/biz-state/compare/runs/${encodeURIComponent(params.runId)}/diffs?${p.toString()}`);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue