运维服务中台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1207 lines
59 KiB

2 years ago
const moment = require('moment')
let isDev = false
2 years ago
// let isDev = true
let proDebug = false
proDebug = true
2 years ago
module.exports = function (app, opts) {
const alarmsPush = app.fs.scheduleInit(
{
interval: '12 */1 * * * *',
immediate: isDev,
proRun: !isDev,
2 years ago
},
async () => {
try {
const { models, ORM: sequelize } = app.fs.dc
const { apMergeDeVeAnxinProjectId = '' } = opts
2 years ago
const { clickHouse } = app.fs
2 years ago
const { database: anxinyun } = clickHouse.anxinyun.opts.config
2 years ago
const { database: dataAlarm } = clickHouse.dataAlarm.opts.config
const { pushBySms, pushByEmail, sendNoticeToWeb } = app.fs.utils
2 years ago
const curMinOfYear = moment().diff(moment().startOf('year'), 'minutes')
const configListRes = await models.AlarmPushConfig.findAll({
where: {
del: false,
disable: false
2 years ago
},
order: ['id'],
2 years ago
})
let pomsProjectId = new Set()
2 years ago
let pepProjectIds = new Set()
for (let { dataValues: c } of configListRes) {
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)
2 years ago
}
}
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(',')}, -1)
2 years ago
`
).toPromise() :
[]
const calcMinute2DHM = (minute) => {
if (!minute && minute != 0) {
return minute
}
2 years ago
let title = ''
let dm = 24 * 60
let d = Math.floor(minute / dm)
let h = Math.floor(minute % dm / 60)
let m = (minute % dm) % 60
2 years ago
if (d) {
title = d + '天'
}
if (h) {
title = title + h + '时'
}
if (h) {
title = title + m + '分'
}
return title
}
2 years ago
for (let { dataValues: c } of configListRes) {
if (c.tacticsParams && c.tactics) {
if (proDebug) {
console.log(`当前运行EM配置:id=${c.id} name=${c.name}`);
}
// pomsProjectId 是个数组 []
const { strucId, pomsProjectId, pomsStrucFactorId } = c
2 years ago
const { interval, deviceProportion } = c.tacticsParams
if (
curMinOfYear % parseInt(interval) == 0 || isDev
2 years ago
) {
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 ?
await clickHouse.anxinyun.query(
`
SELECT
DISTINCT id,
t_structure.id AS id,
t_structure.name AS name,
t_structure.iota_thing_id AS iotaThingId,
t_project.id AS projectId
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 strucThingId = []
let strucMap = {}
let searchStrucIds = strucListRes.map(s => {
if (s.iotaThingId) {
strucThingId.push(s.iotaThingId)
}
strucMap[s.id] = {
...s
}
return s.id
})
// !开发测试用的数据
if (isDev) {
2 years ago
// searchStrucIds = searchStrucIds.concat([991, 1052, 700])
searchStrucIds = searchStrucIds.concat([27, 4003, 700])
}
if (searchStrucIds.length) {
searchStrucIds.unshift(-1)
} else {
// 没有结构物可查
continue
}
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 = []
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),
// + 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' || 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')) {
// 查了设备异常率 去安心云查当前项目下的设备数量
let deviceCountRes =
strucThingId.length ?
await clickHouse.iot.query(`
2 years ago
SELECT count(DeviceId) AS count FROM device WHERE ThingId IN (${strucThingId.map(t => `'${t}'`).join(',')}, '-1')
`).toPromise()
: []
deviceCount = deviceCountRes.length ? deviceCountRes[0].count : 0
}
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
2 years ago
}
emailTitle += `异常率推送服务`
emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${parseInt(deviceCount) + parseInt(cameraCount)}个的 --%,详情如下`
2 years ago
}
}
emailTitle += '_POMS飞尚运维中台'
// 判断告警数据范围
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')) {
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(
`
2 years ago
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,
camera_kind.kind AS cameraKind,
2 years ago
"gbCamera".online AS cameraOnline,
anxinIpc.t_video_ipc.name AS anxinIpcPosition,
anxinStation.id AS anxinStationId,
anxinStation.name AS anxinStationName,
anxinStation.factor AS anxinStationFactorId,
2 years ago
anxinStruc.name AS strucName,
anxinStruc.id AS strucId
FROM (
SELECT
camera.id AS cameraId,
camera.gb_id AS gbId,
camera.name AS cameraName,
camera.kind_id AS cameraKindId,
2 years ago
camera_status_alarm.id AS alarmId,
camera_status_alarm.platform AS platform,
2 years ago
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,
2 years ago
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(',')})
2 years ago
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
2 years ago
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 ')}` : ''}
2 years ago
) 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 camera_kind
ON camera_kind.id = cameraAlarm.cameraKindId
2 years ago
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(',')})
2 years ago
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
ORDER BY cameraAlarm.createTime DESC
2 years ago
`
).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 {
/**按监测因素 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,
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,
cameraKind: a.cameraKind,
factorId: a.anxinStationFactorId,
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
2 years ago
}
}
2 years ago
}
videoAlarms = returnD
}
if (c.alarmType.includes('app_exception')) {
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: [{
model: models.App,
required: true,
include: [{
model: models.ProjectApp,
where: {
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(',')})`)
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
2 years ago
WHERE
${`State NOT IN (3, 4) AND `}
StructureId IN (${searchStrucIds.join(',')})
2 years ago
${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''}
ORDER BY StartTime DESC
`).toPromise() : []
}
2 years ago
let dataAlarmTitle = [{
n: '项目',
k: '',
v: pepProjectName
}, {
n: '结构物',
k: '',
f: (d) => {
return (strucMap[d.StructureId] || { name: '' }).name
// return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).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 ?
'超过' + calcMinute2DHM(moment().diff(moment(d.StartTime), 'minutes')) : ''
}
},]
2 years ago
let dataLnterruptTitle = [{
n: '项目',
k: '',
v: pepProjectName
}, {
n: '结构物',
k: '',
f: (d) => {
return (strucMap[d.StructureId] || { name: '' }).name
// return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name
}
}, {
n: '监测因素(中断比例)',
2 years ago
k: '',
f: (d) => {
// d.factor.join('<b/>')
// console.log(21211231131,d.factor);
2 years ago
let data = []
d.factor.map(f => data.push(f.name + '(' + f.breakData + '/' + f.sum + ')'))
2 years ago
return data.join('<br/>')
}
}, {
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: '告警等级(当前)',
2 years ago
k: '',
f: (d) => {
switch (d.CurrentLevel) {
case 1:
return '一级'
case 2:
return '二级'
case 3:
return '三级'
default:
return ''
}
}
}, {
n: '持续时间',
k: '',
f: (d) => {
return d.StartTime ?
'超过' + calcMinute2DHM(moment().diff(moment(d.StartTime), 'minutes')) : ''
}
},
{
n: '中断时间',
k: '',
f: (d) => {
return d.StartTime ?
moment(d.StartTime).format('YYYY-MM-DD HH:mm:ss') : ''
}
},]
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 ? d.station.map(ds => ds.name).join('、') : ''
}
}, {
n: '位置',
k: '',
f: (d) => {
return d.station ? d.station.map(ds => ds.position).join('、') : ''
}
}, {
n: '告警信息',
k: 'statusDescribe'
}, {
n: '持续时间',
k: '',
f: (d) => {
return d.createTime ?
'超过' + calcMinute2DHM(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 ?
'超过' + calcMinute2DHM(moment().diff(moment(d.createTime), 'minutes')) : ''
}
},]
let ifEmailSend = false
let tableTitlePostfix = ',详情如下:'
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
}
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
}
let apMergeDeVeAnxinProjectId_ = apMergeDeVeAnxinProjectId ?
apMergeDeVeAnxinProjectId.split(',') : [];
let apMergeDeVeAlarms = {
// 结构物id :{
// data_exception 数据异常告警:[],
// video_exception 视频异常告警:[]
// }
}
let dataAlarmG1 = [];
let dataAlarmG2 = [];
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) {
2 years ago
if (d.StructureId) {
//查询结构物的监测因素
const factorData = await clickHouse.anxinyun.query(
`
SELECT
t_structure_factor.structure AS structureId,
t_factor.name AS name,
t_factor.id AS id
FROM
t_structure_factor
LEFT JOIN t_factor
ON t_factor.id = t_structure_factor.factor
WHERE
t_structure_factor.structure = (${d.StructureId})
`
).toPromise() || []
let factorId = factorData.map(f => f.id)
//查询结构物对应的设备
const equipment = await clickHouse.anxinyun.query(
`
SELECT
t_sensor.id AS id,
t_sensor.name AS name,
t_sensor.structure AS structureId,
t_sensor.factor AS factorId,
2 years ago
t_device_sensor.iota_device_id AS iotaDeviceId
2 years ago
FROM
t_sensor
LEFT JOIN t_device_sensor
ON t_device_sensor.sensor = t_sensor.id
WHERE
t_sensor.structure = (${d.StructureId})
AND
t_sensor.factor IN (${factorId.join(',')})
2 years ago
`
).toPromise() || []
const alarmDatas = await clickHouse.dataAlarm.query(
`
SELECT
2 years ago
alarms.AlarmId AS alarmId,
2 years ago
alarms.State AS state,
alarms.AlarmGroup AS alarmGroup,
alarms.SourceId AS sourceId
FROM
alarms
WHERE
alarms.StructureId = (${d.StructureId})
AND
alarms.AlarmGroup = 1
2 years ago
AND
2 years ago
alarms.State < 3
2 years ago
`
).toPromise() || []
2 years ago
2 years ago
equipment.map(r => {
alarmDatas.map(u => {
if (r.iotaDeviceId == u.sourceId) {
r.sourceId = u.sourceId
}
})
})
2 years ago
d.factor = []
factorData.map(c => {
2 years ago
let breakData = equipment.filter(m => (m.factorId == c.id && m.sourceId))
2 years ago
d.factor.push({
name: c.name,
sum: equipment.length,
breakData: breakData.length
})
})
}
dataAlarmG1.push(d)
2 years ago
} 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) {
2 years ago
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)
}
deviceStatistic.add(d.SourceId)
}
if (c.tactics == 'abnormal_rate') {
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>
<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>
`
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({
titlePrefix: '数据中断',
alarmData: dataAlarmG1,
2 years ago
alarmTitleArr: dataLnterruptTitle,
})
}
if (c.alarmType.includes('data_exception')) {
html += packageAlarmData2Table({
titlePrefix: '数据异常',
alarmData: dataAlarmG2,
alarmTitleArr: dataAlarmTitle,
})
}
if (c.alarmType.includes('strategy_hit')) {
html += packageAlarmData2Table({
titlePrefix: '策略命中',
alarmData: dataAlarmG3,
alarmTitleArr: dataAlarmTitle,
})
}
if (c.alarmType.includes('video_exception')) {
html += packageAlarmData2Table({
titlePrefix: '视频异常',
alarmData: videoAlarms,
alarmTitleArr: videoAlarmTitle,
keyOfStartTime: 'createTime',
})
}
if (c.alarmType.includes('app_exception')) {
html += packageAlarmData2Table({
titlePrefix: '应用异常',
alarmData: appAlarms,
alarmTitleArr: appAlarmTitle,
keyOfStartTime: 'createTime',
})
}
if (c.alarmType.includes('device_exception')) {
html += packageAlarmData2Table({
titlePrefix: '设备异常',
alarmData: dataAlarmG45,
alarmTitleArr: dataAlarmTitle,
})
}
if (Object.keys(apMergeDeVeAlarms).length) {
2 years ago
if (proDebug) {
console.log(`查得数据异常、视频异常合并の告警:`);
2 years ago
console.log(apMergeDeVeAlarms);
}
let deviceSensorRes = []
if (deviceIds.size) {
const device4Search = [...deviceIds]
.map(id => "'" + id + "'")
deviceSensorRes = await clickHouse.anxinyun.query(`
2 years ago
SELECT
iota_device_id, sensor
FROM t_device_sensor
WHERE iota_device_id
${device4Search.length > 1 ? `IN (${device4Search.join(',')})` : `= ${device4Search[0]}`}
`).toPromise()
}
2 years ago
if (proDebug) {
console.log(`相关设备及测点信息:`);
2 years ago
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]
2 years ago
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,
})
}
2 years ago
if (ifEmailSend) {
// 查接收人的信息
const receiverRes =
c.receiverPepUserId.length ?
await clickHouse.pepEmis.query(`
SELECT id, name, email FROM user WHERE id IN (${c.receiverPepUserId.join(',')},-1)
`).toPromise()
: []
let receiverId = []
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) {
// !开发测试用的数据
2 years ago
// emails = ['1650192445@qq.com']
emails = ['2798774060@qq.com']
}
if (emails.length) {
await pushByEmail({
email: emails,
title: emailTitle,
text: '',
html: html
})
//存日志 存动态 socket到前端
let dataToSave = {
time: moment().format(),
pushConfigId: c.id,
cfgName: c.name,//策略名称
tactics: c.tactics,
tacticsParams: c.tacticsParams,
projectCorrelationId: pomsProjectId,
toPepUserIds: receiverId.map(r => r.id)
}
let r = await models.EmailSendLog.create(dataToSave, { returning: true })
let dynamic = {
time: r.dataValues.time,
emailSendId: r.dataValues.id,
// projectCorrelationId: r.dataValues.projectCorrelationId,
type: 2//通知
}
await models.LatestDynamicList.create(dynamic);
//消息推送到前端
await sendNoticeToWeb(receiverId, dataToSave);
}
2 years ago
}
}
}
}
} catch (error) {
console.error(error);
}
}
)
return {
alarmsPush,
}
}