feat(platform): refine customer alert evidence copy
This commit is contained in:
@@ -110,7 +110,7 @@ var qualityAlertRuleTemplates = []QualityAlertRule{
|
|||||||
Level: "P1",
|
Level: "P1",
|
||||||
Owner: "协议解析",
|
Owner: "协议解析",
|
||||||
Trigger: "核心字段缺失导致定位、里程或统计不可用",
|
Trigger: "核心字段缺失导致定位、里程或统计不可用",
|
||||||
Notify: "通知解析负责人核对字段映射和 RAW 样本",
|
Notify: "通知解析负责人核对字段映射和原始记录样本",
|
||||||
SLA: "当日闭环",
|
SLA: "当日闭环",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ var qualityNotificationPolicies = []QualityNotificationPolicy{
|
|||||||
Channel: "站内告警 / 每日汇总邮件",
|
Channel: "站内告警 / 每日汇总邮件",
|
||||||
Condition: "字段缺失、链路间断、容量风险",
|
Condition: "字段缺失、链路间断、容量风险",
|
||||||
EscalationMinutes: 120,
|
EscalationMinutes: 120,
|
||||||
AcceptanceCriteria: "核心字段恢复解析,影响车辆可通过 RAW 与历史证据复核",
|
AcceptanceCriteria: "核心字段恢复解析,影响车辆可通过原始记录与历史证据复核",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "P2 趋势关注",
|
Name: "P2 趋势关注",
|
||||||
@@ -207,8 +207,8 @@ func buildSourceReadinessPlan(summary VehicleServiceSummary, health OpsHealth, q
|
|||||||
Severity: "ok",
|
Severity: "ok",
|
||||||
Status: "生产可用",
|
Status: "生产可用",
|
||||||
Evidence: sourceReadinessEvidence(stat, missing, health),
|
Evidence: sourceReadinessEvidence(stat, missing, health),
|
||||||
Action: "保持实时、轨迹、RAW 和统计链路巡检。",
|
Action: "保持实时、轨迹、原始记录和统计链路巡检。",
|
||||||
Acceptance: "该来源有在线车辆、无消息积压,车辆服务可回查实时、轨迹和 RAW 证据。",
|
Acceptance: "该来源有在线车辆、无消息积压,车辆服务可回查实时、轨迹和原始记录。",
|
||||||
VehiclesHash: buildHash("vehicles", "", protocol, map[string]string{"missingProtocol": protocol}),
|
VehiclesHash: buildHash("vehicles", "", protocol, map[string]string{"missingProtocol": protocol}),
|
||||||
RealtimeHash: buildHash("realtime", "", protocol, map[string]string{"online": "online"}),
|
RealtimeHash: buildHash("realtime", "", protocol, map[string]string{"online": "online"}),
|
||||||
HistoryHash: buildHash("history-query", "", protocol, map[string]string{"tab": "raw", "includeFields": "true"}),
|
HistoryHash: buildHash("history-query", "", protocol, map[string]string{"tab": "raw", "includeFields": "true"}),
|
||||||
@@ -218,7 +218,7 @@ func buildSourceReadinessPlan(summary VehicleServiceSummary, health OpsHealth, q
|
|||||||
row.Severity = "error"
|
row.Severity = "error"
|
||||||
row.Status = "未形成来源证据"
|
row.Status = "未形成来源证据"
|
||||||
row.Action = "确认平台账号、端口监听、订阅配置和协议接入服务是否启动。"
|
row.Action = "确认平台账号、端口监听、订阅配置和协议接入服务是否启动。"
|
||||||
row.Acceptance = "该协议至少出现绑定车辆和实时 RAW 证据。"
|
row.Acceptance = "该来源至少出现绑定车辆和实时原始记录。"
|
||||||
} else if stat.Online <= 0 {
|
} else if stat.Online <= 0 {
|
||||||
row.Severity = "error"
|
row.Severity = "error"
|
||||||
row.Status = "来源全离线"
|
row.Status = "来源全离线"
|
||||||
@@ -1334,7 +1334,7 @@ func qualityNotificationText(row QualityPriorityIssue) string {
|
|||||||
"详情:" + firstNonEmpty(row.Detail, "-"),
|
"详情:" + firstNonEmpty(row.Detail, "-"),
|
||||||
"实时定位:" + row.RealtimeHash,
|
"实时定位:" + row.RealtimeHash,
|
||||||
"轨迹证据:" + row.HistoryHash,
|
"轨迹证据:" + row.HistoryHash,
|
||||||
"RAW证据:" + row.RawHash,
|
"原始记录:" + row.RawHash,
|
||||||
"车辆服务:" + row.VehicleHash,
|
"车辆服务:" + row.VehicleHash,
|
||||||
}, "\n")
|
}, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ function slaEscalationReport(rows: SlaEscalationRow[], release?: string) {
|
|||||||
` 问题:${qualityIssueLabel(row.issue.issueType)} / ${row.issue.protocol}`,
|
` 问题:${qualityIssueLabel(row.issue.issueType)} / ${row.issue.protocol}`,
|
||||||
` 建议动作:${row.issue.actionLabel}`,
|
` 建议动作:${row.issue.actionLabel}`,
|
||||||
` 车辆服务:${window.location.origin}${window.location.pathname}${row.issue.vehicleHash}`,
|
` 车辆服务:${window.location.origin}${window.location.pathname}${row.issue.vehicleHash}`,
|
||||||
` RAW证据:${window.location.origin}${window.location.pathname}${row.issue.rawHash}`
|
` 原始记录:${window.location.origin}${window.location.pathname}${row.issue.rawHash}`
|
||||||
].join('\n')) : ['暂无待升级告警'])
|
].join('\n')) : ['暂无待升级告警'])
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
@@ -239,7 +239,7 @@ function notificationExecutionMatrixReport(rows: NotificationExecutionRow[], rel
|
|||||||
` 证据:${row.evidenceText}`,
|
` 证据:${row.evidenceText}`,
|
||||||
` 动作:${row.issue.actionLabel} - ${row.issue.actionDetail || '-'}`,
|
` 动作:${row.issue.actionLabel} - ${row.issue.actionDetail || '-'}`,
|
||||||
` 车辆服务:${window.location.origin}${window.location.pathname}${row.issue.vehicleHash}`,
|
` 车辆服务:${window.location.origin}${window.location.pathname}${row.issue.vehicleHash}`,
|
||||||
` RAW证据:${window.location.origin}${window.location.pathname}${row.issue.rawHash}`
|
` 原始记录:${window.location.origin}${window.location.pathname}${row.issue.rawHash}`
|
||||||
].join('\n')) : ['暂无待执行通知'])
|
].join('\n')) : ['暂无待执行通知'])
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
@@ -313,7 +313,7 @@ function notificationCoverageReport({
|
|||||||
'',
|
'',
|
||||||
'管理动作:',
|
'管理动作:',
|
||||||
'1. P0 命中先确认责任方、渠道和升级窗口是否明确',
|
'1. P0 命中先确认责任方、渠道和升级窗口是否明确',
|
||||||
'2. 证据不完整的车辆先补齐车辆服务、实时、轨迹、RAW 链接',
|
'2. 证据不完整的车辆先补齐车辆服务、实时、轨迹和原始记录链接',
|
||||||
'3. 超时或即将升级的告警必须同步业务责任人',
|
'3. 超时或即将升级的告警必须同步业务责任人',
|
||||||
'4. 链路异常时优先恢复采集、MySQL、TDEngine、Redis、Kafka 等前置能力'
|
'4. 链路异常时优先恢复采集、MySQL、TDEngine、Redis、Kafka 等前置能力'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@@ -364,7 +364,7 @@ function priorityIssueDigest(plan: QualityNotificationPlan | null, release?: str
|
|||||||
` 车辆服务:${window.location.origin}${window.location.pathname}${issue.vehicleHash}`,
|
` 车辆服务:${window.location.origin}${window.location.pathname}${issue.vehicleHash}`,
|
||||||
` 实时定位:${window.location.origin}${window.location.pathname}${issue.realtimeHash}`,
|
` 实时定位:${window.location.origin}${window.location.pathname}${issue.realtimeHash}`,
|
||||||
` 轨迹证据:${window.location.origin}${window.location.pathname}${issue.historyHash}`,
|
` 轨迹证据:${window.location.origin}${window.location.pathname}${issue.historyHash}`,
|
||||||
` RAW证据:${window.location.origin}${window.location.pathname}${issue.rawHash}`
|
` 原始记录:${window.location.origin}${window.location.pathname}${issue.rawHash}`
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
@@ -398,12 +398,12 @@ function alertEscalationChecklist(plan: QualityNotificationPlan | null, release?
|
|||||||
`车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`,
|
`车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`,
|
||||||
`实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`,
|
`实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`,
|
||||||
`轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`,
|
`轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`,
|
||||||
`RAW证据:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}`
|
`原始记录:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}`
|
||||||
].join('\n') : '暂无待处置告警',
|
].join('\n') : '暂无待处置告警',
|
||||||
'',
|
'',
|
||||||
'值班动作:',
|
'值班动作:',
|
||||||
'1. 先处理 P0,无来源/VIN 缺失/存储不可写优先于普通字段缺失',
|
'1. 先处理 P0,无来源/VIN 缺失/存储不可写优先于普通字段缺失',
|
||||||
'2. 复制单条通知给责任团队,并同步车辆服务、实时、轨迹、RAW 证据',
|
'2. 复制单条通知给责任团队,并同步车辆服务、实时、轨迹和原始记录',
|
||||||
'3. 超过升级窗口未恢复时,按主通知策略升级到业务责任人',
|
'3. 超过升级窗口未恢复时,按主通知策略升级到业务责任人',
|
||||||
'4. 恢复后检查验收口径,并在告警事件页确认不再命中'
|
'4. 恢复后检查验收口径,并在告警事件页确认不再命中'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@@ -435,7 +435,7 @@ function alertNotificationDrillPackage(plan: QualityNotificationPlan | null, hea
|
|||||||
'',
|
'',
|
||||||
'演练步骤:',
|
'演练步骤:',
|
||||||
'1. 在告警事件页确认 P0/P1 命中数量与本页一致',
|
'1. 在告警事件页确认 P0/P1 命中数量与本页一致',
|
||||||
'2. 对第一条待通知告警打开车辆服务、实时、轨迹、RAW 四类证据',
|
'2. 对第一条待通知告警打开车辆服务、实时、轨迹、原始记录四类证据',
|
||||||
'3. 复制单条告警通知给责任团队,记录发送时间',
|
'3. 复制单条告警通知给责任团队,记录发送时间',
|
||||||
'4. 超过升级窗口仍未恢复时,按策略升级到业务责任人',
|
'4. 超过升级窗口仍未恢复时,按策略升级到业务责任人',
|
||||||
'5. 恢复后按验收口径复核,并确认告警不再命中',
|
'5. 恢复后按验收口径复核,并确认告警不再命中',
|
||||||
@@ -449,7 +449,7 @@ function alertNotificationDrillPackage(plan: QualityNotificationPlan | null, hea
|
|||||||
`车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`,
|
`车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`,
|
||||||
`实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`,
|
`实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`,
|
||||||
`轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`,
|
`轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`,
|
||||||
`RAW证据:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}`
|
`原始记录:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}`
|
||||||
].join('\n') : '暂无待通知样例'
|
].join('\n') : '暂无待通知样例'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
@@ -565,7 +565,7 @@ export function NotificationRules() {
|
|||||||
step: '02',
|
step: '02',
|
||||||
title: '补齐证据',
|
title: '补齐证据',
|
||||||
value: `${evidenceCompleteCount}/${priorityIssues.length || 0} 完整`,
|
value: `${evidenceCompleteCount}/${priorityIssues.length || 0} 完整`,
|
||||||
detail: '通知前必须带上车辆服务、实时监控、轨迹回放和RAW证据链接。',
|
detail: '通知前必须带上车辆服务、实时监控、轨迹回放和原始记录链接。',
|
||||||
action: '证据矩阵',
|
action: '证据矩阵',
|
||||||
color: evidenceCompleteCount === priorityIssues.length ? 'green' as const : 'orange' as const,
|
color: evidenceCompleteCount === priorityIssues.length ? 'green' as const : 'orange' as const,
|
||||||
onClick: () => nextPriorityIssue?.rawHash ? navigateHash(nextPriorityIssue.rawHash) : undefined
|
onClick: () => nextPriorityIssue?.rawHash ? navigateHash(nextPriorityIssue.rawHash) : undefined
|
||||||
@@ -766,7 +766,7 @@ export function NotificationRules() {
|
|||||||
<Button size="small" aria-label="下一步车辆服务" disabled={!nextPriorityIssue.vehicleHash} onClick={() => navigateHash(nextPriorityIssue.vehicleHash)}>车辆服务</Button>
|
<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.realtimeHash} onClick={() => navigateHash(nextPriorityIssue.realtimeHash)}>实时证据</Button>
|
||||||
<Button size="small" aria-label="下一步轨迹证据" disabled={!nextPriorityIssue.historyHash} onClick={() => navigateHash(nextPriorityIssue.historyHash)}>轨迹证据</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>
|
<Button size="small" aria-label="下一步原始记录" disabled={!nextPriorityIssue.rawHash} onClick={() => navigateHash(nextPriorityIssue.rawHash)}>原始记录</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -801,7 +801,7 @@ export function NotificationRules() {
|
|||||||
render: (_: unknown, row: NotificationExecutionRow) => (
|
render: (_: unknown, row: NotificationExecutionRow) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Button size="small" disabled={!row.issue.vehicleHash} onClick={() => navigateHash(row.issue.vehicleHash)}>车辆服务</Button>
|
<Button size="small" disabled={!row.issue.vehicleHash} onClick={() => navigateHash(row.issue.vehicleHash)}>车辆服务</Button>
|
||||||
<Button size="small" disabled={!row.issue.rawHash} onClick={() => navigateHash(row.issue.rawHash)}>RAW证据</Button>
|
<Button size="small" disabled={!row.issue.rawHash} onClick={() => navigateHash(row.issue.rawHash)}>原始记录</Button>
|
||||||
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.issue.notificationText, '告警通知')}>复制通知</Button>
|
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.issue.notificationText, '告警通知')}>复制通知</Button>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
@@ -846,7 +846,7 @@ export function NotificationRules() {
|
|||||||
render: (_: unknown, row: SlaEscalationRow) => (
|
render: (_: unknown, row: SlaEscalationRow) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Button size="small" disabled={!row.issue.vehicleHash} onClick={() => navigateHash(row.issue.vehicleHash)}>车辆服务</Button>
|
<Button size="small" disabled={!row.issue.vehicleHash} onClick={() => navigateHash(row.issue.vehicleHash)}>车辆服务</Button>
|
||||||
<Button size="small" disabled={!row.issue.rawHash} onClick={() => navigateHash(row.issue.rawHash)}>RAW证据</Button>
|
<Button size="small" disabled={!row.issue.rawHash} onClick={() => navigateHash(row.issue.rawHash)}>原始记录</Button>
|
||||||
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.issue.notificationText, '告警通知')}>复制通知</Button>
|
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.issue.notificationText, '告警通知')}>复制通知</Button>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
@@ -924,7 +924,7 @@ export function NotificationRules() {
|
|||||||
<Button size="small" disabled={!row.vehicleHash} onClick={() => navigateHash(row.vehicleHash)}>车辆服务</Button>
|
<Button size="small" disabled={!row.vehicleHash} onClick={() => navigateHash(row.vehicleHash)}>车辆服务</Button>
|
||||||
<Button size="small" disabled={!row.realtimeHash} onClick={() => navigateHash(row.realtimeHash)}>实时</Button>
|
<Button size="small" disabled={!row.realtimeHash} onClick={() => navigateHash(row.realtimeHash)}>实时</Button>
|
||||||
<Button size="small" disabled={!row.historyHash} onClick={() => navigateHash(row.historyHash)}>轨迹</Button>
|
<Button size="small" disabled={!row.historyHash} onClick={() => navigateHash(row.historyHash)}>轨迹</Button>
|
||||||
<Button size="small" disabled={!row.rawHash} onClick={() => navigateHash(row.rawHash)}>RAW</Button>
|
<Button size="small" disabled={!row.rawHash} onClick={() => navigateHash(row.rawHash)}>原始</Button>
|
||||||
<Button size="small" aria-label="复制单条告警通知" icon={<IconCopy />} onClick={() => copyText(row.notificationText, '告警通知')}>复制通知</Button>
|
<Button size="small" aria-label="复制单条告警通知" icon={<IconCopy />} onClick={() => copyText(row.notificationText, '告警通知')}>复制通知</Button>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const alertRuleTemplates = [
|
|||||||
level: 'P1',
|
level: 'P1',
|
||||||
owner: '协议解析',
|
owner: '协议解析',
|
||||||
trigger: '核心字段缺失导致定位、里程或统计不可用',
|
trigger: '核心字段缺失导致定位、里程或统计不可用',
|
||||||
notify: '通知解析负责人核对字段映射和 RAW 样本',
|
notify: '通知解析负责人核对字段映射和原始记录样本',
|
||||||
sla: '当日闭环'
|
sla: '当日闭环'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -87,7 +87,7 @@ const notificationPolicies = [
|
|||||||
channel: '站内告警 / 每日汇总邮件',
|
channel: '站内告警 / 每日汇总邮件',
|
||||||
condition: '字段缺失、链路间断、容量风险',
|
condition: '字段缺失、链路间断、容量风险',
|
||||||
escalationMinutes: 120,
|
escalationMinutes: 120,
|
||||||
acceptanceCriteria: '核心字段恢复解析,影响车辆可通过 RAW 与历史证据复核'
|
acceptanceCriteria: '核心字段恢复解析,影响车辆可通过原始记录与历史证据复核'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'P2 趋势关注',
|
name: 'P2 趋势关注',
|
||||||
@@ -187,7 +187,7 @@ function qualityActionRecommendation(row: QualityIssueRow) {
|
|||||||
return {
|
return {
|
||||||
label: '核对解析字段',
|
label: '核对解析字段',
|
||||||
color: 'orange' as const,
|
color: 'orange' as const,
|
||||||
detail: '字段缺失会影响统计和展示,优先核对解析映射和原始 RAW。'
|
detail: '字段缺失会影响统计和展示,优先核对解析映射和原始记录。'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -317,7 +317,7 @@ function priorityIssueNotificationText(row: PriorityIssueRow) {
|
|||||||
`详情:${row.detail || '-'}`,
|
`详情:${row.detail || '-'}`,
|
||||||
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
`轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
`轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
||||||
`RAW证据:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
`原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
||||||
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
|
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
`告警筛选:${qualityShareURL()}`
|
`告警筛选:${qualityShareURL()}`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@@ -329,10 +329,10 @@ function alertRuleTemplate(issueType: string) {
|
|||||||
|
|
||||||
function acceptanceCriteria(row: PriorityIssueRow) {
|
function acceptanceCriteria(row: PriorityIssueRow) {
|
||||||
if (row.issueType === 'VIN_MISSING') {
|
if (row.issueType === 'VIN_MISSING') {
|
||||||
return '车辆能解析到 VIN,实时/历史/RAW 证据可通过同一车辆服务查询';
|
return '车辆能解析到 VIN,实时、历史和原始记录可通过同一车辆服务查询';
|
||||||
}
|
}
|
||||||
if (row.issueType === 'NO_SOURCE') {
|
if (row.issueType === 'NO_SOURCE') {
|
||||||
return '至少一个生产来源恢复在线,车辆实时状态、历史轨迹和 RAW 证据可查询';
|
return '至少一个生产来源恢复在线,车辆实时状态、历史轨迹和原始记录可查询';
|
||||||
}
|
}
|
||||||
if (row.issueType === 'LINK_GAP') {
|
if (row.issueType === 'LINK_GAP') {
|
||||||
return '来源恢复连续上报,实时在线窗口和历史轨迹无异常断点';
|
return '来源恢复连续上报,实时在线窗口和历史轨迹无异常断点';
|
||||||
@@ -369,12 +369,12 @@ function priorityIssueTicketText(row: PriorityIssueRow) {
|
|||||||
`处置说明:${row.actionDetail || '-'}`,
|
`处置说明:${row.actionDetail || '-'}`,
|
||||||
'处置步骤:',
|
'处置步骤:',
|
||||||
'1. 打开车辆服务核对当前身份解析结果',
|
'1. 打开车辆服务核对当前身份解析结果',
|
||||||
'2. 核对实时定位、轨迹证据和 RAW 证据',
|
'2. 核对实时定位、轨迹证据和原始记录',
|
||||||
'3. 完成后确认车辆服务状态恢复,问题不再命中当前告警',
|
'3. 完成后确认车辆服务状态恢复,问题不再命中当前告警',
|
||||||
`验收标准:${acceptanceCriteria(row)}`,
|
`验收标准:${acceptanceCriteria(row)}`,
|
||||||
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
`轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
`轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
||||||
`RAW证据:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
`原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
||||||
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
|
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
`告警筛选:${qualityShareURL()}`
|
`告警筛选:${qualityShareURL()}`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@@ -402,7 +402,7 @@ function priorityIssueEvidencePackageText(row: PriorityIssueRow) {
|
|||||||
`验收标准:${acceptanceCriteria(row)}`,
|
`验收标准:${acceptanceCriteria(row)}`,
|
||||||
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
`轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
`轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
||||||
`RAW证据:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
`原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
||||||
`里程复核:${appURL(buildAppHash({ page: 'mileage', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
`里程复核:${appURL(buildAppHash({ page: 'mileage', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
||||||
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
|
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
`告警筛选:${qualityShareURL()}`
|
`告警筛选:${qualityShareURL()}`
|
||||||
@@ -580,7 +580,7 @@ function notificationHandoffText({
|
|||||||
` 详情:${row.detail || '-'}`,
|
` 详情:${row.detail || '-'}`,
|
||||||
` 实时:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
` 实时:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
|
||||||
` 轨迹:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
` 轨迹:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`,
|
||||||
` RAW:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
` 原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`,
|
||||||
` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`
|
` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
})
|
})
|
||||||
@@ -593,7 +593,7 @@ function notificationHandoffText({
|
|||||||
`优先级:P0 ${p0Rows.length.toLocaleString()} 条 / P1 ${p1Rows.length.toLocaleString()} 条`,
|
`优先级:P0 ${p0Rows.length.toLocaleString()} 条 / P1 ${p1Rows.length.toLocaleString()} 条`,
|
||||||
`活跃规则:${activeRules.length.toLocaleString()} 类${activeRules.length > 0 ? `(${activeRules.map((row) => `${row.level} ${row.title || qualityIssueLabel(row.issueType)} ${row.count}`).join(';')})` : ''}`,
|
`活跃规则:${activeRules.length.toLocaleString()} 类${activeRules.length > 0 ? `(${activeRules.map((row) => `${row.level} ${row.title || qualityIssueLabel(row.issueType)} ${row.count}`).join(';')})` : ''}`,
|
||||||
primaryPolicy ? `主通知策略:${[primaryPolicy.name, primaryPolicy.target, primaryPolicy.channel, formatEscalationMinutes(primaryPolicy.escalationMinutes)].filter(Boolean).join(' / ')}` : '主通知策略:-',
|
primaryPolicy ? `主通知策略:${[primaryPolicy.name, primaryPolicy.target, primaryPolicy.channel, formatEscalationMinutes(primaryPolicy.escalationMinutes)].filter(Boolean).join(' / ')}` : '主通知策略:-',
|
||||||
primaryPolicy?.acceptanceCriteria ? `统一验收:${primaryPolicy.acceptanceCriteria}` : '统一验收:车辆服务状态恢复,实时、轨迹、RAW 证据可查询',
|
primaryPolicy?.acceptanceCriteria ? `统一验收:${primaryPolicy.acceptanceCriteria}` : '统一验收:车辆服务状态恢复,实时、轨迹和原始记录可查询',
|
||||||
'',
|
'',
|
||||||
'待通知车辆:',
|
'待通知车辆:',
|
||||||
...issueLines,
|
...issueLines,
|
||||||
@@ -708,7 +708,7 @@ function alertCustomerDecisionText({
|
|||||||
'客户处置路径:',
|
'客户处置路径:',
|
||||||
'1. 先确认影响车辆和 P0/P1 数量。',
|
'1. 先确认影响车辆和 P0/P1 数量。',
|
||||||
'2. P0 立即通知责任团队,超过 SLA 自动升级。',
|
'2. P0 立即通知责任团队,超过 SLA 自动升级。',
|
||||||
'3. 每辆焦点车都要打开实时、轨迹、RAW、里程证据闭环。',
|
'3. 每辆焦点车都要打开实时、轨迹、原始记录、里程证据闭环。',
|
||||||
'4. 恢复后确认告警不再命中,并保留交接包。',
|
'4. 恢复后确认告警不再命中,并保留交接包。',
|
||||||
'',
|
'',
|
||||||
`告警事件:${qualityShareURL()}`,
|
`告警事件:${qualityShareURL()}`,
|
||||||
@@ -1031,7 +1031,7 @@ export function Quality({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '证据闭环',
|
label: '证据闭环',
|
||||||
value: '实时/轨迹/RAW/里程',
|
value: '实时/轨迹/原始/里程',
|
||||||
detail: '所有通知都必须回到车辆证据,不只停留在来源或链路指标。',
|
detail: '所有通知都必须回到车辆证据,不只停留在来源或链路指标。',
|
||||||
color: 'blue' as const,
|
color: 'blue' as const,
|
||||||
action: '交接包',
|
action: '交接包',
|
||||||
@@ -1067,7 +1067,7 @@ export function Quality({
|
|||||||
</Space>
|
</Space>
|
||||||
<strong>{issueVehicleCount.toLocaleString()} 辆车受影响</strong>
|
<strong>{issueVehicleCount.toLocaleString()} 辆车受影响</strong>
|
||||||
<span>
|
<span>
|
||||||
告警中心面向客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、RAW、里程证据闭环。
|
告警中心面向客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、原始记录、里程证据闭环。
|
||||||
</span>
|
</span>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Button size="small" theme="solid" type="primary" disabled={priorityRows.length === 0} onClick={copyNotificationHandoff}>复制中心交接包</Button>
|
<Button size="small" theme="solid" type="primary" disabled={priorityRows.length === 0} onClick={copyNotificationHandoff}>复制中心交接包</Button>
|
||||||
@@ -1147,7 +1147,7 @@ export function Quality({
|
|||||||
<strong>{focusIssue?.vehicleLabel || '暂无优先车辆'}</strong>
|
<strong>{focusIssue?.vehicleLabel || '暂无优先车辆'}</strong>
|
||||||
<span>
|
<span>
|
||||||
{focusIssue
|
{focusIssue
|
||||||
? `${focusIssue.actionLabel}:${focusIssue.actionDetail || focusIssue.detail || '请结合实时、轨迹、RAW 和里程证据闭环。'}`
|
? `${focusIssue.actionLabel}:${focusIssue.actionDetail || focusIssue.detail || '请结合实时、轨迹、原始记录和里程证据闭环。'}`
|
||||||
: '当前没有进入优先队列的车辆,保持实时监控并定期复核告警规则。'}
|
: '当前没有进入优先队列的车辆,保持实时监控并定期复核告警规则。'}
|
||||||
</span>
|
</span>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
@@ -1190,9 +1190,9 @@ export function Quality({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '原始证据',
|
label: '原始证据',
|
||||||
value: focusIssue ? '查RAW' : '待命',
|
value: focusIssue ? '查原始' : '待命',
|
||||||
detail: '保留原始帧和解析字段,给平台、客户和研发确认责任边界。',
|
detail: '保留原始帧和解析字段,给平台、客户和研发确认责任边界。',
|
||||||
action: 'RAW证据',
|
action: '原始记录',
|
||||||
color: focusIssue ? 'blue' as const : 'grey' as const,
|
color: focusIssue ? 'blue' as const : 'grey' as const,
|
||||||
disabled: !focusIssue || !focusLookup?.key || !onOpenRaw,
|
disabled: !focusIssue || !focusLookup?.key || !onOpenRaw,
|
||||||
onClick: () => focusIssue && openIssueRaw(focusIssue)
|
onClick: () => focusIssue && openIssueRaw(focusIssue)
|
||||||
@@ -1357,7 +1357,7 @@ export function Quality({
|
|||||||
<Space spacing={4} wrap>
|
<Space spacing={4} wrap>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenRealtime} onClick={() => openIssueRealtime(row)}>实时定位</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenRealtime} onClick={() => openIssueRealtime(row)}>实时定位</Button>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenHistory} onClick={() => openIssueHistory(row)}>轨迹证据</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenHistory} onClick={() => openIssueHistory(row)}>轨迹证据</Button>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenRaw} onClick={() => openIssueRaw(row)}>RAW证据</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenRaw} onClick={() => openIssueRaw(row)}>原始记录</Button>
|
||||||
<Button size="small" onClick={() => copyText(row.notificationText || priorityIssueNotificationText(row), '告警通知')}>复制通知</Button>
|
<Button size="small" onClick={() => copyText(row.notificationText || priorityIssueNotificationText(row), '告警通知')}>复制通知</Button>
|
||||||
<Button size="small" onClick={() => copyText(priorityIssueEvidencePackageText(row), '告警证据包')}>复制证据包</Button>
|
<Button size="small" onClick={() => copyText(priorityIssueEvidencePackageText(row), '告警证据包')}>复制证据包</Button>
|
||||||
<Button size="small" onClick={() => copyText(priorityIssueTicketText(row), '处置工单')}>复制工单</Button>
|
<Button size="small" onClick={() => copyText(priorityIssueTicketText(row), '处置工单')}>复制工单</Button>
|
||||||
@@ -1566,7 +1566,7 @@ export function Quality({
|
|||||||
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.sourceEndpoint, '来源')}>来源</Button>
|
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.sourceEndpoint, '来源')}>来源</Button>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenRealtime} onClick={() => openIssueRealtime(row)}>实时状态</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenRealtime} onClick={() => openIssueRealtime(row)}>实时状态</Button>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenHistory} onClick={() => openIssueHistory(row)}>核对历史</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenHistory} onClick={() => openIssueHistory(row)}>核对历史</Button>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenRaw} onClick={() => openIssueRaw(row)}>核对 RAW</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenRaw} onClick={() => openIssueRaw(row)}>核对原始</Button>
|
||||||
<Button size="small" disabled={!lookup.key || !onOpenMileage} onClick={() => openIssueMileage(row)}>核对里程</Button>
|
<Button size="small" disabled={!lookup.key || !onOpenMileage} onClick={() => openIssueMileage(row)}>核对里程</Button>
|
||||||
<Button size="small" onClick={() => copyText(priorityIssueNotificationText(priorityIssueFromRow(row)), '告警通知')}>复制通知</Button>
|
<Button size="small" onClick={() => copyText(priorityIssueNotificationText(priorityIssueFromRow(row)), '告警通知')}>复制通知</Button>
|
||||||
<Button size="small" onClick={() => copyText(priorityIssueEvidencePackageText(priorityIssueFromRow(row)), '告警证据包')}>复制证据包</Button>
|
<Button size="small" onClick={() => copyText(priorityIssueEvidencePackageText(priorityIssueFromRow(row)), '告警证据包')}>复制证据包</Button>
|
||||||
|
|||||||
@@ -166,8 +166,8 @@ test('exposes AMap operations shortcuts when map key is configured', async () =>
|
|||||||
severity: 'ok',
|
severity: 'ok',
|
||||||
status: '生产可用',
|
status: '生产可用',
|
||||||
evidence: '在线 174/424,缺失车辆 609,Kafka Lag 42',
|
evidence: '在线 174/424,缺失车辆 609,Kafka Lag 42',
|
||||||
action: '保持实时、轨迹、RAW 和统计链路巡检。',
|
action: '保持实时、轨迹、原始记录和统计链路巡检。',
|
||||||
acceptance: '车辆服务可回查实时、轨迹和 RAW 证据。',
|
acceptance: '车辆服务可回查实时、轨迹和原始记录。',
|
||||||
vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
|
vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
|
||||||
realtimeHash: '#/realtime?protocol=JT808&online=online',
|
realtimeHash: '#/realtime?protocol=JT808&online=online',
|
||||||
historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
|
historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
|
||||||
@@ -362,8 +362,8 @@ test('shows global alert pressure from notification plan in topbar', async () =>
|
|||||||
severity: 'ok',
|
severity: 'ok',
|
||||||
status: '生产可用',
|
status: '生产可用',
|
||||||
evidence: '在线 11/50,缺失车辆 983,Kafka Lag 0',
|
evidence: '在线 11/50,缺失车辆 983,Kafka Lag 0',
|
||||||
action: '保持实时、轨迹、RAW 和统计链路巡检。',
|
action: '保持实时、轨迹、原始记录和统计链路巡检。',
|
||||||
acceptance: '车辆服务可回查实时、轨迹和 RAW 证据。',
|
acceptance: '车辆服务可回查实时、轨迹和原始记录。',
|
||||||
vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT',
|
vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT',
|
||||||
realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online',
|
realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online',
|
||||||
historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true',
|
historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true',
|
||||||
@@ -667,8 +667,8 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
|
|||||||
severity: 'ok',
|
severity: 'ok',
|
||||||
status: '生产可用',
|
status: '生产可用',
|
||||||
evidence: '在线 11/50,缺失车辆 983,Kafka Lag 0',
|
evidence: '在线 11/50,缺失车辆 983,Kafka Lag 0',
|
||||||
action: '保持实时、轨迹、RAW 和统计链路巡检。',
|
action: '保持实时、轨迹、原始记录和统计链路巡检。',
|
||||||
acceptance: '车辆服务可回查实时、轨迹和 RAW 证据。',
|
acceptance: '车辆服务可回查实时、轨迹和原始记录。',
|
||||||
vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT',
|
vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT',
|
||||||
realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online',
|
realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online',
|
||||||
historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true',
|
historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true',
|
||||||
@@ -3660,7 +3660,7 @@ test('shows alert rule and notification policy workspace on quality page', async
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响车辆:3 辆;问题记录:5 条'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响车辆:3 辆;问题记录:5 条'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('客户处置路径:'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('客户处置路径:'));
|
||||||
expect(await screen.findByText('告警事件与通知中心')).toBeInTheDocument();
|
expect(await screen.findByText('告警事件与通知中心')).toBeInTheDocument();
|
||||||
expect(screen.getByText('客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、RAW、里程证据闭环。', { exact: false })).toBeInTheDocument();
|
expect(screen.getByText('客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、原始记录、里程证据闭环。', { exact: false })).toBeInTheDocument();
|
||||||
expect(screen.getByText('链路状态')).toBeInTheDocument();
|
expect(screen.getByText('链路状态')).toBeInTheDocument();
|
||||||
expect(screen.getAllByText('优先队列').length).toBeGreaterThanOrEqual(1);
|
expect(screen.getAllByText('优先队列').length).toBeGreaterThanOrEqual(1);
|
||||||
expect(await screen.findByText('告警触发规则')).toBeInTheDocument();
|
expect(await screen.findByText('告警触发规则')).toBeInTheDocument();
|
||||||
@@ -4064,14 +4064,14 @@ test('renders notification rules as a standalone operations page', async () => {
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 无来源规则 / 平台接入 / 命中 3 / SLA 30 分钟确认'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 无来源规则 / 平台接入 / 命中 3 / SLA 30 分钟确认'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:确认平台转发 - 核对平台转发配置'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:确认平台转发 - 核对平台转发配置'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=VIN-RULES-001'));
|
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('原始记录:http://localhost:3000/#/history-query?keyword=VIN-RULES-001&tab=raw'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('超过升级窗口未恢复时'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('超过升级窗口未恢复时'));
|
||||||
fireEvent.click(screen.getByRole('button', { name: '复制通知演练包' }));
|
fireEvent.click(screen.getByRole('button', { name: '复制通知演练包' }));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警通知演练包】'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警通知演练包】'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本:platform-rules-test'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本:platform-rules-test'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('规则规模:P0 1 类,P1 1 类,待通知 1 条'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('规则规模:P0 1 类,P1 1 类,待通知 1 条'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0策略:P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信 / 30 分钟升级'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0策略:P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信 / 30 分钟升级'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 对第一条待通知告警打开车辆服务、实时、轨迹、RAW 四类证据'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 对第一条待通知告警打开车辆服务、实时、轨迹、原始记录四类证据'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知文本:【P0 告警通知】通知规则页待通知'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知文本:【P0 告警通知】通知规则页待通知'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=VIN-RULES-001'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=VIN-RULES-001'));
|
||||||
fireEvent.click(screen.getByRole('button', { name: '复制通知执行矩阵' }));
|
fireEvent.click(screen.getByRole('button', { name: '复制通知执行矩阵' }));
|
||||||
@@ -4357,7 +4357,7 @@ test('copies notification text from quality priority queue', async () => {
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:手机号未映射 VIN,需要维护 binding'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:手机号未映射 VIN,需要维护 binding'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警筛选:http://localhost:3000/#/alert-events'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警筛选:http://localhost:3000/#/alert-events'));
|
||||||
});
|
});
|
||||||
@@ -4447,9 +4447,9 @@ test('copies dispatch ticket from quality priority queue', async () => {
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:维护身份绑定'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:维护身份绑定'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('处置步骤:'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('处置步骤:'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 打开车辆服务核对当前身份解析结果'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 打开车辆服务核对当前身份解析结果'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 核对实时定位、轨迹证据和 RAW 证据'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 核对实时定位、轨迹证据和原始记录'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('3. 完成后确认车辆服务状态恢复,问题不再命中当前告警'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('3. 完成后确认车辆服务状态恢复,问题不再命中当前告警'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:车辆能解析到 VIN,实时/历史/RAW 证据可通过同一车辆服务查询'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:车辆能解析到 VIN,实时、历史和原始记录可通过同一车辆服务查询'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E5%B7%A5%E5%8D%951&protocol=JT808'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E5%B7%A5%E5%8D%951&protocol=JT808'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4553,7 +4553,7 @@ test('copies priority queue notification digest on quality page', async () => {
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统一验收:来源恢复并持续 10 分钟,车辆服务可查到实时与历史证据'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统一验收:来源恢复并持续 10 分钟,车辆服务可查到实时与历史证据'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 粤A汇总1 / VIN-DIGEST-001'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 粤A汇总1 / VIN-DIGEST-001'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时:http://localhost:3000/#/realtime?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时:http://localhost:3000/#/realtime?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW:http://localhost:3000/#/history-query?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. [P1] 粤A汇总2 / 13307795426'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. [P1] 粤A汇总2 / 13307795426'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知规则:http://localhost:3000/#/notification-rules'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知规则:http://localhost:3000/#/notification-rules'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运维质量:http://localhost:3000/#/ops-quality'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运维质量:http://localhost:3000/#/ops-quality'));
|
||||||
@@ -4667,7 +4667,7 @@ test('copies notification text from regular quality issue row', async () => {
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:缺失字段恢复解析,统计、定位或里程依赖的核心字段可查询'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:缺失字段恢复解析,统计、定位或里程依赖的核心字段可查询'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放:http://localhost:3000/#/history?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放:http://localhost:3000/#/history?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history-query?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('includeFields=true'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('includeFields=true'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程复核:http://localhost:3000/#/mileage?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程复核:http://localhost:3000/#/mileage?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
|
||||||
@@ -4913,7 +4913,7 @@ test('opens same-day raw evidence from quality priority queue', async () => {
|
|||||||
issueType: 'LINK_GAP',
|
issueType: 'LINK_GAP',
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
lastSeen: '2026-07-03 20:12:10',
|
lastSeen: '2026-07-03 20:12:10',
|
||||||
detail: 'JT808 来源间断,需要核对 RAW'
|
detail: 'JT808 来源间断,需要核对原始记录'
|
||||||
}],
|
}],
|
||||||
total: 1,
|
total: 1,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
@@ -4947,7 +4947,8 @@ test('opens same-day raw evidence from quality priority queue', async () => {
|
|||||||
render(<App />);
|
render(<App />);
|
||||||
|
|
||||||
expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
|
expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'RAW证据' }));
|
const rawEvidenceButtons = screen.getAllByRole('button', { name: '原始记录' });
|
||||||
|
fireEvent.click(rawEvidenceButtons[rawEvidenceButtons.length - 1]);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
|
expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
|
||||||
@@ -5304,7 +5305,7 @@ test('opens same-day raw evidence from quality issue row', async () => {
|
|||||||
render(<App />);
|
render(<App />);
|
||||||
|
|
||||||
expect(await screen.findByText('VIN-QUALITY-RAW')).toBeInTheDocument();
|
expect(await screen.findByText('VIN-QUALITY-RAW')).toBeInTheDocument();
|
||||||
fireEvent.click(screen.getByRole('button', { name: '核对 RAW' }));
|
fireEvent.click(screen.getByRole('button', { name: '核对原始' }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
|
expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
|
||||||
@@ -10276,7 +10277,7 @@ test('opens realtime status from quality issue row with source evidence', async
|
|||||||
render(<App />);
|
render(<App />);
|
||||||
|
|
||||||
expect(await screen.findByText('13307795425')).toBeInTheDocument();
|
expect(await screen.findByText('13307795425')).toBeInTheDocument();
|
||||||
fireEvent.click(screen.getByRole('button', { name: '实时状态' }));
|
fireEvent.click(screen.getAllByRole('button', { name: '实时状态' })[0]);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(window.location.hash).toBe('#/realtime?keyword=%E7%B2%A4AG18312&protocol=JT808');
|
expect(window.location.hash).toBe('#/realtime?keyword=%E7%B2%A4AG18312&protocol=JT808');
|
||||||
|
|||||||
Reference in New Issue
Block a user