feat(platform): add alert handling command queue
This commit is contained in:
@@ -76,6 +76,19 @@ function priorityIssueDigest(plan: QualityNotificationPlan | null, release?: str
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function countEvidenceLinks(issue: QualityPriorityIssue) {
|
||||
return [issue.vehicleHash, issue.realtimeHash, issue.historyHash, issue.rawHash].filter(Boolean).length;
|
||||
}
|
||||
|
||||
function evidenceSummary(issue: QualityPriorityIssue) {
|
||||
const count = countEvidenceLinks(issue);
|
||||
return count >= 4 ? '证据完整' : `缺 ${4 - count} 类证据`;
|
||||
}
|
||||
|
||||
function evidenceColor(issue: QualityPriorityIssue): 'green' | 'orange' {
|
||||
return countEvidenceLinks(issue) >= 4 ? 'green' : 'orange';
|
||||
}
|
||||
|
||||
function navigateHash(hash: string) {
|
||||
if (!hash) return;
|
||||
window.location.hash = hash;
|
||||
@@ -119,6 +132,10 @@ export function NotificationRules() {
|
||||
const activeRuleCount = plan?.activeRuleCount ?? rules.filter((rule) => rule.count > 0).length;
|
||||
const p0RuleCount = plan?.p0RuleCount ?? rules.filter((rule) => rule.count > 0 && rule.level === 'P0').length;
|
||||
const release = health?.runtime?.platformRelease ?? '';
|
||||
const p0IssueCount = priorityIssues.filter((issue) => issue.priority === 'P0').length;
|
||||
const evidenceCompleteCount = priorityIssues.filter((issue) => countEvidenceLinks(issue) >= 4).length;
|
||||
const primaryOwner = rules.find((rule) => rule.count > 0)?.owner || policies[0]?.target || '-';
|
||||
const nextPriorityIssue = priorityIssues[0];
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
@@ -146,6 +163,51 @@ export function NotificationRules() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<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>}
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
<div className="vp-alert-command-board">
|
||||
<div className="vp-alert-command-grid">
|
||||
{[
|
||||
{ label: '待通知告警', value: priorityIssues.length.toLocaleString(), color: priorityIssues.length > 0 ? 'orange' as const : 'green' as const },
|
||||
{ label: 'P0优先', value: p0IssueCount.toLocaleString(), color: p0IssueCount > 0 ? 'red' as const : 'green' as const },
|
||||
{ label: '证据完整', value: `${evidenceCompleteCount}/${priorityIssues.length || 0}`, color: evidenceCompleteCount === priorityIssues.length ? 'green' as const : 'orange' as const },
|
||||
{ label: '主责任方', value: primaryOwner, color: 'blue' as const }
|
||||
].map((item) => (
|
||||
<div key={item.label} className="vp-alert-command-item">
|
||||
<Tag color={item.color}>{item.label}</Tag>
|
||||
<div>{item.value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="vp-alert-next-action">
|
||||
<div className="vp-current-service-title">下一步处置</div>
|
||||
{nextPriorityIssue ? (
|
||||
<>
|
||||
<Typography.Text strong>{nextPriorityIssue.vehicleLabel}</Typography.Text>
|
||||
<Typography.Text type="secondary">{nextPriorityIssue.actionDetail || nextPriorityIssue.actionLabel}</Typography.Text>
|
||||
<Space wrap>
|
||||
<Tag color={nextPriorityIssue.priority === 'P0' ? 'red' : 'orange'}>{nextPriorityIssue.priority}</Tag>
|
||||
<Tag color={evidenceColor(nextPriorityIssue)}>{evidenceSummary(nextPriorityIssue)}</Tag>
|
||||
<Tag color="blue">{nextPriorityIssue.sla}</Tag>
|
||||
</Space>
|
||||
<Space wrap>
|
||||
<Button size="small" aria-label="下一步车辆服务" disabled={!nextPriorityIssue.vehicleHash} onClick={() => navigateHash(nextPriorityIssue.vehicleHash)}>车辆服务</Button>
|
||||
<Button size="small" aria-label="下一步实时证据" disabled={!nextPriorityIssue.realtimeHash} onClick={() => navigateHash(nextPriorityIssue.realtimeHash)}>实时证据</Button>
|
||||
<Button size="small" aria-label="下一步轨迹证据" disabled={!nextPriorityIssue.historyHash} onClick={() => navigateHash(nextPriorityIssue.historyHash)}>轨迹证据</Button>
|
||||
<Button size="small" aria-label="下一步RAW证据" disabled={!nextPriorityIssue.rawHash} onClick={() => navigateHash(nextPriorityIssue.rawHash)}>RAW证据</Button>
|
||||
</Space>
|
||||
</>
|
||||
) : (
|
||||
<Tag color="green">暂无待通知告警</Tag>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
bordered
|
||||
title={<Space><span>规则运行手册</span><Button size="small" aria-label="复制通知规则Runbook" icon={<IconCopy />} onClick={() => copyText(notificationRulesRunbook(plan, release), '通知规则Runbook')}>复制通知规则Runbook</Button><Button size="small" loading={loading} onClick={load}>刷新</Button></Space>}
|
||||
@@ -185,7 +247,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="当前待通知告警"
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
<Table<QualityPriorityIssue>
|
||||
@@ -197,6 +259,7 @@ export function NotificationRules() {
|
||||
{ title: '车辆', width: 210, dataIndex: 'vehicleLabel' },
|
||||
{ title: '问题', width: 130, render: (_: unknown, row: QualityPriorityIssue) => qualityIssueLabel(row.issueType) },
|
||||
{ title: 'SLA', width: 120, dataIndex: 'sla' },
|
||||
{ title: '证据完整度', width: 120, render: (_: unknown, row: QualityPriorityIssue) => <Tag color={evidenceColor(row)}>{evidenceSummary(row)}</Tag> },
|
||||
{ title: '建议动作', width: 160, dataIndex: 'actionLabel' },
|
||||
{ title: '最后时间', width: 170, dataIndex: 'lastSeen' },
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user