feat(mysql): 接入只读电池数据源
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@ import { z } from 'zod'
|
||||
|
||||
export const env = createEnv({
|
||||
server: {
|
||||
DATABASE_URL: z.url({ protocol: /^postgres(ql)?$/ }),
|
||||
DATABASE_URL: z.url({ protocol: /^mysql$/ }),
|
||||
LOG_DB: z.stringbool().default(false),
|
||||
LOG_FORMAT: z.enum(['pretty', 'json']).optional(),
|
||||
LOG_LEVEL: z.enum(['trace', 'debug', 'info', 'warning', 'error', 'fatal']).default('info'),
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import mysql, { type Pool, type RowDataPacket } from 'mysql2/promise'
|
||||
|
||||
import { type BatteryInfo, type BatteryInfoSourceRow, toBatteryInfo } from '@/domain/battery'
|
||||
import { env } from '@/env'
|
||||
|
||||
const historyLimit = 500
|
||||
|
||||
type BatteryInfoMysqlRow = RowDataPacket & BatteryInfoSourceRow
|
||||
|
||||
let pool: Pool | undefined
|
||||
|
||||
function getBatteryPool() {
|
||||
pool ??= mysql.createPool({
|
||||
uri: env.DATABASE_URL,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 5,
|
||||
namedPlaceholders: true,
|
||||
})
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
export async function closeBatteryPool() {
|
||||
if (!pool) return
|
||||
|
||||
await pool.end()
|
||||
pool = undefined
|
||||
}
|
||||
|
||||
const sourceColumns = `
|
||||
id,
|
||||
user_id AS userId,
|
||||
mac,
|
||||
dev_model AS devModel,
|
||||
dev_name AS devName,
|
||||
is_low_power AS isLowPower,
|
||||
power_status AS powerStatus,
|
||||
power,
|
||||
create_time AS createTime,
|
||||
remark
|
||||
`
|
||||
|
||||
export async function getBatteryHistory(mac: string): Promise<BatteryInfo[]> {
|
||||
const [rows] = await getBatteryPool().query<BatteryInfoMysqlRow[]>(
|
||||
`
|
||||
SELECT ${sourceColumns}
|
||||
FROM ls_battery_info
|
||||
WHERE mac = :mac
|
||||
ORDER BY create_time DESC, id DESC
|
||||
LIMIT :limit
|
||||
`,
|
||||
{ mac, limit: historyLimit },
|
||||
)
|
||||
|
||||
return rows.map(toBatteryInfo)
|
||||
}
|
||||
|
||||
export async function getLatestBatteryPerDevice(): Promise<BatteryInfo[]> {
|
||||
const [rows] = await getBatteryPool().query<BatteryInfoMysqlRow[]>(`
|
||||
SELECT ${sourceColumns}
|
||||
FROM ls_battery_info AS current_record
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM ls_battery_info AS newer_record
|
||||
WHERE newer_record.mac = current_record.mac
|
||||
AND (
|
||||
newer_record.create_time > current_record.create_time
|
||||
OR (newer_record.create_time = current_record.create_time AND newer_record.id > current_record.id)
|
||||
)
|
||||
)
|
||||
ORDER BY current_record.create_time DESC, current_record.id DESC
|
||||
`)
|
||||
|
||||
return rows.map(toBatteryInfo)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db } from '@/server/db'
|
||||
import { closeBatteryPool } from '@/server/battery/mysql'
|
||||
import { getLogger } from '@/server/logger'
|
||||
|
||||
export default () => {
|
||||
@@ -17,8 +17,8 @@ export default () => {
|
||||
|
||||
await Bun.sleep(500)
|
||||
try {
|
||||
await db.$client.end()
|
||||
logger.info('DB pool closed, exiting')
|
||||
await closeBatteryPool()
|
||||
logger.info('Battery MySQL pool closed, exiting')
|
||||
} catch (error) {
|
||||
logger.error('DB pool close failed during shutdown', { error })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user