feat(platform): refine customer alert evidence copy

This commit is contained in:
lingniu
2026-07-05 11:49:07 +08:00
parent 2b423a3643
commit 856e4f7dfc
4 changed files with 57 additions and 56 deletions

View File

@@ -176,7 +176,7 @@ function slaEscalationReport(rows: SlaEscalationRow[], release?: string) {
` 问题:${qualityIssueLabel(row.issue.issueType)} / ${row.issue.protocol}`,
` 建议动作:${row.issue.actionLabel}`,
` 车辆服务:${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');
}
@@ -239,7 +239,7 @@ function notificationExecutionMatrixReport(rows: NotificationExecutionRow[], rel
` 证据:${row.evidenceText}`,
` 动作:${row.issue.actionLabel} - ${row.issue.actionDetail || '-'}`,
` 车辆服务:${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');
}
@@ -313,7 +313,7 @@ function notificationCoverageReport({
'',
'管理动作:',
'1. P0 命中先确认责任方、渠道和升级窗口是否明确',
'2. 证据不完整的车辆先补齐车辆服务、实时、轨迹、RAW 链接',
'2. 证据不完整的车辆先补齐车辆服务、实时、轨迹和原始记录链接',
'3. 超时或即将升级的告警必须同步业务责任人',
'4. 链路异常时优先恢复采集、MySQL、TDEngine、Redis、Kafka 等前置能力'
].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.realtimeHash}`,
` 轨迹证据:${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');
}
@@ -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.realtimeHash}`,
`轨迹证据:${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') : '暂无待处置告警',
'',
'值班动作:',
'1. 先处理 P0无来源/VIN 缺失/存储不可写优先于普通字段缺失',
'2. 复制单条通知给责任团队,并同步车辆服务、实时、轨迹、RAW 证据',
'2. 复制单条通知给责任团队,并同步车辆服务、实时、轨迹和原始记录',
'3. 超过升级窗口未恢复时,按主通知策略升级到业务责任人',
'4. 恢复后检查验收口径,并在告警事件页确认不再命中'
].join('\n');
@@ -435,7 +435,7 @@ function alertNotificationDrillPackage(plan: QualityNotificationPlan | null, hea
'',
'演练步骤:',
'1. 在告警事件页确认 P0/P1 命中数量与本页一致',
'2. 对第一条待通知告警打开车辆服务、实时、轨迹、RAW 四类证据',
'2. 对第一条待通知告警打开车辆服务、实时、轨迹、原始记录四类证据',
'3. 复制单条告警通知给责任团队,记录发送时间',
'4. 超过升级窗口仍未恢复时,按策略升级到业务责任人',
'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.realtimeHash}`,
`轨迹证据:${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');
}
@@ -565,7 +565,7 @@ export function NotificationRules() {
step: '02',
title: '补齐证据',
value: `${evidenceCompleteCount}/${priorityIssues.length || 0} 完整`,
detail: '通知前必须带上车辆服务、实时监控、轨迹回放和RAW证据链接。',
detail: '通知前必须带上车辆服务、实时监控、轨迹回放和原始记录链接。',
action: '证据矩阵',
color: evidenceCompleteCount === priorityIssues.length ? 'green' as const : 'orange' as const,
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.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>
<Button size="small" aria-label="下一步原始记录" disabled={!nextPriorityIssue.rawHash} onClick={() => navigateHash(nextPriorityIssue.rawHash)}></Button>
</Space>
</>
) : (
@@ -801,7 +801,7 @@ export function NotificationRules() {
render: (_: unknown, row: NotificationExecutionRow) => (
<Space wrap>
<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>
</Space>
)
@@ -846,7 +846,7 @@ export function NotificationRules() {
render: (_: unknown, row: SlaEscalationRow) => (
<Space wrap>
<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>
</Space>
)
@@ -924,7 +924,7 @@ export function NotificationRules() {
<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.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>
</Space>
)

View File

@@ -67,7 +67,7 @@ const alertRuleTemplates = [
level: 'P1',
owner: '协议解析',
trigger: '核心字段缺失导致定位、里程或统计不可用',
notify: '通知解析负责人核对字段映射和 RAW 样本',
notify: '通知解析负责人核对字段映射和原始记录样本',
sla: '当日闭环'
}
];
@@ -87,7 +87,7 @@ const notificationPolicies = [
channel: '站内告警 / 每日汇总邮件',
condition: '字段缺失、链路间断、容量风险',
escalationMinutes: 120,
acceptanceCriteria: '核心字段恢复解析,影响车辆可通过 RAW 与历史证据复核'
acceptanceCriteria: '核心字段恢复解析,影响车辆可通过原始记录与历史证据复核'
},
{
name: 'P2 趋势关注',
@@ -187,7 +187,7 @@ function qualityActionRecommendation(row: QualityIssueRow) {
return {
label: '核对解析字段',
color: 'orange' as const,
detail: '字段缺失会影响统计和展示,优先核对解析映射和原始 RAW。'
detail: '字段缺失会影响统计和展示,优先核对解析映射和原始记录。'
};
}
return {
@@ -317,7 +317,7 @@ function priorityIssueNotificationText(row: PriorityIssueRow) {
`详情:${row.detail || '-'}`,
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
`轨迹证据:${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 }))}`,
`告警筛选:${qualityShareURL()}`
].join('\n');
@@ -329,10 +329,10 @@ function alertRuleTemplate(issueType: string) {
function acceptanceCriteria(row: PriorityIssueRow) {
if (row.issueType === 'VIN_MISSING') {
return '车辆能解析到 VIN实时/历史/RAW 证据可通过同一车辆服务查询';
return '车辆能解析到 VIN实时历史和原始记录可通过同一车辆服务查询';
}
if (row.issueType === 'NO_SOURCE') {
return '至少一个生产来源恢复在线,车辆实时状态、历史轨迹和 RAW 证据可查询';
return '至少一个生产来源恢复在线,车辆实时状态、历史轨迹和原始记录可查询';
}
if (row.issueType === 'LINK_GAP') {
return '来源恢复连续上报,实时在线窗口和历史轨迹无异常断点';
@@ -369,12 +369,12 @@ function priorityIssueTicketText(row: PriorityIssueRow) {
`处置说明:${row.actionDetail || '-'}`,
'处置步骤:',
'1. 打开车辆服务核对当前身份解析结果',
'2. 核对实时定位、轨迹证据和 RAW 证据',
'2. 核对实时定位、轨迹证据和原始记录',
'3. 完成后确认车辆服务状态恢复,问题不再命中当前告警',
`验收标准:${acceptanceCriteria(row)}`,
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
`轨迹证据:${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 }))}`,
`告警筛选:${qualityShareURL()}`
].join('\n');
@@ -402,7 +402,7 @@ function priorityIssueEvidencePackageText(row: PriorityIssueRow) {
`验收标准:${acceptanceCriteria(row)}`,
`实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
`轨迹回放:${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: 'detail', keyword: lookup.key, protocol: row.protocol }))}`,
`告警筛选:${qualityShareURL()}`
@@ -580,7 +580,7 @@ function notificationHandoffText({
` 详情:${row.detail || '-'}`,
` 实时:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`,
` 轨迹:${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 }))}`
].join('\n');
})
@@ -593,7 +593,7 @@ function notificationHandoffText({
`优先级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('')}` : ''}`,
primaryPolicy ? `主通知策略:${[primaryPolicy.name, primaryPolicy.target, primaryPolicy.channel, formatEscalationMinutes(primaryPolicy.escalationMinutes)].filter(Boolean).join(' / ')}` : '主通知策略:-',
primaryPolicy?.acceptanceCriteria ? `统一验收:${primaryPolicy.acceptanceCriteria}` : '统一验收:车辆服务状态恢复,实时、轨迹、RAW 证据可查询',
primaryPolicy?.acceptanceCriteria ? `统一验收:${primaryPolicy.acceptanceCriteria}` : '统一验收:车辆服务状态恢复,实时、轨迹和原始记录可查询',
'',
'待通知车辆:',
...issueLines,
@@ -708,7 +708,7 @@ function alertCustomerDecisionText({
'客户处置路径:',
'1. 先确认影响车辆和 P0/P1 数量。',
'2. P0 立即通知责任团队,超过 SLA 自动升级。',
'3. 每辆焦点车都要打开实时、轨迹、RAW、里程证据闭环。',
'3. 每辆焦点车都要打开实时、轨迹、原始记录、里程证据闭环。',
'4. 恢复后确认告警不再命中,并保留交接包。',
'',
`告警事件:${qualityShareURL()}`,
@@ -1031,7 +1031,7 @@ export function Quality({
},
{
label: '证据闭环',
value: '实时/轨迹/RAW/里程',
value: '实时/轨迹/原始/里程',
detail: '所有通知都必须回到车辆证据,不只停留在来源或链路指标。',
color: 'blue' as const,
action: '交接包',
@@ -1067,7 +1067,7 @@ export function Quality({
</Space>
<strong>{issueVehicleCount.toLocaleString()} </strong>
<span>
RAW
</span>
<Space wrap>
<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>
<span>
{focusIssue
? `${focusIssue.actionLabel}${focusIssue.actionDetail || focusIssue.detail || '请结合实时、轨迹、RAW 和里程证据闭环。'}`
? `${focusIssue.actionLabel}${focusIssue.actionDetail || focusIssue.detail || '请结合实时、轨迹、原始记录和里程证据闭环。'}`
: '当前没有进入优先队列的车辆,保持实时监控并定期复核告警规则。'}
</span>
<Space wrap>
@@ -1190,9 +1190,9 @@ export function Quality({
},
{
label: '原始证据',
value: focusIssue ? '查RAW' : '待命',
value: focusIssue ? '查原始' : '待命',
detail: '保留原始帧和解析字段,给平台、客户和研发确认责任边界。',
action: 'RAW证据',
action: '原始记录',
color: focusIssue ? 'blue' as const : 'grey' as const,
disabled: !focusIssue || !focusLookup?.key || !onOpenRaw,
onClick: () => focusIssue && openIssueRaw(focusIssue)
@@ -1357,7 +1357,7 @@ export function Quality({
<Space spacing={4} wrap>
<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 || !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(priorityIssueEvidencePackageText(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" 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 || !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" onClick={() => copyText(priorityIssueNotificationText(priorityIssueFromRow(row)), '告警通知')}></Button>
<Button size="small" onClick={() => copyText(priorityIssueEvidencePackageText(priorityIssueFromRow(row)), '告警证据包')}></Button>