|
|
@ -1,15 +1,22 @@ |
|
|
|
const moment = require('moment') |
|
|
|
|
|
|
|
let isDev = false |
|
|
|
// isDev = true
|
|
|
|
|
|
|
|
let proDebug = false |
|
|
|
proDebug = true |
|
|
|
|
|
|
|
module.exports = function (app, opts) { |
|
|
|
const alarmsPush = app.fs.scheduleInit( |
|
|
|
{ |
|
|
|
interval: '12 */1 * * * *', |
|
|
|
// immediate: true, // dev
|
|
|
|
proRun: true, |
|
|
|
immediate: isDev, |
|
|
|
proRun: !isDev, |
|
|
|
}, |
|
|
|
async () => { |
|
|
|
try { |
|
|
|
const { models, ORM: sequelize } = app.fs.dc |
|
|
|
const { apMergeDeVeAnxinProjectId = '' } = opts |
|
|
|
const { clickHouse } = app.fs |
|
|
|
const { database: anxinyun } = clickHouse.anxinyun.opts.config |
|
|
|
const { pushBySms, pushByEmail, sendNoticeToWeb } = app.fs.utils |
|
|
@ -20,18 +27,22 @@ module.exports = function (app, opts) { |
|
|
|
disable: false |
|
|
|
}, |
|
|
|
order: ['id'], |
|
|
|
include: [{ |
|
|
|
model: models.ProjectCorrelation, |
|
|
|
where: { |
|
|
|
del: false, |
|
|
|
}, |
|
|
|
required: true |
|
|
|
}], |
|
|
|
}) |
|
|
|
let pomsProjectId = new Set() |
|
|
|
let pepProjectIds = new Set() |
|
|
|
for (let { dataValues: c } of configListRes) { |
|
|
|
if (c.projectCorrelation.pepProjectId) { |
|
|
|
pepProjectIds.add(c.projectCorrelation.pepProjectId) |
|
|
|
if (c.pomsProjectId) { |
|
|
|
c.pomsProjectId.forEach(pid => pomsProjectId.add(pid)) |
|
|
|
} |
|
|
|
} |
|
|
|
const pomsProjectRes = pomsProjectId.size ? await models.ProjectCorrelation.findAll({ |
|
|
|
where: { |
|
|
|
id: { $in: [...pomsProjectId] } |
|
|
|
} |
|
|
|
}) : [] |
|
|
|
for (let { dataValues: c } of pomsProjectRes) { |
|
|
|
if (c.pepProjectId) { |
|
|
|
pepProjectIds.add(c.pepProjectId) |
|
|
|
} |
|
|
|
} |
|
|
|
const pepProjectRes = pepProjectIds.size ? |
|
|
@ -55,24 +66,42 @@ module.exports = function (app, opts) { |
|
|
|
|
|
|
|
for (let { dataValues: c } of configListRes) { |
|
|
|
if (c.tacticsParams && c.tactics) { |
|
|
|
const { projectCorrelation, strucId, pomsProjectId, } = c |
|
|
|
if (proDebug) { |
|
|
|
console.log(`当前运行EM配置:id=${c.id} name=${c.name}`); |
|
|
|
} |
|
|
|
// pomsProjectId 是个数组 []
|
|
|
|
const { strucId, pomsProjectId, pomsStrucFactorId } = c |
|
|
|
const { interval, deviceProportion } = c.tacticsParams |
|
|
|
|
|
|
|
if ( |
|
|
|
curMinOfYear % parseInt(interval) == 0 |
|
|
|
curMinOfYear % parseInt(interval) == 0 || isDev |
|
|
|
) { |
|
|
|
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 |
|
|
|
|
|
|
|
const corPomsProject = pomsProjectRes.filter(poms => pomsProjectId.includes(poms.id)) |
|
|
|
let curAnxinProjectId = new Set() |
|
|
|
let pepProjectName_ = [] |
|
|
|
for (let { dataValues: poms } of corPomsProject) { |
|
|
|
if (poms.pepProjectId) { |
|
|
|
// 找对应的项企项目
|
|
|
|
const corPepProject = |
|
|
|
pepProjectRes.find(p => p.id == poms.pepProjectId) |
|
|
|
if (corPepProject && c.timeType.some(ct => ct == corPepProject.construction_status_id)) { |
|
|
|
pepProjectName_.push(corPepProject.project_name) |
|
|
|
} else { |
|
|
|
// 不符合当前项目的时间节点
|
|
|
|
continue |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 是自定义项目
|
|
|
|
poms.name ? pepProjectName_.push(poms.name) : null |
|
|
|
} |
|
|
|
// 筛选全部的 anxinProjectId pepProjectId
|
|
|
|
for (let axId of poms.anxinProjectId) { |
|
|
|
curAnxinProjectId.add(axId) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const anxinProjectId = [...curAnxinProjectId] |
|
|
|
// 查当前 poms 下的结构物 并把不包含的去掉
|
|
|
|
// 可能有结构物已解绑
|
|
|
|
const strucListRes = strucId.length && anxinProjectId.length ? |
|
|
@ -82,7 +111,8 @@ module.exports = function (app, opts) { |
|
|
|
DISTINCT id, |
|
|
|
t_structure.id AS id, |
|
|
|
t_structure.name AS name, |
|
|
|
t_structure.iota_thing_id AS iotaThingId |
|
|
|
t_structure.iota_thing_id AS iotaThingId, |
|
|
|
t_project.id AS projectId |
|
|
|
FROM |
|
|
|
t_project |
|
|
|
LEFT JOIN |
|
|
@ -109,32 +139,45 @@ module.exports = function (app, opts) { |
|
|
|
project_state != -1 |
|
|
|
AND t_project.id IN (${anxinProjectId.join(',')}) |
|
|
|
AND t_structure.id IN (${strucId.join(',')}) |
|
|
|
|
|
|
|
` |
|
|
|
).toPromise() : |
|
|
|
[] |
|
|
|
let strucThingId = [] |
|
|
|
let strucMap = {} |
|
|
|
let searchStrucIds = strucListRes.map(s => { |
|
|
|
if (s.iotaThingId) { |
|
|
|
strucThingId.push(s.iotaThingId) |
|
|
|
} |
|
|
|
strucMap[s.id] = { |
|
|
|
...s |
|
|
|
} |
|
|
|
return s.id |
|
|
|
}) |
|
|
|
|
|
|
|
// 开发测试用的数据
|
|
|
|
// searchStrucIds = searchStrucIds.concat([991, 1052, 700])
|
|
|
|
// !开发测试用的数据
|
|
|
|
if (isDev) { |
|
|
|
searchStrucIds = searchStrucIds.concat([991, 1052, 700]) |
|
|
|
} |
|
|
|
|
|
|
|
if (searchStrucIds.length) { |
|
|
|
searchStrucIds.unshift(-1) |
|
|
|
} else { |
|
|
|
// 没有结构物可查
|
|
|
|
continue |
|
|
|
} |
|
|
|
let pepProjectName = pepProjectId ? |
|
|
|
pepProjectRes.find(p => p.id == pepProjectId).project_name |
|
|
|
: projectCorrelation.name |
|
|
|
let emailTitle = `${pepProjectName}-${c.name}-` |
|
|
|
let pepProjectName = |
|
|
|
pepProjectName_.length ? |
|
|
|
pepProjectName_.join('<br/>') |
|
|
|
: '' |
|
|
|
|
|
|
|
let emailTitle = `${pepProjectName_.length ? |
|
|
|
pepProjectName_.join('、') |
|
|
|
: ''}-${c.name}-` |
|
|
|
let emailSubTitle = '' |
|
|
|
|
|
|
|
let dataAlarmOption = [] |
|
|
|
let dataAlarmGroupOption = [] |
|
|
|
let dataAlarmSubType = [] |
|
|
|
let dataAlarms = [] |
|
|
|
|
|
|
|
let videoAlarmWhereOption = [] |
|
|
@ -211,15 +254,26 @@ module.exports = function (app, opts) { |
|
|
|
// 判断告警数据范围
|
|
|
|
if (c.alarmType.includes('data_outages')) { |
|
|
|
dataAlarmGroupOption.push(1) |
|
|
|
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['data_outages']) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('data_exception')) { |
|
|
|
dataAlarmGroupOption.push(2) |
|
|
|
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['data_exception']) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('strategy_hit')) { |
|
|
|
dataAlarmGroupOption.push(3) |
|
|
|
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['strategy_hit']) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('video_exception')) { |
|
|
|
videoAlarms = searchStrucIds.length ? await clickHouse.vcmp.query( |
|
|
|
let videoAlarmSubType = c.alarmSubType ? c.alarmSubType['video_exception'] : [] |
|
|
|
if (videoAlarmSubType.length == 1) { |
|
|
|
videoAlarmSubType.push(-1) |
|
|
|
} |
|
|
|
videoAlarms = |
|
|
|
searchStrucIds.length && ( |
|
|
|
!c.alarmSubType || videoAlarmSubType.length > 0 |
|
|
|
) ? |
|
|
|
await clickHouse.vcmp.query( |
|
|
|
` |
|
|
|
SELECT |
|
|
|
cameraAlarm.cameraId AS cameraId, |
|
|
@ -237,6 +291,7 @@ module.exports = function (app, opts) { |
|
|
|
anxinIpc.t_video_ipc.name AS anxinIpcPosition, |
|
|
|
anxinStation.id AS anxinStationId, |
|
|
|
anxinStation.name AS anxinStationName, |
|
|
|
anxinStation.factor AS anxinStationFactorId, |
|
|
|
anxinStruc.name AS strucName, |
|
|
|
anxinStruc.id AS strucId |
|
|
|
FROM ( |
|
|
@ -263,6 +318,7 @@ module.exports = function (app, opts) { |
|
|
|
AND camera.channel_no = camera_status_alarm.channel_no |
|
|
|
AND camera.delete = false |
|
|
|
AND camera.recycle_time is null |
|
|
|
${c.alarmSubType ? `AND camera.kind_id in (${videoAlarmSubType.join(',')})` : ""} |
|
|
|
WHERE |
|
|
|
camera_status_alarm.confirm_time IS null |
|
|
|
${videoAlarmWhereOption.length ? ` AND ${videoAlarmWhereOption.join(' AND ')}` : ''} |
|
|
@ -310,6 +366,21 @@ module.exports = function (app, opts) { |
|
|
|
}) |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
/**按监测因素 factor 筛选告警*/ |
|
|
|
// if (pomsStrucFactorId) {
|
|
|
|
// if (!a.strucId || !a.anxinStationFactorId) {
|
|
|
|
// // 当前告警没有绑定结构物或者摄像头没有绑定测点
|
|
|
|
// continue
|
|
|
|
// } else if (!pomsStrucFactorId[a.strucId]) {
|
|
|
|
// // 推送配置没配置这个结构物
|
|
|
|
// continue
|
|
|
|
// } else if (!pomsStrucFactorId[a.strucId].includes(a.anxinStationFactorId)) {
|
|
|
|
// // 不包含这个监测因素
|
|
|
|
// continue
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
let d = { |
|
|
|
cameraId: a.cameraId, |
|
|
|
cameraName: a.cameraName, |
|
|
@ -325,6 +396,7 @@ module.exports = function (app, opts) { |
|
|
|
confirmContent: a.confirmContent, |
|
|
|
confirmTime: a.confirmTime, |
|
|
|
cameraKind: a.cameraKind, |
|
|
|
factorId: a.anxinStationFactorId, |
|
|
|
struc: [], |
|
|
|
station: [] |
|
|
|
} |
|
|
@ -349,11 +421,14 @@ module.exports = function (app, opts) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
let p = 1 |
|
|
|
videoAlarms = returnD |
|
|
|
} |
|
|
|
if (c.alarmType.includes('app_exception')) { |
|
|
|
appAlarms = await models.AppAlarm.findAll({ |
|
|
|
if (c.alarmSubType) { |
|
|
|
appAlarmWhereOption.type = { $in: c.alarmSubType['app_exception'] || [] } |
|
|
|
} |
|
|
|
appAlarms = c.alarmSubType && c.alarmSubType['app_exception'].length ? |
|
|
|
await models.AppAlarm.findAll({ |
|
|
|
where: appAlarmWhereOption, |
|
|
|
order: [['createTime', 'DESC']], |
|
|
|
include: [{ |
|
|
@ -362,31 +437,41 @@ module.exports = function (app, opts) { |
|
|
|
include: [{ |
|
|
|
model: models.ProjectApp, |
|
|
|
where: { |
|
|
|
projectId: pomsProjectId |
|
|
|
projectId: { $in: pomsProjectId } |
|
|
|
}, |
|
|
|
required: true, |
|
|
|
}] |
|
|
|
}] |
|
|
|
}) |
|
|
|
}) : [] |
|
|
|
} |
|
|
|
if (c.alarmType.includes('device_exception')) { |
|
|
|
dataAlarmGroupOption.push(4) |
|
|
|
dataAlarmGroupOption.push(5) |
|
|
|
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['device_exception']) |
|
|
|
} |
|
|
|
|
|
|
|
// 查数据告警 三警合一
|
|
|
|
if (dataAlarmGroupOption.length && searchStrucIds.length) { |
|
|
|
dataAlarmGroupOption.push(-1) |
|
|
|
dataAlarmOption.push(`AlarmGroup IN (${dataAlarmGroupOption.join(',')})`) |
|
|
|
dataAlarms = await clickHouse.dataAlarm.query(` |
|
|
|
if (c.alarmSubType && dataAlarmSubType.length) { |
|
|
|
dataAlarmSubType.push(-1) |
|
|
|
dataAlarmOption.push(`AlarmGroupUnit IN (${dataAlarmSubType.join(',')})`) |
|
|
|
} |
|
|
|
dataAlarms = |
|
|
|
!c.alarmSubType || dataAlarmSubType.length ? |
|
|
|
await clickHouse.dataAlarm.query(` |
|
|
|
SELECT * FROM alarms |
|
|
|
LEFT JOIN ${anxinyun}.t_sensor |
|
|
|
AS anxinStation |
|
|
|
ON toString(anxinStation.id) = alarms.SourceId |
|
|
|
AND alarms.SourceTypeId = 2 |
|
|
|
WHERE |
|
|
|
${`State NOT IN (3, 4) AND `} |
|
|
|
StructureId IN (${searchStrucIds.join(',')}) |
|
|
|
${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''} |
|
|
|
ORDER BY StartTime DESC |
|
|
|
`).toPromise()
|
|
|
|
console.log(dataAlarms); |
|
|
|
`).toPromise() : []
|
|
|
|
} |
|
|
|
|
|
|
|
let dataAlarmTitle = [{ |
|
|
@ -397,7 +482,8 @@ module.exports = function (app, opts) { |
|
|
|
n: '结构物', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name |
|
|
|
return (strucMap[d.StructureId] || { name: '' }).name |
|
|
|
// return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name
|
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '告警源名称', |
|
|
@ -470,13 +556,13 @@ module.exports = function (app, opts) { |
|
|
|
n: '测点', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.station.map(ds => ds.name).join('、') |
|
|
|
return d.station ? d.station.map(ds => ds.name).join('、') : '' |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '位置', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.station.map(ds => ds.position).join('、') |
|
|
|
return d.station ? d.station.map(ds => ds.position).join('、') : '' |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '告警信息', |
|
|
@ -542,15 +628,15 @@ module.exports = function (app, opts) { |
|
|
|
tableTitle += '</tr>' |
|
|
|
return tableTitle |
|
|
|
} |
|
|
|
function packageTableData (data, titleArr) { |
|
|
|
function packageTableData ({ data, titleArr }) { |
|
|
|
let tableData = '<tr>' |
|
|
|
for (let t of titleArr) { |
|
|
|
if (t.v) { |
|
|
|
tableData += `<td>${t.v}</td>` |
|
|
|
tableData += `<td>${t.v || ''}</td>` |
|
|
|
} else if (t.f) { |
|
|
|
tableData += `<td>${t.f(data)}</td>` |
|
|
|
tableData += `<td>${t.f(data) || ''}</td>` |
|
|
|
} else if (t.k) { |
|
|
|
tableData += `<td>${data[t.k]}</td>` |
|
|
|
tableData += `<td>${data[t.k] || ''}</td>` |
|
|
|
} else { |
|
|
|
tableData += `<td></td>` |
|
|
|
} |
|
|
@ -559,33 +645,13 @@ module.exports = function (app, opts) { |
|
|
|
return tableData |
|
|
|
} |
|
|
|
|
|
|
|
function packageAlarmData2Table (titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime') { |
|
|
|
if (!alarmData.length) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
ifEmailSend = true |
|
|
|
let tableTitlePrefix = titlePrefix + '告警源' |
|
|
|
let newAddCount = 0 |
|
|
|
let alarmHtml = '<table border="1" cellspacing="0" style="border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;">' |
|
|
|
let alarmContent = '' |
|
|
|
let alarmHtmlTitle = packageTableTitle(alarmTitleArr) |
|
|
|
|
|
|
|
for (let a of alarmData) { |
|
|
|
alarmContent += packageTableData(a, alarmTitleArr) |
|
|
|
if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) { |
|
|
|
newAddCount++ |
|
|
|
} |
|
|
|
} |
|
|
|
tableTitlePrefix += |
|
|
|
c.tactics == 'abnormal_rate' ? |
|
|
|
`${alarmData.length}个` : |
|
|
|
`新增${newAddCount}个,未解决累计${alarmData.length}个` + tableTitlePostfix |
|
|
|
alarmHtml += `<tr><td colspan="${alarmTitleArr.length}" style="background-color:#ffff00">` + tableTitlePrefix + '</td></tr>' |
|
|
|
alarmHtml += alarmHtmlTitle |
|
|
|
alarmHtml += alarmContent |
|
|
|
alarmHtml += '</table><br/>' |
|
|
|
|
|
|
|
return alarmHtml |
|
|
|
let apMergeDeVeAnxinProjectId_ = apMergeDeVeAnxinProjectId ? |
|
|
|
apMergeDeVeAnxinProjectId.split(',') : []; |
|
|
|
let apMergeDeVeAlarms = { |
|
|
|
// 结构物id :{
|
|
|
|
// data_exception 数据异常告警:[],
|
|
|
|
// video_exception 视频异常告警:[]
|
|
|
|
// }
|
|
|
|
} |
|
|
|
|
|
|
|
let dataAlarmG1 = []; |
|
|
@ -593,12 +659,114 @@ module.exports = function (app, opts) { |
|
|
|
let dataAlarmG3 = []; |
|
|
|
let dataAlarmG45 = []; |
|
|
|
let deviceStatistic = new Set() |
|
|
|
let dataAlarmDetails = [] |
|
|
|
if (proDebug) { |
|
|
|
console.log(`查得数据告警 ${dataAlarms.length} 条`); |
|
|
|
console.log(dataAlarms); |
|
|
|
} |
|
|
|
if (dataAlarms.length) { |
|
|
|
const alarmIds = dataAlarms |
|
|
|
.map(ar => "'" + ar.AlarmId + "'") |
|
|
|
dataAlarmDetails = |
|
|
|
await clickHouse.dataAlarm.query(` |
|
|
|
SELECT * FROM alarm_details |
|
|
|
WHERE AlarmId IN (${alarmIds.join(',')}, '-1') |
|
|
|
AND AlarmState = 0 |
|
|
|
`).toPromise()
|
|
|
|
} |
|
|
|
if (proDebug) { |
|
|
|
console.log(`查得数据告警详情数据 ${dataAlarmDetails.length} 条`); |
|
|
|
} |
|
|
|
if (proDebug) { |
|
|
|
console.log(`pomsStrucFactorId:`, pomsStrucFactorId); |
|
|
|
} |
|
|
|
|
|
|
|
let deviceIds = new Set() |
|
|
|
for (let d of dataAlarms) { |
|
|
|
d = { ...d, stationId: d.id } |
|
|
|
|
|
|
|
/** 按监测因素筛选 且为测点告警 */ |
|
|
|
// if (pomsStrucFactorId && d.SourceTypeId == 2) {
|
|
|
|
// // 做了监测因素筛选 且当前告警有监测因素
|
|
|
|
// if (!d.factor || d.factor == 0) {
|
|
|
|
// // 监测因素不对劲
|
|
|
|
// continue
|
|
|
|
// } else if (!d.StructureId) {
|
|
|
|
// // 当前告警没有绑定结构物
|
|
|
|
// continue
|
|
|
|
// } else if (!pomsStrucFactorId[d.StructureId]) {
|
|
|
|
// // 推送配置没配置这个结构物
|
|
|
|
// continue
|
|
|
|
// } else if (!pomsStrucFactorId[d.StructureId].includes(d.factor)) {
|
|
|
|
// // 不包含这个监测因素
|
|
|
|
// continue
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
if (d.AlarmGroup == 1) { |
|
|
|
dataAlarmG1.push(d) |
|
|
|
} else if (d.AlarmGroup == 2) { |
|
|
|
dataAlarmG2.push(d) |
|
|
|
/** 注1 |
|
|
|
* 根据指定的安心云结构物把数据异常和视频告警合并在一起的代码 |
|
|
|
* 还要指定是扬尘设备 |
|
|
|
* 以测点关联 |
|
|
|
*/ |
|
|
|
if ( |
|
|
|
isDev || |
|
|
|
( |
|
|
|
apMergeDeVeAnxinProjectId_.length && |
|
|
|
d.SourceName && d.SourceName.includes('扬尘') && |
|
|
|
apMergeDeVeAnxinProjectId_.some(pid => pid == (strucMap[d.StructureId] || {}).projectId) |
|
|
|
) |
|
|
|
) { |
|
|
|
// SourceTypeId 0: 'DTU' / 1: '传感器' / 2: '测点'
|
|
|
|
|
|
|
|
if (d.SourceTypeId != 2) { |
|
|
|
deviceIds.add(d.SourceId) |
|
|
|
} |
|
|
|
if (apMergeDeVeAlarms[d.StructureId]) { |
|
|
|
apMergeDeVeAlarms[d.StructureId].data_exception.push(d) |
|
|
|
} else { |
|
|
|
apMergeDeVeAlarms[d.StructureId] = { |
|
|
|
data_exception: [d], |
|
|
|
video_exception: [] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// 注1 END
|
|
|
|
|
|
|
|
} else if (d.AlarmGroup == 3) { |
|
|
|
|
|
|
|
/** 按监测因项 factor-item 的名称筛选*/ |
|
|
|
if (pomsStrucFactorId) { |
|
|
|
// 兼容之前的设置 有这个信息才进行判断
|
|
|
|
if (!d.StructureId) { |
|
|
|
// 当前告警没有绑定结构物
|
|
|
|
continue |
|
|
|
} else if (!pomsStrucFactorId[d.StructureId]) { |
|
|
|
// 推送配置没配置这个结构物
|
|
|
|
continue |
|
|
|
} else { |
|
|
|
let corDetail = dataAlarmDetails.find(da => da.AlarmId == d.AlarmId) |
|
|
|
if (proDebug) { |
|
|
|
console.log(`相应告警详情(策略命中):`, corDetail); |
|
|
|
} |
|
|
|
if (corDetail) { |
|
|
|
// 判断告警详情信息里有没有监测项关键字
|
|
|
|
if (!pomsStrucFactorId[d.StructureId].some(factorItemName => { |
|
|
|
return corDetail.Content.includes(factorItemName) |
|
|
|
})) { |
|
|
|
continue |
|
|
|
} |
|
|
|
} else { |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (proDebug) { |
|
|
|
console.log(`符合条件的策略命中 + 1`, d); |
|
|
|
} |
|
|
|
dataAlarmG3.push(d) |
|
|
|
} else if (d.AlarmGroup == 4 || d.AlarmGroup == 5) { |
|
|
|
dataAlarmG45.push(d) |
|
|
@ -609,11 +777,23 @@ module.exports = function (app, opts) { |
|
|
|
let rate = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))); |
|
|
|
|
|
|
|
if (rate < parseFloat(deviceProportion)) { |
|
|
|
// 设备异常率低于设定值
|
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
emailSubTitle = emailSubTitle.replace('--%', rate.toFixed(1) + '%') |
|
|
|
} |
|
|
|
// 注1
|
|
|
|
if (apMergeDeVeAnxinProjectId_.length || isDev) { |
|
|
|
for (let a of videoAlarms) { |
|
|
|
let existStruc = a.struc.find(asc => apMergeDeVeAlarms[asc.id] || isDev) |
|
|
|
if (existStruc) { |
|
|
|
apMergeDeVeAlarms[existStruc.id].video_exception.push(a) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 注1 END
|
|
|
|
let html = ` |
|
|
|
<html> |
|
|
|
<head> |
|
|
@ -633,49 +813,151 @@ module.exports = function (app, opts) { |
|
|
|
</html> |
|
|
|
<h5 style="margin-bottom:12px">${emailSubTitle}</h5> |
|
|
|
` |
|
|
|
|
|
|
|
function packageAlarmData2Table ({ |
|
|
|
titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime' |
|
|
|
}) { |
|
|
|
if (!alarmData.length) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
ifEmailSend = true |
|
|
|
let tableTitlePrefix = titlePrefix + '告警源' |
|
|
|
let newAddCount = 0 |
|
|
|
let alarmHtml = '<table border="1" cellspacing="0" style="border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;">' |
|
|
|
let alarmContent = '' |
|
|
|
let alarmHtmlTitle = packageTableTitle(alarmTitleArr) |
|
|
|
|
|
|
|
for (let [index, a] of alarmData.entries()) { |
|
|
|
alarmContent += packageTableData({ data: a, titleArr: alarmTitleArr }) |
|
|
|
|
|
|
|
if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) { |
|
|
|
newAddCount++ |
|
|
|
} |
|
|
|
} |
|
|
|
tableTitlePrefix += |
|
|
|
titlePrefix != '数据异常&视频异常' ? |
|
|
|
c.tactics == 'abnormal_rate' ? |
|
|
|
`${alarmData.length}个` : |
|
|
|
`新增${newAddCount}个,未解决累计${alarmData.length}个` + tableTitlePostfix |
|
|
|
: '' |
|
|
|
alarmHtml += `<tr><td colspan="${alarmTitleArr.length}" style="background-color:#ffff00">` + tableTitlePrefix + '</td></tr>' |
|
|
|
alarmHtml += alarmHtmlTitle |
|
|
|
alarmHtml += alarmContent |
|
|
|
alarmHtml += '</table><br/>' |
|
|
|
|
|
|
|
return alarmHtml |
|
|
|
} |
|
|
|
if (c.alarmType.includes('data_outages')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'数据中断', |
|
|
|
dataAlarmG1, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '数据中断', |
|
|
|
alarmData: dataAlarmG1, |
|
|
|
alarmTitleArr: dataAlarmTitle, |
|
|
|
}) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('data_exception')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'数据异常', |
|
|
|
dataAlarmG2, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '数据异常', |
|
|
|
alarmData: dataAlarmG2, |
|
|
|
alarmTitleArr: dataAlarmTitle, |
|
|
|
}) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('strategy_hit')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'策略命中', |
|
|
|
dataAlarmG3, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '策略命中', |
|
|
|
alarmData: dataAlarmG3, |
|
|
|
alarmTitleArr: dataAlarmTitle, |
|
|
|
}) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('video_exception')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'视频异常', |
|
|
|
videoAlarms, |
|
|
|
videoAlarmTitle, |
|
|
|
'createTime', |
|
|
|
) |
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '视频异常', |
|
|
|
alarmData: videoAlarms, |
|
|
|
alarmTitleArr: videoAlarmTitle, |
|
|
|
keyOfStartTime: 'createTime', |
|
|
|
}) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('app_exception')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'应用异常', |
|
|
|
appAlarms, |
|
|
|
appAlarmTitle, |
|
|
|
'createTime', |
|
|
|
) |
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '应用异常', |
|
|
|
alarmData: appAlarms, |
|
|
|
alarmTitleArr: appAlarmTitle, |
|
|
|
keyOfStartTime: 'createTime', |
|
|
|
}) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('device_exception')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'设备异常', |
|
|
|
dataAlarmG45, |
|
|
|
dataAlarmTitle, |
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '设备异常', |
|
|
|
alarmData: dataAlarmG45, |
|
|
|
alarmTitleArr: dataAlarmTitle, |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
if (Object.keys(apMergeDeVeAlarms).length) { |
|
|
|
if (proDebug) { |
|
|
|
console.log(`查得数据异常、视频异常合并の告警:`); |
|
|
|
console.log(apMergeDeVeAlarms); |
|
|
|
} |
|
|
|
let deviceSensorRes = [] |
|
|
|
if (deviceIds.size) { |
|
|
|
const device4Search = [...deviceIds] |
|
|
|
.map(id => "'" + id + "'") |
|
|
|
deviceSensorRes = await clickHouse.anxinyun.query(` |
|
|
|
SELECT |
|
|
|
iota_device_id, sensor |
|
|
|
FROM t_device_sensor |
|
|
|
WHERE iota_device_id |
|
|
|
${device4Search.length > 1 ? `IN (${device4Search.join(',')})` : `= ${device4Search[0]}`} |
|
|
|
|
|
|
|
`).toPromise()
|
|
|
|
} |
|
|
|
if (proDebug) { |
|
|
|
console.log(`相关设备及测点信息:`); |
|
|
|
console.log(deviceSensorRes); |
|
|
|
} |
|
|
|
let alarmTitle = dataAlarmTitle.concat( |
|
|
|
videoAlarmTitle.slice(2).map(v => { |
|
|
|
return { |
|
|
|
...v, |
|
|
|
n: |
|
|
|
v.n == '持续时间' ? |
|
|
|
'摄像头告警' + v.n : |
|
|
|
'摄像头' + v.n |
|
|
|
} |
|
|
|
}) |
|
|
|
) |
|
|
|
let alarmData = [] |
|
|
|
for (let aKey in apMergeDeVeAlarms) { |
|
|
|
let curStrucAlarm = apMergeDeVeAlarms[aKey] |
|
|
|
|
|
|
|
for (let de of curStrucAlarm.data_exception) { |
|
|
|
if (!de.id) { |
|
|
|
let corSensor = deviceSensorRes.find(ds => ds.iota_device_id == de.SourceId) |
|
|
|
if (corSensor) { |
|
|
|
de.id = corSensor.sensor |
|
|
|
} |
|
|
|
} |
|
|
|
let corVideoException = curStrucAlarm.video_exception.filter(v => { |
|
|
|
// ! de.id 是告警信息关联查出来的测点的id
|
|
|
|
return v.station.some(vs => vs.id == de.id) |
|
|
|
}) |
|
|
|
if (!corVideoException.length) { |
|
|
|
// 构造一个长度 以便在for循环里把 data_exception 放进去
|
|
|
|
corVideoException.push({}) |
|
|
|
} |
|
|
|
for (let ve of corVideoException) { |
|
|
|
alarmData.push({ |
|
|
|
...de, |
|
|
|
...ve |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
html += packageAlarmData2Table({ |
|
|
|
titlePrefix: '数据异常&视频异常', |
|
|
|
alarmData: alarmData, |
|
|
|
alarmTitleArr: alarmTitle, |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
if (ifEmailSend) { |
|
|
@ -687,13 +969,19 @@ module.exports = function (app, opts) { |
|
|
|
`).toPromise()
|
|
|
|
: [] |
|
|
|
let receiverId = [] |
|
|
|
const emails = receiverRes.reduce((arr, r) => { |
|
|
|
let emails = receiverRes.reduce((arr, r) => { |
|
|
|
if (r.email) { |
|
|
|
arr.push(r.email) |
|
|
|
receiverId.push({ id: r.id, name: r.name }) |
|
|
|
} |
|
|
|
return arr |
|
|
|
}, []) |
|
|
|
|
|
|
|
if (isDev) { |
|
|
|
// !开发测试用的数据
|
|
|
|
emails = ['1650192445@qq.com'] |
|
|
|
} |
|
|
|
|
|
|
|
if (emails.length) { |
|
|
|
await pushByEmail({ |
|
|
|
email: emails, |
|
|
@ -717,12 +1005,11 @@ module.exports = function (app, opts) { |
|
|
|
let dynamic = { |
|
|
|
time: r.dataValues.time, |
|
|
|
emailSendId: r.dataValues.id, |
|
|
|
projectCorrelationId: r.dataValues.projectCorrelationId, |
|
|
|
// projectCorrelationId: r.dataValues.projectCorrelationId,
|
|
|
|
type: 2//通知
|
|
|
|
} |
|
|
|
await models.LatestDynamicList.create(dynamic); |
|
|
|
|
|
|
|
|
|
|
|
//消息推送到前端
|
|
|
|
await sendNoticeToWeb(receiverId, dataToSave); |
|
|
|
} |
|
|
@ -730,7 +1017,6 @@ module.exports = function (app, opts) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error(error); |
|
|
|