提交小程序合入代码
This commit is contained in:
175
banban-mini/src/services/system-banner.ts
Normal file
175
banban-mini/src/services/system-banner.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import { Binding, getBindings } from './binding'
|
||||
import { DeviceAlarmItem, getDeviceAlarms } from './device'
|
||||
|
||||
const LAST_SEEN_ALARM_MAP_KEY = 'systemBannerLastSeenAlarmMap'
|
||||
const DEVICE_ALARM_FETCH_LIMIT = 20
|
||||
const LAST_POLL_TIME_KEY = 'systemBannerLastPollTime'
|
||||
|
||||
type LastSeenAlarmMap = Record<string, number>
|
||||
|
||||
export interface SystemBannerNotification {
|
||||
key: string
|
||||
title: string
|
||||
summary: string
|
||||
detail: string
|
||||
timeLabel: string
|
||||
route: string
|
||||
alarmId: number
|
||||
deviceId: string
|
||||
}
|
||||
|
||||
function readLastSeenAlarmMap(): LastSeenAlarmMap {
|
||||
const storedValue = Taro.getStorageSync(LAST_SEEN_ALARM_MAP_KEY)
|
||||
if (!storedValue || typeof storedValue !== 'object') return {}
|
||||
return storedValue as LastSeenAlarmMap
|
||||
}
|
||||
|
||||
function writeLastSeenAlarmMap(map: LastSeenAlarmMap) {
|
||||
Taro.setStorageSync(LAST_SEEN_ALARM_MAP_KEY, map)
|
||||
}
|
||||
|
||||
function readLastPollTime(): number {
|
||||
const value = Number(Taro.getStorageSync(LAST_POLL_TIME_KEY) || 0)
|
||||
return Number.isFinite(value) ? value : 0
|
||||
}
|
||||
|
||||
function writeLastPollTime(value: number) {
|
||||
Taro.setStorageSync(LAST_POLL_TIME_KEY, value)
|
||||
}
|
||||
|
||||
function setDeviceAlarmSeen(deviceId: string, alarmId: number, map: LastSeenAlarmMap) {
|
||||
map[deviceId] = alarmId
|
||||
}
|
||||
|
||||
function hasDeviceAlarmBaseline(deviceId: string, map: LastSeenAlarmMap): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(map, deviceId)
|
||||
}
|
||||
|
||||
function getAlarmChildLabel(alarm: DeviceAlarmItem, fallbackChildName?: string | null): string {
|
||||
const childName = String(alarm.child_name || '').trim()
|
||||
if (childName) return childName
|
||||
|
||||
const fallbackName = String(fallbackChildName || '').trim()
|
||||
if (fallbackName) return fallbackName
|
||||
|
||||
if (alarm.child_id) return `儿童 ${alarm.child_id}`
|
||||
return '当前孩子'
|
||||
}
|
||||
|
||||
function getAlarmTypeLabel(sourceMsgId?: string | null): string {
|
||||
const normalizedValue = String(sourceMsgId || '').trim()
|
||||
if (normalizedValue === '010') return '紧急告警'
|
||||
if (!normalizedValue) return '设备提醒'
|
||||
return normalizedValue
|
||||
}
|
||||
|
||||
function formatBannerTime(value?: string | null): string {
|
||||
if (!value) return ''
|
||||
const date = new Date(value)
|
||||
if (Number.isNaN(date.getTime())) return ''
|
||||
const hour = `${date.getHours()}`.padStart(2, '0')
|
||||
const minute = `${date.getMinutes()}`.padStart(2, '0')
|
||||
return `${hour}:${minute}`
|
||||
}
|
||||
|
||||
function buildDeviceAlarmNotification(
|
||||
alarm: DeviceAlarmItem,
|
||||
fallbackChildName?: string | null
|
||||
): SystemBannerNotification {
|
||||
const childLabel = getAlarmChildLabel(alarm, fallbackChildName)
|
||||
const alarmTypeLabel = getAlarmTypeLabel(alarm.source_msg_id)
|
||||
|
||||
return {
|
||||
key: `device-alarm-${alarm.device_id}-${alarm.alarm_id}`,
|
||||
title: '系统消息',
|
||||
summary: `${childLabel} 的伴伴触发了${alarmTypeLabel}`,
|
||||
detail: `设备 ${alarm.device_id}`,
|
||||
timeLabel: formatBannerTime(alarm.created_at),
|
||||
route: '/pages/device/index',
|
||||
alarmId: alarm.alarm_id,
|
||||
deviceId: alarm.device_id,
|
||||
}
|
||||
}
|
||||
|
||||
function sortAlarmsByCreatedAt(left: DeviceAlarmItem, right: DeviceAlarmItem): number {
|
||||
const leftTime = new Date(left.created_at).getTime()
|
||||
const rightTime = new Date(right.created_at).getTime()
|
||||
return leftTime - rightTime
|
||||
}
|
||||
|
||||
export async function pollSystemBannerNotifications(): Promise<SystemBannerNotification[]> {
|
||||
const bindingResponse = await getBindings(undefined, 100)
|
||||
const bindings = bindingResponse.items || []
|
||||
|
||||
if (bindings.length === 0) return []
|
||||
|
||||
const lastSeenAlarmMap = readLastSeenAlarmMap()
|
||||
const previousPollTime = readLastPollTime()
|
||||
const currentPollTime = Date.now()
|
||||
let hasMapChanged = false
|
||||
|
||||
const alarmResults = await Promise.allSettled(
|
||||
bindings.map(async (binding) => ({
|
||||
binding,
|
||||
response: await getDeviceAlarms(binding.device_id, DEVICE_ALARM_FETCH_LIMIT),
|
||||
}))
|
||||
)
|
||||
|
||||
const nextNotifications: Array<{ alarm: DeviceAlarmItem; binding: Binding }> = []
|
||||
|
||||
alarmResults.forEach((result) => {
|
||||
if (result.status !== 'fulfilled') return
|
||||
|
||||
const { binding, response } = result.value
|
||||
const deviceId = String(binding.device_id || '').trim()
|
||||
if (!deviceId) return
|
||||
|
||||
const alarms = response.items || []
|
||||
const latestAlarmId = alarms[0]?.alarm_id || 0
|
||||
const hasBaseline = hasDeviceAlarmBaseline(deviceId, lastSeenAlarmMap)
|
||||
const knownAlarmId = Number(lastSeenAlarmMap[deviceId] || 0)
|
||||
|
||||
// Establish a baseline on first poll so old history does not flood the user.
|
||||
if (!hasBaseline) {
|
||||
setDeviceAlarmSeen(deviceId, latestAlarmId, lastSeenAlarmMap)
|
||||
hasMapChanged = true
|
||||
return
|
||||
}
|
||||
|
||||
if (latestAlarmId < knownAlarmId) {
|
||||
setDeviceAlarmSeen(deviceId, latestAlarmId, lastSeenAlarmMap)
|
||||
hasMapChanged = true
|
||||
return
|
||||
}
|
||||
|
||||
if (latestAlarmId === knownAlarmId) {
|
||||
return
|
||||
}
|
||||
|
||||
setDeviceAlarmSeen(deviceId, latestAlarmId, lastSeenAlarmMap)
|
||||
hasMapChanged = true
|
||||
|
||||
alarms
|
||||
.filter((alarm) => {
|
||||
if (alarm.alarm_id <= knownAlarmId) return false
|
||||
if (previousPollTime <= 0) return true
|
||||
|
||||
const createdAt = new Date(alarm.created_at).getTime()
|
||||
if (Number.isNaN(createdAt)) return true
|
||||
return createdAt >= previousPollTime
|
||||
})
|
||||
.forEach((alarm) => {
|
||||
nextNotifications.push({ alarm, binding })
|
||||
})
|
||||
})
|
||||
|
||||
if (hasMapChanged) {
|
||||
writeLastSeenAlarmMap(lastSeenAlarmMap)
|
||||
}
|
||||
writeLastPollTime(currentPollTime)
|
||||
|
||||
return nextNotifications
|
||||
.sort((left, right) => sortAlarmsByCreatedAt(left.alarm, right.alarm))
|
||||
.map(({ alarm, binding }) => buildDeviceAlarmNotification(alarm, binding.child_name))
|
||||
}
|
||||
Reference in New Issue
Block a user