From 299224acaf4387da1d9ba34d63cbd5e075cd4008 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Wed, 14 Oct 2026 10:10:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=9B=AE=E5=BD=95=E9=97=AD?= =?UTF-8?q?=E7=8E=AF=EF=BC=88=E5=89=8D=E7=AB=AF=EF=BC=89=E5=A4=8D=E6=A0=B8?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=EF=BC=88=E5=AE=A1=E9=98=85=E9=98=9F=E5=88=97?= =?UTF-8?q?=EF=BC=89=EF=BC=9A=E6=AF=8F=E4=B8=80=E9=A1=B9=E7=9A=84=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E6=8C=89=20path=20=E8=AF=B4=E5=88=B0=E5=88=97?= =?UTF-8?q?=EF=BC=88=E3=80=8C=E5=88=97=20ticket=5Fno=20=E7=9A=84=E5=BA=A6?= =?UTF-8?q?=E9=87=8F=E7=B1=BB=E5=9E=8B=E3=80=8D=E3=80=8C=E8=A1=A8=E7=9A=84?= =?UTF-8?q?=E6=9C=89=E6=95=88=E8=AE=B0=E5=BD=95=E6=9D=A1=E4=BB=B6=E3=80=8D?= =?UTF-8?q?=E3=80=8C=E6=8C=87=E5=90=91=20members=20=E7=9A=84=E5=85=B3?= =?UTF-8?q?=E8=81=94=E5=85=B3=E7=B3=BB=E3=80=8D=EF=BC=8C=E5=90=8C=E6=8F=90?= =?UTF-8?q?=E6=A1=88=E5=8D=A1=E7=89=87=EF=BC=89=EF=BC=8C=E5=A4=8D=E9=80=89?= =?UTF-8?q?=E6=A1=86=E7=9A=84=E8=AF=BB=E5=B1=8F=E5=90=8D=E4=B9=9F=E5=86=99?= =?UTF-8?q?=E5=88=B0=E5=88=97=E2=80=94=E2=80=94=E6=9C=8D=E5=8A=A1=E7=AB=AF?= =?UTF-8?q?=E7=9A=84=20field=5Flabel=20=E5=8F=AA=E6=98=AF=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=9A=84=E5=8F=AB=E6=B3=95=EF=BC=88=E3=80=8C=E7=A0=81?= =?UTF-8?q?=E5=80=BC=E3=80=8D=E3=80=8C=E4=B8=AD=E6=96=87=E5=90=8D=E3=80=8D?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E5=88=97=E5=90=8D=E5=8F=A6=E6=94=BE=E5=9C=A8?= =?UTF-8?q?=20column=EF=BC=8C=E5=88=97=E7=BA=A7=E7=9A=84=E9=A1=B9=E5=8F=AA?= =?UTF-8?q?=E5=86=99=E5=AE=83=E5=88=86=E4=B8=8D=E6=B8=85=E6=98=AF=E5=93=AA?= =?UTF-8?q?=E4=B8=80=E5=88=97=EF=BC=9B=E5=AE=A1=E9=98=85=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E4=B8=89=E8=A1=8C=E5=90=8C=E4=B8=80=E4=B8=AA=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=EF=BC=9A=E5=B7=B2=E7=A1=AE=E8=AE=A4=20=C3=B7=EF=BC=88=E5=B7=B2?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=20+=20=E5=BE=85=E7=A1=AE=E8=AE=A4=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E5=B7=B2=E9=A9=B3=E5=9B=9E=E7=9A=84=E3=80=81=E6=9C=89?= =?UTF-8?q?=E7=A1=AE=E8=AF=81=E7=9A=84=E4=B8=8D=E8=AE=A1=E5=85=A5=EF=BC=8C?= =?UTF-8?q?=E5=AE=A1=E5=AE=8C=E4=BA=86=E5=B0=B1=E6=98=AF=20100%=EF=BC=8C?= =?UTF-8?q?=E6=82=AC=E5=81=9C=E5=86=99=E6=98=8E=E5=8F=A3=E5=BE=84=EF=BC=9B?= =?UTF-8?q?=E5=B8=B8=E7=94=A8=E8=A1=A8=E5=86=99=E5=AE=9E=E9=99=85=E5=87=A0?= =?UTF-8?q?=E5=BC=A0=EF=BC=88=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=8F=AA=E7=AE=97?= =?UTF-8?q?=E8=A2=AB=E8=BF=90=E8=A1=8C=E7=94=A8=E8=BF=87=E7=9A=84=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E6=B2=A1=E6=9C=89=E6=97=B6=E5=86=99=E3=80=8C=E8=BF=98?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=BF=90=E8=A1=8C=E7=94=A8=E8=BF=87=E7=9A=84?= =?UTF-8?q?=E8=A1=A8=E3=80=8D=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=86=99=E6=AD=BB?= =?UTF-8?q?=E3=80=8C=E5=B8=B8=E7=94=A8=2020=20=E5=BC=A0=E8=A1=A8=E3=80=8D?= =?UTF-8?q?=EF=BC=9B=E3=80=8C=E7=94=A8=E8=BF=87=E8=BF=99=E5=BC=A0=E8=A1=A8?= =?UTF-8?q?=E7=9A=84=E8=BF=90=E8=A1=8C=E3=80=8D=E5=86=99=E3=80=8C=E5=85=B1?= =?UTF-8?q?=20N=20=E6=AC=A1=E8=BF=90=E8=A1=8C=E3=80=8D=E5=B9=B6=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E5=92=8C=E8=A1=A8=E5=A4=B4=E6=8C=89=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=AE=97=E7=9A=84=E4=BD=BF=E7=94=A8=E6=AC=A1=E6=95=B0=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E4=B8=80=E4=B8=AA=E6=95=B0=EF=BC=8C=E9=98=9F=E5=88=97?= =?UTF-8?q?=E9=87=8C=E4=BD=BF=E7=94=A8=E6=AC=A1=E6=95=B0=E7=9A=84=E6=82=AC?= =?UTF-8?q?=E5=81=9C=E5=90=8C=E8=A1=A8=E6=B8=85=E5=8D=95=EF=BC=9Bcheck-cat?= =?UTF-8?q?alog-loop=20=E7=9A=84=E9=98=9F=E5=88=97=E3=80=81=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E3=80=81=E5=AF=BC=E5=85=A5=E9=A2=84=E8=A7=88=E5=A4=B9?= =?UTF-8?q?=E5=85=B7=E7=85=A7=E6=9C=8D=E5=8A=A1=E7=AB=AF=E7=9A=84=E7=9C=9F?= =?UTF-8?q?=E5=AE=9E=E5=BD=A2=E7=8A=B6=EF=BC=88field=5Flabel=20=E5=8F=AA?= =?UTF-8?q?=E5=86=99=E5=AD=97=E6=AE=B5=E5=90=8D=E3=80=81=E5=B8=A6=20column?= =?UTF-8?q?=EF=BC=8Creasons=20=E7=94=A8=E6=9C=8D=E5=8A=A1=E7=AB=AF?= =?UTF-8?q?=E7=9A=84=E8=AF=B4=E6=B3=95=EF=BC=8C=E8=BF=9B=E5=BA=A6=E5=B8=A6?= =?UTF-8?q?=20items=5Fproposed=EF=BC=8C=E5=AF=BC=E5=85=A5=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E5=B8=A6=20version=E3=80=81error=E3=80=81=E5=86=B2?= =?UTF-8?q?=E7=AA=81=E7=9A=84=E7=8A=B6=E6=80=81=EF=BC=89=EF=BC=8C=E8=A1=A5?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E3=80=81=E8=BF=9B=E5=BA=A6=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=E7=9A=84=E7=BA=AF=E5=87=BD=E6=95=B0=E5=92=8C=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/lib/terms.ts | 32 ++++-- frontend/src/pages/catalog/ReviewQueue.tsx | 73 +++++++++----- frontend/src/pages/catalog/TableUsage.tsx | 2 +- frontend/src/pages/catalog/review.ts | 27 +++++ frontend/src/types.ts | 20 +++- scripts/check-catalog-loop.mjs | 111 ++++++++++++++++----- 6 files changed, 204 insertions(+), 61 deletions(-) diff --git a/frontend/src/lib/terms.ts b/frontend/src/lib/terms.ts index 2810b22d..0e1c6428 100644 --- a/frontend/src/lib/terms.ts +++ b/frontend/src/lib/terms.ts @@ -3082,7 +3082,11 @@ export const CATALOG_ASSISTANT_TEXT = { // - 「影响数字的项」:有效记录条件、去重规则、业务日期、关联关系、业务主键、度量类型——写错了查出来的数就不对,排在最前; // 「名称和含义」:中文名、含义、单位、码值、粒度、表类型。 // - 审阅过的项原地标出结果(已确认、已驳回、未生效),刷新队列后才移除;「撤销审阅」回到起草时的状态(推断)。 -// - 「常用 20 张表」:使用次数最多的 20 张。进度写「已确认 42%」。 +// - 每一项的位置写「列 status 的码值」「表的有效记录条件」「指向 parks 的关联关系」(同提案卡片),不只写字段名。 +// - 「常用 N 张表」:被运行用过、使用次数最多的前几张(最多 20 张),N 写实际张数;没有运行用过的表时写明。 +// - 进度写「已确认 42%」,三行同一个口径:已确认 ÷(已确认 + 待确认);已驳回的、有确证的(外键约束)不计入。 +// - 「使用 N 次」是所在表被运行查询过的次数(按查询算,同表清单);「用过这张表的运行」写「共 N 次运行」(按运行算), +// 两个数不一样,各自写明单位。 // =========================================================================== export const CATALOG_REVIEW_TEXT = { @@ -3120,7 +3124,7 @@ export const CATALOG_REVIEW_TEXT = { selected: (n: number) => `已选 ${formatNumber(n)} 项`, clearSelection: '取消选择', usage: (n: number) => `使用 ${formatNumber(n)} 次`, - usageHint: '所在表被运行查询过的次数', + usageHint: '所在表被运行查询过的次数,同一次运行中结果相同的重复查询只算一次', open: (where: string, table: string) => `打开「${table}」,查看${where}`, reasonsLabel: '排在这里的原因', // ---- 成批审阅 @@ -3151,15 +3155,25 @@ export const CATALOG_REVIEW_TEXT = { failedAll: (reason: string) => `未能提交:${reason}`, // ---- 进度 progressTitle: '审阅进度', + /** 常用表:被运行用过、使用次数最多的前几张(最多 topN 张),n 是实际张数 */ progressTop: (n: number) => `常用 ${formatNumber(n)} 张表`, - progressTopHint: (n: number) => `使用次数最多的 ${formatNumber(n)} 张表里的项`, + progressTopHint: (n: number, topN: number) => (n > 0 + ? `被运行用过、使用次数最多的 ${formatNumber(n)} 张表里的项(最多取 ${formatNumber(topN)} 张)` + : `被运行用过、使用次数最多的表里的项(最多取 ${formatNumber(topN)} 张)`), + progressTopNone: '常用表', + progressTopEmpty: '还没有运行用过的表', progressAll: '全部表', + progressAllHint: '这个数据源全部表里的项', progressFacts: '影响数字的项', progressFactsHint: '有效记录条件、去重规则、业务日期、关联关系、业务主键、度量类型', - confirmedPct: (pct: number | null) => (pct == null ? '还没有目录项' : `已确认 ${formatNumber(pct)}%`), - itemsOf: (confirmed: number, total: number) => `${formatNumber(confirmed)} / ${formatNumber(total)} 项`, - allDetail: (proposed: number, rejected: number) => `推断 ${formatNumber(proposed)} 项、已驳回 ${formatNumber(rejected)} 项`, - factsDetail: (proposed: number, confirmed: number) => `待确认 ${formatNumber(proposed)} 项、已确认 ${formatNumber(confirmed)} 项`, + progressRule: '已确认 ÷(已确认 + 待确认):已驳回的、有确证的(外键约束)不用再确认,不计入', + /** 一行进度:已确认百分比和待确认几项;不知道待确认几项时只写已确认几项 */ + progressText: (pct: number | null, confirmed: number, proposed: number | null, total: number | null) => { + if (total === 0) return '还没有目录项' + if (proposed == null) return `已确认 ${formatNumber(confirmed)} 项` + if (pct == null) return '没有待确认的项' + return `已确认 ${formatNumber(pct)}% · 待确认 ${formatNumber(proposed)} 项` + }, progressFailed: '无法读取审阅进度', // ---- 没选中项时的右栏 overviewTitle: '从左侧队列挑一项开始审阅', @@ -3178,8 +3192,10 @@ export const CATALOG_USAGE_TEXT = { show: '查看用过这张表的运行', hide: '收起', none: '还没有运行查询过这张表', + /** 按运行算(一次运行查询了几次这张表都只算一次),和表头按查询算的「使用 N 次」不是一个数 */ count: (shown: number, total: number) => (total > shown - ? `最近 ${formatNumber(shown)} 次,共 ${formatNumber(total)} 次` : `共 ${formatNumber(total)} 次`), + ? `共 ${formatNumber(total)} 次运行,显示最近 ${formatNumber(shown)} 次` : `共 ${formatNumber(total)} 次运行`), + countHint: '按运行算:一次运行查询了几次这张表都只算一次;表头的使用次数按查询算,两个数可能不一样', error: '无法读取用过这张表的运行', retry: '重试', unnamed: '未保存的工作流', diff --git a/frontend/src/pages/catalog/ReviewQueue.tsx b/frontend/src/pages/catalog/ReviewQueue.tsx index c1dda666..ba3a1d6c 100644 --- a/frontend/src/pages/catalog/ReviewQueue.tsx +++ b/frontend/src/pages/catalog/ReviewQueue.tsx @@ -10,15 +10,17 @@ import { errorMessage } from '../../lib/errors' import { CATALOG_REVIEW_TEXT as RT } from '../../lib/terms' import { StatusChip } from './parts' import { - REVIEW_BATCH_MAX, REVIEW_KINDS, REVIEW_PAGE, applyResults, batchTargets, itemKey, percentOf, statusOf, syncFromDetail, + REVIEW_BATCH_MAX, REVIEW_KINDS, REVIEW_PAGE, applyResults, batchTargets, confirmProgress, itemKey, reviewWhere, statusOf, + syncFromDetail, } from './review' import type { ItemOutcome } from './review' // =========================================================================== // 审阅队列(数据目录页的「审阅队列」视图,左栏):跨表列出推断项,顺序由服务端排好(所在表的使用次数 × 这一项对数字的 // 影响 × 来源),每一项写为什么排在这里。可以按项(影响数字的项 / 名称和含义)和表筛选;多选后成批确认、驳回,审阅过的 -// 原地标出结果(冲突的标出来),选中可以撤销审阅;刷新队列后审阅过的才移除。顶上是审阅进度:常用 20 张表、全部表、 -// 影响数字的项各确认了多少。点开一项,右栏是所在表的详情(滚到那一项、标出来)和用过这张表的运行。 +// 原地标出结果(冲突的标出来),选中可以撤销审阅;刷新队列后审阅过的才移除。顶上是审阅进度:常用表(使用次数最多、 +// 被运行用过的前 20 张)、全部表、影响数字的项各确认了多少,三行同一个口径。点开一项,右栏是所在表的详情(滚到那一项、 +// 标出来)和用过这张表的运行。 // =========================================================================== export function ReviewQueue({ sourceId, rows, kind, table, activeKey, detailSync, onParams, onOpenItem, onChanged }: { @@ -313,7 +315,8 @@ const QueueRow = memo(function QueueRow({ item: i, itemKeyText, tableLabel, outc onOpen: (item: CatalogReviewItem) => void }) { const status = statusOf(i, outcome) - const where = i.field_label || i.path + // 位置按 path 说(「列 status 的码值」):服务端的 field_label 只是字段的叫法,列级的项只写它分不清是哪一列 + const where = reviewWhere(i) const tableText = tableLabel ? `${tableLabel}(${i.table_name})` : i.table_name const mono = i.field === 'valid_filter' || i.field === 'keys' return ( @@ -327,7 +330,7 @@ const QueueRow = memo(function QueueRow({ item: i, itemKeyText, tableLabel, outc