feat(platform): include evidence links in alert notifications
This commit is contained in:
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { api } from '../api/client';
|
import { api } from '../api/client';
|
||||||
import type { OpsHealth, QualitySummary, QualityIssueRow } from '../api/types';
|
import type { OpsHealth, QualitySummary, QualityIssueRow } from '../api/types';
|
||||||
import { PageHeader } from '../components/PageHeader';
|
import { PageHeader } from '../components/PageHeader';
|
||||||
|
import { buildAppHash } from '../domain/appRoute';
|
||||||
import { qualityIssueLabel, qualityIssueOptions, qualityProtocolLabel, qualityProtocolOptions } from '../domain/qualityIssue';
|
import { qualityIssueLabel, qualityIssueOptions, qualityProtocolLabel, qualityProtocolOptions } from '../domain/qualityIssue';
|
||||||
import { qualityIssueVehicleLookup } from '../domain/vehicleLookup';
|
import { qualityIssueVehicleLookup } from '../domain/vehicleLookup';
|
||||||
|
|
||||||
@@ -121,6 +122,10 @@ function qualityShareURL() {
|
|||||||
return `${window.location.origin}${window.location.pathname}${window.location.hash}`;
|
return `${window.location.origin}${window.location.pathname}${window.location.hash}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function appURL(hash: string) {
|
||||||
|
return `${window.location.origin}${window.location.pathname}${hash}`;
|
||||||
|
}
|
||||||
|
|
||||||
function issueEvidenceDate(value?: string) {
|
function issueEvidenceDate(value?: string) {
|
||||||
const match = String(value ?? '').match(/^(\d{4})-(\d{2})-(\d{2})/);
|
const match = String(value ?? '').match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||||
return match ? `${match[1]}-${match[2]}-${match[3]}` : '';
|
return match ? `${match[1]}-${match[2]}-${match[3]}` : '';
|
||||||
@@ -237,6 +242,13 @@ function priorityIssueRows(rows: QualityIssueRow[]): PriorityIssueRow[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function priorityIssueNotificationText(row: PriorityIssueRow) {
|
function priorityIssueNotificationText(row: PriorityIssueRow) {
|
||||||
|
const lookup = qualityIssueVehicleLookup(row);
|
||||||
|
const dateFrom = issueEvidenceDate(row.lastSeen);
|
||||||
|
const dateTo = nextDate(dateFrom);
|
||||||
|
const evidenceFilters = {
|
||||||
|
...(dateFrom ? { dateFrom } : {}),
|
||||||
|
...(dateTo ? { dateTo } : {})
|
||||||
|
};
|
||||||
return [
|
return [
|
||||||
`【${row.priority} 告警通知】${qualityIssueLabel(row.issueType)}`,
|
`【${row.priority} 告警通知】${qualityIssueLabel(row.issueType)}`,
|
||||||
`车辆:${row.vehicleLabel}`,
|
`车辆:${row.vehicleLabel}`,
|
||||||
@@ -246,7 +258,11 @@ function priorityIssueNotificationText(row: PriorityIssueRow) {
|
|||||||
`SLA:${row.sla}`,
|
`SLA:${row.sla}`,
|
||||||
`最后时间:${row.lastSeen || '-'}`,
|
`最后时间:${row.lastSeen || '-'}`,
|
||||||
`详情:${row.detail || '-'}`,
|
`详情:${row.detail || '-'}`,
|
||||||
`证据入口:${qualityShareURL()}`
|
`实时定位:${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', 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');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2495,7 +2495,11 @@ test('copies notification text from quality priority queue', async () => {
|
|||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('SLA:2 小时修复'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('SLA:2 小时修复'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最后时间:2026-07-03 20:12:10'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最后时间:2026-07-03 20:12:10'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:手机号未映射 VIN,需要维护 binding'));
|
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:手机号未映射 VIN,需要维护 binding'));
|
||||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/#/quality'));
|
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('RAW证据:http://localhost:3000/#/history?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/#/quality'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('opens realtime map context from quality priority queue', async () => {
|
test('opens realtime map context from quality priority queue', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user