// 通知服务 - 支持多渠道通知 const axios = require('axios'); class NotificationService { constructor() { // 企业微信 this.weixinCorpId = process.env.WEIXIN_CORP_ID; this.weixinAgentId = process.env.WEIXIN_AGENT_ID; this.weixinSecret = process.env.WEIXIN_SECRET; this.weixinToken = null; this.weixinTokenExpiry = null; // 钉钉 this.dingtalkToken = process.env.DINGTALK_TOKEN; // 邮件 this.smtpConfig = { host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, user: process.env.SMTP_USER, password: process.env.SMTP_PASSWORD, }; } // 获取企业微信 Access Token async getWeChatToken() { if (this.weixinToken && this.weixinTokenExpiry > Date.now()) { return this.weixinToken; } const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken`; const params = { corpid: this.weixinCorpId, corpsecret: this.weixinSecret, }; const response = await axios.get(url, { params }); const { access_token, expires_in } = response.data; if (response.data.errcode !== 0) { throw new Error(`企业微信 Token 获取失败: ${response.data.errmsg}`); } this.weixinToken = access_token; this.weixinTokenExpiry = Date.now() + (expires_in - 60) * 1000; return access_token; } // 发送企业微信消息 async sendWeChat(userIds, message) { if (!this.weixinCorpId || !this.weixinAgentId) { console.log('[通知] 企业微信未配置,跳过'); return { success: false, reason: 'not_configured' }; } try { const token = await this.getWeChatToken(); const url = `https://qyapi.weixin.qq.com/cgi-bin/message/send`; const response = await axios.post( url, { touser: userIds.join('|'), msgtype: 'text', agentid: this.weixinAgentId, text: { content: message }, }, { params: { access_token: token } } ); if (response.data.errcode !== 0) { throw new Error(response.data.errmsg); } return { success: true, messageId: response.data.msgid }; } catch (error) { console.error('[通知] 企业微信发送失败:', error.message); return { success: false, error: error.message }; } } // 发送钉钉消息 async sendDingTalk(userIds, message) { if (!this.dingtalkToken) { console.log('[通知] 钉钉未配置,跳过'); return { success: false, reason: 'not_configured' }; } try { const url = `https://oapi.dingtalk.com/robot/send`; const response = await axios.post( url, { msgtype: 'text', text: { content: message }, at: { atUserIds: userIds, }, }, { params: { access_token: this.dingtalkToken } } ); if (response.data.errcode !== 0) { throw new Error(response.data.errmsg); } return { success: true }; } catch (error) { console.error('[通知] 钉钉发送失败:', error.message); return { success: false, error: error.message }; } } // 发送邮件 async sendEmail(recipients, subject, htmlBody) { if (!this.smtpConfig.host) { console.log('[通知] 邮件 SMTP 未配置,跳过'); return { success: false, reason: 'not_configured' }; } // 实际项目中应使用 nodemailer 或邮件服务 console.log(`[通知] 发送邮件给 ${recipients.join(', ')}: ${subject}`); return { success: true }; } // 发送短信(预留接口) async sendSMS(phoneNumbers, content) { console.log(`[通知] 发送短信给 ${phoneNumbers.join(', ')}: ${content}`); return { success: true }; } // 统一发送接口 async sendToUsers(userIds, message, channels) { const results = {}; for (const channel of channels) { const userIdsForChannel = userIds; // 可以根据用户偏好筛选 switch (channel) { case 'wechat': results.wechat = await this.sendWeChat(userIdsForChannel, message); break; case 'dingtalk': results.dingtalk = await this.sendDingTalk(userIdsForChannel, message); break; case 'email': results.email = await this.sendEmail(userIdsForChannel, '预警通知', message); break; case 'sms': results.sms = await this.sendSMS(userIdsForChannel, message); break; default: results[channel] = { success: false, reason: 'unknown_channel' }; } } return results; } // 格式化预警消息 formatAlertMessage(rule, value, threshold) { const severityEmoji = { low: 'ℹ️', medium: '⚠️', high: '🔴', critical: '🚨', }; const severityZh = { low: '低', medium: '中', high: '高', critical: '严重', }; return ` ${severityEmoji[rule.severity] || '⚠️'} 【${severityZh[rule.severity] || '中'}级预警】${rule.name} 📊 当前值: ${value} 🎯 阈值: ${rule.condition === 'gt' ? '大于' : rule.condition === 'lt' ? '小于' : rule.condition} ${threshold} ⏰ 时间: ${new Date().toLocaleString()} 请及时处理! `.trim(); } } module.exports = new NotificationService();