const moment = require('moment') module.exports = function (app, opts) { const alarmsPush = app.fs.scheduleInit( { interval: '12 */1 * * * *', immediate: true, // dev proRun: true, }, async () => { try { const { models, ORM: sequelize } = app.fs.dc const { clickHouse } = app.fs const { database: anxinyun } = clickHouse.anxinyun.opts.config const { pushBySms, pushByEmail } = app.fs.utils const curMinOfYear = moment().diff(moment().startOf('year'), 'minutes') const configListRes = await models.AlarmPushConfig.findAll({ where: { del: false }, include: [{ model: models.ProjectCorrelation, where: { del: false }, required: true }] }) let pepProjectIds = new Set() for (let { dataValues: c } of configListRes) { if (c.projectCorrelation.pepProjectId) { pepProjectIds.add(c.projectCorrelation.pepProjectId) } } const pepProjectRes = pepProjectIds.size ? await clickHouse.projectManage.query( ` SELECT t_pim_project.id AS id, t_pim_project.project_name AS project_name, t_pim_project.isdelete AS isdelete, t_pim_project_construction.construction_status_id AS construction_status_id, t_pim_project_state.construction_status AS construction_status FROM t_pim_project LEFT JOIN t_pim_project_construction ON t_pim_project.id = t_pim_project_construction.project_id LEFT JOIN t_pim_project_state ON t_pim_project_construction.construction_status_id = t_pim_project_state.id WHERE id IN (${[...pepProjectIds].join(',')}) ` ).toPromise() : [] for (let { dataValues: c } of configListRes) { if (c.tacticsParams && c.tactics) { const { projectCorrelation, strucId, pomsProjectId, } = c const { interval, deviceProportion } = c.tacticsParams if ( curMinOfYear % parseInt(interval) == 0 ) { const corPepProject = projectCorrelation.pepProjectId ? pepProjectRes.find(p => p.id == projectCorrelation.pepProjectId) : null if ( !projectCorrelation.pepProjectId || ( corPepProject && c.timeType.some(ct => ct == corPepProject.construction_status_id) ) ) { const { anxinProjectId, pepProjectId } = projectCorrelation // 查当前 poms 下的结构物 并把不包含的去掉 // 可能有结构物已解绑 const strucListRes = strucId.length && anxinProjectId.length ? await clickHouse.anxinyun.query( ` SELECT DISTINCT id, t_structure.id AS id, t_structure.name AS name, t_structure.iota_thing_id AS iotaThingId FROM t_project LEFT JOIN t_project_structure ON t_project_structure.project = t_project.id LEFT JOIN t_project_structuregroup ON t_project_structuregroup.project = t_project.id LEFT JOIN t_structuregroup_structure ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup LEFT JOIN t_project_construction ON t_project_construction.project = t_project.id LEFT JOIN t_structure_site ON t_structure_site.siteid = t_project_construction.construction RIGHT JOIN t_structure ON t_structure.id = t_project_structure.structure OR t_structure.id = t_structuregroup_structure.structure OR t_structure.id = t_structure_site.structid WHERE project_state != -1 AND t_project.id IN (${anxinProjectId.join(',')}) AND t_structure.id IN (${strucId.join(',')}) ` ).toPromise() : [] let searchStrucIds = strucListRes.map(s => s.id) if (searchStrucIds.length) { searchStrucIds.unshift(-1) } let pepProjectName = pepProjectId ? pepProjectRes.find(p => p.id == pepProjectId).project_name : projectCorrelation.name let emailTitle = `${pepProjectName}-${c.name}-` let emailSubTitle = '' let dataAlarmOption = [] let dataAlarmGroupOption = [] let dataAlarms = [] let videoAlarmWhereOption = [] let videoAlarms = [] let appAlarmWhereOption = { confirmTime: null, } let appAlarms = [] let deviceCount = 0 let alarmDeviceCount = 0 let cameraCount = 0 let alarmCameraCount = 0 // 判断推送策略 let nowTime = moment().startOf('minute') let pointTime = moment().subtract(parseInt(interval), 'minute').startOf('minute').format('YYYY-MM-DD HH:mm:ss') if (c.tactics == 'immediately') { // dataAlarmOption.push(`StartTime >= '${pointTime}'`); // appAlarmWhereOption.createTime = { $gte: pointTime } // videoAlarmWhereOption.push(`camera_status_alarm.create_time >= '${pointTime}'`) emailTitle += `即时推送服务` emailSubTitle += `截止${moment(pointTime).format('YYYY年MM月DD日 HH时mm分')}-${moment(nowTime).format('HH时mm分')}:` } else if (c.tactics == 'continue') { // dataAlarmOption.push(`StartTime <= '${pointTime}'`); // appAlarmWhereOption.createTime = { $lte: pointTime } // videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`) emailTitle += `持续时长推送服务` emailSubTitle += `告警持续时长超${interval}分钟的告警源,详情如下:` } else if (c.tactics == 'abnormal_rate') { // dataAlarmOption.push(`StartTime <= '${pointTime}'`); if (c.alarmType.includes('data_outages') || c.alarmType.includes('data_exception')) { // 查了设备异常率 去安心云查当前项目下的设备数量 // TODO 等同步以太数据再查 deviceCount = 9999 // await clickHouse.anxinyun.query(` // SELECT count(*) FROM // `).toPromise() } if (c.alarmType.includes('video_exception')) { // 查了视频异常 去安心云查 接入的 萤石 设备数量 cameraCount = searchStrucIds.length ? (await clickHouse.anxinyun.query(` SELECT count(*) AS count FROM t_video_ipc WHERE structure IN (${searchStrucIds.join(',')}) `).toPromise())[0].count : 0 } // appAlarmWhereOption.createTime = { $lte: pointTime } // videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`) emailTitle += `异常率推送服务` emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${deviceCount + cameraCount}个的${interval}%,详情如下` } emailTitle += '——POMS飞尚运维中台' // 判断告警数据范围 if (c.alarmType.includes('data_outages')) { dataAlarmGroupOption.push(1) } if (c.alarmType.includes('data_exception')) { dataAlarmGroupOption.push(2) } if (c.alarmType.includes('strategy_hit')) { dataAlarmGroupOption.push(3) } if (c.alarmType.includes('video_exception')) { videoAlarms = searchStrucIds.length ? await clickHouse.vcmp.query( ` SELECT cameraAlarm.cameraId AS cameraId, cameraAlarm.cameraName AS cameraName, cameraAlarm.cameraSerialNo AS cameraSerialNo, cameraAlarm.cameraChannelNo AS cameraChannelNo, cameraAlarm.alarmId AS alarmId, cameraAlarm.createTime AS createTime, cameraAlarm.confirmContent AS confirmContent, cameraAlarm.confirmTime AS confirmTime, cameraAlarm.autoRestore AS autoRestore, camera_status.describe AS statusDescribe, "gbCamera".online AS cameraOnline, anxinIpc.t_video_ipc.name AS anxinIpcPosition, anxinStation.id AS anxinStationId, anxinStation.name AS anxinStationName, anxinStruc.name AS strucName, anxinStruc.id AS strucId FROM ( SELECT camera.id AS cameraId, camera.gb_id AS gbId, camera.name AS cameraName, camera_status_alarm.id AS alarmId, camera_status_alarm.create_time AS createTime, camera_status_alarm.status_id AS statusId, camera_status_alarm.serial_no AS cameraSerialNo, camera_status_alarm.channel_no AS cameraChannelNo, camera_status_alarm.confirm AS confirmContent, camera_status_alarm.auto_restore AS autoRestore, camera_status_alarm.confirm_time AS confirmTime FROM camera_status_alarm INNER JOIN ${anxinyun}.t_video_ipc ON toString(${anxinyun}.t_video_ipc.channel_no) = camera_status_alarm.channel_no AND ${anxinyun}.t_video_ipc.serial_no = camera_status_alarm.serial_no AND ${anxinyun}.t_video_ipc.structure IN (${searchStrucIds.join(',')}) INNER JOIN camera ON camera.serial_no = camera_status_alarm.serial_no AND camera.channel_no = camera_status_alarm.channel_no AND camera.delete = false AND camera.recycle_time is null WHERE camera_status_alarm.confirm_time IS null ${videoAlarmWhereOption.length ? ` AND ${videoAlarmWhereOption.join(' AND ')}` : ''} ) AS cameraAlarm LEFT JOIN camera_status ON cameraAlarm.platform = camera_status.platform AND cameraAlarm.statusId = camera_status.id LEFT JOIN "gbCamera" ON "gbCamera".id = cameraAlarm.gbId LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo LEFT JOIN ${anxinyun}.t_structure AS anxinStruc ON anxinStruc.id = anxinIpc.structure AND anxinStruc.id IN (${searchStrucIds.join(',')}) LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation ON anxinIpcStation.ipc = anxinIpc.id LEFT JOIN ${anxinyun}.t_sensor AS anxinStation ON anxinStation.id = anxinIpcStation.station ` ).toPromise() : [] let returnD = [] let positionD = {} // 每个设备一个告警 for (let a of videoAlarms) { if (positionD[a.cameraId]) { let curD = returnD[positionD[a.cameraId].positionReturnD] if (a.strucId && !curD.struc.some(s => s.id == a.strucId)) { curD.struc.push({ id: a.strucId, projectId: a.projectId, name: a.strucName }) } if (a.anxinStationId && !curD.station.some(s => s.id == a.anxinStationId)) { curD.station.push({ id: a.anxinStationId, name: a.anxinStationName, position: a.anxinIpcPosition }) } } else { let d = { cameraId: a.cameraId, cameraName: a.cameraName, camerOnline: a.cameraOnline, cameraSerialNo: a.cameraSerialNo, cameraChannelNo: a.cameraChannelNo, autoRestore: a.autoRestore, createTime: a.createTime, updateTime: a.updateTime, platform: a.platform, statusDescribe: a.statusDescribe, alarmId: a.alarmId, confirmContent: a.confirmContent, confirmTime: a.confirmTime, struc: [], station: [] } if (a.strucId) { d.struc.push({ id: a.strucId, projectId: a.projectId, name: a.strucName }) } if (a.anxinStationId) { d.station.push({ id: a.anxinStationId, name: a.anxinStationName, position: a.anxinIpcPosition }) } returnD.push(d) positionD[a.cameraId] = { positionReturnD: returnD.length - 1 } } } let p = 1 } if (c.alarmType.includes('app_exception')) { appAlarms = await models.AppAlarm.findAll({ where: appAlarmWhereOption, include: [{ model: models.App, include: [{ model: models.ProjectApp, where: { projectId: pomsProjectId }, }] }] }) let a = 2 } if (c.alarmType.includes('device_exception')) { dataAlarmGroupOption.push(4) dataAlarmGroupOption.push(5) } // 查数据告警 三警合一 if (dataAlarmGroupOption.length && searchStrucIds.length) { dataAlarmGroupOption.push(-1) dataAlarmOption.push(`AlarmGroup IN (${dataAlarmGroupOption.join(',')})`) dataAlarms = await clickHouse.dataAlarm.query(` SELECT * FROM alarms WHERE State NOT IN (3, 4) AND StructureId IN (${searchStrucIds.join(',')}) ${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''} `).toPromise() console.log(dataAlarms); } let dataAlarmTitle = [{ n: '项目', k: '', v: pepProjectName }, { n: '结构物', k: '', f: (d) => { return (strucListRes.find(s => s.id == d.StructureId) || {}).name } }, { n: '告警源名称', k: 'SourceName' }, { n: '告警源类型', k: '', f: (d) => { switch (d.SourceTypeId) { case 0: return 'DTU' case 1: return '传感器' case 2: return '测点' default: return '' } } }, { n: '告警信息', k: 'AlarmContent' }, { n: '告警等级(当前)', k: '', f: (d) => { switch (d.CurrentLevel) { case 1: return '一级' case 2: return '二级' case 3: return '三级' default: return '' } } }, { n: '持续时间', k: '', f: (d) => { return d.StartTime ? '超过' + moment().diff(moment(d.StartTime), 'minutes') + '分钟' : '' } },] let html = '' let tableTitlePostfix = ',详情如下:' function packageTableTitle (tArr) { } if (c.alarmType.includes('data_outages')) { let tableTitlePrefix = '数据中断告警源' let newAddCount = 0 let alarmHtml = '' if (c.tactics == 'immediately') { for (let a of dataAlarms.filter(d => d.AlarmGroup == 1)) { alarmHtml += 1 } tableTitlePrefix += 1 } else if (c.tactics == 'continue') { } else if (c.tactics == 'abnormal_rate') { } } if (c.alarmType.includes('data_exception')) { } if (c.alarmType.includes('strategy_hit')) { } if (c.alarmType.includes('video_exception')) { } if (c.alarmType.includes('app_exception')) { } if (c.alarmType.includes('device_exception')) { } await pushByEmail({ email: ['1650192445@qq.com'], title: emailTitle, text: '', html: '' }) } } } } } catch (error) { console.error(error); } } ) return { alarmsPush, } }