/** * 通知服务 * @file notificationService.js * @description 实现邮件/短信预警通知功能,确保5分钟内响应时间 */ const nodemailer = require('nodemailer'); const logger = require('../utils/logger'); const { User, Farm } = require('../models'); class NotificationService { constructor() { this.emailTransporter = null; this.smsService = null; // 短信服务接口(后续可扩展) this.notificationQueue = []; // 通知队列 this.isProcessing = false; this.maxRetries = 3; this.retryDelay = 1000; // 1秒重试延迟 // 初始化邮件服务 this.initEmailService(); } /** * 初始化邮件服务 */ initEmailService() { try { // 配置邮件传输器(使用环境变量配置) this.emailTransporter = nodemailer.createTransport({ host: process.env.SMTP_HOST || 'smtp.example.com', port: process.env.SMTP_PORT || 587, secure: false, // true for 465, false for other ports auth: { user: process.env.SMTP_USER || 'noreply@farm-monitor.com', pass: process.env.SMTP_PASS || 'your_email_password' }, tls: { rejectUnauthorized: false } }); logger.info('邮件服务初始化完成'); } catch (error) { logger.error('邮件服务初始化失败:', error); } } /** * 发送预警通知 * @param {Object} alert 预警对象 * @param {Array} recipients 接收人列表 * @param {Object} options 通知选项 */ async sendAlertNotification(alert, recipients = [], options = {}) { const { urgent = false, includeSMS = false, maxResponseTime = 300000 // 5分钟 = 300000毫秒 } = options; const notification = { id: `alert_${alert.id}_${Date.now()}`, type: 'alert', alert, recipients, urgent, includeSMS, createdAt: new Date(), maxResponseTime, retryCount: 0, status: 'pending' }; // 添加到通知队列 this.notificationQueue.push(notification); // 立即处理紧急通知 if (urgent) { await this.processNotification(notification); } else { // 非紧急通知进入队列处理 this.processQueue(); } logger.info(`预警通知已加入队列: ${notification.id}, 紧急程度: ${urgent}`); return notification.id; } /** * 处理通知队列 */ async processQueue() { if (this.isProcessing) return; this.isProcessing = true; try { while (this.notificationQueue.length > 0) { const notification = this.notificationQueue.shift(); // 检查是否超时 const timePassed = Date.now() - notification.createdAt.getTime(); if (timePassed > notification.maxResponseTime) { logger.warn(`通知 ${notification.id} 已超时,跳过处理`); continue; } await this.processNotification(notification); } } catch (error) { logger.error('处理通知队列失败:', error); } finally { this.isProcessing = false; } } /** * 处理单个通知 * @param {Object} notification 通知对象 */ async processNotification(notification) { try { const startTime = Date.now(); // 获取接收人列表 const recipients = await this.getNotificationRecipients(notification); // 发送邮件通知 const emailResults = await this.sendEmailNotifications(notification, recipients); // 发送短信通知(如果需要) let smsResults = []; if (notification.includeSMS) { smsResults = await this.sendSMSNotifications(notification, recipients); } const endTime = Date.now(); const responseTime = endTime - startTime; // 记录通知发送结果 notification.status = 'completed'; notification.responseTime = responseTime; notification.emailResults = emailResults; notification.smsResults = smsResults; logger.info(`通知 ${notification.id} 发送完成,响应时间: ${responseTime}ms`); // 检查是否超过5分钟响应时间要求 if (responseTime > 300000) { logger.warn(`通知 ${notification.id} 响应时间超过5分钟: ${responseTime}ms`); } } catch (error) { logger.error(`处理通知 ${notification.id} 失败:`, error); // 重试机制 if (notification.retryCount < this.maxRetries) { notification.retryCount++; notification.status = 'retrying'; setTimeout(() => { this.notificationQueue.unshift(notification); // 重新加入队列头部 this.processQueue(); }, this.retryDelay * notification.retryCount); logger.info(`通知 ${notification.id} 将进行第 ${notification.retryCount} 次重试`); } else { notification.status = 'failed'; logger.error(`通知 ${notification.id} 达到最大重试次数,标记为失败`); } } } /** * 获取通知接收人 * @param {Object} notification 通知对象 * @returns {Array} 接收人列表 */ async getNotificationRecipients(notification) { try { const alert = notification.alert; const recipients = []; // 如果指定了接收人,直接使用 if (notification.recipients && notification.recipients.length > 0) { return notification.recipients; } // 获取农场相关负责人 if (alert.farm_id) { const farm = await Farm.findByPk(alert.farm_id); if (farm && farm.contact) { recipients.push({ name: farm.contact, email: `${farm.contact.toLowerCase().replace(/\s+/g, '')}@farm-monitor.com`, phone: farm.phone, role: 'farm_manager' }); } } // 获取系统管理员 const admins = await User.findAll({ include: [{ model: require('../models').Role, as: 'role', where: { name: 'admin' } }] }); for (const admin of admins) { recipients.push({ name: admin.username, email: admin.email, phone: admin.phone, role: 'admin' }); } return recipients; } catch (error) { logger.error('获取通知接收人失败:', error); return []; } } /** * 发送邮件通知 * @param {Object} notification 通知对象 * @param {Array} recipients 接收人列表 * @returns {Array} 发送结果 */ async sendEmailNotifications(notification, recipients) { if (!this.emailTransporter) { logger.warn('邮件服务未初始化,跳过邮件发送'); return []; } const results = []; const alert = notification.alert; for (const recipient of recipients) { if (!recipient.email) continue; try { const emailContent = this.generateEmailContent(alert, recipient); const mailOptions = { from: process.env.SMTP_FROM || '"宁夏智慧养殖监管平台" ', to: recipient.email, subject: emailContent.subject, html: emailContent.html, priority: alert.level === 'critical' ? 'high' : 'normal' }; const result = await this.emailTransporter.sendMail(mailOptions); results.push({ recipient: recipient.email, status: 'success', messageId: result.messageId }); logger.info(`邮件通知发送成功: ${recipient.email}`); } catch (error) { results.push({ recipient: recipient.email, status: 'failed', error: error.message }); logger.error(`邮件通知发送失败: ${recipient.email}`, error); } } return results; } /** * 生成邮件内容 * @param {Object} alert 预警对象 * @param {Object} recipient 接收人信息 * @returns {Object} 邮件内容 */ generateEmailContent(alert, recipient) { const levelMap = { low: { text: '低级', color: '#52c41a' }, medium: { text: '中级', color: '#1890ff' }, high: { text: '高级', color: '#fa8c16' }, critical: { text: '紧急', color: '#f5222d' } }; const levelInfo = levelMap[alert.level] || levelMap.medium; const urgentFlag = alert.level === 'critical' ? '🚨 ' : ''; const subject = `${urgentFlag}宁夏智慧养殖监管平台 - ${levelInfo.text}预警通知`; const html = `

宁夏智慧养殖监管平台

智慧养殖 · 科学监管

${urgentFlag}${levelInfo.text}预警通知

尊敬的 ${recipient.name}:

系统检测到以下预警信息,请及时处理:

预警级别: ${levelInfo.text}
预警类型: ${alert.type}
预警内容: ${alert.message}
所属养殖场: ${alert.farm_name || '未知'}
发生时间: ${new Date(alert.created_at).toLocaleString('zh-CN')}
${alert.level === 'critical' ? '
⚠️ 这是一个紧急预警,请立即处理!
' : '

请及时登录系统查看详细信息并处理相关问题。

' }
立即处理预警

此邮件由宁夏智慧养殖监管平台自动发送,请勿回复

如需帮助,请联系系统管理员

`; return { subject, html }; } /** * 发送短信通知(预留接口) * @param {Object} notification 通知对象 * @param {Array} recipients 接收人列表 * @returns {Array} 发送结果 */ async sendSMSNotifications(notification, recipients) { // 这里可以集成阿里云短信、腾讯云短信等服务 const results = []; const alert = notification.alert; for (const recipient of recipients) { if (!recipient.phone) continue; try { // 短信内容 const smsContent = `【宁夏智慧养殖监管平台】${alert.level === 'critical' ? '紧急' : ''}预警:${alert.message}。请及时处理。时间:${new Date().toLocaleString('zh-CN')}`; // 这里实现具体的短信发送逻辑 // const smsResult = await this.sendSMS(recipient.phone, smsContent); // 目前记录到日志(实际部署时替换为真实短信服务) logger.info(`短信通知(模拟)已发送到 ${recipient.phone}: ${smsContent}`); results.push({ recipient: recipient.phone, status: 'success', content: smsContent }); } catch (error) { results.push({ recipient: recipient.phone, status: 'failed', error: error.message }); logger.error(`短信通知发送失败: ${recipient.phone}`, error); } } return results; } /** * 发送系统状态通知 * @param {Object} statusData 系统状态数据 * @param {Array} adminEmails 管理员邮箱列表 */ async sendSystemStatusNotification(statusData, adminEmails = []) { if (!this.emailTransporter) return; const subject = `宁夏智慧养殖监管平台 - 系统状态报告`; const html = `

系统状态报告

宁夏智慧养殖监管平台

系统运行状况

API响应时间: ${statusData.apiResponseTime || 'N/A'}ms
数据库连接状态: ${statusData.dbConnected ? '正常' : '异常'}
系统内存使用率: ${statusData.memoryUsage || 'N/A'}%
活跃用户数: ${statusData.activeUsers || 0}
报告时间: ${new Date().toLocaleString('zh-CN')}

此邮件由系统自动发送

`; for (const email of adminEmails) { try { await this.emailTransporter.sendMail({ from: process.env.SMTP_FROM || '"宁夏智慧养殖监管平台" ', to: email, subject, html }); logger.info(`系统状态报告已发送到: ${email}`); } catch (error) { logger.error(`系统状态报告发送失败: ${email}`, error); } } } /** * 测试邮件服务 * @param {string} testEmail 测试邮箱 * @returns {boolean} 测试结果 */ async testEmailService(testEmail) { if (!this.emailTransporter) { logger.error('邮件服务未初始化'); return false; } try { const result = await this.emailTransporter.sendMail({ from: process.env.SMTP_FROM || '"宁夏智慧养殖监管平台" ', to: testEmail, subject: '宁夏智慧养殖监管平台 - 邮件服务测试', html: `

邮件服务测试

如果您收到此邮件,说明邮件服务配置正确。

测试时间: ${new Date().toLocaleString('zh-CN')}

` }); logger.info(`测试邮件发送成功: ${testEmail}, MessageID: ${result.messageId}`); return true; } catch (error) { logger.error(`测试邮件发送失败: ${testEmail}`, error); return false; } } /** * 获取通知统计信息 * @returns {Object} 统计信息 */ getNotificationStats() { const completedNotifications = this.notificationQueue.filter(n => n.status === 'completed'); const failedNotifications = this.notificationQueue.filter(n => n.status === 'failed'); const avgResponseTime = completedNotifications.length > 0 ? completedNotifications.reduce((sum, n) => sum + n.responseTime, 0) / completedNotifications.length : 0; return { totalNotifications: this.notificationQueue.length, completedCount: completedNotifications.length, failedCount: failedNotifications.length, avgResponseTime: Math.round(avgResponseTime), queueLength: this.notificationQueue.filter(n => n.status === 'pending').length }; } } // 创建单例实例 const notificationService = new NotificationService(); module.exports = notificationService;