141 lines
4.5 KiB
TypeScript
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 || []);
|
|
}
|