feat(platform): separate ops quality as evidence layer

This commit is contained in:
lingniu
2026-07-05 20:40:29 +08:00
parent 59d55e7bd6
commit 1e4a11d806
3 changed files with 128 additions and 4 deletions

View File

@@ -75,6 +75,33 @@ type OpsActionItem = {
acceptance: string;
};
const customerEvidencePaths = [
{
title: '车辆不在线',
action: '看实时写入',
evidence: 'Redis 在线 Key、快速写入 pending、NATS ACK',
detail: '解释客户看到车辆离线时,是否是实时帧、在线 TTL 或快速写入链路中断。'
},
{
title: '轨迹缺数据',
action: '看历史消费',
evidence: 'Kafka Lag、历史批次、历史行待写',
detail: '解释轨迹回放或历史明细缺失时,数据是否卡在 Kafka、TDengine 或批量写入。'
},
{
title: '无法导出',
action: '看存储/API',
evidence: 'TDengine、MySQL、请求超时、运行版本',
detail: '解释客户导出失败、查询慢或字段缺失时,核心存储和 API 是否可用。'
},
{
title: '地图不可用',
action: '看高德配置',
evidence: 'Web JS Key、服务端 API Key、安全代理',
detail: '解释实时地图、轨迹回放和地理能力不可用时,地图配置是否完整且安全。'
}
];
const emptyCapacityMetrics: CapacityMetrics = {
activeConnections: 0,
kafkaLag: 0,
@@ -457,8 +484,8 @@ export function OpsQuality() {
return (
<div className="vp-page">
<PageHeader
title="运维质量"
description="集中查看网关、消息队列、缓存、时序库、关系库、地图配置和运行版本状态"
title="运维证据层"
description="客户主流程外的链路、容量、存储和地图配置证据,用来解释车辆实时、轨迹、统计、导出或告警为何不可用。"
actions={(
<Space>
<Button size="small" icon={<IconCopy />} aria-label="复制容量交接" disabled={!health} onClick={() => copyText(opsCapacityHandoffText(health), '运维容量交接')}></Button>
@@ -468,6 +495,32 @@ export function OpsQuality() {
)}
/>
<Card bordered className="vp-ops-evidence-layer-card">
<div className="vp-ops-evidence-layer-head">
<div>
<Typography.Title heading={5} style={{ margin: 0 }}></Typography.Title>
<Typography.Text type="secondary"></Typography.Text>
</div>
<Tag color="blue"></Tag>
</div>
<div className="vp-ops-evidence-path-grid">
{customerEvidencePaths.map((item) => (
<button
key={item.title}
type="button"
className="vp-ops-evidence-path-item"
aria-label={`客户问题解释路径 ${item.title} ${item.action}`}
onClick={() => Toast.info(item.evidence)}
>
<span className="vp-ops-evidence-path-title">{item.title}</span>
<span className="vp-ops-evidence-path-action">{item.action}</span>
<span className="vp-ops-evidence-path-evidence">{item.evidence}</span>
<span className="vp-ops-evidence-path-detail">{item.detail}</span>
</button>
))}
</div>
</Card>
<div className="vp-kpi-grid">
<Card bordered loading={loading}>
<div className="vp-kpi-value">{formatNumber(health?.kafkaLag)}</div>