import { IconRefresh, IconSearch, IconTickCircle } from '@douyinfe/semi-icons';
import { Button, Card, CardGroup, Checkbox, Descriptions, Empty, Input, Progress, SideSheet, Spin, Table, Tag, Typography } from '@douyinfe/semi-ui';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { FormEvent, useDeferredValue, useEffect, useState } from 'react';
import { api } from '../../api/client';
import type { VehicleCoverageRow, VehicleLocationSourceEvidence, VehicleSourceDiagnostic } from '../../api/types';
import { InlineError } from '../shared/AsyncState';
import { SegmentedTabs } from '../shared/SegmentedTabs';
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
import { LIVE_QUERY_POLICY, QUERY_MEMORY } from '../queryPolicy';
import { useMobileLayout } from '../hooks/useMobileLayout';
import { useSideSheetA11y } from '../hooks/useSideSheetA11y';
import ReconciliationCenter from './ReconciliationCenter';
type OperationsWorkspace = 'reconciliation' | 'diagnostic' | 'health';
function statusLabel(status: string) {
return { ok: '正常', warning: '关注', error: '异常' }[status] ?? status;
}
function HealthTag({ status, children }: { status: string; children?: string }) {
const color = status === 'ok' ? 'green' : status === 'warning' ? 'orange' : status === 'error' ? 'red' : 'grey';
return {children ?? statusLabel(status)};
}
function fmt(value?: string) {
if (!value) return '—';
const parsed = new Date(value.replace(' ', 'T'));
return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString('zh-CN', { hour12: false });
}
function number(value?: number, digits = 1) {
return value == null || !Number.isFinite(value) ? '—' : value.toLocaleString('zh-CN', { maximumFractionDigits: digits });
}
function OpsMetric({ label, value, detail, tone = 'neutral' }: {
label: string;
value: string;
detail: string;
tone?: 'neutral' | 'success' | 'warning' | 'danger';
}) {
return
{label}
{value}
{detail}
;
}
function sourceOnlineRate(online: number, total: number, onlineRate?: number) {
if (onlineRate != null && Number.isFinite(onlineRate)) return Math.max(0, Math.min(100, onlineRate));
return total > 0 ? Math.max(0, Math.min(100, (online / total) * 100)) : 0;
}
function SourcePolicyEditor({ vin, source, diagnostic, editable, onSaved }: {
vin: string;
source: VehicleLocationSourceEvidence;
diagnostic: VehicleSourceDiagnostic;
editable: boolean;
onSaved: (next: VehicleSourceDiagnostic) => void;
}) {
const [enabled, setEnabled] = useState(source.enabled);
const [priority, setPriority] = useState(source.priority);
const [providerName, setProviderName] = useState(source.providerOverride || '');
const [providerEvidence, setProviderEvidence] = useState('');
const [remark, setRemark] = useState(source.policyRemark || '');
useEffect(() => {
setEnabled(source.enabled);
setPriority(source.priority);
setProviderName(source.providerOverride || '');
setProviderEvidence('');
setRemark(source.policyRemark || '');
}, [source.enabled, source.priority, source.providerOverride, source.policyRemark, source.sourceRef]);
const save = useMutation({
mutationFn: () => api.updateVehicleSourcePolicy(vin, {
version: diagnostic.policy.version,
sourceRef: source.sourceRef ?? '',
providerName,
providerEvidence,
enabled,
priority,
remark
}),
onSuccess: onSaved
});
const providerChanged = providerName.trim() !== (source.providerOverride || '');
const policyEditable = source.sourceKind !== 'CANONICAL';
const policyChanged = enabled !== source.enabled || priority !== source.priority || remark.trim() !== (source.policyRemark || '');
const changed = (policyEditable && policyChanged) || providerChanged;
return
setEnabled(Boolean(event.target.checked))} aria-label="启用">启用
setPriority(Number(value))} />
{save.isError ? {save.error instanceof Error ? save.error.message : '保存失败'} : null}
;
}
function SourceIdentity({ source }: { source: VehicleLocationSourceEvidence }) {
return {source.sourceLabel}{source.recommended ? 推荐 : null}{source.terminalLabel || source.sourceKind || '未维护终端'}
;
}
function SourceProtocol({ source }: { source: VehicleLocationSourceEvidence }) {
return {source.protocol}{source.selectedWithinProtocol ? '协议内已选' : '协议内候选'}
;
}
function SourceHealth({ source }: { source: VehicleLocationSourceEvidence }) {
return {source.online ? '在线' : '离线'}{source.qualityStatus || '未知'}{source.qualityReason ? ` · ${source.qualityReason}` : ''}
;
}
function SourceTiming({ source }: { source: VehicleLocationSourceEvidence }) {
return {fmt(source.firstSeenAt)}最近 {fmt(source.receivedAt || source.eventTime)}
;
}
function SourceInterval({ source }: { source: VehicleLocationSourceEvidence }) {
return {source.reportIntervalSec == null ? '—' : `${source.reportIntervalSec}s`}{source.reportSampleCount ? `${source.reportSampleCount.toLocaleString('zh-CN')} 个里程样本` : '暂无累计样本'}
;
}
function SourcePosition({ source }: { source: VehicleLocationSourceEvidence }) {
return {source.longitude == null || source.latitude == null ? '—' : `${source.longitude.toFixed(6)}, ${source.latitude.toFixed(6)}`}{number(source.speedKmh)} km/h · {number(source.totalMileageKm)} km
;
}
function SourceDecision({ source }: { source: VehicleLocationSourceEvidence }) {
return {source.recommended ? '当前推荐' : source.selectedWithinProtocol ? '协议首选' : '备用来源'}{source.selectionReason || '等待选举说明'}
;
}
function sourceRowKey(source?: VehicleLocationSourceEvidence) {
return source?.sourceRef || `${source?.protocol ?? ''}-${source?.sourceLabel ?? ''}-${source?.terminalLabel ?? ''}`;
}
function SourcePolicySummary({ source, onEdit }: {
source: VehicleLocationSourceEvidence;
onEdit: () => void;
}) {
return
{source.enabled ? '已启用' : '已禁用'}优先级 {source.priority}
{source.providerOverride || '提供方未维护'}
;
}
function SourcePolicySheet({ vin, source, diagnostic, editable, onClose, onSaved }: {
vin: string;
source?: VehicleLocationSourceEvidence;
diagnostic: VehicleSourceDiagnostic;
editable: boolean;
onClose: () => void;
onSaved: (next: VehicleSourceDiagnostic) => void;
}) {
useSideSheetA11y(Boolean(source), '.v2-source-policy-sidesheet', 'v2-source-policy-sheet', '车辆来源策略', '关闭车辆来源策略');
return {source.sourceLabel}{source.terminalLabel || source.protocol} · {source.sourceKind === 'CANONICAL' ? '仅维护提供方' : '来源策略'} : '来源策略'}
onCancel={onClose}
footer={null}
>
{source ?
{ onSaved(next); onClose(); }} /> : null}
;
}
function SourceDiagnosticTable({ vin, sources, diagnostic, editable, onSaved }: {
vin: string;
sources: VehicleLocationSourceEvidence[];
diagnostic: VehicleSourceDiagnostic;
editable: boolean;
onSaved: (next: VehicleSourceDiagnostic) => void;
}) {
const [editingSource, setEditingSource] = useState();
const columns = [
{ title: '来源 / 终端', dataIndex: 'sourceLabel', width: 180, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '协议', dataIndex: 'protocol', width: 120, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '在线 / 质量', dataIndex: 'online', width: 190, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '首次 / 最近上报', dataIndex: 'firstSeenAt', width: 220, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '上报周期', dataIndex: 'reportIntervalSec', width: 145, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '位置 / 里程', dataIndex: 'longitude', width: 230, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '选举结论', dataIndex: 'recommended', width: 220, render: (_: unknown, source: VehicleLocationSourceEvidence) => },
{ title: '运维策略', dataIndex: 'policy', width: 200, render: (_: unknown, source: VehicleLocationSourceEvidence) => setEditingSource(source)} /> }
];
return <> source ? ({ className: source.recommended ? 'is-recommended' : '' }) : ({})}
/>
setEditingSource(undefined)} onSaved={onSaved} />
>;
}
function SourceDiagnosticCards({ vin, sources, diagnostic, editable, onSaved }: {
vin: string;
sources: VehicleLocationSourceEvidence[];
diagnostic: VehicleSourceDiagnostic;
editable: boolean;
onSaved: (next: VehicleSourceDiagnostic) => void;
}) {
const [editingSource, setEditingSource] = useState();
return <>{sources.map((source) =>
},
{ key: '首次 / 最近上报', value: },
{ key: '上报周期', value: },
{ key: '位置 / 里程', value: },
{ key: '选举结论', value: }
]} />
运维策略 setEditingSource(source)} />
)}
setEditingSource(undefined)} onSaved={onSaved} />
>;
}
function SourceDiagnosticWorkspace() {
const queryClient = useQueryClient();
const mobileLayout = useMobileLayout();
const [keyword, setKeyword] = useState('');
const deferredKeyword = useDeferredValue(keyword.trim());
const [selected, setSelected] = useState();
const [candidateOffset, setCandidateOffset] = useState(0);
const [filtersCollapsed, setFiltersCollapsed] = useState(false);
useEffect(() => setCandidateOffset(0), [deferredKeyword]);
const candidates = useQuery({
queryKey: ['ops-source-candidates', deferredKeyword, candidateOffset],
queryFn: ({ signal }) => api.vehicleCoverage(new URLSearchParams({ keyword: deferredKeyword, bindingStatus: 'bound', limit: '20', offset: String(candidateOffset) }), signal),
enabled: deferredKeyword.length > 0,
staleTime: 15_000,
gcTime: QUERY_MEMORY.optionGcTime
});
const session = useQuery({ queryKey: ['ops-session'], queryFn: ({ signal }) => api.session(signal), staleTime: 60_000 });
const diagnostic = useQuery({
queryKey: ['ops-source-diagnostic', selected?.vin],
queryFn: ({ signal }) => api.vehicleSourceDiagnostic(selected!.vin, signal),
enabled: Boolean(selected?.vin),
staleTime: 5_000,
gcTime: QUERY_MEMORY.highVolumeGcTime
});
const choose = (vehicle: VehicleCoverageRow) => {
setSelected(vehicle);
setKeyword(vehicle.plate || vehicle.vin);
if (mobileLayout) setFiltersCollapsed(true);
};
const submit = (event: FormEvent) => {
event.preventDefault();
const first = candidates.data?.items[0];
if (first) choose(first);
};
const data = diagnostic.data;
const editable = session.data?.role === 'admin';
const selectedLabel = selected ? selected.plate || selected.vin : '';
const filterStatus = selectedLabel
? `已选 ${selectedLabel}`
: deferredKeyword
? candidates.isFetching ? '正在查找' : `${candidates.data?.total ?? 0} 辆候选`
: '等待选择车辆';
const onSourceSaved = (next: VehicleSourceDiagnostic) => {
queryClient.setQueryData(['ops-source-diagnostic', next.evidence.vin], next);
void Promise.all([
queryClient.invalidateQueries({ queryKey: ['access-summary'] }),
queryClient.invalidateQueries({ queryKey: ['access-vehicles'] }),
queryClient.invalidateQueries({ queryKey: ['ops-source-readiness-v2'] })
]);
};
return <>
setFiltersCollapsed((value) => !value)}
>
{data ? `${data.evidence.locationSources.length} 个来源` : selected ? '正在读取证据' : '等待选择车辆'}}
actions={selected ? } onClick={() => diagnostic.refetch()} disabled={diagnostic.isFetching}>刷新该车 : null}
/>
{diagnostic.isError ? diagnostic.refetch()} /> : null}
{!selected ? : null}
{selected && diagnostic.isPending ? 正在读取来源证据只查询当前车辆,不会扫描整车队。
: null}
{data ? <>
车辆{data.evidence.plate || selected?.plate || '未绑定车牌'}{data.evidence.vin}
当前推荐{data.evidence.recommendedLocationLabel || '暂无推荐'}{data.evidence.recommendedLocationProtocol || '—'}
来源状态{data.evidence.locationSources.filter((item) => item.online).length} / {data.evidence.locationSources.length} 在线{data.evidence.locationConflict ? `位置冲突 ${number(data.evidence.conflictDistanceM)}m` : '未发现实时位置冲突'}
策略版本v{data.policy.version}{data.policy.updatedAt ? `${data.policy.updatedBy} · ${fmt(data.policy.updatedAt)}` : '尚无人工调整'}
{data.recommendationReason}
{!editable ? 只读权限当前账号可查看诊断证据;只有管理员可以调整启停和优先级。
: null}
{mobileLayout
?
: }
{data.policy.audit.length ? {data.policy.audit.map((item) => - v{item.version}{item.summary}{item.changeType === 'provider' ? '提供方' : '策略'} · {item.actor} · {fmt(item.changedAt)}
)}
: 该车辆尚无人工来源策略或提供方变更。
}
> : null}
>;
}
export default function OperationsPage() {
const [workspace, setWorkspace] = useState('reconciliation');
const health = useQuery({ queryKey: ['ops-health-v2'], queryFn: ({ signal }) => api.opsHealth(signal), refetchInterval: 15_000, staleTime: 8_000, gcTime: QUERY_MEMORY.summaryGcTime, ...LIVE_QUERY_POLICY });
const readiness = useQuery({ queryKey: ['ops-source-readiness-v2'], queryFn: ({ signal }) => api.sourceReadiness(signal), refetchInterval: 30_000, staleTime: 15_000, gcTime: QUERY_MEMORY.summaryGcTime, ...LIVE_QUERY_POLICY });
const data = health.data; const sources = readiness.data;
const refresh = () => Promise.all([health.refetch(), readiness.refetch()]);
const linkIssueCount = data?.linkHealth.filter((item) => item.status !== 'ok').length ?? 0;
const runtimeIssueCount = data ? Number(!data.mysqlWritable) + Number(!data.tdengineWritable) + Number(data.runtime.dataMode !== 'production') : 0;
const capacityIssueCount = data?.capacityFindings.length ?? 0;
const healthIssueCount = linkIssueCount + runtimeIssueCount + capacityIssueCount + Number((data?.kafkaLag ?? 0) > 0);
const overallStatus = !data ? 'unknown' : healthIssueCount > 0 ? 'warning' : 'ok';
return
{data?.runtime.platformRelease ? `版本 ${data.runtime.platformRelease}` : '正在读取运行版本'}}
actions={} loading={health.isFetching || readiness.isFetching} onClick={refresh}>刷新全局证据}
/>
{workspace === 'reconciliation' ? : null}
{workspace === 'diagnostic' ? : null}
{workspace === 'health' ? <>
{health.isError ? : null}
{readiness.isError ? readiness.refetch()} /> : null}
{!data ? '正在读取' : healthIssueCount > 0 ? `${healthIssueCount} 项需关注` : '全部正常'}15 秒自动刷新}
/>
0 ? '需要处理' : '运行正常'} detail={!data ? '等待服务端健康证据' : healthIssueCount > 0 ? `${healthIssueCount} 项证据需要关注` : '链路与写入探针均正常'} tone={!data ? 'neutral' : healthIssueCount > 0 ? 'warning' : 'success'} />
0 ? 'warning' : 'ok'}>{!data ? '读取中' : linkIssueCount > 0 ? `${linkIssueCount} 条异常` : '链路正常'}} />{data?.linkHealth.length ? data.linkHealth.map((item) =>
{item.name}{item.detail || '无补充信息'}
) :
}
0 ? 'warning' : 'ok'}>{!data ? '读取中' : runtimeIssueCount + capacityIssueCount > 0 ? `${runtimeIssueCount + capacityIssueCount} 项关注` : '配置正常'}} />{data?.runtime.dataMode === 'production' ? '已启用' : '未启用'} },
{ key: 'MySQL 写探针', value: {data?.mysqlWritable ? '正常' : '异常'} },
{ key: 'TDengine 写探针', value: {data?.tdengineWritable ? '正常' : '异常'} },
{ key: '请求超时', value: `${data?.runtime.requestTimeoutMs ?? '—'} ms` },
{ key: '高德安全代理', value: {data?.runtime.amapSecurityProxyEnabled ? '服务端代理' : '未启用'} }
]} />{data?.capacityFindings?.length ? {data.capacityFindings.map((item) =>
待处理{item}
)}
: } title="容量检查通过" description="当前没有需要处理的容量风险。" />}
{sources ? `${sources.sources.length} 个协议` : '正在读取'}} />{sources ? sources.sources.length ? {sources.sources.map((source) => {
const onlineRate = sourceOnlineRate(source.online, source.total, source.onlineRate);
return {source.protocol}{source.role}} headerExtraContent={{source.status || statusLabel(source.severity)}} headerLine>
在线覆盖{source.online.toLocaleString('zh-CN')} / {source.total.toLocaleString('zh-CN')}{number(onlineRate)}% 在线{source.missingVehicles ? ` · ${source.missingVehicles.toLocaleString('zh-CN')} 辆待恢复` : ''}
;
})} : :
}
> : null}
;
}