Browse Source

告警推送定制

dev
巴林闲侠 2 years ago
parent
commit
4f45beeac9
  1. 11
      api/.vscode/launch.json
  2. 233
      api/app/lib/schedule/alarms_push.js
  3. 5
      api/config.js

11
api/.vscode/launch.json

@ -16,13 +16,14 @@
"-p 4600",
"-f http://localhost:4600",
//
"-g postgres://postgres:123@10.8.30.32:5432/orational_service",
// "-g postgres://postgres:123@10.8.30.32:5432/orational_service",
//
// "-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS",
"-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS",
"-k 10.8.30.72:29092,10.8.30.73:29092,10.8.30.74:29092",
"--iotaProxy http://10.8.30.157:17007",
"--redisHost 10.8.30.112",
"--redisPort 6379",
"--apMergeDeVeAnxinProjectId 1,2,3",
"--axyApiUrl http://127.0.0.1:4100",
// "--apiEmisUrl http://10.8.30.112:14000",
//
@ -43,11 +44,9 @@
"--clickHouseUrl http://10.8.30.161",
// "--clickHouseUrl https://clickhouse01.anxinyun.cn/play",
"--clickHousePort 30123",
// * 2
// "--clickHouseUser ",
// "--clickHousePassword ",
//
// "--clickHouseAnxincloud anxinyun",
// "--clickHousePepEmis pepca",
@ -55,15 +54,13 @@
// "--clickHouseVcmp video_accrss1",
// "--clickHouseDataAlarm default",
// "--clickHouseIot iot",
//
"--clickHouseAnxincloud anxinyun1",
"--clickHousePepEmis pepca8",
"--clickHouseProjectManage peppm8",
"--clickHouseProjectManage peppm",
"--clickHouseVcmp video_access_dev",
"--clickHouseDataAlarm default",
"--clickHouseIot iot",
"--confirmAlarmAnxinUserId 1",
"--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
"--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",

233
api/app/lib/schedule/alarms_push.js

@ -4,7 +4,7 @@ let isDev = false
// isDev = true
let proDebug = false
proDebug = true
// proDebug = true
module.exports = function (app, opts) {
const alarmsPush = app.fs.scheduleInit(
@ -16,6 +16,7 @@ module.exports = function (app, opts) {
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
@ -73,7 +74,7 @@ module.exports = function (app, opts) {
const { interval, deviceProportion } = c.tacticsParams
if (
curMinOfYear % parseInt(interval) == 0
curMinOfYear % parseInt(interval) == 0 || isDev
) {
const corPomsProject = pomsProjectRes.filter(poms => pomsProjectId.includes(poms.id))
@ -110,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
@ -141,10 +143,14 @@ module.exports = function (app, opts) {
).toPromise() :
[]
let strucThingId = []
let strucMap = {}
let searchStrucIds = strucListRes.map(s => {
if (s.iotaThingId) {
strucThingId.push(s.iotaThingId)
}
strucMap[s.id] = {
...s
}
return s.id
})
@ -476,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: '告警源名称',
@ -549,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: '告警信息',
@ -621,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>`
}
@ -638,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 = [];
@ -693,6 +680,7 @@ module.exports = function (app, opts) {
if (proDebug) {
console.log(`pomsStrucFactorId:`, pomsStrucFactorId);
}
for (let d of dataAlarms) {
/** 按监测因素筛选 且为测点告警 */
@ -717,6 +705,30 @@ module.exports = function (app, opts) {
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)
)
) {
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 的名称筛选*/
@ -764,6 +776,17 @@ module.exports = function (app, opts) {
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>
@ -783,49 +806,123 @@ 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) {
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) {
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) {

5
api/config.js

@ -19,6 +19,8 @@ args.option('redisHost', 'redisHost');
args.option('redisPort', 'redisPort');
args.option('redisPswd', 'redisPassword');
args.option('apMergeDeVeAnxinProjectId', '告警推送自定义の合并数据异常(De)视频异常(Ve)的指定的结构物id');
args.option('axyApiUrl', '安心云 api');
args.option('apiEmisUrl', '企业管理 api');
args.option('apiVcmpUrl', '视频平台 api');
@ -67,6 +69,8 @@ const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redis
const IOTA_REDIS_SERVER_PORT = process.env.IOTA_REDIS_SERVER_PORT || flags.redisPort || "6379";//redis 端口
const IOTA_REDIS_SERVER_PWD = process.env.IOTA_REDIS_SERVER_PWD || flags.redisPswd || "";//redis 密码
const AP_MERGE_DEVE_ANXINPROJECT_ID = process.env.AP_MERGE_DEVE_ANXINPROJECT_ID || flags.apMergeDeVeAnxinProjectId || "";
// 安心云api
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.axyApiUrl;
// 企业管理 api
@ -155,6 +159,7 @@ const product = {
{ p: '/alarm/application/api', o: 'POST' },
{ p: '/alarm/video/added_log', o: 'POST' }
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
apMergeDeVeAnxinProjectId: AP_MERGE_DEVE_ANXINPROJECT_ID,
anxinCloud: {
confirmAlarmAnxinUserId: CONFIRM_ALARM_ANXIN_USER_ID
},

Loading…
Cancel
Save