Files
ln-bi/scripts/backfill-mileage-report-vehicle-trends.ts
T

79 lines
3.0 KiB
TypeScript

import type { RowDataPacket } from 'mysql2';
import pool from '../src/server/db.js';
import type {
DailyMileageReport,
MileageReportTrendPoint,
} from '../src/shared/mileage/daily-report.js';
import { fetchOneOsMileageDates } from '../src/server/routes/mileage/oneos-api.js';
import { DEFAULT_ONEOS_PROTOCOL_PRIORITY } from '../src/server/routes/mileage/source-policy.js';
import { roundMileage } from '../src/server/routes/mileage/daily-report-model.js';
import { updateDailyReportSnapshotPayload } from '../src/server/routes/mileage/daily-report-store.js';
interface SnapshotRow extends RowDataPacket {
report_date: string;
source: string;
report_payload: DailyMileageReport | string;
}
function reportDates(reportDate: string): string[] {
const end = new Date(`${reportDate}T00:00:00Z`);
return Array.from({ length: 7 }, (_, index) => {
const date = new Date(end);
date.setUTCDate(end.getUTCDate() - (6 - index));
return date.toISOString().slice(0, 10);
});
}
function parseReport(value: DailyMileageReport | string): DailyMileageReport {
return typeof value === 'string' ? JSON.parse(value) as DailyMileageReport : value;
}
async function trendByPlate(report: DailyMileageReport): Promise<Map<string, MileageReportTrendPoint[]>> {
const dates = reportDates(report.reportDate);
const plates = report.groups.flatMap(group => group.vehicles.map(vehicle => vehicle.plate));
const rowsByDate = await fetchOneOsMileageDates(dates, plates, DEFAULT_ONEOS_PROTOCOL_PRIORITY);
const result = new Map<string, MileageReportTrendPoint[]>();
for (const plate of plates) {
result.set(plate, dates.map(date => {
const mileage = (rowsByDate.get(date) || [])
.filter(row => row.status === 'NORMAL' && row.plateNumber === plate)
.reduce((maximum, row) => Math.max(maximum, Number(row.dailyMileageKm) || 0), 0);
return { date, totalMileage: roundMileage(Math.max(0, mileage)) };
}));
}
return result;
}
const apply = process.argv.includes('--apply');
const [rows] = await pool.query<SnapshotRow[]>(`
SELECT DATE_FORMAT(report_date, '%Y-%m-%d') AS report_date, source, report_payload
FROM lingniu_prod.tab_mileage_daily_report
WHERE source <> 'XLSX_IMPORT'
ORDER BY report_date
`);
try {
for (const row of rows) {
const report = parseReport(row.report_payload);
if (report.groups.every(group => group.vehicles.every(vehicle => vehicle.trend?.length === 7))) {
console.log(`${row.report_date}: already complete`);
continue;
}
const trends = await trendByPlate(report);
const updated: DailyMileageReport = {
...report,
groups: report.groups.map(group => ({
...group,
vehicles: group.vehicles.map(vehicle => ({
...vehicle,
trend: trends.get(vehicle.plate) || [],
})),
})),
};
if (apply) await updateDailyReportSnapshotPayload(updated);
console.log(`${row.report_date}: ${apply ? 'updated' : 'would update'} ${trends.size} vehicles`);
}
} finally {
await pool.end();
}