function cloneObject(value) { return JSON.parse(JSON.stringify(value || {})); } class StateCache { constructor(options = {}) { this.includeDeviceIdField = options.includeDeviceIdField !== false; this.deviceIdField = options.deviceIdField || 'n'; this.entries = new Map(); } update(device, metric = {}, meta = {}) { if (!device || !device.deviceId) { throw new Error('设备缺少 deviceId'); } const entry = this.getOrCreateEntry(device); entry.device = device; entry.lastUpdateAt = Date.now(); if (meta.messageType === 'realtime') { entry.lastRealtimeAt = entry.lastUpdateAt; } if (meta.messageType === 'blood-pressure') { entry.lastBloodPressureAt = entry.lastUpdateAt; } Object.assign(entry.payload, metric); if (this.includeDeviceIdField) { entry.payload[this.deviceIdField] = device.deviceId; } return this.getPayload(device.deviceId); } getOrCreateEntry(device) { if (!this.entries.has(device.deviceId)) { const payload = {}; if (this.includeDeviceIdField) { payload[this.deviceIdField] = device.deviceId; } this.entries.set(device.deviceId, { device, payload, firstSeenAt: Date.now(), lastUpdateAt: 0, lastRealtimeAt: 0, lastBloodPressureAt: 0, }); } return this.entries.get(device.deviceId); } getPayload(deviceId) { const entry = this.entries.get(deviceId); if (!entry) { return null; } return cloneObject(entry.payload); } getSnapshot(deviceId) { if (deviceId) { const entry = this.entries.get(deviceId); if (!entry) { return null; } return { deviceId, payload: cloneObject(entry.payload), firstSeenAt: entry.firstSeenAt, lastUpdateAt: entry.lastUpdateAt, lastRealtimeAt: entry.lastRealtimeAt, lastBloodPressureAt: entry.lastBloodPressureAt, }; } return Array.from(this.entries.keys()).map((key) => this.getSnapshot(key)); } getSnapshotMap() { const map = new Map(); for (const item of this.getSnapshot()) { map.set(item.deviceId, item); } return map; } } module.exports = { StateCache, };