import { Hono } from 'hono'; import { fetchOneOsMileageDates } from './oneos-api.js'; import { parseMileageSourcePriority, protocolsForSourcePriority, sourceCategoryFromProtocol, } from './source-policy.js'; const app = new Hono(); function fmt(d: Date): string { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const dd = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${dd}`; } function parseYmd(s: string): Date | null { const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s); if (!m) return null; const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])); d.setHours(0, 0, 0, 0); return Number.isFinite(d.getTime()) ? d : null; } const MAX_DAYS = 366; app.get('/:plate/recent', async (c) => { const plate = c.req.param('plate'); if (!plate) return c.json({ plate: '', days: [] }, 400); const sourcePriority = parseMileageSourcePriority(c.req.query('sourcePriority')); const protocolPriority = protocolsForSourcePriority(sourcePriority); const today = new Date(); today.setHours(0, 0, 0, 0); // 区间参数:优先 start/end;否则回退 days(兼容旧调用) const startQ = c.req.query('start'); const endQ = c.req.query('end'); let start: Date; let end: Date; if (startQ) { const ps = parseYmd(startQ); if (!ps) return c.json({ plate, days: [] }, 400); start = ps; end = endQ ? (parseYmd(endQ) ?? today) : today; } else { const days = Math.min(Math.max(Number(c.req.query('days')) || 15, 1), MAX_DAYS); end = today; start = new Date(today); start.setDate(today.getDate() - (days - 1)); } if (start > end) [start, end] = [end, start]; // 限制区间长度 const span = Math.round((end.getTime() - start.getTime()) / 86400000) + 1; if (span > MAX_DAYS) { start = new Date(end); start.setDate(end.getDate() - (MAX_DAYS - 1)); } try { const dates: string[] = []; const dateCursor = new Date(start); while (dateCursor <= end) { dates.push(fmt(dateCursor)); dateCursor.setDate(dateCursor.getDate() + 1); } const rowsByDate = await fetchOneOsMileageDates(dates, [plate], protocolPriority); // 补全:从 start 到 end 每天一条 const result: { date: string; dailyKm: number; isDataSynced: boolean; sourceProtocol: 'GB32960' | 'MQTT' | 'JT808' | null; sourceCategory: 'INSTRUMENT' | 'GPS' | null; }[] = []; const cursor = new Date(start); while (cursor <= end) { const key = fmt(cursor); const hit = (rowsByDate.get(key) || []).find(row => row.plateNumber === plate); result.push({ date: key, dailyKm: hit?.status === 'NORMAL' ? (hit.dailyMileageKm || 0) : 0, isDataSynced: hit?.status === 'NORMAL', sourceProtocol: hit?.sourceProtocol || null, sourceCategory: sourceCategoryFromProtocol(hit?.sourceProtocol || null), }); cursor.setDate(cursor.getDate() + 1); } return c.json({ plate, start: fmt(start), end: fmt(end), sourcePriority, days: result, }); } catch (e: unknown) { console.error('vehicle recent error:', e); return c.json({ plate, days: [] }, 500); } }); export default app;