feat(platform): add alert escalation checklist

This commit is contained in:
lingniu
2026-07-04 20:06:35 +08:00
parent 06d9dc1562
commit 7a2a8e8a83
2 changed files with 51 additions and 1 deletions

View File

@@ -76,6 +76,46 @@ function priorityIssueDigest(plan: QualityNotificationPlan | null, release?: str
].join('\n');
}
function alertEscalationChecklist(plan: QualityNotificationPlan | null, release?: string) {
const rules = plan?.rules ?? [];
const policies = plan?.policies ?? [];
const issues = plan?.priorityIssues ?? [];
const p0Issues = issues.filter((issue) => issue.priority === 'P0');
const nextIssue = issues[0];
const primaryPolicy = policies.find((policy) => policy.name.startsWith('P0')) ?? policies[0];
const activeRules = rules.filter((rule) => Number(rule.count ?? 0) > 0);
return [
'【告警升级值班清单】',
`运行版本:${release?.trim() || '-'}`,
`值班结论:${p0Issues.length > 0 ? `P0 ${p0Issues.length.toLocaleString()} 条,需要立即确认` : issues.length > 0 ? `待通知 ${issues.length.toLocaleString()} 条,按策略跟进` : '暂无待通知告警'}`,
`主通知策略:${primaryPolicy ? `${primaryPolicy.name} / ${primaryPolicy.target} / ${primaryPolicy.channel}` : '-'}`,
`升级窗口:${primaryPolicy ? formatEscalationMinutes(primaryPolicy.escalationMinutes) : '-'}`,
`验收口径:${primaryPolicy?.acceptanceCriteria || '-'}`,
'',
'活跃规则:',
...(activeRules.length > 0 ? activeRules.map((rule, index) => `${index + 1}. [${rule.level}] ${ruleTitle(rule)} / ${rule.owner} / 命中 ${Number(rule.count ?? 0).toLocaleString()} / SLA ${rule.sla}`) : ['暂无活跃规则']),
'',
'下一条处置:',
nextIssue ? [
`[${nextIssue.priority}] ${nextIssue.vehicleLabel}`,
`问题:${qualityIssueLabel(nextIssue.issueType)} / ${nextIssue.protocol}`,
`建议动作:${nextIssue.actionLabel} - ${nextIssue.actionDetail || '-'}`,
`SLA${nextIssue.sla}`,
`证据:${evidenceSummary(nextIssue)}`,
`车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`,
`实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`,
`轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`,
`RAW证据${window.location.origin}${window.location.pathname}${nextIssue.rawHash}`
].join('\n') : '暂无待处置告警',
'',
'值班动作:',
'1. 先处理 P0无来源/VIN 缺失/存储不可写优先于普通字段缺失',
'2. 复制单条通知给责任团队并同步车辆服务、实时、轨迹、RAW 证据',
'3. 超过升级窗口未恢复时,按主通知策略升级到业务责任人',
'4. 恢复后检查验收口径,并在告警事件页确认不再命中'
].join('\n');
}
function countEvidenceLinks(issue: QualityPriorityIssue) {
return [issue.vehicleHash, issue.realtimeHash, issue.historyHash, issue.rawHash].filter(Boolean).length;
}
@@ -166,7 +206,7 @@ export function NotificationRules() {
<Card
bordered
loading={loading}
title={<Space><span></span><Button size="small" aria-label="复制待通知汇总" disabled={priorityIssues.length === 0} icon={<IconCopy />} onClick={() => copyText(priorityIssueDigest(plan, release), '待通知告警')}></Button></Space>}
title={<Space><span></span><Button size="small" aria-label="复制待通知汇总" disabled={priorityIssues.length === 0} icon={<IconCopy />} onClick={() => copyText(priorityIssueDigest(plan, release), '待通知告警')}></Button><Button size="small" aria-label="复制升级值班清单" icon={<IconCopy />} onClick={() => copyText(alertEscalationChecklist(plan, release), '升级值班清单')}></Button></Space>}
style={{ marginTop: 16 }}
>
<div className="vp-alert-command-board">

View File

@@ -3459,6 +3459,16 @@ test('renders notification rules as a standalone operations page', async () => {
fireEvent.click(screen.getByRole('button', { name: '复制待通知汇总' }));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【当前待通知告警】'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A规则1 / VIN-RULES-001'));
fireEvent.click(screen.getByRole('button', { name: '复制升级值班清单' }));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警升级值班清单】'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('值班结论P0 1 条,需要立即确认'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主通知策略P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('升级窗口30 分钟升级'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 无来源规则 / 平台接入 / 命中 3 / SLA 30 分钟确认'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:确认平台转发 - 核对平台转发配置'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务http://localhost:3000/#/detail?keyword=VIN-RULES-001'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据http://localhost:3000/#/history-query?keyword=VIN-RULES-001&tab=raw'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('超过升级窗口未恢复时'));
fireEvent.click(screen.getByRole('button', { name: '复制单条告警通知' }));
expect(writeText).toHaveBeenCalledWith('【P0 告警通知】通知规则页待通知');
fireEvent.click(screen.getByRole('button', { name: '车辆服务' }));