fix: correct operational statistics and form interactions

This commit is contained in:
hectorzhao
2026-09-09 23:15:42 +08:00
parent 6d63eb5452
commit 5bcdbb2a03
33 changed files with 2920 additions and 829 deletions
@@ -0,0 +1,68 @@
import { BadRequestException } from '@nestjs/common';
import { createHash } from 'node:crypto';
export function alertHistoryRange(from?: string, to?: string, now = new Date()) {
const dateKey = (date: Date) => new Date(date.getTime() + 8 * 3600_000).toISOString().slice(0, 10);
const endDate = to || dateKey(now);
const startDate = from || dateKey(new Date(now.getTime() - 6 * 86400_000));
const parse = (value: string) => {
const result = new Date(`${value}T00:00:00+08:00`);
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(result.getTime()) || dateKey(result) !== value) {
throw new BadRequestException('告警日期无效');
}
return result.getTime() / 1000;
};
const start = parse(startDate);
const end = parse(endDate) + 86400;
if (end <= start || end - start > 31 * 86400) throw new BadRequestException('告警日期范围须为1至31天');
return { startDate, endDate, start, end: Math.min(end, now.getTime() / 1000) };
}
export type AlertHistoryItem = {
id: string;
name: string;
severity: string;
service: string;
instance: string;
startedAt: string;
firstObservedAt: string;
lastObservedAt: string;
};
// ALERTS_FOR_STATE stores activeAt as the sample value, separating repeated trigger cycles.
// Observation boundaries are not claimed as exact recovery times.
export function mergeAlertHistory(
target: Map<string, AlertHistoryItem>,
series: Array<{ metric: Record<string, string>; values?: [number, string][] }>,
start: number,
end: number,
) {
for (const { metric, values } of series) {
const labels = Object.entries(metric)
.filter(([key]) => key !== '__name__')
.sort(([a], [b]) => a.localeCompare(b));
const fingerprint = createHash('sha256').update(JSON.stringify(labels)).digest('hex');
for (const [time, rawActiveAt] of values ?? []) {
const activeAt = Number(rawActiveAt);
if (time < start || time >= end || !Number.isFinite(activeAt) || activeAt <= 0 || activeAt > time) continue;
const id = `${fingerprint}:${activeAt}`;
const observed = new Date(time * 1000).toISOString();
const item = target.get(id);
if (item) {
if (observed < item.firstObservedAt) item.firstObservedAt = observed;
if (observed > item.lastObservedAt) item.lastObservedAt = observed;
} else {
target.set(id, {
id,
name: metric.alertname || '未命名告警',
severity: metric.severity || 'info',
service: metric.service || '',
instance: metric.instance || '',
startedAt: new Date(activeAt * 1000).toISOString(),
firstObservedAt: observed,
lastObservedAt: observed,
});
}
}
}
}