feat(api): 支持电池分页和安全预测
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { LRUCache } from 'lru-cache'
|
||||
import { z } from 'zod'
|
||||
import type { BatteryInfo, BatteryPrediction } from '@/domain/battery'
|
||||
import { env } from '@/env'
|
||||
@@ -60,13 +61,12 @@ const predictionResponseSchema = z.object({
|
||||
updated_at: z.string().nullable().optional(),
|
||||
})
|
||||
|
||||
type CacheEntry = {
|
||||
expiresAt: number
|
||||
value: SohPrediction
|
||||
}
|
||||
|
||||
const logger = getLogger(['prediction'])
|
||||
const cache = new Map<string, CacheEntry>()
|
||||
const cache = new LRUCache<string, SohPrediction>({
|
||||
max: 5_000,
|
||||
ttl: env.SOH_PREDICTION_CACHE_TTL_SECONDS * 1000,
|
||||
})
|
||||
const inFlightRequests = new Map<string, Promise<SohPrediction | null>>()
|
||||
|
||||
const round2 = (value: number) => Math.round(value * 100) / 100
|
||||
|
||||
@@ -155,7 +155,22 @@ export async function predictSoh(battery: BatteryInfo, history: BatteryInfo[]):
|
||||
|
||||
const cacheKey = createCacheKey(battery, history)
|
||||
const cached = cache.get(cacheKey)
|
||||
if (cached && cached.expiresAt > Date.now()) return cached.value
|
||||
if (cached) return cached
|
||||
const pendingRequest = inFlightRequests.get(cacheKey)
|
||||
if (pendingRequest) return pendingRequest
|
||||
|
||||
const requestPromise = requestPrediction(cacheKey, battery, request)
|
||||
inFlightRequests.set(cacheKey, requestPromise)
|
||||
|
||||
return requestPromise
|
||||
}
|
||||
|
||||
async function requestPrediction(
|
||||
cacheKey: string,
|
||||
battery: BatteryInfo,
|
||||
request: PredictionRequest,
|
||||
): Promise<SohPrediction | null> {
|
||||
if (!env.SOH_PREDICTION_API_BASE_URL) return null
|
||||
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), env.SOH_PREDICTION_TIMEOUT_MS)
|
||||
@@ -179,10 +194,7 @@ export async function predictSoh(battery: BatteryInfo, history: BatteryInfo[]):
|
||||
|
||||
const json = await response.json()
|
||||
const prediction = normalizePrediction(predictionResponseSchema.parse(json))
|
||||
cache.set(cacheKey, {
|
||||
expiresAt: Date.now() + env.SOH_PREDICTION_CACHE_TTL_SECONDS * 1000,
|
||||
value: prediction,
|
||||
})
|
||||
cache.set(cacheKey, prediction)
|
||||
|
||||
return prediction
|
||||
} catch (error) {
|
||||
@@ -190,9 +202,11 @@ export async function predictSoh(battery: BatteryInfo, history: BatteryInfo[]):
|
||||
return null
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
inFlightRequests.delete(cacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearPredictionCache() {
|
||||
cache.clear()
|
||||
inFlightRequests.clear()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user