perf: reuse mileage target snapshots
This commit is contained in:
@@ -173,7 +173,7 @@ SUM(fee) / NULLIF(SUM(kwh), 0)
|
||||
- 已完成电能数据截至时间和实际趋势月展示。
|
||||
- 电能日期下钻自动核对日汇总与订单全量合计的日期、车辆范围、电量和费用,并显式展示通过或差异状态。
|
||||
- 已完成氢能结构化故障状态与重试体验。
|
||||
- 已完成里程今日仪表快照复用:普通查询命中分钟级快照,手动刷新生成一致的分页快照。
|
||||
- 已完成里程今日仪表快照复用:监控查询和考核车辆当前日明细命中同一分钟级快照,手动刷新生成一致的分页快照;历史日仍使用独立日期缓存。
|
||||
- 已在各 BI 模块头部提供当前数据源的真实请求状态、失败率和最后成功/失败时间。
|
||||
- 待恢复氢能数据库后进行总览、站点、客户三层对账。
|
||||
- 待 ETC 同步后进行通行记录、账单和收款三层对账。
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { targetVehicleMileageFromMonitoringCache } from './target-vehicle-cache.js';
|
||||
import type { CachedVehicle, MonitoringCache } from './types.js';
|
||||
|
||||
function vehicle(plate: string, overrides: Partial<CachedVehicle> = {}): CachedVehicle {
|
||||
return {
|
||||
plate,
|
||||
vin: '',
|
||||
dailyKm: 12.3,
|
||||
totalKm: 456.7,
|
||||
source: 'ONEOS_API',
|
||||
sourceProtocol: 'GB32960',
|
||||
sourceCategory: 'INSTRUMENT',
|
||||
dataTime: '2026-08-07T10:00:00+08:00',
|
||||
calculatedAt: null,
|
||||
updatedAt: null,
|
||||
isOnline: true,
|
||||
isDataSynced: true,
|
||||
customer: null,
|
||||
department: null,
|
||||
manager: null,
|
||||
managerId: null,
|
||||
rentStatus: null,
|
||||
entity: null,
|
||||
project: null,
|
||||
region: null,
|
||||
brand: null,
|
||||
targetNames: [],
|
||||
yesterdayKm: 0,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function cache(vehicles: CachedVehicle[]): MonitoringCache {
|
||||
return {
|
||||
vehicles,
|
||||
stats: { totalToday: 0, totalAll: 0, vehicleCount: vehicles.length },
|
||||
filters: {
|
||||
departments: [], customers: [], plates: [], projects: [], entities: [],
|
||||
rentStatuses: [], platePrefixes: [], targetNames: [], regions: [], brands: [],
|
||||
},
|
||||
targetPlatesMap: new Map(),
|
||||
updatedAt: '2026-08-07T10:00:00+08:00',
|
||||
};
|
||||
}
|
||||
|
||||
test('reuses only the current-day monitoring snapshot and filters target plates', () => {
|
||||
const monitoringCache = cache([
|
||||
vehicle('粤A1'),
|
||||
vehicle('粤A2', { dailyKm: -1, totalKm: null, isOnline: false }),
|
||||
]);
|
||||
|
||||
const result = targetVehicleMileageFromMonitoringCache(
|
||||
monitoringCache,
|
||||
'2026-08-07',
|
||||
'2026-08-07',
|
||||
['粤A2'],
|
||||
);
|
||||
assert.deepEqual(Array.from(result || []), [['粤A2', {
|
||||
dailyKm: 0,
|
||||
totalKm: null,
|
||||
isOnline: false,
|
||||
}]]);
|
||||
assert.equal(
|
||||
targetVehicleMileageFromMonitoringCache(monitoringCache, '2026-08-06', '2026-08-07', ['粤A2']),
|
||||
null,
|
||||
);
|
||||
assert.equal(targetVehicleMileageFromMonitoringCache(null, '2026-08-07', '2026-08-07', ['粤A2']), null);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { MonitoringCache } from './types.js';
|
||||
|
||||
export interface TargetVehicleMileageSnapshot {
|
||||
dailyKm: number;
|
||||
totalKm: number | null;
|
||||
isOnline: boolean;
|
||||
}
|
||||
|
||||
export function targetVehicleMileageFromMonitoringCache(
|
||||
cache: MonitoringCache | null,
|
||||
requestedDate: string,
|
||||
today: string,
|
||||
plates: string[],
|
||||
): Map<string, TargetVehicleMileageSnapshot> | null {
|
||||
if (!cache || !requestedDate || requestedDate !== today) return null;
|
||||
|
||||
const selected = new Set(plates);
|
||||
return new Map(
|
||||
cache.vehicles
|
||||
.filter(vehicle => selected.has(vehicle.plate))
|
||||
.map(vehicle => [vehicle.plate, {
|
||||
dailyKm: Math.max(0, Number(vehicle.dailyKm) || 0),
|
||||
totalKm: vehicle.totalKm == null ? null : Math.max(0, Number(vehicle.totalKm) || 0),
|
||||
isOnline: vehicle.isOnline,
|
||||
}]),
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import { getCache } from './cache.js';
|
||||
import { fetchOneOsDailyMileage } from './oneos-api.js';
|
||||
import { fetchVehicleInfoByPlates } from './vehicle-info.js';
|
||||
import { filterByPermission, maskCustomerNames } from '../../auth/permissions.js';
|
||||
import { shanghaiDate } from './monitoring-cache-policy.js';
|
||||
import { targetVehicleMileageFromMonitoringCache } from './target-vehicle-cache.js';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
@@ -252,11 +254,20 @@ app.get('/:id/vehicles', async (c) => {
|
||||
) as [any[], unknown];
|
||||
|
||||
const plates: string[] = rows.map((r: any) => r.plate_number);
|
||||
const infoMap = await fetchVehicleInfoByPlates(plates);
|
||||
const cachedDateMileageMap = targetVehicleMileageFromMonitoringCache(
|
||||
getCache(),
|
||||
date,
|
||||
shanghaiDate(),
|
||||
plates,
|
||||
);
|
||||
const [infoMap, mileageRows] = await Promise.all([
|
||||
fetchVehicleInfoByPlates(plates),
|
||||
date && !cachedDateMileageMap ? fetchOneOsDailyMileage(date, plates) : Promise.resolve([]),
|
||||
]);
|
||||
|
||||
const dateMileageMap = new Map<string, { dailyKm: number; totalKm: number | null; isOnline: boolean }>();
|
||||
if (date && plates.length > 0) {
|
||||
const mileageRows = await fetchOneOsDailyMileage(date, plates);
|
||||
const dateMileageMap = cachedDateMileageMap
|
||||
|| new Map<string, { dailyKm: number; totalKm: number | null; isOnline: boolean }>();
|
||||
if (!cachedDateMileageMap && date && plates.length > 0) {
|
||||
for (const m of mileageRows) {
|
||||
const existing = dateMileageMap.get(m.plateNumber);
|
||||
const dailyKm = m.status === 'NORMAL' ? (Number(m.dailyMileageKm) || 0) : 0;
|
||||
|
||||
Reference in New Issue
Block a user