feat(platform): add ops capacity handoff
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Button, Card, Space, Table, Tag, Toast, Typography } from '@douyinfe/semi-ui';
|
||||
import { IconCopy } from '@douyinfe/semi-icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { api } from '../api/client';
|
||||
import type { CapacityMetrics, LinkHealth, OpsHealth } from '../api/types';
|
||||
@@ -226,6 +227,44 @@ function capacityRisk(finding: string): CapacityRisk {
|
||||
};
|
||||
}
|
||||
|
||||
function opsCapacityHandoffText(health: OpsHealth | null) {
|
||||
const metrics = getCapacityMetrics(health);
|
||||
const risks = (health?.capacityFindings ?? []).map(capacityRisk);
|
||||
const unhealthyLinks = (health?.linkHealth ?? []).filter((item) => item.status !== 'ok');
|
||||
const runtime = health?.runtime;
|
||||
return [
|
||||
'【车辆数据中台运维容量交接】',
|
||||
`运行版本:${runtime?.platformRelease || '-'}`,
|
||||
`Kafka Lag:${formatNumber(metrics.kafkaLag)}`,
|
||||
`活跃连接:${formatNumber(metrics.activeConnections)}`,
|
||||
`Redis 在线 Key:${formatNumber(health?.redisOnlineKeys)}`,
|
||||
`NATS桥接:consumer pending ${formatNumber(metrics.bridgeConsumerPending)} / ack pending ${formatNumber(metrics.bridgeAckPending)} / batch pending ${formatNumber(metrics.bridgeBatchPendingMessages)}`,
|
||||
`快速写入:consumer pending ${formatNumber(metrics.fastWriterConsumerPending)} / ack pending ${formatNumber(metrics.fastWriterAckPending)} / batch pending ${formatNumber(metrics.fastWriterBatchPending)}`,
|
||||
`历史写入:batch pending ${formatNumber(metrics.historyBatchPending)} / rows pending ${formatNumber(metrics.historyRowsPending)}`,
|
||||
`存储状态:TDengine ${statusText(health?.tdengineWritable)} / MySQL ${statusText(health?.mysqlWritable)}`,
|
||||
`地图配置:Web JS ${runtime?.amapWebJsConfigured ? '已配置' : '未配置'} / API ${runtime?.amapApiConfigured ? '已配置' : '未配置'} / ${amapSecurityText(health)}`,
|
||||
`异常链路:${unhealthyLinks.length > 0 ? unhealthyLinks.map((item) => `${item.name}=${item.status}(${item.detail || '-'})`).join(';') : '正常'}`,
|
||||
'',
|
||||
'容量发现:',
|
||||
...(risks.length > 0 ? risks.map((risk, index) => `${index + 1}. [${risk.category}] ${risk.finding} -> ${risk.action}`) : ['暂无容量风险']),
|
||||
'',
|
||||
'处置顺序:',
|
||||
'1. 先看 Kafka Lag 与 NATS ACK pending,确认消息是否卡在桥接或下游消费',
|
||||
'2. 再看快速写入 pending,确认 Redis/TDengine 实时链路是否影响在线和实时数据',
|
||||
'3. 再看历史写入 pending,确认轨迹、RAW、统计是否滞后',
|
||||
'4. 最后核对 MySQL、TDengine、Redis 和地图配置,确认中台查询体验是否受影响'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
async function copyText(value: string, label: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(value);
|
||||
Toast.success(`已复制${label}`);
|
||||
} catch {
|
||||
Toast.error(`复制${label}失败`);
|
||||
}
|
||||
}
|
||||
|
||||
export function OpsQuality() {
|
||||
const [health, setHealth] = useState<OpsHealth | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -252,7 +291,12 @@ export function OpsQuality() {
|
||||
<PageHeader
|
||||
title="运维质量"
|
||||
description="集中查看网关、消息队列、缓存、时序库、关系库、地图配置和运行版本状态"
|
||||
actions={<Button size="small" loading={loading} onClick={load}>刷新</Button>}
|
||||
actions={(
|
||||
<Space>
|
||||
<Button size="small" icon={<IconCopy />} aria-label="复制容量交接" disabled={!health} onClick={() => copyText(opsCapacityHandoffText(health), '运维容量交接')}>复制容量交接</Button>
|
||||
<Button size="small" loading={loading} onClick={load}>刷新</Button>
|
||||
</Space>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="vp-kpi-grid">
|
||||
|
||||
Reference in New Issue
Block a user