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> { 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(); 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(` 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(); }