@@ -111,13 +196,29 @@ export function OpsQuality() {
- {health?.capacityFindings?.length ? (
+ {capacityRisks.length ? (
-
- {health.capacityFindings.map((item) => (
- {item}
+
+ {capacityRisks.map((item) => (
+
+
+ {item.category}
+ {item.finding}
+
+
{item.value}
+
{item.title}
+
{item.action}
+
))}
-
+
+ {bridgeRisks.length ? (
+
+ 桥接处置顺序
+
+ 先确认 Kafka broker 监听和磁盘空间,再观察 ACK pending 是否回到 0,最后确认 consumer pending 持续下降。
+
+
+ ) : null}
) : null}
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css
index e2495d4a..9f61af25 100644
--- a/vehicle-data-platform/apps/web/src/styles/global.css
+++ b/vehicle-data-platform/apps/web/src/styles/global.css
@@ -542,6 +542,41 @@ body {
line-height: 28px;
}
+.vp-risk-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 12px;
+}
+
+.vp-risk-item {
+ min-height: 158px;
+ padding: 12px;
+ border: 1px solid var(--vp-border);
+ border-radius: var(--vp-radius);
+ background: #fbfcff;
+ display: grid;
+ gap: 10px;
+ align-content: start;
+}
+
+.vp-risk-value {
+ color: var(--vp-text);
+ font-size: 26px;
+ font-weight: 700;
+ line-height: 32px;
+}
+
+.vp-risk-runbook {
+ margin-top: 12px;
+ padding: 12px;
+ border: 1px solid var(--vp-border);
+ border-radius: var(--vp-radius);
+ background: #f5f9ff;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
.vp-alert-ops-grid {
display: grid;
grid-template-columns: minmax(0, 1.4fr) minmax(360px, 0.6fr);
@@ -981,6 +1016,7 @@ body {
.vp-conclusion-grid,
.vp-monitor-layout,
.vp-alert-flow,
+ .vp-risk-grid,
.vp-alert-ops-grid,
.vp-stat-workspace,
.vp-stat-insight-grid,
diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx
index 9edc6f0a..92835777 100644
--- a/vehicle-data-platform/apps/web/src/test/App.test.tsx
+++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx
@@ -12,7 +12,7 @@ afterEach(() => {
test('renders vehicle platform shell', () => {
render(
);
expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
- expect(screen.getAllByText('运营驾驶舱').length).toBeGreaterThanOrEqual(1);
+ expect(screen.getAllByText('车辆服务总览').length).toBeGreaterThanOrEqual(1);
});
test('exposes AMap operations shortcuts when map key is configured', async () => {
@@ -345,7 +345,7 @@ test('dashboard presents one vehicle service operating posture', async () => {
render(
);
- expect(await screen.findByText('统一车辆服务')).toBeInTheDocument();
+ expect(await screen.findByText('统一车辆服务入口')).toBeInTheDocument();
expect(screen.getByText('208 / 1,033 在线')).toBeInTheDocument();
expect(screen.getByText('181 多源覆盖')).toBeInTheDocument();
expect(screen.getByText('7 告警事件')).toBeInTheDocument();
@@ -448,7 +448,7 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
const renderDashboard = async () => {
window.history.replaceState(null, '', '/#/dashboard');
render(
);
- expect(await screen.findByText('车联网能力矩阵')).toBeInTheDocument();
+ expect(await screen.findByText('车辆服务能力矩阵')).toBeInTheDocument();
};
await renderDashboard();
@@ -3374,7 +3374,12 @@ test('renders ops quality as a standalone runtime health page', async () => {
],
kafkaLag: 42,
activeConnections: 1234,
- capacityFindings: ['Kafka lag 42', 'Redis online key below expected'],
+ capacityFindings: [
+ 'Kafka lag 42',
+ 'bridge ack pending 4000 exceeds 100',
+ 'bridge consumer pending 1482047 exceeds 10000',
+ 'Redis online key below expected'
+ ],
redisOnlineKeys: 368,
tdengineWritable: true,
mysqlWritable: false,
@@ -3407,7 +3412,7 @@ test('renders ops quality as a standalone runtime health page', async () => {
expect(await screen.findByRole('heading', { name: '运维质量' })).toBeInTheDocument();
expect(screen.getByText('platform-ops-test')).toBeInTheDocument();
- expect(screen.getByText('42')).toBeInTheDocument();
+ expect(screen.getAllByText('42').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('1,234')).toBeInTheDocument();
expect(screen.getByText('368')).toBeInTheDocument();
expect(screen.getByText('TDengine 写入')).toBeInTheDocument();
@@ -3417,6 +3422,9 @@ test('renders ops quality as a standalone runtime health page', async () => {
expect(screen.getByText('安全码未暴露')).toBeInTheDocument();
expect(screen.getByText('gb32960-gateway')).toBeInTheDocument();
expect(screen.getByText('Kafka lag 42')).toBeInTheDocument();
+ expect(screen.getByText('NATS 桥接 ACK 未确认')).toBeInTheDocument();
+ expect(screen.getByText('NATS 桥接待消费')).toBeInTheDocument();
+ expect(screen.getByText('桥接处置顺序')).toBeInTheDocument();
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/ops/health'), undefined);
});