From c00e04dfb0ce02bcd3e06b67f19018c751de3762 Mon Sep 17 00:00:00 2001 From: imbytecat Date: Tue, 12 May 2026 01:43:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(prediction):=20=E8=B7=B3=E8=BF=87=E7=BC=BA?= =?UTF-8?q?=E5=B0=91=E9=81=A5=E6=B5=8B=E5=AD=97=E6=AE=B5=E7=9A=84=E9=A2=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/prediction/client.test.ts | 18 ++-------- src/server/prediction/client.ts | 54 +++++++--------------------- 2 files changed, 15 insertions(+), 57 deletions(-) diff --git a/src/server/prediction/client.test.ts b/src/server/prediction/client.test.ts index d39c9a9..be1b380 100644 --- a/src/server/prediction/client.test.ts +++ b/src/server/prediction/client.test.ts @@ -71,22 +71,8 @@ describe('prediction client helpers', () => { expect(isPredictionForBattery({ batteryId: battery.id, mac: 'RING-B11' }, battery as BatteryInfo)).toBe(false) }) - test('returns null for history-insufficient prediction requests', () => { + test('returns null when required telemetry fields are unavailable', () => { expect(createPredictionRequest(battery, [battery, battery, battery, battery])).toBeNull() - }) - - test('uses only real battery history fields in prediction requests', () => { - const request = createPredictionRequest(battery, [battery, battery, battery, battery, battery]) - const firstHistory = request?.history[0] - - expect(firstHistory).toEqual({ - id: battery.id, - power: battery.power, - power_status: battery.powerStatus, - is_low_power: MYSQL_BOOLEAN.FALSE, - timestamp: battery.createTime, - }) - expect(Object.hasOwn(firstHistory ?? {}, 'charge_capacity_ah')).toBe(false) - expect(Object.hasOwn(firstHistory ?? {}, 'coulombic_efficiency_pct')).toBe(false) + expect(createPredictionRequest(battery, [battery, battery, battery, battery, battery])).toBeNull() }) }) diff --git a/src/server/prediction/client.ts b/src/server/prediction/client.ts index addd90c..997aceb 100644 --- a/src/server/prediction/client.ts +++ b/src/server/prediction/client.ts @@ -1,6 +1,6 @@ import { LRUCache } from 'lru-cache' import { z } from 'zod' -import { type BatteryInfo, type BatteryPrediction, type PowerStatus, toMysqlBoolean } from '@/domain/battery' +import type { BatteryInfo, BatteryPrediction, PowerStatus } from '@/domain/battery' import { env } from '@/env' import { getLogger } from '@/server/logger' @@ -20,10 +20,14 @@ export const sohPredictionSchema = z.object({ export type SohPrediction = BatteryPrediction & z.infer type PredictionHistoryItem = { - id: number - power: number - power_status: PowerStatus - is_low_power: string + cycle: number + charge_capacity_ah: number + discharge_capacity_ah: number + charge_energy_wh: number + discharge_energy_wh: number + charge_time: string + discharge_time: string + coulombic_efficiency_pct: number timestamp: string } @@ -68,12 +72,6 @@ const negativeCache = new LRUCache({ }) const inFlightRequests = new Map>() -function normalizeMysqlDateTime(value: string) { - if (!value.includes('T')) return value - - return value.slice(0, 19).replace('T', ' ') -} - function createCacheKey(battery: BatteryInfo, history: BatteryInfo[]) { const latestHistory = history.at(-1) const latestId = latestHistory?.id ?? battery.id @@ -82,36 +80,10 @@ function createCacheKey(battery: BatteryInfo, history: BatteryInfo[]) { return `${battery.mac}:${latestId}:${latestTime}` } -function createHistoryItem(item: BatteryInfo): PredictionHistoryItem { - return { - id: item.id, - power: item.power, - power_status: item.powerStatus, - is_low_power: toMysqlBoolean(item.isLowPower), - timestamp: normalizeMysqlDateTime(item.createTime), - } -} - -export function createPredictionRequest(battery: BatteryInfo, history: BatteryInfo[]): PredictionRequest | null { - const sourceHistory = history.length > 0 ? history : [battery] - - if (sourceHistory.length < 5) return null - - return { - battery: { - id: battery.id, - user_id: battery.userId, - mac: battery.mac, - dev_model: battery.devModel, - dev_name: battery.devName, - is_low_power: toMysqlBoolean(battery.isLowPower), - power_status: battery.powerStatus, - power: battery.power, - create_time: normalizeMysqlDateTime(battery.createTime), - remark: battery.remark ?? '', - }, - history: sourceHistory.map(createHistoryItem), - } +export function createPredictionRequest(_battery: BatteryInfo, _history: BatteryInfo[]): PredictionRequest | null { + // The current customer table lacks the real capacity/energy telemetry required by the prediction service. + // Returning null avoids both synthetic features and predictable 422 responses from the service. + return null } export function normalizePrediction(response: z.infer): SohPrediction {