feat(platform): add alert dispatch board

This commit is contained in:
lingniu
2026-07-04 21:10:58 +08:00
parent 09c86022b2
commit 0fff9e3890
3 changed files with 114 additions and 0 deletions

View File

@@ -123,6 +123,16 @@ type PriorityIssueRow = QualityIssueRow & {
notificationText?: string;
};
type AlertDispatchOwnerRow = {
owner: string;
activeRuleCount: number;
hitCount: number;
p0Count: number;
primaryIssueType: string;
primarySla: string;
primaryHitCount: number;
};
function qualityParams(values: Record<string, string>) {
const params = new URLSearchParams();
if (values?.keyword) params.set('keyword', values.keyword);
@@ -217,6 +227,35 @@ function alertRuleRows(summary: QualitySummary, health: OpsHealth | null): Alert
return rows;
}
function alertDispatchOwnerRows(rules: AlertRuleRow[]): AlertDispatchOwnerRow[] {
const ownerMap = new Map<string, AlertDispatchOwnerRow>();
rules.forEach((rule) => {
const owner = rule.owner || '未分配';
const current = ownerMap.get(owner) ?? {
owner,
activeRuleCount: 0,
hitCount: 0,
p0Count: 0,
primaryIssueType: rule.issueType,
primarySla: rule.sla,
primaryHitCount: 0
};
current.activeRuleCount += rule.count > 0 ? 1 : 0;
current.hitCount += rule.count;
current.p0Count += rule.count > 0 && rule.level === 'P0' ? 1 : 0;
const shouldReplacePrimary = rule.count > current.primaryHitCount || (rule.count === current.primaryHitCount && rule.level === 'P0');
if (shouldReplacePrimary) {
current.primaryIssueType = rule.issueType;
current.primarySla = rule.sla;
current.primaryHitCount = rule.count;
}
ownerMap.set(owner, current);
});
return [...ownerMap.values()]
.filter((row) => row.hitCount > 0 || row.activeRuleCount > 0)
.sort((left, right) => right.p0Count - left.p0Count || right.hitCount - left.hitCount || left.owner.localeCompare(right.owner));
}
function issueSla(issueType: string) {
return alertRuleTemplates.find((item) => item.issueType === issueType)?.sla ?? '当日闭环';
}
@@ -627,6 +666,7 @@ export function Quality({
const activeRuleCount = notificationPlan?.activeRuleCount ?? rules.filter((item) => item.count > 0).length;
const p0RuleCount = notificationPlan?.p0RuleCount ?? rules.filter((item) => item.count > 0 && item.level === 'P0').length;
const priorityRows = normalizePriorityIssues(notificationPlan?.priorityIssues) ?? priorityIssueRows(issues);
const dispatchOwnerRows = alertDispatchOwnerRows(rules);
const loadIssues = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
setLoadingIssues(true);
@@ -836,6 +876,33 @@ export function Quality({
))}
</div>
</Card>
<Card bordered title="告警分派看板" style={{ marginTop: 16 }}>
<div className="vp-alert-dispatch-grid">
{dispatchOwnerRows.length === 0 ? (
<Tag color="green"></Tag>
) : dispatchOwnerRows.map((item) => (
<button
key={item.owner}
type="button"
className="vp-alert-dispatch-item"
aria-label={`分派团队 ${item.owner}`}
onClick={() => drillIssueType(item.primaryIssueType)}
>
<div className="vp-alert-dispatch-head">
<Tag color={item.p0Count > 0 ? 'red' : 'orange'}>{item.owner}</Tag>
<strong>{item.hitCount.toLocaleString()} </strong>
</div>
<div className="vp-alert-policy-detail">
{item.activeRuleCount.toLocaleString()} / P0 {item.p0Count.toLocaleString()}
</div>
<div className="vp-alert-policy-detail">
{item.primaryIssueType === 'CAPACITY_RISK' ? '容量与存储风险' : qualityIssueLabel(item.primaryIssueType)}
</div>
<div className="vp-alert-policy-detail">SLA{item.primarySla}</div>
</button>
))}
</div>
</Card>
<Card
bordered
title={(