|
|
@ -5,7 +5,7 @@ module.exports = function (app, opts) { |
|
|
|
{ |
|
|
|
interval: '12 */1 * * * *', |
|
|
|
immediate: true, // dev
|
|
|
|
proRun: true, |
|
|
|
// proRun: true,
|
|
|
|
}, |
|
|
|
async () => { |
|
|
|
try { |
|
|
@ -18,13 +18,14 @@ module.exports = function (app, opts) { |
|
|
|
where: { |
|
|
|
del: false |
|
|
|
}, |
|
|
|
order: ['id'], |
|
|
|
include: [{ |
|
|
|
model: models.ProjectCorrelation, |
|
|
|
where: { |
|
|
|
del: false |
|
|
|
}, |
|
|
|
required: true |
|
|
|
}] |
|
|
|
}], |
|
|
|
}) |
|
|
|
let pepProjectIds = new Set() |
|
|
|
for (let { dataValues: c } of configListRes) { |
|
|
@ -112,6 +113,9 @@ module.exports = function (app, opts) { |
|
|
|
).toPromise() : |
|
|
|
[] |
|
|
|
let searchStrucIds = strucListRes.map(s => s.id) |
|
|
|
|
|
|
|
searchStrucIds = searchStrucIds.concat([991, 1052, 700]) |
|
|
|
|
|
|
|
if (searchStrucIds.length) { |
|
|
|
searchStrucIds.unshift(-1) |
|
|
|
} |
|
|
@ -139,42 +143,58 @@ module.exports = function (app, opts) { |
|
|
|
let alarmCameraCount = 0 |
|
|
|
// 判断推送策略
|
|
|
|
let nowTime = moment().startOf('minute') |
|
|
|
let pointTime = moment().subtract(parseInt(interval), 'minute').startOf('minute').format('YYYY-MM-DD HH:mm:ss') |
|
|
|
let pointTime = |
|
|
|
moment() |
|
|
|
.subtract( |
|
|
|
parseInt(interval), |
|
|
|
// + 1440 * 365,
|
|
|
|
'minute' |
|
|
|
) |
|
|
|
.startOf('minute') |
|
|
|
.format('YYYY-MM-DD HH:mm:ss') |
|
|
|
|
|
|
|
let newAddStartTime = pointTime |
|
|
|
let newAddEndTime = nowTime.clone() |
|
|
|
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 |
|
|
|
} else if (c.tactics == 'continue' || c.tactics == 'abnormal_rate') { |
|
|
|
// 新增的应该是上一个时间节点到上上个节点之间
|
|
|
|
dataAlarmOption.push(`StartTime <= '${pointTime}'`); |
|
|
|
appAlarmWhereOption.createTime = { $lte: pointTime } |
|
|
|
videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`) |
|
|
|
// 新增的应该是上一个时间节点到上上个节点之间
|
|
|
|
newAddStartTime = moment(pointTime).subtract(parseInt(interval)).format('YYYY-MM-DD HH:mm:ss') |
|
|
|
newAddEndTime = pointTime |
|
|
|
|
|
|
|
if (c.tactics == 'continue') { |
|
|
|
emailTitle += `持续时长推送服务` |
|
|
|
emailSubTitle += `告警持续时长超${interval}分钟的告警源,详情如下:` |
|
|
|
} else { |
|
|
|
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 |
|
|
|
} |
|
|
|
emailTitle += `异常率推送服务` |
|
|
|
emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${parseInt(deviceCount) + parseInt(cameraCount)}个的 --%,详情如下` |
|
|
|
} |
|
|
|
// appAlarmWhereOption.createTime = { $lte: pointTime }
|
|
|
|
// videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`)
|
|
|
|
emailTitle += `异常率推送服务` |
|
|
|
emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${deviceCount + cameraCount}个的${interval}%,详情如下` |
|
|
|
} |
|
|
|
emailTitle += '——POMS飞尚运维中台' |
|
|
|
|
|
|
@ -202,6 +222,7 @@ module.exports = function (app, opts) { |
|
|
|
cameraAlarm.confirmTime AS confirmTime, |
|
|
|
cameraAlarm.autoRestore AS autoRestore, |
|
|
|
camera_status.describe AS statusDescribe, |
|
|
|
camera_kind.kind AS cameraKind, |
|
|
|
"gbCamera".online AS cameraOnline, |
|
|
|
anxinIpc.t_video_ipc.name AS anxinIpcPosition, |
|
|
|
anxinStation.id AS anxinStationId, |
|
|
@ -213,6 +234,7 @@ module.exports = function (app, opts) { |
|
|
|
camera.id AS cameraId, |
|
|
|
camera.gb_id AS gbId, |
|
|
|
camera.name AS cameraName, |
|
|
|
camera.kind_id AS cameraKindId, |
|
|
|
camera_status_alarm.id AS alarmId, |
|
|
|
camera_status_alarm.create_time AS createTime, |
|
|
|
camera_status_alarm.status_id AS statusId, |
|
|
@ -240,7 +262,8 @@ module.exports = function (app, opts) { |
|
|
|
AND cameraAlarm.statusId = camera_status.id |
|
|
|
LEFT JOIN "gbCamera" |
|
|
|
ON "gbCamera".id = cameraAlarm.gbId |
|
|
|
|
|
|
|
LEFT JOIN camera_kind |
|
|
|
ON camera_kind.id = cameraAlarm.cameraKindId |
|
|
|
LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc |
|
|
|
ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo |
|
|
|
AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo |
|
|
@ -251,6 +274,7 @@ module.exports = function (app, opts) { |
|
|
|
ON anxinIpcStation.ipc = anxinIpc.id |
|
|
|
LEFT JOIN ${anxinyun}.t_sensor AS anxinStation |
|
|
|
ON anxinStation.id = anxinIpcStation.station |
|
|
|
ORDER BY cameraAlarm.createTime DESC |
|
|
|
` |
|
|
|
).toPromise() : [] |
|
|
|
|
|
|
@ -290,6 +314,7 @@ module.exports = function (app, opts) { |
|
|
|
alarmId: a.alarmId, |
|
|
|
confirmContent: a.confirmContent, |
|
|
|
confirmTime: a.confirmTime, |
|
|
|
cameraKind: a.cameraKind, |
|
|
|
struc: [], |
|
|
|
station: [] |
|
|
|
} |
|
|
@ -315,10 +340,12 @@ module.exports = function (app, opts) { |
|
|
|
} |
|
|
|
} |
|
|
|
let p = 1 |
|
|
|
videoAlarms = returnD |
|
|
|
} |
|
|
|
if (c.alarmType.includes('app_exception')) { |
|
|
|
appAlarms = await models.AppAlarm.findAll({ |
|
|
|
where: appAlarmWhereOption, |
|
|
|
order: [['createTime', 'DESC']], |
|
|
|
include: [{ |
|
|
|
model: models.App, |
|
|
|
include: [{ |
|
|
@ -329,7 +356,6 @@ module.exports = function (app, opts) { |
|
|
|
}] |
|
|
|
}] |
|
|
|
}) |
|
|
|
let a = 2 |
|
|
|
} |
|
|
|
if (c.alarmType.includes('device_exception')) { |
|
|
|
dataAlarmGroupOption.push(4) |
|
|
@ -343,9 +369,10 @@ module.exports = function (app, opts) { |
|
|
|
dataAlarms = await clickHouse.dataAlarm.query(` |
|
|
|
SELECT * FROM alarms |
|
|
|
WHERE |
|
|
|
State NOT IN (3, 4) |
|
|
|
AND StructureId IN (${searchStrucIds.join(',')}) |
|
|
|
${/*`'State NOT IN (3, 4) AND '`*/''} |
|
|
|
StructureId IN (${searchStrucIds.join(',')}) |
|
|
|
${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''} |
|
|
|
ORDER BY StartTime DESC |
|
|
|
`).toPromise()
|
|
|
|
console.log(dataAlarms); |
|
|
|
} |
|
|
@ -358,7 +385,7 @@ module.exports = function (app, opts) { |
|
|
|
n: '结构物', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return (strucListRes.find(s => s.id == d.StructureId) || {}).name |
|
|
|
return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '告警源名称', |
|
|
@ -405,49 +432,258 @@ module.exports = function (app, opts) { |
|
|
|
} |
|
|
|
},] |
|
|
|
|
|
|
|
let html = '' |
|
|
|
let videoAlarmTitle = [{ |
|
|
|
n: '项目', |
|
|
|
k: '', |
|
|
|
v: pepProjectName |
|
|
|
}, { |
|
|
|
n: '结构物', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.struc.map(ds => ds.name).join('、') |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '告警源名称', |
|
|
|
k: 'cameraName' |
|
|
|
}, { |
|
|
|
n: '告警源类型', |
|
|
|
k: 'cameraKind' |
|
|
|
}, { |
|
|
|
n: '序列号', |
|
|
|
k: 'cameraSerialNo' |
|
|
|
}, { |
|
|
|
n: '通道号', |
|
|
|
k: 'cameraChannelNo' |
|
|
|
}, { |
|
|
|
n: '测点', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.station.map(ds => ds.name).join('、') |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '位置', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.station.map(ds => ds.position).join('、') |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '告警信息', |
|
|
|
k: 'statusDescribe' |
|
|
|
}, { |
|
|
|
n: '持续时间', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.createTime ? |
|
|
|
'超过' + moment().diff(moment(d.createTime), 'minutes') + '分钟' : '' |
|
|
|
} |
|
|
|
},] |
|
|
|
|
|
|
|
let appAlarmTitle = [{ |
|
|
|
n: '项目', |
|
|
|
k: '', |
|
|
|
v: pepProjectName |
|
|
|
}, { |
|
|
|
n: '应用名称', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.app ? d.app.name : '' |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '异常类型', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
if (d.type == 'element') { |
|
|
|
return '元素异常' |
|
|
|
} else if (d.type == 'apiError') { |
|
|
|
return '接口报错' |
|
|
|
} else { |
|
|
|
return '' |
|
|
|
} |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '异常信息', |
|
|
|
k: 'alarmContent' |
|
|
|
}, { |
|
|
|
n: 'URL地址', |
|
|
|
k: 'cameraName', |
|
|
|
f: (d) => { |
|
|
|
return d.app && d.app.url ? `<a href="${d.app.url}">${d.app.url}</a>` : '' |
|
|
|
} |
|
|
|
}, { |
|
|
|
n: '持续时间', |
|
|
|
k: '', |
|
|
|
f: (d) => { |
|
|
|
return d.createTime ? |
|
|
|
'超过' + moment().diff(moment(d.createTime), 'minutes') + '分钟' : '' |
|
|
|
} |
|
|
|
},] |
|
|
|
|
|
|
|
|
|
|
|
let ifEmailSend = false |
|
|
|
let tableTitlePostfix = ',详情如下:' |
|
|
|
function packageTableTitle (tArr) { |
|
|
|
|
|
|
|
function packageTableTitle (titleArr) { |
|
|
|
let tableTitle = '<tr>' |
|
|
|
for (let t of titleArr) { |
|
|
|
tableTitle += `<th style="background-color:#8faadc; color:#fff; text-align:left">${t.n}</th>` |
|
|
|
} |
|
|
|
tableTitle += '</tr>' |
|
|
|
return tableTitle |
|
|
|
} |
|
|
|
if (c.alarmType.includes('data_outages')) { |
|
|
|
let tableTitlePrefix = '数据中断告警源' |
|
|
|
function packageTableData (data, titleArr) { |
|
|
|
let tableData = '<tr>' |
|
|
|
for (let t of titleArr) { |
|
|
|
if (t.v) { |
|
|
|
tableData += `<td>${t.v}</td>` |
|
|
|
} else if (t.f) { |
|
|
|
tableData += `<td>${t.f(data)}</td>` |
|
|
|
} else if (t.k) { |
|
|
|
tableData += `<td>${data[t.k]}</td>` |
|
|
|
} else { |
|
|
|
tableData += `<td></td>` |
|
|
|
} |
|
|
|
} |
|
|
|
tableData += '</tr>' |
|
|
|
return tableData |
|
|
|
} |
|
|
|
|
|
|
|
function packageAlarmData2Table (titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime') { |
|
|
|
if (!alarmData.length) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
ifEmailSend = true |
|
|
|
let tableTitlePrefix = titlePrefix + '告警源' |
|
|
|
let newAddCount = 0 |
|
|
|
let alarmHtml = '' |
|
|
|
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) |
|
|
|
|
|
|
|
if (c.tactics == 'immediately') { |
|
|
|
for (let a of dataAlarms.filter(d => d.AlarmGroup == 1)) { |
|
|
|
alarmHtml += 1 |
|
|
|
for (let a of alarmData) { |
|
|
|
alarmContent += packageTableData(a, alarmTitleArr) |
|
|
|
if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) { |
|
|
|
newAddCount++ |
|
|
|
} |
|
|
|
tableTitlePrefix += 1 |
|
|
|
} else if (c.tactics == 'continue') { |
|
|
|
} |
|
|
|
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/>' |
|
|
|
|
|
|
|
} else if (c.tactics == 'abnormal_rate') { |
|
|
|
return alarmHtml |
|
|
|
} |
|
|
|
|
|
|
|
let dataAlarmG1 = []; |
|
|
|
let dataAlarmG2 = []; |
|
|
|
let dataAlarmG3 = []; |
|
|
|
let dataAlarmG45 = []; |
|
|
|
let deviceStatistic = new Set() |
|
|
|
for (let d of dataAlarms) { |
|
|
|
if (d.AlarmGroup == 1) { |
|
|
|
dataAlarmG1.push(d) |
|
|
|
} else if (d.AlarmGroup == 2) { |
|
|
|
dataAlarmG2.push(d) |
|
|
|
} else if (d.AlarmGroup == 3) { |
|
|
|
dataAlarmG3.push(d) |
|
|
|
} else if (d.AlarmGroup == 4 || d.AlarmGroup == 5) { |
|
|
|
dataAlarmG45.push(d) |
|
|
|
} |
|
|
|
deviceStatistic.add(d.SourceId) |
|
|
|
} |
|
|
|
if (c.tactics == 'abnormal_rate') { |
|
|
|
let a = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))).toFixed(1) + '%' |
|
|
|
emailSubTitle = emailSubTitle.replace('--%', ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))).toFixed(1) + '%') |
|
|
|
} |
|
|
|
let html = ` |
|
|
|
<html> |
|
|
|
<head> |
|
|
|
<meta charset="utf-8"> |
|
|
|
<style> |
|
|
|
table { |
|
|
|
border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888; |
|
|
|
} |
|
|
|
th,td{ |
|
|
|
border-right:1px solid #888;border-bottom:1px solid #888;padding:5px 15px; |
|
|
|
} |
|
|
|
th{ |
|
|
|
font-weight:bold; |
|
|
|
} |
|
|
|
</style> |
|
|
|
</head> |
|
|
|
</html> |
|
|
|
<h5 style="margin-bottom:12px">${emailSubTitle}</h5> |
|
|
|
` |
|
|
|
if (c.alarmType.includes('data_outages')) { |
|
|
|
html += packageAlarmData2Table( |
|
|
|
'数据中断', |
|
|
|
dataAlarmG1, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('data_exception')) { |
|
|
|
|
|
|
|
html += packageAlarmData2Table( |
|
|
|
'数据异常', |
|
|
|
dataAlarmG2, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('strategy_hit')) { |
|
|
|
|
|
|
|
html += packageAlarmData2Table( |
|
|
|
'策略命中', |
|
|
|
dataAlarmG3, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('video_exception')) { |
|
|
|
|
|
|
|
html += packageAlarmData2Table( |
|
|
|
'视频异常', |
|
|
|
videoAlarms, |
|
|
|
videoAlarmTitle, |
|
|
|
'createTime', |
|
|
|
) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('app_exception')) { |
|
|
|
|
|
|
|
html += packageAlarmData2Table( |
|
|
|
'应用异常', |
|
|
|
appAlarms, |
|
|
|
appAlarmTitle, |
|
|
|
'createTime', |
|
|
|
) |
|
|
|
} |
|
|
|
if (c.alarmType.includes('device_exception')) { |
|
|
|
|
|
|
|
html += packageAlarmData2Table( |
|
|
|
'设备异常', |
|
|
|
dataAlarmG45, |
|
|
|
dataAlarmTitle, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
await pushByEmail({ |
|
|
|
email: ['1650192445@qq.com'], |
|
|
|
title: emailTitle, |
|
|
|
text: '', |
|
|
|
html: '' |
|
|
|
}) |
|
|
|
if (ifEmailSend) { |
|
|
|
// 查接收人的信息
|
|
|
|
const receiverRes = |
|
|
|
c.receiverPepUserId.length ? |
|
|
|
await clickHouse.pepEmis.query(` |
|
|
|
SELECT email FROM user WHERE id IN (${c.receiverPepUserId.join(',')},-1) |
|
|
|
`).toPromise()
|
|
|
|
: [] |
|
|
|
const emails = receiverRes.reduce((arr, r) => { |
|
|
|
if (r.email) { |
|
|
|
arr.push(r.email) |
|
|
|
} |
|
|
|
return arr |
|
|
|
}, []) |
|
|
|
if (emails.length || 1) { |
|
|
|
await pushByEmail({ |
|
|
|
email: ['1650192445@qq.com', '777y'], |
|
|
|
title: emailTitle, |
|
|
|
text: '', |
|
|
|
html: html |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|