feat(platform): position dashboard as vehicle service center
This commit is contained in:
@@ -525,7 +525,7 @@ export function Dashboard({
|
||||
{
|
||||
title: '按时间窗复盘',
|
||||
value: taskTimeMonitorSummary,
|
||||
detail: '同一时间窗贯穿轨迹、里程、RAW 和告警,不需要跨页面重复输入。',
|
||||
detail: '同一时间窗贯穿轨迹、里程、原始记录和告警,不需要跨页面重复输入。',
|
||||
color: 'blue' as const,
|
||||
primaryAction: '轨迹回放',
|
||||
secondaryAction: '里程统计',
|
||||
@@ -871,7 +871,7 @@ export function Dashboard({
|
||||
{
|
||||
label: '数据可导出',
|
||||
value: `${formatCount(summary?.frameToday)} 今日数据`,
|
||||
detail: '历史查询默认带 RAW 与解析字段,支持按字段缩小导出体积。',
|
||||
detail: '历史查询默认带原始记录与解析字段,支持按字段缩小导出体积。',
|
||||
color: (summary?.frameToday ?? 0) > 0 ? 'blue' as const : 'orange' as const
|
||||
},
|
||||
{
|
||||
@@ -894,7 +894,7 @@ export function Dashboard({
|
||||
`时间范围:${scope.dateFrom || '-'} 至 ${scope.dateTo || '-'}`,
|
||||
`轨迹证据:今日活跃 ${formatCount(summary?.activeToday)},用于回放位置、速度、里程断点`,
|
||||
`统计证据:车辆口径 ${formatCount(serviceSummary?.totalVehicles)},用于区间里程和日统计闭合`,
|
||||
`历史证据:今日数据 ${formatCount(summary?.frameToday)},用于 RAW 与解析字段复核`,
|
||||
`历史证据:今日数据 ${formatCount(summary?.frameToday)},用于原始记录与解析字段复核`,
|
||||
`告警事件:${formatCount(summary?.issueVehicles)} 辆车存在告警`,
|
||||
'交付核对:',
|
||||
...timeMonitorChecklist.map((item, index) => `${index + 1}. ${item.label}:${item.value};${item.detail}`),
|
||||
@@ -926,7 +926,7 @@ export function Dashboard({
|
||||
{
|
||||
label: '历史证据',
|
||||
value: `${formatCount(summary?.frameToday)} 帧`,
|
||||
detail: '按车辆、时间和字段裁剪导出 RAW 与解析字段。',
|
||||
detail: '按车辆、时间和字段裁剪导出原始记录与解析字段。',
|
||||
action: '导出证据',
|
||||
color: 'blue' as const,
|
||||
onClick: openTimeMonitorRaw
|
||||
@@ -943,10 +943,10 @@ export function Dashboard({
|
||||
const dataFlowStages = [
|
||||
{
|
||||
stage: '01',
|
||||
title: '协议接入',
|
||||
owner: '网关接收',
|
||||
title: '车辆接入',
|
||||
owner: '接入网关',
|
||||
evidence: `32960 / 808 / MQTT,连接 ${formatCount(opsHealth?.activeConnections)}`,
|
||||
detail: '只接收真实实时数据帧和必要鉴权注册帧,协议不是最终产品。',
|
||||
detail: '只接收真实实时数据帧和必要鉴权注册帧,接入来源最终归并到车辆服务。',
|
||||
action: '运维质量',
|
||||
onClick: () => onOpenQuality()
|
||||
},
|
||||
@@ -981,8 +981,8 @@ export function Dashboard({
|
||||
stage: '05',
|
||||
title: '历史证据',
|
||||
owner: 'TDengine / MySQL',
|
||||
evidence: `轨迹 ${formatCount(summary?.activeToday)} 活跃,RAW ${formatCount(summary?.frameToday)} 帧`,
|
||||
detail: '位置、RAW 和解析字段可分页查询,并能回跳轨迹、车辆服务和告警。',
|
||||
evidence: `轨迹 ${formatCount(summary?.activeToday)} 活跃,原始记录 ${formatCount(summary?.frameToday)} 帧`,
|
||||
detail: '位置、原始记录和解析字段可分页查询,并能回跳轨迹、车辆服务和告警。',
|
||||
action: '轨迹回放',
|
||||
onClick: () => onOpenHistory()
|
||||
},
|
||||
@@ -999,7 +999,7 @@ export function Dashboard({
|
||||
const scenarioNavigation = [
|
||||
{
|
||||
title: '实时监控',
|
||||
objective: '确认车辆是否在线、位置是否可信、三类来源是否齐全。',
|
||||
objective: '确认车辆是否在线、位置是否可信、必要接入来源是否齐全。',
|
||||
evidence: `Redis 在线态 / 最新定位 / 有效坐标 ${commandLocatedCount.toLocaleString()}`,
|
||||
sla: '目标 0-1 秒内进入实时视图',
|
||||
primaryAction: '实时监控',
|
||||
@@ -1054,7 +1054,7 @@ export function Dashboard({
|
||||
const highPriorityLookupKey = highPriorityLookup?.key;
|
||||
const highPriorityFilters = highPriorityEvidenceFilters;
|
||||
const lines = [
|
||||
'【车辆数据中台运营交接摘要】',
|
||||
'【车辆服务中台运营交接摘要】',
|
||||
`在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} / ${formatCount(serviceSummary?.totalVehicles)}`,
|
||||
`今日活跃:${formatCount(summary?.activeToday)}`,
|
||||
`今日帧量:${formatCount(summary?.frameToday)}`,
|
||||
@@ -1076,8 +1076,8 @@ export function Dashboard({
|
||||
};
|
||||
const copyScenarioBlueprint = () => {
|
||||
const lines = [
|
||||
'【车辆数据中台功能蓝图】',
|
||||
'定位:32960 / 808 / 宇通 MQTT 都只是车辆服务证据源,最终围绕一辆车提供实时、轨迹、历史、告警、统计能力。',
|
||||
'【车辆服务中台功能蓝图】',
|
||||
'定位:32960 / 808 / 宇通 MQTT 都只是车辆接入来源,最终围绕一辆车提供实时、轨迹、历史、告警、统计能力。',
|
||||
`高德地图:Web JS ${amapConfigured ? '已配置' : '待配置'};服务端 API ${amapApiConfigured ? '已配置' : '待配置'};安全代理 ${amapSecurityProxyEnabled ? '已启用' : '未启用'};安全码${amapSecurityCodeExposed ? '已暴露' : '未暴露'}`,
|
||||
`车辆规模:${formatCount(serviceSummary?.totalVehicles)};在线:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)};告警:${formatCount(summary?.issueVehicles)}`,
|
||||
'',
|
||||
@@ -1094,8 +1094,8 @@ export function Dashboard({
|
||||
};
|
||||
const copyDataFlowBlueprint = () => {
|
||||
const lines = [
|
||||
'【车辆数据中台数据流转图】',
|
||||
'原则:32960 / 808 / 宇通 MQTT 是接入证据,最终都归并为同一辆车的车辆服务。',
|
||||
'【车辆服务中台数据流转图】',
|
||||
'原则:32960 / 808 / 宇通 MQTT 是车辆接入来源,最终都归并为同一辆车的车辆服务。',
|
||||
`运行态:连接 ${formatCount(opsHealth?.activeConnections)};在线 Key ${formatCount(opsHealth?.redisOnlineKeys)};Kafka Lag ${formatLag(summary?.kafkaLag)};今日帧 ${formatCount(summary?.frameToday)}`,
|
||||
'',
|
||||
...dataFlowStages.map((item) => [
|
||||
@@ -1272,7 +1272,7 @@ export function Dashboard({
|
||||
{
|
||||
title: '复盘时间',
|
||||
value: dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo),
|
||||
detail: '同一个时间窗贯穿轨迹回放、里程统计、RAW 证据和告警复盘。',
|
||||
detail: '同一个时间窗贯穿轨迹回放、里程统计、原始记录和告警复盘。',
|
||||
action: '轨迹回放',
|
||||
color: 'blue' as const,
|
||||
onClick: openTimeMonitorHistory
|
||||
@@ -1299,7 +1299,7 @@ export function Dashboard({
|
||||
const rawFilters = { ...scope, tab: 'raw', includeFields: 'true' };
|
||||
const lines = [
|
||||
'【车辆客户服务说明】',
|
||||
'服务对象:车辆,不是协议数据源。',
|
||||
'服务对象:车辆,不是接入来源。',
|
||||
`车辆规模:${formatCount(serviceSummary?.totalVehicles)},在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)},有效定位:${commandLocatedCount.toLocaleString()}`,
|
||||
`当前时间窗:${timeMonitorSummary}(${dayRangeText(scope.dateFrom, scope.dateTo)})`,
|
||||
`告警车辆:${formatCount(summary?.issueVehicles)},最高优先级:${highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无'}`,
|
||||
@@ -1308,7 +1308,7 @@ export function Dashboard({
|
||||
'1. 找车:先按 VIN、车牌、手机号或 OEM 锁定车辆。',
|
||||
'2. 看位置:打开实时地图确认车辆在线、坐标和最新上报。',
|
||||
'3. 复盘时间:同一时间窗查看轨迹、里程和告警。',
|
||||
'4. 交付数据:按车辆、时间、字段导出位置历史、RAW 和字段明细。',
|
||||
'4. 交付数据:按车辆、时间、字段导出位置历史、原始记录和字段明细。',
|
||||
'5. 闭环异常:告警事件进入通知和处置流程。',
|
||||
`车辆中心:${appURL(buildAppHash({ page: 'vehicles' }))}`,
|
||||
`实时地图:${appURL(buildAppHash({ page: 'map', filters: { online: 'online' } }))}`,
|
||||
|
||||
@@ -891,12 +891,12 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
|
||||
expect(screen.getAllByText('今日帧 1,286,320').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('告警 7').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('统计车辆 1,033').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('确认车辆是否在线、位置是否可信、三类来源是否齐全。')).toBeInTheDocument();
|
||||
expect(screen.getByText('确认车辆是否在线、位置是否可信、必要接入来源是否齐全。')).toBeInTheDocument();
|
||||
expect(screen.getByText('按车辆复盘位置、速度、里程和断点,定位平台转发或车辆异常。')).toBeInTheDocument();
|
||||
expect(screen.getByText('优先返回必要字段,避免大 JSON 拖慢查询')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '复制功能蓝图' }));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台功能蓝图】'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('32960 / 808 / 宇通 MQTT 都只是车辆服务证据源'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆服务中台功能蓝图】'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('32960 / 808 / 宇通 MQTT 都只是车辆接入来源'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆规模:1,033;在线:208;告警:7'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('高德地图:Web JS 已配置;服务端 API 已配置;安全代理 已启用;安全码未暴露'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 实时监控'));
|
||||
@@ -1281,8 +1281,8 @@ test('dashboard shows vehicle service action queue', async () => {
|
||||
|
||||
test.each([
|
||||
{ buttonName: '查看在线车辆', expectedHash: '#/vehicles?online=online' },
|
||||
{ buttonName: '实时地图', expectedHash: '#/map?online=online' },
|
||||
{ buttonName: '查看告警事件', expectedHash: '#/alert-events' }
|
||||
{ buttonName: '打开实时地图', expectedHash: '#/map?online=online' },
|
||||
{ buttonName: '顶部告警事件', expectedHash: '#/alert-events' }
|
||||
])('dashboard posture opens %s', async ({ buttonName, expectedHash }) => {
|
||||
window.history.replaceState(null, '', '/#/dashboard');
|
||||
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
|
||||
@@ -1448,7 +1448,7 @@ test('dashboard exposes end-to-end operations workflow entries', async () => {
|
||||
expect(screen.getByText('今天车辆轨迹')).toBeInTheDocument();
|
||||
expect(screen.getByText('历史时间窗')).toBeInTheDocument();
|
||||
expect(screen.getByText('区间里程')).toBeInTheDocument();
|
||||
expect(screen.getByText('告警复盘')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('告警复盘').length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '时间监控 今天车辆轨迹' }));
|
||||
|
||||
@@ -1570,7 +1570,7 @@ test('dashboard surfaces highest priority quality issue with evidence shortcuts'
|
||||
expect(screen.getByText('实时证据')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('轨迹证据').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('告警证据')).toBeInTheDocument();
|
||||
expect(screen.getByText('统计证据')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('统计证据').length).toBeGreaterThanOrEqual(1);
|
||||
fireEvent.click(screen.getByRole('button', { name: '复制处置卡' }));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【重点车辆服务处置卡】'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆:粤A告警1 / 13307795425'));
|
||||
@@ -1578,7 +1578,7 @@ test('dashboard surfaces highest priority quality issue with evidence shortcuts'
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&includeFields=true'));
|
||||
expect(await screen.findByText('最高优先级告警')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('粤A告警1 / 13307795425').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('VIN 缺失')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('VIN 缺失').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('JT808 数据缺少 VIN 映射').length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '首页告警轨迹证据' }));
|
||||
@@ -1713,7 +1713,7 @@ test('dashboard copies highest priority quality issue notification text', async
|
||||
fireEvent.click(screen.getByRole('button', { name: '复制运营交接摘要' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台运营交接摘要】'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆服务中台运营交接摘要】'));
|
||||
});
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆:88 / 1,033'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日活跃:96'));
|
||||
|
||||
Reference in New Issue
Block a user