perf: reuse mileage target snapshots
This commit is contained in:
@@ -173,7 +173,7 @@ SUM(fee) / NULLIF(SUM(kwh), 0)
|
|||||||
- 已完成电能数据截至时间和实际趋势月展示。
|
- 已完成电能数据截至时间和实际趋势月展示。
|
||||||
- 电能日期下钻自动核对日汇总与订单全量合计的日期、车辆范围、电量和费用,并显式展示通过或差异状态。
|
- 电能日期下钻自动核对日汇总与订单全量合计的日期、车辆范围、电量和费用,并显式展示通过或差异状态。
|
||||||
- 已完成氢能结构化故障状态与重试体验。
|
- 已完成氢能结构化故障状态与重试体验。
|
||||||
- 已完成里程今日仪表快照复用:普通查询命中分钟级快照,手动刷新生成一致的分页快照。
|
- 已完成里程今日仪表快照复用:监控查询和考核车辆当前日明细命中同一分钟级快照,手动刷新生成一致的分页快照;历史日仍使用独立日期缓存。
|
||||||
- 已在各 BI 模块头部提供当前数据源的真实请求状态、失败率和最后成功/失败时间。
|
- 已在各 BI 模块头部提供当前数据源的真实请求状态、失败率和最后成功/失败时间。
|
||||||
- 待恢复氢能数据库后进行总览、站点、客户三层对账。
|
- 待恢复氢能数据库后进行总览、站点、客户三层对账。
|
||||||
- 待 ETC 同步后进行通行记录、账单和收款三层对账。
|
- 待 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 { fetchOneOsDailyMileage } from './oneos-api.js';
|
||||||
import { fetchVehicleInfoByPlates } from './vehicle-info.js';
|
import { fetchVehicleInfoByPlates } from './vehicle-info.js';
|
||||||
import { filterByPermission, maskCustomerNames } from '../../auth/permissions.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();
|
const app = new Hono();
|
||||||
|
|
||||||
@@ -252,11 +254,20 @@ app.get('/:id/vehicles', async (c) => {
|
|||||||
) as [any[], unknown];
|
) as [any[], unknown];
|
||||||
|
|
||||||
const plates: string[] = rows.map((r: any) => r.plate_number);
|
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 }>();
|
const dateMileageMap = cachedDateMileageMap
|
||||||
if (date && plates.length > 0) {
|
|| new Map<string, { dailyKm: number; totalKm: number | null; isOnline: boolean }>();
|
||||||
const mileageRows = await fetchOneOsDailyMileage(date, plates);
|
if (!cachedDateMileageMap && date && plates.length > 0) {
|
||||||
for (const m of mileageRows) {
|
for (const m of mileageRows) {
|
||||||
const existing = dateMileageMap.get(m.plateNumber);
|
const existing = dateMileageMap.get(m.plateNumber);
|
||||||
const dailyKm = m.status === 'NORMAL' ? (Number(m.dailyMileageKm) || 0) : 0;
|
const dailyKm = m.status === 'NORMAL' ? (Number(m.dailyMileageKm) || 0) : 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user