Files
ln-bi/src/server/routes/mileage/cache.ts
T
2026-11-03 09:29:54 +08:00

141 lines
4.5 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import pool from '../../db.js';
import { fetchVehicleInfoMap } from './vehicle-info.js';
import { fetchOneOsDailyMileage } from './oneos-api.js';
import { queryMonitoringRange } from './query-cache.js';
import { DEFAULT_ONEOS_PROTOCOL_PRIORITY, type OneOsProtocol } from './source-policy.js';
import { buildSummaryMileage } from './summary-model.js';
import {
buildMonitoringFilters,
buildPlateTargetNamesMap,
buildTargetPlatesMap,
dailyMileageMap,
mergeMonitoringVehicles,
previousMileageDate,
toMileageRows,
type TargetRow,
} from './cache-model.js';
import type { CachedVehicle, MonitoringCache, MonitoringFilters } from './types.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const regionMap: Record<string, string> = JSON.parse(
readFileSync(join(__dirname, 'region-map.json'), 'utf8')
);
let monitoringCache: MonitoringCache | null = null;
export function getCache(): MonitoringCache | null {
return monitoringCache;
}
function shanghaiDate(): string {
return new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Shanghai',
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).format(new Date());
}
async function fetchTargetRows(): Promise<TargetRow[]> {
return pool.execute(
`SELECT t.id, t.target_name, v.plate_number
FROM lingniu_prod.tab_mileage_assessment_target t
JOIN lingniu_prod.tab_mileage_assessment_vehicle v ON v.target_id = t.id AND v.is_deleted = 0
WHERE t.is_deleted = 0`
).then(([rows]) => rows as TargetRow[]);
}
export async function refreshMonitoringCache(): Promise<void> {
try {
console.log('[mileage] refreshing monitoring cache...');
const start = Date.now();
const date = shanghaiDate();
const [apiRows, yesterdayRows, infoMap, targetRows] = await Promise.all([
fetchOneOsDailyMileage(date),
fetchOneOsDailyMileage(previousMileageDate(date)),
fetchVehicleInfoMap(),
fetchTargetRows(),
]);
const mileageRows = toMileageRows(apiRows);
const yesterdayMap = dailyMileageMap(yesterdayRows);
const targetPlatesMap = buildTargetPlatesMap(targetRows);
const targetNamesByPlate = buildPlateTargetNamesMap(targetRows);
const targetNames = Array.from(targetPlatesMap.keys());
const vehicles = mergeMonitoringVehicles(
mileageRows,
infoMap,
yesterdayMap,
targetNamesByPlate,
regionMap,
);
const totalToday = Math.round(vehicles.reduce((sum, v) => sum + v.dailyKm, 0));
const totalAll = vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0);
monitoringCache = {
vehicles,
stats: { totalToday, totalAll, vehicleCount: vehicles.length },
filters: buildMonitoringFilters(vehicles, targetNames),
targetPlatesMap,
updatedAt: new Date().toISOString(),
};
console.log(`[mileage] cache refreshed: ${vehicles.length} vehicles in ${Date.now() - start}ms`);
} catch (e: unknown) {
console.error('[mileage] cache refresh error:', e);
}
}
export async function queryDateMileage(
dateStr: string,
protocolPriority?: OneOsProtocol[],
): Promise<CachedVehicle[]> {
const [apiRows, yesterdayRows, infoMap, targetRows] = await Promise.all([
fetchOneOsDailyMileage(dateStr, undefined, protocolPriority),
fetchOneOsDailyMileage(previousMileageDate(dateStr), undefined, protocolPriority),
fetchVehicleInfoMap(),
fetchTargetRows(),
]);
const mileageRows = toMileageRows(apiRows);
const yesterdayMap = dailyMileageMap(yesterdayRows);
return mergeMonitoringVehicles(
mileageRows,
infoMap,
yesterdayMap,
buildPlateTargetNamesMap(targetRows),
regionMap,
);
}
export async function queryRangeMileage(
startDate: string,
endDate: string,
protocolPriority: OneOsProtocol[] = DEFAULT_ONEOS_PROTOCOL_PRIORITY,
refresh = false,
) {
const [cached, infoMap, targetRows] = await Promise.all([
queryMonitoringRange(startDate, endDate, protocolPriority, refresh),
fetchVehicleInfoMap(),
fetchTargetRows(),
]);
const { summary, yesterdayRows } = cached.result;
return {
vehicles: buildSummaryMileage(summary, yesterdayRows, infoMap, targetRows, regionMap, cached.result.priorOdometers),
summary,
start: startDate, end: endDate,
queriedAt: cached.queriedAt,
cacheStatus: cached.cacheStatus,
};
}
export function buildDateFilters(vehicles: CachedVehicle[]): MonitoringFilters {
return buildMonitoringFilters(vehicles, monitoringCache?.filters.targetNames || []);
}