refactor(domain): 标注 SoH 来源语义
This commit is contained in:
+28
-15
@@ -26,6 +26,7 @@ export const fleetUnitSchema = z.object({
|
||||
firmware: z.string(),
|
||||
cycles: z.number().int(),
|
||||
soh: z.number(),
|
||||
sohSource: z.union([z.literal('prediction'), z.literal('unavailable')]),
|
||||
soh30d: z.number(),
|
||||
soh60d: z.number(),
|
||||
soh90d: z.number(),
|
||||
@@ -87,9 +88,16 @@ export const batteriesResponseSchema = z.object({
|
||||
lowPower: z.number().int(),
|
||||
charging: z.number().int(),
|
||||
items: z.array(batteryInfoSchema),
|
||||
nextCursor: z.string().nullable(),
|
||||
})
|
||||
export type BatteriesResponse = z.infer<typeof batteriesResponseSchema>
|
||||
|
||||
export type BatteriesPageSummary = {
|
||||
total?: number
|
||||
lowPower?: number
|
||||
charging?: number
|
||||
}
|
||||
|
||||
export type BatteryPrediction = {
|
||||
mac: string
|
||||
nowSoh: number
|
||||
@@ -194,39 +202,43 @@ export function toBatteryInfo(row: BatteryInfoSourceRow): BatteryInfo {
|
||||
}
|
||||
}
|
||||
|
||||
export function createBatteriesResponse(items: BatteryInfo[], now = new Date()): BatteriesResponse {
|
||||
export function createBatteriesResponse(
|
||||
items: BatteryInfo[],
|
||||
now = new Date(),
|
||||
summary: BatteriesPageSummary = {},
|
||||
nextCursor: string | null = null,
|
||||
): BatteriesResponse {
|
||||
return {
|
||||
updatedAt: now.toISOString(),
|
||||
total: items.length,
|
||||
lowPower: items.filter((item) => item.isLowPower).length,
|
||||
charging: items.filter((item) => item.powerStatus === 1).length,
|
||||
total: summary.total ?? items.length,
|
||||
lowPower: summary.lowPower ?? items.filter((item) => item.isLowPower).length,
|
||||
charging: summary.charging ?? items.filter((item) => item.powerStatus === 1).length,
|
||||
items,
|
||||
nextCursor,
|
||||
}
|
||||
}
|
||||
|
||||
function toFleetUnit(item: BatteryInfo, index: number, prediction?: BatteryPrediction): FleetUnit {
|
||||
const soh = prediction?.nowSoh ?? item.power
|
||||
const status = prediction ? getDeviceStatusByRisk(prediction) : getDeviceStatus(soh)
|
||||
const hasPrediction = Boolean(prediction)
|
||||
const soh = prediction ? round1(clamp(prediction.nowSoh, 0, 100)) : 0
|
||||
const status = prediction ? getDeviceStatusByRisk(prediction) : item.isLowPower || item.power <= 20 ? '关注' : '健康'
|
||||
const riskFactors: string[] = []
|
||||
|
||||
if (item.isLowPower || item.power <= 20) riskFactors.push('低电量')
|
||||
if (item.powerStatus === 1) riskFactors.push('充电中')
|
||||
if (status === '预警') riskFactors.push('衰减加速')
|
||||
if (!hasPrediction) riskFactors.push('SoH预测不可用')
|
||||
if (prediction && status === '预警') riskFactors.push('衰减加速')
|
||||
if (item.remark?.includes('v3.7')) riskFactors.push('旧固件')
|
||||
if (prediction?.riskLevel) riskFactors.push(`AI风险:${prediction.riskLevel}`)
|
||||
|
||||
const thermalPressure = index % 3
|
||||
const soh30d = prediction
|
||||
? round1(clamp(prediction.monthSoh, 0, 100))
|
||||
: round1(clamp(soh - 0.8 - thermalPressure * 0.25, 0, 100))
|
||||
const soh90d = prediction
|
||||
? round1(clamp(prediction.trmonthSoh, 0, 100))
|
||||
: round1(clamp(soh - 2.8 - thermalPressure * 0.45, 0, 100))
|
||||
const soh60d = prediction ? round1((soh30d + soh90d) / 2) : round1(clamp(soh - 1.7 - thermalPressure * 0.35, 0, 100))
|
||||
const soh30d = prediction ? round1(clamp(prediction.monthSoh, 0, 100)) : 0
|
||||
const soh90d = prediction ? round1(clamp(prediction.trmonthSoh, 0, 100)) : 0
|
||||
const soh60d = prediction ? round1((soh30d + soh90d) / 2) : 0
|
||||
const temperature = round1(29.5 + thermalPressure * 2.1 + (item.isLowPower ? 1.4 : 0))
|
||||
const chargeEfficiency = round1(clamp(91 + item.power / 12 - riskFactors.length * 1.8, 80, 98))
|
||||
const riskScore = Math.round(
|
||||
clamp(prediction?.riskScore ?? 12 + (100 - soh) * 1.45 + riskFactors.length * 8 + thermalPressure * 4, 8, 96),
|
||||
clamp(prediction?.riskScore ?? 18 + riskFactors.length * 10 + thermalPressure * 4 + (item.isLowPower ? 18 : 0), 8, 96),
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -235,6 +247,7 @@ function toFleetUnit(item: BatteryInfo, index: number, prediction?: BatteryPredi
|
||||
firmware: prediction?.modelName ?? item.remark ?? 'unknown',
|
||||
cycles: 120 + index * 17 + Math.round((100 - soh) * 2.2),
|
||||
soh,
|
||||
sohSource: prediction ? 'prediction' : 'unavailable',
|
||||
soh30d,
|
||||
soh60d,
|
||||
soh90d,
|
||||
|
||||
Reference in New Issue
Block a user