79 lines
3.0 KiB
TypeScript
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();
|
|
}
|