feat(platform): prioritize alert handling
This commit is contained in:
@@ -102,6 +102,13 @@ type AlertRuleRow = {
|
||||
count: number;
|
||||
};
|
||||
|
||||
type PriorityIssueRow = QualityIssueRow & {
|
||||
priority: 'P0' | 'P1';
|
||||
actionLabel: string;
|
||||
sla: string;
|
||||
vehicleLabel: string;
|
||||
};
|
||||
|
||||
function qualityParams(values: Record<string, string>) {
|
||||
const params = new URLSearchParams();
|
||||
if (values?.keyword) params.set('keyword', values.keyword);
|
||||
@@ -154,6 +161,13 @@ function issueCount(summary: QualitySummary, issueType: string) {
|
||||
return summary.issueTypes.find((item) => item.name === issueType)?.count ?? 0;
|
||||
}
|
||||
|
||||
const issuePriorityWeight: Record<string, number> = {
|
||||
NO_SOURCE: 10,
|
||||
VIN_MISSING: 9,
|
||||
LINK_GAP: 8,
|
||||
FIELD_MISSING: 7
|
||||
};
|
||||
|
||||
function alertRuleRows(summary: QualitySummary, health: OpsHealth | null): AlertRuleRow[] {
|
||||
const storageWritable = health == null || (health.tdengineWritable && health.mysqlWritable);
|
||||
const capacityCount = health?.capacityFindings?.length ?? 0;
|
||||
@@ -173,6 +187,43 @@ function alertRuleRows(summary: QualitySummary, health: OpsHealth | null): Alert
|
||||
return rows;
|
||||
}
|
||||
|
||||
function issueSla(issueType: string) {
|
||||
return alertRuleTemplates.find((item) => item.issueType === issueType)?.sla ?? '当日闭环';
|
||||
}
|
||||
|
||||
function priorityIssueLevel(row: QualityIssueRow): 'P0' | 'P1' {
|
||||
if (row.severity === 'error' || row.issueType === 'NO_SOURCE') {
|
||||
return 'P0';
|
||||
}
|
||||
return 'P1';
|
||||
}
|
||||
|
||||
function priorityVehicleLabel(row: QualityIssueRow) {
|
||||
const identity = row.vin?.trim() || row.phone?.trim() || row.sourceEndpoint?.trim() || '-';
|
||||
return [row.plate?.trim(), identity].filter(Boolean).join(' / ');
|
||||
}
|
||||
|
||||
function priorityIssueRows(rows: QualityIssueRow[]): PriorityIssueRow[] {
|
||||
return rows
|
||||
.map((row) => {
|
||||
const action = qualityActionRecommendation(row);
|
||||
return {
|
||||
...row,
|
||||
priority: priorityIssueLevel(row),
|
||||
actionLabel: action.label,
|
||||
sla: issueSla(row.issueType),
|
||||
vehicleLabel: priorityVehicleLabel(row)
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
if (a.priority !== b.priority) return a.priority === 'P0' ? -1 : 1;
|
||||
const weightDelta = (issuePriorityWeight[b.issueType] ?? 0) - (issuePriorityWeight[a.issueType] ?? 0);
|
||||
if (weightDelta !== 0) return weightDelta;
|
||||
return String(b.lastSeen ?? '').localeCompare(String(a.lastSeen ?? ''));
|
||||
})
|
||||
.slice(0, 5);
|
||||
}
|
||||
|
||||
function ruleStatusColor(count: number, level: string): 'green' | 'orange' | 'red' | 'grey' {
|
||||
if (count <= 0) return 'green';
|
||||
if (level === 'P0') return 'red';
|
||||
@@ -221,6 +272,7 @@ export function Quality({
|
||||
const rules = alertRuleRows(summary, health);
|
||||
const activeRuleCount = rules.filter((item) => item.count > 0).length;
|
||||
const p0RuleCount = rules.filter((item) => item.count > 0 && item.level === 'P0').length;
|
||||
const priorityRows = priorityIssueRows(issues);
|
||||
|
||||
const loadIssues = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
|
||||
setLoadingIssues(true);
|
||||
@@ -348,6 +400,31 @@ export function Quality({
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<Card bordered title="处置优先队列" style={{ marginTop: 16 }}>
|
||||
<Table<PriorityIssueRow>
|
||||
loading={loadingIssues}
|
||||
pagination={false}
|
||||
rowKey={(row?: PriorityIssueRow) => `${row?.priority ?? ''}-${row?.protocol ?? ''}-${row?.issueType ?? ''}-${row?.vehicleLabel ?? ''}-${row?.lastSeen ?? ''}`}
|
||||
dataSource={priorityRows}
|
||||
columns={[
|
||||
{ title: '优先级', width: 90, render: (_: unknown, row: PriorityIssueRow) => <Tag color={row.priority === 'P0' ? 'red' : 'orange'}>{row.priority}</Tag> },
|
||||
{ title: '车辆', width: 210, dataIndex: 'vehicleLabel' },
|
||||
{ title: '问题', width: 140, render: (_: unknown, row: PriorityIssueRow) => qualityIssueLabel(row.issueType) },
|
||||
{ title: '建议动作', width: 150, dataIndex: 'actionLabel' },
|
||||
{ title: 'SLA', width: 130, dataIndex: 'sla' },
|
||||
{ title: '最后时间', width: 170, dataIndex: 'lastSeen' },
|
||||
{ title: '说明', dataIndex: 'detail' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
render: (_: unknown, row: PriorityIssueRow) => {
|
||||
const lookup = qualityIssueVehicleLookup(row);
|
||||
return <Button size="small" disabled={!lookup.key} onClick={() => onOpenVehicle(lookup.key, row.protocol)}>进入车辆服务</Button>;
|
||||
}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
<div className="vp-alert-ops-grid">
|
||||
<Card bordered title="告警触发规则">
|
||||
<Table<AlertRuleRow>
|
||||
|
||||
Reference in New Issue
Block a user