Compare commits

..

2 Commits

Author SHA1 Message Date
imbytecat 11cf298332 fix(ui): 限制分页游标历史长度 2026-05-12 00:48:16 +08:00
imbytecat 25d7f1c315 fix(api): 修正看板和历史统计口径 2026-05-12 00:48:16 +08:00
4 changed files with 29 additions and 9 deletions
+14
View File
@@ -56,6 +56,20 @@ describe('battery domain', () => {
expect(response.items[0]?.createTime).toBe('2026-05-10T23:00:00.000Z')
})
test('keeps explicit window summaries for limited history slices', () => {
const now = new Date('2026-05-11T00:00:00.000Z')
const items = rows.map(toBatteryInfo)
const response = createBatteriesResponse(items, now, {
total: items.length,
lowPower: 1,
charging: 1,
})
expect(response.total).toBe(2)
expect(response.lowPower).toBe(1)
expect(response.charging).toBe(1)
})
test('creates dashboard aggregate shape without using power as fake SOH', () => {
const now = new Date('2026-05-11T00:00:00.000Z')
const snapshot = createDashboardSnapshot(rows.map(toBatteryInfo), now)
+1 -1
View File
@@ -205,7 +205,7 @@ function BatteriesPage() {
search: (prev) => ({
...prev,
cursor: nextCursor,
cursors: [...(prev.cursors || []), prev.cursor || firstPageCursor],
cursors: [...(prev.cursors || []), prev.cursor || firstPageCursor].slice(-100),
}),
})
}
+7 -2
View File
@@ -1,4 +1,4 @@
import { createBatteriesResponse, createDashboardSnapshot } from '@/domain/battery'
import { createBatteriesResponse, createDashboardSnapshot, POWER_STATUS } from '@/domain/battery'
import { os } from '@/server/api/server'
import {
getBatteryHistory,
@@ -63,5 +63,10 @@ export const batteries = os.battery.batteries.handler(async ({ input }) => {
export const history = os.battery.history.handler(async ({ input }) => {
const items = await getBatteryHistory(input.mac)
return createBatteriesResponse(items)
// History returns a limited window, so the counters must describe only this returned slice.
return createBatteriesResponse(items, new Date(), {
total: items.length,
lowPower: items.filter((item) => item.isLowPower).length,
charging: items.filter((item) => item.powerStatus === POWER_STATUS.CHARGING).length,
})
})
+7 -6
View File
@@ -15,8 +15,6 @@ import { env } from '@/env'
const historyLimit = 500
const predictionHistoryLimit = 10
const dashboardLatestLimit = 100
type BatteryInfoMysqlRow = RowDataPacket & BatteryInfoSourceRow
type CountMysqlRow = RowDataPacket & {
total: number
@@ -314,16 +312,19 @@ export async function getLatestBatteryPage(input: LatestBatteryPageInput): Promi
}
}
export async function getLatestBatteryPerDevice(limit = dashboardLatestLimit): Promise<BatteryInfo[]> {
export async function getLatestBatteryPerDevice(limit?: number): Promise<BatteryInfo[]> {
const appliedLimit = typeof limit === 'number' && limit > 0 ? limit : undefined
const limitSql = appliedLimit ? '\n LIMIT :limit' : ''
const queryParams = appliedLimit ? { limit: appliedLimit } : {}
const [rows] = await getBatteryPool().query<BatteryInfoMysqlRow[]>(
`
SELECT ${sourceColumns}
FROM ls_battery_info AS current_record
WHERE ${latestRecordPredicate}
ORDER BY current_record.create_time DESC, current_record.id DESC
LIMIT :limit
ORDER BY current_record.create_time DESC, current_record.id DESC${limitSql}
`,
{ limit: Math.min(Math.max(limit, 1), dashboardLatestLimit) },
queryParams,
)
return rows.map(toBatteryInfo)