数据目录 F2b / B3:发布前检查里 SQL 检查的问题带上和证据面板同一个级别标识(错误 / 提醒),并说清两个发布级别拦不拦:错误级在已发布级别只提醒、不拦,受管级别会拦下;结论那句话里点出其中几处是 SQL 检查的错误级问题,不再和「没指定模型」这类提示一个样;检查脚本的伪造门禁补上没有口径卡的图;补检查脚本的断言

This commit is contained in:
ThearchyHelios
2026-10-04 02:36:26 +08:00
parent ec818d5b69
commit ac4ea030a8
3 changed files with 53 additions and 4 deletions
+24 -3
View File
@@ -12,8 +12,9 @@ import {
import { useCatalog } from '../store/catalog'
import { Modal, Spinner, toast, useRadioGroup } from '../components/ui'
import { humanizeError } from '../lib/errors'
import { PUBLISH_FIX_TEXT as T, WORKFLOW_STATUS_HINT, WORKFLOW_STATUS_LABEL } from '../lib/terms'
import { CATALOG_UI_TEXT as UT, PUBLISH_FIX_TEXT as T, WORKFLOW_STATUS_HINT, WORKFLOW_STATUS_LABEL } from '../lib/terms'
import { isSqlCheckCode, sqlRuleLabel } from '../lib/sqlcheck'
import { SqlLevelBadge } from '../run/SqlChecks'
import { setLocalActor, useLocalActor } from '../lib/actor'
import {
autoFixIds, choiceReady, contentSig, fixFieldLabel, fixFor, fixValueLines, fixValueText, isMissingEndpoint, mergeIssues,
@@ -443,6 +444,15 @@ export function usePreflight(workflowId: string | undefined, level: PublishLevel
}
}
/**
* 发布前检查里一条 SQL 检查问题原本的级别。门禁只收错误和提醒两级(提示的依据只是推断,不进门禁),问题的 level 换成了
* 「拦不拦」:受管档错误级拦(error),其余都是 warning——原来是哪一级看不出来了。错误级只有两条规则
* (服务端 sqlcheck._ERROR_CODES:一对多关联后重复计算、存量跨期加总),按规则编号认回来
*/
const SQL_ERROR_CODES = new Set(['fanout_sum', 'stock_summed'])
const publishSqlLevel = (code: unknown): 'error' | 'warning' | null =>
(isSqlCheckCode(code) ? (SQL_ERROR_CODES.has(code) ? 'error' : 'warning') : null)
function fixLabelOf(data: PublishCheck | undefined, id: string): string {
return data?.fixes.find((f) => f.id === id)?.label ?? ''
}
@@ -467,6 +477,8 @@ export function Preflight({ pf, gate, onLocate, stale }: {
const rows = useMemo(() => (issues ? mergeIssues(issues) : null), [issues])
const errors = (rows ?? []).filter((r) => r.issue.level === 'error').length
const warns = (rows ?? []).length - errors
// 已发布档只提醒、不拦的 SQL 检查错误级问题:结论那句话里点出来,别和「没指定模型」这类提示混成一个数
const sqlErrors = (rows ?? []).filter((r) => r.issue.level !== 'error' && publishSqlLevel(r.issue.code) === 'error').length
const canFix = !lock && check.status !== 'unsupported'
const autoIds = issues ? autoFixIds(issues, fixes) : []
const labelOf = (id?: string | null) => (id ? nodeOf(id)?.label || id : '')
@@ -507,6 +519,7 @@ export function Preflight({ pf, gate, onLocate, stale }: {
<span className="min-w-0">
{gate ? T.gateBlocked(errors) : errors ? T.blocked(errors) : T.passed}
{warns > 0 && <span className="font-normal text-faint">{errors || gate ? ',' : ';'}{T.warnings(warns)}</span>}
{sqlErrors > 0 && <span className="font-normal" style={{ color: 'var(--st-failed)' }} data-preflight-sql-errors={sqlErrors}>{UT.publishSqlErrors(sqlErrors)}</span>}
</span>
</span>
)}
@@ -551,8 +564,9 @@ function PreflightRow({ issue, others, fix, where, pf, onLocate }: {
}) {
const err = issue.level === 'error'
const Icon = err ? XCircle : AlertTriangle
const sqlLevel = publishSqlLevel(issue.code)
return (
<li data-preflight-issue={issue.code ?? ''} data-fix-kind={fix?.kind}>
<li data-preflight-issue={issue.code ?? ''} data-fix-kind={fix?.kind} data-sql-check-level={sqlLevel ?? undefined}>
<button
type="button"
disabled={!issue.node_id}
@@ -566,9 +580,16 @@ function PreflightRow({ issue, others, fix, where, pf, onLocate }: {
aria-label={err ? '错误' : '提示'} />
<span className="min-w-0 flex-1 [overflow-wrap:anywhere]">
{where && !String(issue.message).includes(`「${where}」`) && <b className="font-semibold">「{where}」</b>}
{/* 对照数据目录的 SQL 检查:先写中文规则名,不露规则编号 */}
{/* 对照数据目录的 SQL 检查:级别标识和证据面板同一个(错误 / 提醒),再写中文规则名,不露规则编号 */}
{sqlLevel && <SqlLevelBadge level={sqlLevel} className="mr-1 align-[1px]" />}
{isSqlCheckCode(issue.code) && <span className="font-medium" data-preflight-rule="">{sqlRuleLabel(issue.code)}:</span>}
{issue.message}
{/* 这一级在两个发布级别下拦不拦:错误级只有受管档拦,已发布档只提醒(设计如此),要说清楚 */}
{sqlLevel && (
<span className="mt-0.5 block text-faint" data-preflight-sql-gate={sqlLevel === 'error' ? (err ? 'blocked' : 'reminded') : 'warning'}>
{sqlLevel === 'error' ? (err ? UT.publishSqlErrorBlocked : UT.publishSqlErrorReminded) : UT.publishSqlWarning}
</span>
)}
</span>
{issue.node_id && <CornerDownRight size={10} className="mt-px shrink-0 text-faint" aria-hidden />}
</button>
+5
View File
@@ -2533,6 +2533,11 @@ export const CATALOG_UI_TEXT = {
patchNote: '说明',
// ---- 合并查询的输入:连线时还没选工具、后来才选上的查询节点,一次带出来
mergeAddConnected: (n: number) => `带出连着的 ${formatNumber(n)} 个查询节点`,
// ---- 发布前检查里的 SQL 检查问题:级别标识和证据面板一致,并说清两个发布级别下拦不拦
publishSqlErrorReminded: 'SQL 检查发现的错误级问题:已发布级别只提醒、不拦发布;受管级别会拦下',
publishSqlErrorBlocked: 'SQL 检查发现的错误级问题:受管级别会拦下发布,请先修改 SQL',
publishSqlWarning: 'SQL 检查的提醒:两个发布级别都只提醒、不拦发布',
publishSqlErrors: (n: number) => `,其中 ${formatNumber(n)} 处是 SQL 检查发现的错误级问题(受管级别会拦下)`,
}
// ===========================================================================
+24 -1
View File
@@ -209,7 +209,7 @@ function lint(graph, level, { followsNever = false } = {}) {
issues.push({ level: 'error', node_id: null, code: 'governed.no_contract', fix: id, field: null,
message: '受管级别要求至少一个「成果」节点声明出具契约,否则无法判定出具档位' })
fixes.push({ id, code: 'governed.no_contract', node_id: outputs[0].id, kind: 'choice', multiple: true,
label: `为${who(outputs[0])}生成出具契约:核对${report ? who(report) : '「报告撰写」'}的文档,指标来自${who(cards[0])},再选择「必需指标」`,
label: `为${who(outputs[0])}生成出具契约:核对${report ? who(report) : '「报告撰写」'}的文档,指标来自${cards.length ? who(cards[0]) : '「口径卡」'},再选择「必需指标」`,
options: cards.flatMap(metricOptions) })
}
for (const n of graph.nodes) {
@@ -834,6 +834,29 @@ await section('SQL 检查(数据目录阶段 4B):发布前检查写中文
const text = await row.innerText()
check('发布弹窗:SQL 检查的问题写节点名和中文规则名,不露规则编号', text.includes('「订单汇总」') && text.includes('一对多关联后重复计算:')
&& text.includes('会重复计算') && !/fanout_sum|args\.sql/.test(text), text)
// 错误级的 SQL 检查在已发布档只提醒(设计如此),但不能和「没指定模型」这类提示一个样:级别标识和证据面板同一个
// (SqlLevelBadge:图标 + 「错误」、失败色),并说清已发布级别只提醒、受管级别会拦
const badge = row.locator('[data-sql-level]')
const badgeLook = await badge.evaluate((el) => {
const probe = document.createElement('span')
probe.style.color = 'var(--st-failed)'
document.body.append(probe)
const failed = getComputedStyle(probe).color
probe.remove()
return { level: el.getAttribute('data-sql-level'), text: el.innerText.trim(), color: getComputedStyle(el).color, failed, icon: !!el.querySelector('svg') }
}).catch(() => null)
check('……带证据面板同一个级别标识:「错误」、失败色、带图标', badgeLook?.level === 'error' && badgeLook.text === '错误'
&& badgeLook.color === badgeLook.failed && badgeLook.icon, JSON.stringify(badgeLook))
check('……写明这是 SQL 检查发现的错误级问题:已发布级别只提醒、不拦,受管级别会拦下',
(await row.locator('[data-preflight-sql-gate="reminded"]').innerText().catch(() => '')) === 'SQL 检查发现的错误级问题:已发布级别只提醒、不拦发布;受管级别会拦下')
check('……结论那句话里点出来,不和别的提示混成一个数', (await dialog(page).locator('[data-preflight-sql-errors="1"]').innerText().catch(() => ''))
.includes('其中 1 处是 SQL 检查发现的错误级问题'))
await dialog(page).locator('[role="radio"]:has-text("受管")').click()
await waitFor(page, async () => (await row.locator('[data-preflight-sql-gate="blocked"]').count()) === 1)
check('换成受管:同一条写「受管级别会拦下发布」,行首是错误', (await row.locator('[data-preflight-sql-gate]').innerText()).includes('受管级别会拦下发布')
&& await row.locator('svg[aria-label="错误"]').count() === 1 && await dialog(page).locator('[data-preflight-sql-errors]').count() === 0)
await dialog(page).locator('[role="radio"]:has-text("已发布")').click()
await waitFor(page, async () => (await row.locator('[data-preflight-sql-gate="reminded"]').count()) === 1)
await shoot(page, 'preflight-sqlcheck', dialog(page))
await row.locator('button').first().click()
const where = () => page.evaluate(() => {