feat(platform): center console on vehicle service
This commit is contained in:
@@ -413,7 +413,7 @@ export function Dashboard({
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="运营驾驶舱" description="围绕一个车辆服务汇总实时态势、轨迹证据、告警事件、统计查询和链路健康" />
|
||||
<PageHeader title="车辆服务总览" description="三个数据源最终汇总为一个车辆服务,统一承载实时监控、轨迹回放、历史查询、告警通知、统计查询和链路质量" />
|
||||
<Spin spinning={loading}>
|
||||
<div className="vp-kpi-grid">
|
||||
{kpis.map((item) => (
|
||||
@@ -425,7 +425,7 @@ export function Dashboard({
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<Card bordered title="统一车辆服务" style={{ marginBottom: 16 }}>
|
||||
<Card bordered title="统一车辆服务入口" style={{ marginBottom: 16 }}>
|
||||
<Space wrap>
|
||||
<Tag color="blue">{vehicleServiceOnlineText(serviceSummary, summary)}</Tag>
|
||||
<Button size="small" theme="light" type="primary" onClick={() => onOpenVehicles({ online: 'online' })}>查看在线车辆</Button>
|
||||
@@ -522,7 +522,7 @@ export function Dashboard({
|
||||
</div>
|
||||
</Card>
|
||||
) : null}
|
||||
<Card bordered title="车联网能力矩阵" style={{ marginBottom: 16 }}>
|
||||
<Card bordered title="车辆服务能力矩阵" style={{ marginBottom: 16 }}>
|
||||
<div className="vp-capability-grid">
|
||||
{capabilities.map((item) => (
|
||||
<div key={item.title} className="vp-capability-item">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Card, Space, Table, Tag, Toast } from '@douyinfe/semi-ui';
|
||||
import { Button, Card, Space, Table, Tag, Toast, Typography } from '@douyinfe/semi-ui';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { api } from '../api/client';
|
||||
import type { LinkHealth, OpsHealth } from '../api/types';
|
||||
@@ -37,6 +37,89 @@ function amapSecurityColor(health: OpsHealth | null): 'green' | 'orange' | 'red'
|
||||
return 'orange';
|
||||
}
|
||||
|
||||
type CapacityRisk = {
|
||||
key: string;
|
||||
title: string;
|
||||
value: string;
|
||||
category: string;
|
||||
severity: 'warning' | 'error';
|
||||
finding: string;
|
||||
action: string;
|
||||
};
|
||||
|
||||
function firstNumber(value: string) {
|
||||
const match = value.match(/[\d,.]+/);
|
||||
return match ? match[0] : '-';
|
||||
}
|
||||
|
||||
function capacityRisk(finding: string): CapacityRisk {
|
||||
const lower = finding.toLowerCase();
|
||||
if (lower.includes('bridge') && lower.includes('ack pending')) {
|
||||
return {
|
||||
key: finding,
|
||||
title: 'NATS 桥接 ACK 未确认',
|
||||
value: firstNumber(finding),
|
||||
category: 'NATS -> Kafka',
|
||||
severity: 'error',
|
||||
finding,
|
||||
action: '优先检查 Kafka 是否监听 9092、磁盘是否打满,以及桥接服务是否持续写入成功。'
|
||||
};
|
||||
}
|
||||
if (lower.includes('bridge') && lower.includes('consumer pending')) {
|
||||
return {
|
||||
key: finding,
|
||||
title: 'NATS 桥接待消费',
|
||||
value: firstNumber(finding),
|
||||
category: 'NATS -> Kafka',
|
||||
severity: 'warning',
|
||||
finding,
|
||||
action: '确认 Kafka 已恢复后观察 pending 是否持续下降;若不下降,扩容桥接或降低 Kafka topic 保留压力。'
|
||||
};
|
||||
}
|
||||
if (lower.includes('kafka') || lower.includes('lag')) {
|
||||
return {
|
||||
key: finding,
|
||||
title: 'Kafka 消费积压',
|
||||
value: firstNumber(finding),
|
||||
category: 'Kafka',
|
||||
severity: 'warning',
|
||||
finding,
|
||||
action: '检查消费者组 lag、topic retention、broker 磁盘和生产写入错误。'
|
||||
};
|
||||
}
|
||||
if (lower.includes('redis')) {
|
||||
return {
|
||||
key: finding,
|
||||
title: 'Redis 在线投影异常',
|
||||
value: firstNumber(finding),
|
||||
category: 'Redis',
|
||||
severity: 'warning',
|
||||
finding,
|
||||
action: '核对 realtime KV、online TTL 和协议实时帧过滤,确认车辆在线统计来源。'
|
||||
};
|
||||
}
|
||||
if (lower.includes('connection')) {
|
||||
return {
|
||||
key: finding,
|
||||
title: '网关连接压力',
|
||||
value: firstNumber(finding),
|
||||
category: 'Gateway',
|
||||
severity: 'warning',
|
||||
finding,
|
||||
action: '查看 32960、808、MQTT 连接数、系统 FD、CPU 和接入端口队列。'
|
||||
};
|
||||
}
|
||||
return {
|
||||
key: finding,
|
||||
title: '容量风险',
|
||||
value: firstNumber(finding),
|
||||
category: 'General',
|
||||
severity: 'warning',
|
||||
finding,
|
||||
action: '进入对应链路日志和指标继续定位,确认是否影响车辆实时服务。'
|
||||
};
|
||||
}
|
||||
|
||||
export function OpsQuality() {
|
||||
const [health, setHealth] = useState<OpsHealth | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -54,6 +137,8 @@ export function OpsQuality() {
|
||||
}, []);
|
||||
|
||||
const runtime = health?.runtime;
|
||||
const capacityRisks = (health?.capacityFindings ?? []).map(capacityRisk);
|
||||
const bridgeRisks = capacityRisks.filter((item) => item.category === 'NATS -> Kafka');
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
@@ -111,13 +196,29 @@ export function OpsQuality() {
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
{health?.capacityFindings?.length ? (
|
||||
{capacityRisks.length ? (
|
||||
<Card bordered title="容量与风险发现" style={{ marginTop: 16 }}>
|
||||
<Space wrap>
|
||||
{health.capacityFindings.map((item) => (
|
||||
<Tag key={item} color="orange">{item}</Tag>
|
||||
<div className="vp-risk-grid">
|
||||
{capacityRisks.map((item) => (
|
||||
<div key={item.key} className="vp-risk-item">
|
||||
<Space wrap>
|
||||
<Tag color={item.severity === 'error' ? 'red' : 'orange'}>{item.category}</Tag>
|
||||
<Tag color="grey">{item.finding}</Tag>
|
||||
</Space>
|
||||
<div className="vp-risk-value">{item.value}</div>
|
||||
<Typography.Title heading={6} style={{ margin: 0 }}>{item.title}</Typography.Title>
|
||||
<Typography.Text type="secondary">{item.action}</Typography.Text>
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
</div>
|
||||
{bridgeRisks.length ? (
|
||||
<div className="vp-risk-runbook">
|
||||
<Tag color="blue">桥接处置顺序</Tag>
|
||||
<Typography.Text>
|
||||
先确认 Kafka broker 监听和磁盘空间,再观察 ACK pending 是否回到 0,最后确认 consumer pending 持续下降。
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user