const dayjs = require('dayjs'); const Sequelize = require('sequelize'); const db = require('../dbProxy'); const config = require('./config'); const utils = require('./utils'); /** * 设备管理器(简化版) * 合并了 Monitor 和 Strategy 的核心功能 */ class DeviceManager { constructor() { // 设备状态 { sn_code: { isOnline, lastHeartbeat, lastSearch, lastApply, lastChat, dailyCounts } } this.devices = new Map(); // 系统统计 this.stats = { totalDevices: 0, onlineDevices: 0, totalTasks: 0, completedTasks: 0, failedTasks: 0, startTime: new Date() }; } /** * 初始化 */ async init() { console.log('[设备管理器] 初始化中...'); await this.loadStats(); console.log('[设备管理器] 初始化完成'); } /** * 加载统计数据 */ async loadStats() { try { const devices = await db.getModel('pla_account').findAll(); this.stats.totalDevices = devices.length; const completedCount = await db.getModel('task_status').count({ where: { status: 'completed' } }); const failedCount = await db.getModel('task_status').count({ where: { status: 'failed' } }); this.stats.completedTasks = completedCount; this.stats.failedTasks = failedCount; this.stats.totalTasks = completedCount + failedCount; } catch (error) { console.error('[设备管理器] 加载统计失败:', error); } } /** * 记录心跳 */ async recordHeartbeat(sn_code, heartbeatData = {}) { const now = Date.now(); if (!this.devices.has(sn_code)) { this.devices.set(sn_code, { isOnline: true, lastHeartbeat: now, dailyCounts: { date: utils.getTodayString(), searchCount: 0, applyCount: 0, chatCount: 0 } }); } const device = this.devices.get(sn_code); device.isOnline = true; device.lastHeartbeat = now; } /** * 检查设备是否在线 */ isDeviceOnline(sn_code) { const device = this.devices.get(sn_code); if (!device) return false; const elapsed = Date.now() - device.lastHeartbeat; if (elapsed > config.monitoring.heartbeatTimeout) { device.isOnline = false; return false; } return device.isOnline; } /** * 检查是否可以执行操作 */ canExecuteOperation(sn_code, operationType) { // 检查工作时间 if (!config.isWorkingHours()) { return { allowed: false, reason: '不在工作时间内' }; } // 检查频率限制 const device = this.devices.get(sn_code); if (device) { const lastTime = device[`last${operationType.charAt(0).toUpperCase() + operationType.slice(1)}`] || 0; const interval = config.getRateLimit(operationType); if (Date.now() - lastTime < interval) { return { allowed: false, reason: '操作过于频繁' }; } } // 检查日限制 if (device && device.dailyCounts) { const today = utils.getTodayString(); if (device.dailyCounts.date !== today) { device.dailyCounts = { date: today, searchCount: 0, applyCount: 0, chatCount: 0 }; } const countKey = `${operationType}Count`; const current = device.dailyCounts[countKey] || 0; const max = config.getDailyLimit(operationType); if (current >= max) { return { allowed: false, reason: `今日${operationType}操作已达上限` }; } } return { allowed: true }; } /** * 记录操作 */ recordOperation(sn_code, operationType) { const device = this.devices.get(sn_code) || {}; device[`last${operationType.charAt(0).toUpperCase() + operationType.slice(1)}`] = Date.now(); if (device.dailyCounts) { const countKey = `${operationType}Count`; device.dailyCounts[countKey] = (device.dailyCounts[countKey] || 0) + 1; } this.devices.set(sn_code, device); } /** * 记录任务开始 */ recordTaskStart(sn_code, task) { // 简化实现,只记录日志 console.log(`[设备管理器] 设备 ${sn_code} 开始执行任务: ${task.taskName}`); } /** * 记录任务完成 */ recordTaskComplete(sn_code, task, success, duration) { if (success) { this.stats.completedTasks++; } else { this.stats.failedTasks++; } this.stats.totalTasks++; console.log(`[设备管理器] 设备 ${sn_code} 任务${success ? '成功' : '失败'}: ${task.taskName} (${duration}ms)`); } /** * 获取系统统计 */ getSystemStats() { const onlineCount = Array.from(this.devices.values()).filter(d => d.isOnline).length; return { ...this.stats, onlineDevices: onlineCount, uptime: utils.formatDuration(Date.now() - this.stats.startTime.getTime()) }; } /** * 获取所有设备状态 */ getAllDevicesStatus() { const result = {}; for (const [sn_code, device] of this.devices.entries()) { result[sn_code] = { isOnline: device.isOnline, lastHeartbeat: device.lastHeartbeat, dailyCounts: device.dailyCounts || {} }; } return result; } /** * 检查心跳状态(异步更新数据库) */ async checkHeartbeatStatus() { try { const now = Date.now(); const device_status = db.getModel('device_status'); const offlineDevices = []; for (const [sn_code, device] of this.devices.entries()) { if (now - device.lastHeartbeat > config.monitoring.heartbeatTimeout) { // 如果之前是在线状态,现在检测到离线,需要更新数据库 if (device.isOnline) { device.isOnline = false; offlineDevices.push(sn_code); } } } // 批量更新数据库中的离线设备状态 if (offlineDevices.length > 0) { await device_status.update( { isOnline: false }, { where: { sn_code: { [Sequelize.Op.in]: offlineDevices }, isOnline: true // 只更新当前在线的设备,避免重复更新 } } ); console.log(`[设备管理器] 检测到 ${offlineDevices.length} 个设备心跳超时,已同步到数据库: ${offlineDevices.join(', ')}`); } // 同时检查数据库中的设备状态(处理内存中没有但数据库中有心跳超时的情况) const heartbeatTimeout = config.monitoring.heartbeatTimeout; const heartbeatThreshold = new Date(now - heartbeatTimeout); const timeoutDevices = await device_status.findAll({ where: { isOnline: true, lastHeartbeatTime: { [Sequelize.Op.lt]: heartbeatThreshold } }, attributes: ['sn_code', 'lastHeartbeatTime'] }); if (timeoutDevices.length > 0) { const timeoutSnCodes = timeoutDevices.map(dev => dev.sn_code); await device_status.update( { isOnline: false }, { where: { sn_code: { [Sequelize.Op.in]: timeoutSnCodes } } } ); console.log(`[设备管理器] 从数据库检测到 ${timeoutSnCodes.length} 个心跳超时设备,已更新为离线: ${timeoutSnCodes.join(', ')}`); } } catch (error) { console.error('[设备管理器] 检查心跳状态失败:', error); } } /** * 重置所有日计数器 */ resetAllDailyCounters() { const today = utils.getTodayString(); for (const device of this.devices.values()) { if (device.dailyCounts && device.dailyCounts.date !== today) { device.dailyCounts = { date: today, searchCount: 0, applyCount: 0, chatCount: 0 }; } } } /** * 清理离线设备 */ cleanupOfflineDevices(threshold = 3600000) { const now = Date.now(); for (const [sn_code, device] of this.devices.entries()) { if (now - device.lastHeartbeat > threshold) { this.devices.delete(sn_code); } } } } // 导出单例 const deviceManager = new DeviceManager(); module.exports = deviceManager;