This commit is contained in:
@@ -65,6 +65,21 @@ interface MileageRow {
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface DailyMileageRow {
|
||||
plate: string;
|
||||
vin: string | null;
|
||||
date: string;
|
||||
daily_km: string | number | null;
|
||||
source: string | null;
|
||||
}
|
||||
|
||||
export interface RangeMileageResult {
|
||||
vehicles: CachedVehicle[];
|
||||
dailyTotals: { date: string; totalKm: number }[];
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
||||
|
||||
interface TargetRow {
|
||||
id: number;
|
||||
target_name: string;
|
||||
@@ -160,31 +175,32 @@ function mergeVehicles(
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(mileageMap.values()).map(m => {
|
||||
const info = infoMap.get(m.plate);
|
||||
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 Array.from(infoMap.values()).map(info => {
|
||||
const m = mileageMap.get(info.plate);
|
||||
const plate = info.plate;
|
||||
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(plate);
|
||||
const bizTotal = bizTotalMap.get(plate);
|
||||
return {
|
||||
plate: m.plate,
|
||||
vin: m.vin,
|
||||
plate,
|
||||
vin: m?.vin || info.vin || '',
|
||||
dailyKm,
|
||||
totalKm: gpsTotal !== null ? gpsTotal : (latestPgTotal ?? bizTotal ?? null),
|
||||
source,
|
||||
isOnline: source !== 'NONE' && dailyKm > 0,
|
||||
isDataSynced: source !== 'NONE',
|
||||
customer: info?.customer || null,
|
||||
department: info?.department || null,
|
||||
manager: info?.manager || null,
|
||||
managerId: info?.manager_id || null,
|
||||
rentStatus: info?.rent_status || null,
|
||||
entity: info?.entity || null,
|
||||
project: info?.project || null,
|
||||
region: regionMap[m.plate] || null,
|
||||
targetNames: targetNamesByPlate.get(m.plate) || [],
|
||||
yesterdayKm: yesterdayMap.get(m.plate) || 0,
|
||||
customer: info.customer || null,
|
||||
department: info.department || null,
|
||||
manager: info.manager || null,
|
||||
managerId: info.manager_id || null,
|
||||
rentStatus: info.rent_status || null,
|
||||
entity: info.entity || null,
|
||||
project: info.project || null,
|
||||
region: regionMap[plate] || null,
|
||||
targetNames: targetNamesByPlate.get(plate) || [],
|
||||
yesterdayKm: yesterdayMap.get(plate) || 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -281,6 +297,110 @@ export async function queryDateMileage(dateStr: string): Promise<CachedVehicle[]
|
||||
);
|
||||
}
|
||||
|
||||
function datesBetween(start: string, end: string): string[] {
|
||||
const result: string[] = [];
|
||||
const [sy, sm, sd] = start.split('-').map(Number);
|
||||
const [ey, em, ed] = end.split('-').map(Number);
|
||||
const cursor = new Date(sy, sm - 1, sd);
|
||||
const last = new Date(ey, em - 1, ed);
|
||||
cursor.setHours(0, 0, 0, 0);
|
||||
last.setHours(0, 0, 0, 0);
|
||||
while (cursor <= last) {
|
||||
result.push(`${cursor.getFullYear()}-${String(cursor.getMonth() + 1).padStart(2, '0')}-${String(cursor.getDate()).padStart(2, '0')}`);
|
||||
cursor.setDate(cursor.getDate() + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function queryRangeMileage(startDate: string, endDate: string): Promise<RangeMileageResult> {
|
||||
const days = datesBetween(startDate, endDate);
|
||||
const [dailyRows, yesterdayRows, infoMap, targetRows, bizTotalMap, latestPgTotalMap] = await Promise.all([
|
||||
mileagePool.execute(
|
||||
`SELECT plate,
|
||||
DATE_FORMAT(stat_date, '%Y-%m-%d') AS date,
|
||||
vin,
|
||||
daily_km,
|
||||
source
|
||||
FROM v_vehicle_daily_stats
|
||||
WHERE stat_date >= ? AND stat_date <= ?
|
||||
ORDER BY stat_date, plate`,
|
||||
[startDate, endDate]
|
||||
).then(([r]) => r as DailyMileageRow[]),
|
||||
mileagePool.execute(
|
||||
'SELECT plate, daily_km FROM v_vehicle_daily_stats WHERE stat_date = DATE_SUB(?, INTERVAL 1 DAY)',
|
||||
[startDate]
|
||||
).then(([r]) => r as { plate: string; daily_km: string }[]),
|
||||
fetchVehicleInfoMap(),
|
||||
fetchTargetRows(),
|
||||
fetchBizTotalMileageMap(),
|
||||
fetchLatestPgTotalMileageMap(endDate),
|
||||
]);
|
||||
|
||||
const perVehicleDaily = new Map<string, Record<string, number>>();
|
||||
const perVehicleSum = new Map<string, { plate: string; vin: string; daily_km: string; total_km: null; source: string }>();
|
||||
const dailyTotals = new Map<string, number>();
|
||||
const bestDailyRows = new Map<string, DailyMileageRow>();
|
||||
|
||||
for (const day of days) dailyTotals.set(day, 0);
|
||||
|
||||
for (const row of dailyRows) {
|
||||
const key = `${row.plate}\u0000${row.date}`;
|
||||
const km = Math.max(0, Number(row.daily_km) || 0);
|
||||
const existing = bestDailyRows.get(key);
|
||||
if (!existing || km > Math.max(0, Number(existing.daily_km) || 0)) {
|
||||
bestDailyRows.set(key, row);
|
||||
}
|
||||
}
|
||||
|
||||
for (const row of bestDailyRows.values()) {
|
||||
const km = Math.max(0, Number(row.daily_km) || 0);
|
||||
const date = row.date;
|
||||
const plate = row.plate;
|
||||
dailyTotals.set(date, (dailyTotals.get(date) || 0) + km);
|
||||
|
||||
const daily = perVehicleDaily.get(plate) || {};
|
||||
daily[date] = km;
|
||||
perVehicleDaily.set(plate, daily);
|
||||
|
||||
const existing = perVehicleSum.get(plate);
|
||||
perVehicleSum.set(plate, {
|
||||
plate,
|
||||
vin: existing?.vin || row.vin || '',
|
||||
daily_km: String((Number(existing?.daily_km) || 0) + km),
|
||||
total_km: null,
|
||||
source: existing?.source !== 'NONE' && existing?.source ? existing.source : (row.source || 'NONE'),
|
||||
});
|
||||
}
|
||||
|
||||
const yesterdayMap = new Map<string, number>();
|
||||
for (const r of yesterdayRows) {
|
||||
const km = Number(r.daily_km) || 0;
|
||||
const existing = yesterdayMap.get(r.plate) || 0;
|
||||
if (km > existing) yesterdayMap.set(r.plate, km);
|
||||
}
|
||||
|
||||
const vehicles = mergeVehicles(
|
||||
Array.from(perVehicleSum.values()),
|
||||
infoMap,
|
||||
yesterdayMap,
|
||||
bizTotalMap,
|
||||
latestPgTotalMap,
|
||||
buildPlateTargetNamesMap(targetRows),
|
||||
).map(vehicle => {
|
||||
const dailyMileage = perVehicleDaily.get(vehicle.plate) || {};
|
||||
const completedDailyMileage: Record<string, number> = {};
|
||||
for (const day of days) completedDailyMileage[day] = dailyMileage[day] || 0;
|
||||
return { ...vehicle, dailyMileage: completedDailyMileage };
|
||||
});
|
||||
|
||||
return {
|
||||
vehicles,
|
||||
dailyTotals: days.map(date => ({ date, totalKm: dailyTotals.get(date) || 0 })),
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDateFilters(vehicles: CachedVehicle[]): MonitoringFilters {
|
||||
return buildFilters(vehicles, monitoringCache?.filters.targetNames || []);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user