fix: 实时监控累计总里程少算,G7S 数据源 total_km 为 NULL 时用业务库 vehicle_total_mileage 兜底
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,10 +49,26 @@ interface MileageRow {
|
|||||||
source: string;
|
source: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchBizTotalMileageMap(): Promise<Map<string, number>> {
|
||||||
|
// v_vehicle_daily_stats.total_km 对 G7S 数据源常为 NULL(G7 只回传日增量),
|
||||||
|
// 业务库 tab_mileage_assessment_vehicle.vehicle_total_mileage 是累加后的权威累计值,
|
||||||
|
// 用它兜底保证 totalKm 汇总完整。
|
||||||
|
const [rows] = await pool.execute(
|
||||||
|
'SELECT plate_number, vehicle_total_mileage FROM tab_mileage_assessment_vehicle WHERE is_deleted = 0'
|
||||||
|
) as [{ plate_number: string; vehicle_total_mileage: string | number | null }[], unknown];
|
||||||
|
const map = new Map<string, number>();
|
||||||
|
for (const r of rows) {
|
||||||
|
const km = Number(r.vehicle_total_mileage);
|
||||||
|
if (Number.isFinite(km) && km > 0) map.set(r.plate_number, km);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
function mergeVehicles(
|
function mergeVehicles(
|
||||||
mileageRows: MileageRow[],
|
mileageRows: MileageRow[],
|
||||||
infoMap: Map<string, VehicleInfoRow>,
|
infoMap: Map<string, VehicleInfoRow>,
|
||||||
yesterdayMap: Map<string, number>,
|
yesterdayMap: Map<string, number>,
|
||||||
|
bizTotalMap: Map<string, number>,
|
||||||
): CachedVehicle[] {
|
): CachedVehicle[] {
|
||||||
const mileageMap = new Map<string, MileageRow>();
|
const mileageMap = new Map<string, MileageRow>();
|
||||||
for (const row of mileageRows) {
|
for (const row of mileageRows) {
|
||||||
@@ -66,11 +82,13 @@ function mergeVehicles(
|
|||||||
const info = infoMap.get(m.plate);
|
const info = infoMap.get(m.plate);
|
||||||
const dailyKm = Number(m.daily_km) || 0;
|
const dailyKm = Number(m.daily_km) || 0;
|
||||||
const source = m.source || 'NONE';
|
const source = m.source || 'NONE';
|
||||||
|
const gpsTotal = m.total_km !== null ? Number(m.total_km) : null;
|
||||||
|
const bizTotal = bizTotalMap.get(m.plate);
|
||||||
return {
|
return {
|
||||||
plate: m.plate,
|
plate: m.plate,
|
||||||
vin: m.vin,
|
vin: m.vin,
|
||||||
dailyKm,
|
dailyKm,
|
||||||
totalKm: m.total_km !== null ? Number(m.total_km) : null,
|
totalKm: gpsTotal !== null ? gpsTotal : (bizTotal ?? null),
|
||||||
source,
|
source,
|
||||||
isOnline: source !== 'NONE' && dailyKm > 0,
|
isOnline: source !== 'NONE' && dailyKm > 0,
|
||||||
isDataSynced: source !== 'NONE',
|
isDataSynced: source !== 'NONE',
|
||||||
@@ -91,7 +109,7 @@ export async function refreshMonitoringCache(): Promise<void> {
|
|||||||
console.log('[mileage] refreshing monitoring cache...');
|
console.log('[mileage] refreshing monitoring cache...');
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
const [mileageRows, yesterdayMap, infoMap, targetRows] = await Promise.all([
|
const [mileageRows, yesterdayMap, infoMap, targetRows, bizTotalMap] = await Promise.all([
|
||||||
(async () => {
|
(async () => {
|
||||||
const [dateRows] = await mileagePool.execute(
|
const [dateRows] = await mileagePool.execute(
|
||||||
'SELECT MAX(stat_date) as latest FROM v_vehicle_daily_stats'
|
'SELECT MAX(stat_date) as latest FROM v_vehicle_daily_stats'
|
||||||
@@ -124,6 +142,7 @@ export async function refreshMonitoringCache(): Promise<void> {
|
|||||||
JOIN tab_mileage_assessment_vehicle v ON v.target_id = t.id AND v.is_deleted = 0
|
JOIN tab_mileage_assessment_vehicle v ON v.target_id = t.id AND v.is_deleted = 0
|
||||||
WHERE t.is_deleted = 0`
|
WHERE t.is_deleted = 0`
|
||||||
).then(([rows]) => rows as { id: number; target_name: string; plate_number: string }[]),
|
).then(([rows]) => rows as { id: number; target_name: string; plate_number: string }[]),
|
||||||
|
fetchBizTotalMileageMap(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const targetPlatesMap = new Map<string, Set<string>>();
|
const targetPlatesMap = new Map<string, Set<string>>();
|
||||||
@@ -134,7 +153,7 @@ export async function refreshMonitoringCache(): Promise<void> {
|
|||||||
}
|
}
|
||||||
const targetNames = Array.from(targetPlatesMap.keys());
|
const targetNames = Array.from(targetPlatesMap.keys());
|
||||||
|
|
||||||
const vehicles = mergeVehicles(mileageRows, infoMap, yesterdayMap);
|
const vehicles = mergeVehicles(mileageRows, infoMap, yesterdayMap, bizTotalMap);
|
||||||
const totalToday = vehicles.reduce((sum, v) => sum + v.dailyKm, 0);
|
const totalToday = vehicles.reduce((sum, v) => sum + v.dailyKm, 0);
|
||||||
const totalAll = vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0);
|
const totalAll = vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0);
|
||||||
|
|
||||||
@@ -153,7 +172,7 @@ export async function refreshMonitoringCache(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function queryDateMileage(dateStr: string): Promise<CachedVehicle[]> {
|
export async function queryDateMileage(dateStr: string): Promise<CachedVehicle[]> {
|
||||||
const [mileageRows, yesterdayRows, infoMap] = await Promise.all([
|
const [mileageRows, yesterdayRows, infoMap, bizTotalMap] = await Promise.all([
|
||||||
mileagePool.execute(
|
mileagePool.execute(
|
||||||
'SELECT plate, vin, daily_km, total_km, source FROM v_vehicle_daily_stats WHERE stat_date = ?',
|
'SELECT plate, vin, daily_km, total_km, source FROM v_vehicle_daily_stats WHERE stat_date = ?',
|
||||||
[dateStr]
|
[dateStr]
|
||||||
@@ -163,6 +182,7 @@ export async function queryDateMileage(dateStr: string): Promise<CachedVehicle[]
|
|||||||
[dateStr]
|
[dateStr]
|
||||||
).then(([r]) => r as { plate: string; daily_km: string }[]),
|
).then(([r]) => r as { plate: string; daily_km: string }[]),
|
||||||
fetchVehicleInfoMap(),
|
fetchVehicleInfoMap(),
|
||||||
|
fetchBizTotalMileageMap(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const yesterdayMap = new Map<string, number>();
|
const yesterdayMap = new Map<string, number>();
|
||||||
@@ -172,7 +192,7 @@ export async function queryDateMileage(dateStr: string): Promise<CachedVehicle[]
|
|||||||
if (km > existing) yesterdayMap.set(r.plate, km);
|
if (km > existing) yesterdayMap.set(r.plate, km);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mergeVehicles(mileageRows, infoMap, yesterdayMap);
|
return mergeVehicles(mileageRows, infoMap, yesterdayMap, bizTotalMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildDateFilters(vehicles: CachedVehicle[]): MonitoringFilters {
|
export function buildDateFilters(vehicles: CachedVehicle[]): MonitoringFilters {
|
||||||
|
|||||||
Reference in New Issue
Block a user