fix: separate sentinel check counts from samples

This commit is contained in:
Codex
2026-06-28 08:05:40 +00:00
parent 72cbcc21f2
commit 4c7c103905
3 changed files with 126 additions and 40 deletions
@@ -40,6 +40,7 @@ createApp({
});
const selectedRun = computed(() => runs.value.find((run) => run.id === selectedRunId.value) || latestRun.value);
const trendRows = computed(() => runs.value.slice().sort((a, b) => Date.parse(a.updatedAt || a.createdAt || "") - Date.parse(b.updatedAt || b.createdAt || "")).slice(-48));
const latestTrendRun = computed(() => trendRows.value.length > 0 ? trendRows.value[trendRows.value.length - 1] : latestRun.value);
const trendMax = computed(() => Math.max(1, ...trendRows.value.flatMap((run) => [trendErrorCount(run), trendWarningCount(run), trendTotalCount(run)])));
const trendPolylines = computed(() => ({
red: trendPolyline((run) => trendErrorCount(run)),
@@ -221,6 +222,7 @@ createApp({
filteredRuns,
selectedRun,
trendRows,
latestTrendRun,
trendPolylines,
trendDots,
timelineRuns,
@@ -237,7 +239,11 @@ createApp({
redCount,
warningCount,
findingCount,
findingSampleCount,
alertSampleCount,
trendTotalCount,
trendErrorCount,
trendWarningCount,
severityClass,
formatDate,
formatAbsoluteDate,
@@ -247,6 +253,7 @@ createApp({
findingTitle,
findingCode,
levelLabel,
findingGroupCountLabel,
detailSummaryText,
detailSummaryRows,
commandSummary,
@@ -302,13 +309,13 @@ createApp({
<section class="trend-panel" aria-labelledby="trend-heading">
<div class="panel-header">
<div>
<h2 id="trend-heading">错误 / 警告 / 合计曲线</h2>
<p>按运行更新时间展示最近 {{ trendRows.length }} 次变化</p>
<h2 id="trend-heading">错误 / 警告样本曲线</h2>
<p>按运行更新时间展示最近 {{ trendRows.length }} 次变化,点位为单次运行样本数</p>
</div>
<span class="pill" :class="cadence.stale ? 'warning' : 'healthy'">{{ cadence.stale ? "非阻塞报警" : "新鲜" }}</span>
</div>
<div class="trend-chart-wrap">
<svg class="trend-chart" viewBox="0 0 720 142" role="img" aria-label="错误和警告监测项数量趋势" data-monitor-trend-curve="true">
<svg class="trend-chart" viewBox="0 0 720 142" role="img" aria-label="错误和警告样本数量趋势" data-monitor-trend-curve="true">
<line class="trend-grid-line" x1="24" x2="696" y1="24" y2="24"></line>
<line class="trend-grid-line" x1="24" x2="696" y1="75" y2="75"></line>
<line class="trend-grid-line" x1="24" x2="696" y1="126" y2="126"></line>
@@ -351,9 +358,10 @@ createApp({
<div v-if="trendRows.length === 0" class="trend-empty">暂无运行数据</div>
</div>
<div class="trend-legend">
<span class="legend-item"><span class="legend-swatch red"></span>错误 {{ redCount({ severityCounts: severityTotals }) }}</span>
<span class="legend-item"><span class="legend-swatch warning"></span>警告 {{ warningCount({ severityCounts: severityTotals }) }}</span>
<span class="legend-item"><span class="legend-swatch total"></span>错误+警告合计 {{ trendTotalCount({ severityCounts: severityTotals }) }}</span>
<span class="legend-item"><span class="legend-swatch red"></span>最新点错误 {{ trendErrorCount(latestTrendRun) }}</span>
<span class="legend-item"><span class="legend-swatch warning"></span>最新点警告 {{ trendWarningCount(latestTrendRun) }}</span>
<span class="legend-item"><span class="legend-swatch total"></span>最新点错误+警告合计 {{ trendTotalCount(latestTrendRun) }}</span>
<span class="legend-item">历史样本累计 错误 {{ redCount({ severityCounts: severityTotals }) }} / 警告 {{ warningCount({ severityCounts: severityTotals }) }}</span>
<span class="legend-item">{{ cadence.alert }}</span>
</div>
</section>
@@ -394,11 +402,11 @@ createApp({
<strong>{{ cadence.latestAge >= 0 ? formatDuration(cadence.latestAge) : "-" }}</strong>
</div>
<div class="metric">
<span>错误</span>
<span>历史错误样本</span>
<strong>{{ redCount({ severityCounts: severityTotals }) }}</strong>
</div>
<div class="metric">
<span>警告</span>
<span>历史警告样本</span>
<strong>{{ warningCount({ severityCounts: severityTotals }) }}</strong>
</div>
<div class="metric">
@@ -463,7 +471,9 @@ createApp({
<h3>摘要</h3>
<div class="detail-grid">
<div class="metric"><span>状态</span><strong>{{ selectedRun.status || "-" }}</strong></div>
<div class="metric"><span>监测项</span><strong>{{ findingCount(selectedRun) }}</strong></div>
<div class="metric"><span>监测项类型</span><strong>{{ findingCount(selectedRun) }}</strong></div>
<div class="metric"><span>错误/警告样本</span><strong>{{ alertSampleCount(selectedRun) }}</strong></div>
<div class="metric"><span>全部样本</span><strong>{{ findingSampleCount(selectedRun) }}</strong></div>
<div class="metric"><span>Observer</span><strong>{{ selectedRun.observerId || "-" }}</strong></div>
<div class="metric"><span>更新时间</span><strong>{{ formatDate(selectedRun.updatedAt || selectedRun.createdAt) }}</strong></div>
</div>
@@ -512,7 +522,7 @@ createApp({
>
<span class="row-line">
<span class="severity-line"><span class="severity-dot"></span><span class="check-code">{{ findingCode(item) }}</span><strong>{{ findingTitle(item) }}</strong></span>
<span class="tag" :class="severityClass(item)">{{ levelLabel(item) }} · {{ item.runCount || item.count || 1 }}</span>
<span class="tag" :class="severityClass(item)">{{ levelLabel(item) }} · {{ findingGroupCountLabel(item) }}</span>
</span>
<p class="muted">{{ rootCauseText(item) }}</p>
<p v-if="item.nextAction" class="muted">处理: {{ item.nextAction }}</p>
@@ -593,14 +603,27 @@ function trendTotalCount(item) {
}
function findingCount(item) {
const counts = item?.severityCounts;
if (counts && typeof counts === "object") return Object.values(counts).reduce((sum, value) => sum + number(value), 0);
if (Number.isFinite(Number(item?.findingTypeCount))) return Number(item.findingTypeCount);
if (Number.isFinite(Number(item?.findingCount))) return Number(item.findingCount);
if (Number.isFinite(Number(item?.finding_count))) return Number(item.finding_count);
if (Number.isFinite(Number(item?.count))) return Number(item.count);
const counts = item?.severityCounts;
if (counts && typeof counts === "object") return Object.keys(counts).length;
return Object.values(item || {}).reduce((sum, value) => sum + number(value), 0);
}
function findingSampleCount(item) {
if (Number.isFinite(Number(item?.findingSampleCount))) return Number(item.findingSampleCount);
const counts = item?.severityCounts;
if (counts && typeof counts === "object") return Object.values(counts).reduce((sum, value) => sum + number(value), 0);
return 0;
}
function alertSampleCount(item) {
if (Number.isFinite(Number(item?.findingAlertSampleCount))) return Number(item.findingAlertSampleCount);
return redCount(item) + warningCount(item);
}
function severityClass(item) {
const explicit = String(item?.maxSeverity || item?.checkLevel || item?.severity || item?.level || "").toLowerCase();
if (["red", "critical", "error", "blocked"].includes(explicit)) return "red";
@@ -678,6 +701,15 @@ function levelLabel(item) {
return "未知";
}
function findingGroupCountLabel(item) {
const runCount = Number.isFinite(Number(item?.runCount)) ? Number(item.runCount) : null;
const sampleCount = Number.isFinite(Number(item?.count)) ? Number(item.count) : null;
if (runCount !== null && sampleCount !== null) return `${runCount} 次运行 / ${sampleCount} 样本`;
if (sampleCount !== null) return `${sampleCount} 样本`;
if (runCount !== null) return `${runCount} 次运行`;
return "1 样本";
}
function findingSearchText(item) {
return [
item?.checkCode,
@@ -713,9 +745,11 @@ function detailSummaryRows(detail) {
{ key: "run", label: "运行", value: shortId(run.id || run.runId || detail.runId) },
{ key: "scenario", label: "场景", value: run.scenarioId || run.scenario_id || "-" },
{ key: "status", label: "状态", value: run.status || "-" },
{ key: "checks", label: "监测项", value: String(run.findingCount ?? run.finding_count ?? 0) },
{ key: "error", label: "错误", value: String(redCount({ severityCounts: counts })) },
{ key: "warning", label: "警告", value: String(warningCount({ severityCounts: counts })) },
{ key: "checks", label: "监测项类型", value: String(findingCount(run)) },
{ key: "alertSamples", label: "错误/警告样本", value: String(alertSampleCount(run)) },
{ key: "allSamples", label: "全部样本", value: String(findingSampleCount(run)) },
{ key: "error", label: "错误样本", value: String(redCount({ severityCounts: counts })) },
{ key: "warning", label: "警告样本", value: String(warningCount({ severityCounts: counts })) },
{ key: "observer", label: "Observer", value: run.observerId || run.observer_id || "-" },
{ key: "updated", label: "更新时间", value: formatAbsoluteDate(run.updatedAt || run.updated_at || run.createdAt || run.created_at) },
{ key: "report", label: "报告", value: shortHash(artifacts.reportJsonSha256 || run.reportJsonSha256 || run.report_json_sha256 || "") || "-" },