feat: build vehicle data platform and production pipeline

This commit is contained in:
lingniu
2026-07-14 12:35:33 +08:00
parent b452be3b94
commit bb59303a4b
270 changed files with 88016 additions and 1975 deletions

View File

@@ -0,0 +1,104 @@
import { useQuery } from '@tanstack/react-query';
import { api } from '../../api/client';
import type { VehicleRealtimeRow } from '../../api/types';
export type MonitorFilters = {
keyword: string;
protocol: string;
status: string;
};
export type MonitorViewport = {
zoom: number;
bounds: string;
};
export const MONITOR_REFRESH = {
summary: 30_000,
fleet: 15_000,
selected: 10_000,
alerts: 15_000
} as const;
export function monitorQueryParams(filters: MonitorFilters, limit: number) {
const params = new URLSearchParams({ limit: String(limit) });
if (filters.keyword.trim()) params.set('keyword', filters.keyword.trim());
if (filters.protocol) params.set('protocol', filters.protocol);
if (filters.status) params.set('status', filters.status);
if (filters.status === 'online' || filters.status === 'offline') params.set('online', filters.status);
return params;
}
export function monitorMapQueryParams(filters: MonitorFilters, viewport: MonitorViewport) {
const params = monitorQueryParams(filters, 10_000);
params.set('zoom', String(viewport.zoom));
if (viewport.bounds && !filters.keyword.trim()) params.set('bounds', viewport.bounds);
return params;
}
export function useMonitorData(filters: MonitorFilters, viewport: MonitorViewport, selectedVin: string) {
const params = monitorQueryParams(filters, 200);
const mapParams = monitorMapQueryParams(filters, viewport);
const summary = useQuery({
queryKey: ['monitor', 'summary', params.toString()],
queryFn: () => api.monitorSummary(params),
refetchInterval: MONITOR_REFRESH.summary
});
const vehicles = useQuery({
queryKey: ['monitor', 'vehicles', params.toString()],
queryFn: () => api.vehicleRealtime(params),
refetchInterval: MONITOR_REFRESH.fleet
});
const map = useQuery({
queryKey: ['monitor', 'map', mapParams.toString()],
queryFn: () => api.monitorMap(mapParams),
placeholderData: (previous) => previous,
staleTime: 5_000,
refetchInterval: MONITOR_REFRESH.fleet
});
const selectedVehicle = useQuery({
queryKey: ['monitor', 'selected-vehicle', selectedVin],
queryFn: () => api.vehicleRealtime(new URLSearchParams({ keyword: selectedVin, limit: '1', offset: '0' })),
enabled: Boolean(selectedVin),
staleTime: 5_000,
refetchInterval: selectedVin ? MONITOR_REFRESH.selected : false
});
return { summary, vehicles, map, selectedVehicle };
}
export function useMonitorVehicleCard(vin: string, vehicle?: VehicleRealtimeRow, activelyTracked = false) {
const enabled = Boolean(vin);
const longitude = vehicle?.longitude;
const latitude = vehicle?.latitude;
const hasCoordinate = Number.isFinite(longitude) && Number.isFinite(latitude)
&& Math.abs(longitude ?? 0) <= 180 && Math.abs(latitude ?? 0) <= 90;
const detail = useQuery({
queryKey: ['monitor', 'vehicle-card', 'detail', vin],
queryFn: () => api.vehicleDetail(new URLSearchParams({ keyword: vin })),
enabled,
staleTime: 30_000
});
const activeAlerts = useQuery({
queryKey: ['monitor', 'vehicle-card', 'active-alerts', vin],
queryFn: () => api.alertEventsV2({ keyword: vin, status: 'active', limit: 20, offset: 0 }),
enabled,
staleTime: 10_000,
refetchInterval: enabled && activelyTracked ? MONITOR_REFRESH.alerts : false
});
const address = useQuery({
queryKey: ['monitor', 'vehicle-card', 'address', longitude, latitude],
queryFn: () => api.reverseGeocode(new URLSearchParams({
longitude: longitude!.toFixed(6),
latitude: latitude!.toFixed(6)
})),
enabled: enabled && hasCoordinate,
staleTime: 60 * 60_000
});
return { detail, activeAlerts, address };
}