/** * 智能项圈预警控制器 * @file smartCollarAlertController.js * @description 处理智能项圈预警相关的请求 */ const { IotXqClient } = require('../models'); const { Op } = require('sequelize'); /** * 获取智能项圈预警统计 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ exports.getCollarAlertStats = async (req, res) => { try { console.log('=== 获取智能项圈预警统计 ==='); // 获取项圈设备总数 const totalDevices = await IotXqClient.count(); console.log('项圈设备总数:', totalDevices); // 获取所有设备数据用于生成预警统计 const allDevices = await IotXqClient.findAll({ order: [['uptime', 'DESC'], ['id', 'DESC']] }); // 统计各类预警数量 let stats = { totalDevices: totalDevices, lowBattery: 0, offline: 0, highTemperature: 0, lowTemperature: 0, abnormalMovement: 0, wearOff: 0, totalAlerts: 0 }; allDevices.forEach(device => { const actualBattery = parseInt(device.battery) || 0; const actualTemperature = parseFloat(device.temperature) || 0; const totalSteps = parseInt(device.steps) || 0; const yesterdaySteps = parseInt(device.y_steps) || 0; const dailySteps = totalSteps - yesterdaySteps; // 离线预警 if (device.state === 0) { stats.offline++; stats.totalAlerts++; } // 低电量预警 if (actualBattery > 0 && actualBattery < 20) { stats.lowBattery++; stats.totalAlerts++; } // 温度预警 if (actualTemperature > 0) { if (actualTemperature < 30) { stats.lowTemperature++; stats.totalAlerts++; } else if (actualTemperature > 40) { stats.highTemperature++; stats.totalAlerts++; } } // 异常运动预警 if (dailySteps === 0 && totalSteps > 0) { stats.abnormalMovement++; stats.totalAlerts++; } // 项圈脱落预警 if (device.bandge_status === 0) { stats.wearOff++; stats.totalAlerts++; } }); console.log('预警统计结果:', stats); res.json({ success: true, data: stats, message: '获取智能项圈预警统计成功' }); } catch (error) { console.error('获取智能项圈预警统计失败:', error); res.status(500).json({ success: false, message: '获取智能项圈预警统计失败', error: error.message }); } }; /** * 获取智能项圈预警列表 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ exports.getCollarAlerts = async (req, res) => { try { const { page = 1, limit = 10, status, search, alertType, alertLevel, startDate, endDate } = req.query; const offset = (page - 1) * limit; console.log('智能项圈预警API请求参数:', { page, limit, status, search, alertType, alertLevel, startDate, endDate }); // 构建查询条件 const whereConditions = {}; // 状态筛选 if (status) { switch (status) { case 'online': whereConditions.state = 1; break; case 'offline': whereConditions.state = 0; break; case 'alarm': whereConditions.state = 2; break; case 'maintenance': whereConditions.state = 3; break; } } // 搜索条件 if (search) { whereConditions[Op.or] = [ { sn: { [Op.like]: `%${search}%` } }, { deviceId: { [Op.like]: `%${search}%` } } ]; } // 时间范围筛选 if (startDate || endDate) { whereConditions.uptime = {}; if (startDate) { whereConditions.uptime[Op.gte] = new Date(startDate); } if (endDate) { whereConditions.uptime[Op.lte] = new Date(endDate); } } // 查询所有符合条件的设备数据 const allDevices = await IotXqClient.findAll({ where: whereConditions, order: [['uptime', 'DESC'], ['id', 'DESC']] }); // 生成预警数据 const allAlerts = []; allDevices.forEach(device => { const deviceId = device.sn || `COLLAR${device.id}`; const alertTime = new Date(device.uptime || Date.now()).toISOString().replace('T', ' ').substring(0, 19); const actualBattery = parseInt(device.battery) || 0; const actualTemperature = parseFloat(device.temperature) || 0; const totalSteps = parseInt(device.steps) || 0; const yesterdaySteps = parseInt(device.y_steps) || 0; const dailySteps = totalSteps - yesterdaySteps; // 添加基础信息到预警的函数 const addBaseInfoToAlert = (alert) => { alert.deviceId = device.id; alert.deviceName = deviceId; alert.collarNumber = deviceId; alert.dailySteps = dailySteps; alert.totalSteps = totalSteps; alert.yesterdaySteps = yesterdaySteps; alert.battery = actualBattery; alert.temperature = actualTemperature; alert.alertTime = alertTime; alert.deviceStatus = device.state === 1 ? '在线' : '离线'; alert.gpsSignal = device.state === 1 ? '强' : '无'; alert.wearStatus = device.bandge_status === 1 ? '已佩戴' : '未佩戴'; alert.longitude = 116.3974 + (device.id % 100) * 0.0001; alert.latitude = 39.9093 + (device.id % 100) * 0.0001; return alert; }; // 离线预警 if (device.state === 0) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_offline`, alertType: 'offline', alertLevel: 'high', description: '设备已离线超过30分钟', movementStatus: '静止' })); } // 低电量预警 if (actualBattery > 0 && actualBattery < 20) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_battery`, alertType: 'battery', alertLevel: actualBattery < 10 ? 'high' : 'medium', description: `设备电量低于20%,当前电量${actualBattery}%`, movementStatus: '正常' })); } // 温度预警 if (actualTemperature > 0) { if (actualTemperature < 30) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_temperature_low`, alertType: 'temperature', alertLevel: actualTemperature < 20 ? 'high' : 'medium', description: `设备温度过低,当前温度${actualTemperature}°C`, movementStatus: '正常' })); } else if (actualTemperature > 40) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_temperature_high`, alertType: 'temperature', alertLevel: actualTemperature > 45 ? 'high' : 'medium', description: `设备温度过高,当前温度${actualTemperature}°C`, movementStatus: '正常' })); } } // 异常运动预警 if (dailySteps === 0 && totalSteps > 0) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_movement_zero`, alertType: 'movement', alertLevel: 'high', description: '检测到步数异常,当日运动量为0步,可能为设备故障或动物异常', movementStatus: '异常' })); } // 项圈脱落预警 if (device.bandge_status === 0) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_wear`, alertType: 'wear', alertLevel: 'high', description: '设备佩戴状态异常,可能已脱落', movementStatus: '正常' })); } }); // 预警类型筛选 let filteredAlerts = allAlerts; if (alertType && alertType.trim() !== '') { filteredAlerts = allAlerts.filter(alert => alert.alertType === alertType); } // 预警级别筛选 if (alertLevel && alertLevel.trim() !== '') { filteredAlerts = filteredAlerts.filter(alert => alert.alertLevel === alertLevel); } // 计算统计数据 const stats = { lowBattery: filteredAlerts.filter(alert => alert.alertType === 'battery').length, offline: filteredAlerts.filter(alert => alert.alertType === 'offline').length, highTemperature: filteredAlerts.filter(alert => alert.alertType === 'temperature').length, abnormalMovement: filteredAlerts.filter(alert => alert.alertType === 'movement').length, wearOff: filteredAlerts.filter(alert => alert.alertType === 'wear').length }; // 分页处理 const startIndex = parseInt(offset); const endIndex = startIndex + parseInt(limit); const paginatedAlerts = filteredAlerts.slice(startIndex, endIndex); res.json({ success: true, data: paginatedAlerts, total: filteredAlerts.length, stats: stats, pagination: { page: parseInt(page), limit: parseInt(limit), total: filteredAlerts.length, pages: Math.ceil(filteredAlerts.length / limit) }, message: '获取智能项圈预警列表成功' }); } catch (error) { console.error('获取智能项圈预警列表失败:', error); res.status(500).json({ success: false, message: '获取智能项圈预警列表失败', error: error.message }); } }; /** * 获取单个智能项圈预警详情 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ exports.getCollarAlertById = async (req, res) => { try { const { id } = req.params; // 解析预警ID,格式为 deviceId_alertType const [deviceId, alertType] = id.split('_'); if (!deviceId || !alertType) { return res.status(400).json({ success: false, message: '无效的预警ID格式' }); } // 查找设备 const device = await IotXqClient.findByPk(deviceId); if (!device) { return res.status(404).json({ success: false, message: '设备不存在' }); } const deviceNumber = device.sn || `COLLAR${device.id}`; const alertTime = new Date(device.uptime || Date.now()).toISOString().replace('T', ' ').substring(0, 19); const actualBattery = parseInt(device.battery) || 0; const actualTemperature = parseFloat(device.temperature) || 0; const totalSteps = parseInt(device.steps) || 0; const yesterdaySteps = parseInt(device.y_steps) || 0; const dailySteps = totalSteps - yesterdaySteps; // 根据预警类型生成详情 let alertDetail = { id: id, deviceId: device.id, deviceName: deviceNumber, collarNumber: deviceNumber, alertType: alertType, alertTime: alertTime, battery: actualBattery, temperature: actualTemperature, dailySteps: dailySteps, totalSteps: totalSteps, yesterdaySteps: yesterdaySteps, deviceStatus: device.state === 1 ? '在线' : '离线', gpsSignal: device.state === 1 ? '强' : '无', wearStatus: device.bandge_status === 1 ? '已佩戴' : '未佩戴', longitude: 116.3974 + (device.id % 100) * 0.0001, latitude: 39.9093 + (device.id % 100) * 0.0001, movementStatus: '正常' }; // 根据预警类型设置具体信息 switch (alertType) { case 'offline': alertDetail.alertLevel = 'high'; alertDetail.description = '设备已离线超过30分钟'; alertDetail.movementStatus = '静止'; break; case 'battery': alertDetail.alertLevel = actualBattery < 10 ? 'high' : 'medium'; alertDetail.description = `设备电量低于20%,当前电量${actualBattery}%`; break; case 'temperature': if (actualTemperature < 30) { alertDetail.alertLevel = actualTemperature < 20 ? 'high' : 'medium'; alertDetail.description = `设备温度过低,当前温度${actualTemperature}°C`; } else if (actualTemperature > 40) { alertDetail.alertLevel = actualTemperature > 45 ? 'high' : 'medium'; alertDetail.description = `设备温度过高,当前温度${actualTemperature}°C`; } break; case 'movement': alertDetail.alertLevel = 'high'; alertDetail.description = '检测到步数异常,当日运动量为0步,可能为设备故障或动物异常'; alertDetail.movementStatus = '异常'; break; case 'wear': alertDetail.alertLevel = 'high'; alertDetail.description = '设备佩戴状态异常,可能已脱落'; break; } res.json({ success: true, data: alertDetail, message: '获取智能项圈预警详情成功' }); } catch (error) { console.error('获取智能项圈预警详情失败:', error); res.status(500).json({ success: false, message: '获取智能项圈预警详情失败', error: error.message }); } }; /** * 处理智能项圈预警 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ exports.handleCollarAlert = async (req, res) => { try { const { id } = req.params; const { action, notes, handler } = req.body; // 解析预警ID const [deviceId, alertType] = id.split('_'); if (!deviceId || !alertType) { return res.status(400).json({ success: false, message: '无效的预警ID格式' }); } // 这里可以实现预警处理逻辑,比如记录处理历史、发送通知等 // 目前只是返回成功响应 const result = { alertId: id, action: action || 'acknowledged', notes: notes || '', handler: handler || 'system', processedAt: new Date().toISOString(), status: 'processed' }; console.log('处理智能项圈预警:', result); res.json({ success: true, data: result, message: '预警处理成功' }); } catch (error) { console.error('处理智能项圈预警失败:', error); res.status(500).json({ success: false, message: '处理智能项圈预警失败', error: error.message }); } }; /** * 批量处理智能项圈预警 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ exports.batchHandleCollarAlerts = async (req, res) => { try { const { alertIds, action, notes, handler } = req.body; if (!alertIds || !Array.isArray(alertIds) || alertIds.length === 0) { return res.status(400).json({ success: false, message: '请提供有效的预警ID列表' }); } const results = []; for (const alertId of alertIds) { const [deviceId, alertType] = alertId.split('_'); if (deviceId && alertType) { results.push({ alertId: alertId, action: action || 'acknowledged', notes: notes || '', handler: handler || 'system', processedAt: new Date().toISOString(), status: 'processed' }); } } console.log('批量处理智能项圈预警:', results); res.json({ success: true, data: { processedCount: results.length, results: results }, message: `成功处理 ${results.length} 个预警` }); } catch (error) { console.error('批量处理智能项圈预警失败:', error); res.status(500).json({ success: false, message: '批量处理智能项圈预警失败', error: error.message }); } }; /** * 导出智能项圈预警数据 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ exports.exportCollarAlerts = async (req, res) => { try { const { search, alertType, alertLevel, startDate, endDate, format = 'json' } = req.query; // 构建查询条件(与获取列表相同的逻辑) const whereConditions = {}; if (search) { whereConditions[Op.or] = [ { sn: { [Op.like]: `%${search}%` } }, { deviceId: { [Op.like]: `%${search}%` } } ]; } if (startDate || endDate) { whereConditions.uptime = {}; if (startDate) { whereConditions.uptime[Op.gte] = new Date(startDate); } if (endDate) { whereConditions.uptime[Op.lte] = new Date(endDate); } } // 获取所有设备数据 const allDevices = await IotXqClient.findAll({ where: whereConditions, order: [['uptime', 'DESC'], ['id', 'DESC']] }); // 生成预警数据(与获取列表相同的逻辑) const allAlerts = []; allDevices.forEach(device => { const deviceId = device.sn || `COLLAR${device.id}`; const alertTime = new Date(device.uptime || Date.now()).toISOString().replace('T', ' ').substring(0, 19); const actualBattery = parseInt(device.battery) || 0; const actualTemperature = parseFloat(device.temperature) || 0; const totalSteps = parseInt(device.steps) || 0; const yesterdaySteps = parseInt(device.y_steps) || 0; const dailySteps = totalSteps - yesterdaySteps; // 添加基础信息到预警的函数 const addBaseInfoToAlert = (alert) => { alert.deviceId = device.id; alert.deviceName = deviceId; alert.collarNumber = deviceId; alert.dailySteps = dailySteps; alert.totalSteps = totalSteps; alert.yesterdaySteps = yesterdaySteps; alert.battery = actualBattery; alert.temperature = actualTemperature; alert.alertTime = alertTime; alert.deviceStatus = device.state === 1 ? '在线' : '离线'; alert.gpsSignal = device.state === 1 ? '强' : '无'; alert.wearStatus = device.bandge_status === 1 ? '已佩戴' : '未佩戴'; alert.longitude = 116.3974 + (device.id % 100) * 0.0001; alert.latitude = 39.9093 + (device.id % 100) * 0.0001; return alert; }; // 生成各类预警(与获取列表相同的逻辑) if (device.state === 0) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_offline`, alertType: 'offline', alertLevel: 'high', description: '设备已离线超过30分钟', movementStatus: '静止' })); } if (actualBattery > 0 && actualBattery < 20) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_battery`, alertType: 'battery', alertLevel: actualBattery < 10 ? 'high' : 'medium', description: `设备电量低于20%,当前电量${actualBattery}%`, movementStatus: '正常' })); } if (actualTemperature > 0) { if (actualTemperature < 30) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_temperature_low`, alertType: 'temperature', alertLevel: actualTemperature < 20 ? 'high' : 'medium', description: `设备温度过低,当前温度${actualTemperature}°C`, movementStatus: '正常' })); } else if (actualTemperature > 40) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_temperature_high`, alertType: 'temperature', alertLevel: actualTemperature > 45 ? 'high' : 'medium', description: `设备温度过高,当前温度${actualTemperature}°C`, movementStatus: '正常' })); } } if (dailySteps === 0 && totalSteps > 0) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_movement_zero`, alertType: 'movement', alertLevel: 'high', description: '检测到步数异常,当日运动量为0步,可能为设备故障或动物异常', movementStatus: '异常' })); } if (device.bandge_status === 0) { allAlerts.push(addBaseInfoToAlert({ id: `${device.id}_wear`, alertType: 'wear', alertLevel: 'high', description: '设备佩戴状态异常,可能已脱落', movementStatus: '正常' })); } }); // 应用筛选条件 let filteredAlerts = allAlerts; if (alertType && alertType.trim() !== '') { filteredAlerts = allAlerts.filter(alert => alert.alertType === alertType); } if (alertLevel && alertLevel.trim() !== '') { filteredAlerts = filteredAlerts.filter(alert => alert.alertLevel === alertLevel); } // 根据格式返回数据 if (format === 'csv') { // 这里可以实现CSV格式导出 res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename="collar_alerts.csv"'); res.send('CSV格式导出功能待实现'); } else { res.json({ success: true, data: filteredAlerts, total: filteredAlerts.length, message: '导出智能项圈预警数据成功' }); } } catch (error) { console.error('导出智能项圈预警数据失败:', error); res.status(500).json({ success: false, message: '导出智能项圈预警数据失败', error: error.message }); } };