diff --git a/src/server/routes/mileage/cache.ts b/src/server/routes/mileage/cache.ts index 31e1e1d..c2ed968 100644 --- a/src/server/routes/mileage/cache.ts +++ b/src/server/routes/mileage/cache.ts @@ -80,11 +80,39 @@ async function fetchBizTotalMileageMap(): Promise> { return map; } +async function fetchLatestPgTotalMileageMap(asOf?: string): Promise> { + // 当日 ln_vehicle_day_total_pg 无记录或 total_mileage 为 NULL 时, + // 回填该车 dates <= asOf 的最近一条非空 total_mileage(÷1000 转 km), + // 让视图 total_km 为 NULL 的车也能显示历史累计。 + const sql = ` + SELECT plate_number, total_mileage + FROM ( + SELECT plate_number, total_mileage, + ROW_NUMBER() OVER (PARTITION BY plate_number ORDER BY dates DESC) AS rn + FROM ln_vehicle_day_total_pg + WHERE total_mileage IS NOT NULL + ${asOf ? 'AND dates <= ?' : ''} + ) x + WHERE rn = 1`; + const params = asOf ? [asOf] : []; + const [rows] = await mileagePool.execute(sql, params) as [ + { plate_number: string; total_mileage: string | number | null }[], + unknown, + ]; + const map = new Map(); + for (const r of rows) { + const km = Number(r.total_mileage) / 1000; + if (Number.isFinite(km) && km > 0) map.set(r.plate_number, km); + } + return map; +} + function mergeVehicles( mileageRows: MileageRow[], infoMap: Map, yesterdayMap: Map, bizTotalMap: Map, + latestPgTotalMap: Map, ): CachedVehicle[] { const mileageMap = new Map(); for (const row of mileageRows) { @@ -99,12 +127,13 @@ function mergeVehicles( const dailyKm = Number(m.daily_km) || 0; const source = m.source || 'NONE'; const gpsTotal = m.total_km !== null ? Number(m.total_km) : null; + const latestPgTotal = latestPgTotalMap.get(m.plate); const bizTotal = bizTotalMap.get(m.plate); return { plate: m.plate, vin: m.vin, dailyKm, - totalKm: gpsTotal !== null ? gpsTotal : (bizTotal ?? null), + totalKm: gpsTotal !== null ? gpsTotal : (latestPgTotal ?? bizTotal ?? null), source, isOnline: source !== 'NONE' && dailyKm > 0, isDataSynced: source !== 'NONE', @@ -126,7 +155,7 @@ export async function refreshMonitoringCache(): Promise { console.log('[mileage] refreshing monitoring cache...'); const start = Date.now(); - const [mileageRows, yesterdayMap, infoMap, targetRows, bizTotalMap] = await Promise.all([ + const [mileageRows, yesterdayMap, infoMap, targetRows, bizTotalMap, latestPgTotalMap] = await Promise.all([ (async () => { const [dateRows] = await mileagePool.execute( 'SELECT MAX(stat_date) as latest FROM v_vehicle_daily_stats' @@ -160,6 +189,7 @@ export async function refreshMonitoringCache(): Promise { WHERE t.is_deleted = 0` ).then(([rows]) => rows as { id: number; target_name: string; plate_number: string }[]), fetchBizTotalMileageMap(), + fetchLatestPgTotalMileageMap(), ]); const targetPlatesMap = new Map>(); @@ -170,7 +200,7 @@ export async function refreshMonitoringCache(): Promise { } const targetNames = Array.from(targetPlatesMap.keys()); - const vehicles = mergeVehicles(mileageRows, infoMap, yesterdayMap, bizTotalMap); + const vehicles = mergeVehicles(mileageRows, infoMap, yesterdayMap, bizTotalMap, latestPgTotalMap); const totalToday = vehicles.reduce((sum, v) => sum + v.dailyKm, 0); const totalAll = vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0); @@ -189,7 +219,7 @@ export async function refreshMonitoringCache(): Promise { } export async function queryDateMileage(dateStr: string): Promise { - const [mileageRows, yesterdayRows, infoMap, bizTotalMap] = await Promise.all([ + const [mileageRows, yesterdayRows, infoMap, bizTotalMap, latestPgTotalMap] = await Promise.all([ mileagePool.execute( 'SELECT plate, vin, daily_km, total_km, source FROM v_vehicle_daily_stats WHERE stat_date = ?', [dateStr] @@ -200,6 +230,7 @@ export async function queryDateMileage(dateStr: string): Promise r as { plate: string; daily_km: string }[]), fetchVehicleInfoMap(), fetchBizTotalMileageMap(), + fetchLatestPgTotalMileageMap(dateStr), ]); const yesterdayMap = new Map(); @@ -209,7 +240,7 @@ export async function queryDateMileage(dateStr: string): Promise existing) yesterdayMap.set(r.plate, km); } - return mergeVehicles(mileageRows, infoMap, yesterdayMap, bizTotalMap); + return mergeVehicles(mileageRows, infoMap, yesterdayMap, bizTotalMap, latestPgTotalMap); } export function buildDateFilters(vehicles: CachedVehicle[]): MonitoringFilters {