From 82dac759beae24e0df9eed532cfcfea36a8d6af3 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Wed, 1 Apr 2026 23:50:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=8E=AF=E6=AF=94=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E8=B7=9F=E9=9A=8F=E7=AD=9B=E9=80=89=E6=9D=A1=E4=BB=B6=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 每辆车缓存其昨日里程(yesterdayKm),筛选后的环比基于 相同筛选条件下的车辆计算,而非全局对比。 例如筛选"业务一部"后,今日和昨日都只统计一部的车辆。 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/server/routes/mileage.ts | 50 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/server/routes/mileage.ts b/src/server/routes/mileage.ts index 09b3497..9952c1b 100644 --- a/src/server/routes/mileage.ts +++ b/src/server/routes/mileage.ts @@ -39,11 +39,12 @@ interface CachedVehicle { rentStatus: string | null; entity: string | null; project: string | null; + yesterdayKm: number; } interface MonitoringCache { vehicles: CachedVehicle[]; - stats: { totalToday: number; totalAll: number; vehicleCount: number; yesterdayTotal: number }; + stats: { totalToday: number; totalAll: number; vehicleCount: number }; filters: { departments: string[]; customers: string[]; plates: string[]; projects: string[]; entities: string[] }; updatedAt: string; } @@ -72,10 +73,16 @@ async function refreshMonitoringCache() { })(), (async () => { const [rows] = await mileagePool.execute( - `SELECT SUM(daily_km) as total FROM v_vehicle_daily_stats + `SELECT plate, daily_km FROM v_vehicle_daily_stats WHERE stat_date = DATE_SUB((SELECT MAX(stat_date) FROM v_vehicle_daily_stats), INTERVAL 1 DAY)` ) as any; - return Number(rows[0]?.total) || 0; + const map = new Map(); + for (const r of rows) { + const existing = map.get(r.plate) || 0; + const km = Number(r.daily_km) || 0; + if (km > existing) map.set(r.plate, km); + } + return map; })(), pool.execute(VEHICLE_INFO_SQL).then(([rows]) => rows as any[]), ]); @@ -114,6 +121,7 @@ async function refreshMonitoringCache() { rentStatus: info?.rent_status || null, entity: info?.entity || null, project: info?.project || null, + yesterdayKm: yesterdayResult.get(m.plate) || 0, }; }); @@ -137,7 +145,7 @@ async function refreshMonitoringCache() { monitoringCache = { vehicles, - stats: { totalToday, totalAll, vehicleCount: vehicles.length, yesterdayTotal: yesterdayResult }, + stats: { totalToday, totalAll, vehicleCount: vehicles.length }, filters: { departments, customers, plates, projects, entities }, updatedAt: new Date().toISOString(), }; @@ -153,18 +161,20 @@ refreshMonitoringCache(); setInterval(refreshMonitoringCache, 2 * 60 * 1000); // 查询指定日期的里程数据(非缓存) -async function queryDateMileage(dateStr: string): Promise<{ vehicles: CachedVehicle[]; yesterdayTotal: number }> { - const [mileageRows] = await mileagePool.execute( - `SELECT plate, vin, daily_km, total_km, source FROM v_vehicle_daily_stats WHERE stat_date = ?`, - [dateStr] - ) as any; - const [yesterdayRows] = await mileagePool.execute( - `SELECT SUM(daily_km) as total FROM v_vehicle_daily_stats WHERE stat_date = DATE_SUB(?, INTERVAL 1 DAY)`, - [dateStr] - ) as any; - const [infoRows] = await pool.execute(VEHICLE_INFO_SQL) as any; +async function queryDateMileage(dateStr: string): Promise<{ vehicles: CachedVehicle[] }> { + const [mileageRows, yesterdayRows, infoRows] = await Promise.all([ + mileagePool.execute(`SELECT plate, vin, daily_km, total_km, source FROM v_vehicle_daily_stats WHERE stat_date = ?`, [dateStr]).then(([r]) => r as any[]), + mileagePool.execute(`SELECT plate, daily_km FROM v_vehicle_daily_stats WHERE stat_date = DATE_SUB(?, INTERVAL 1 DAY)`, [dateStr]).then(([r]) => r as any[]), + pool.execute(VEHICLE_INFO_SQL).then(([r]) => r as any[]), + ]); const infoMap = new Map(); for (const row of infoRows) infoMap.set(row.plate, row); + const yesterdayMap = new Map(); + 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 mileageMap = new Map(); for (const row of mileageRows) { const existing = mileageMap.get(row.plate); @@ -181,9 +191,10 @@ async function queryDateMileage(dateStr: string): Promise<{ vehicles: CachedVehi customer: info?.customer || null, department: info?.department || null, manager: info?.manager || null, rentStatus: info?.rent_status || null, entity: info?.entity || null, project: info?.project || null, + yesterdayKm: yesterdayMap.get(m.plate) || 0, }; }); - return { vehicles, yesterdayTotal: Number(yesterdayRows[0]?.total) || 0 }; + return { vehicles }; } // GET /monitoring — 从缓存取数据(或指定日期实时查询),支持筛选/排序/分页 @@ -205,7 +216,6 @@ app.get('/monitoring', async (c) => { const date = c.req.query('date') || ''; let allVehicles: CachedVehicle[]; - let yesterdayTotal: number; let filters: MonitoringCache['filters']; if (date) { @@ -213,8 +223,6 @@ app.get('/monitoring', async (c) => { try { const result = await queryDateMileage(date); allVehicles = result.vehicles; - yesterdayTotal = result.yesterdayTotal; - // 从查询结果提取筛选选项 const deptOrder = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十']; filters = { departments: (Array.from(new Set(allVehicles.map(v => v.department).filter(Boolean))) as string[]).sort((a, b) => { @@ -231,10 +239,8 @@ app.get('/monitoring', async (c) => { return c.json(emptyResponse, 500); } } else { - // 默认:从缓存取 if (!monitoringCache) return c.json(emptyResponse); allVehicles = monitoringCache.vehicles; - yesterdayTotal = monitoringCache.stats.yesterdayTotal; filters = monitoringCache.filters; } @@ -259,12 +265,12 @@ app.get('/monitoring', async (c) => { const total = vehicles.length; - // 基于筛选后的数据计算统计 + // 基于筛选后的数据计算统计(yesterdayTotal 也基于筛选后的车辆) const filteredStats = { totalToday: vehicles.reduce((sum, v) => sum + v.dailyKm, 0), totalAll: vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0), vehicleCount: vehicles.length, - yesterdayTotal, + yesterdayTotal: vehicles.reduce((sum, v) => sum + v.yesterdayKm, 0), }; // 排序