41 changed files with 3170 additions and 705 deletions
@ -0,0 +1,49 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const moment = require('moment') |
||||
|
|
||||
|
async function alarmConfirmLog(ctx, confirmPost, content) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
//存日志
|
||||
|
let logDatas = []; |
||||
|
confirmPost.map(cp => { |
||||
|
let { pepUserId, projectCorrelationIds, alarmInfo } = cp; |
||||
|
projectCorrelationIds.map(id => { |
||||
|
logDatas.push({ |
||||
|
pepUserId, |
||||
|
projectCorrelationId: id, |
||||
|
alarmInfo,//包含告警id,type,source
|
||||
|
confirmTime: moment().format(), |
||||
|
confirmContent: content |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
let rslt = await models.AlarmConfirmLog.bulkCreate(logDatas, { returning: true }); |
||||
|
|
||||
|
//存最新动态
|
||||
|
let dynamics = rslt.map(r => { |
||||
|
return { |
||||
|
time: r.confirmTime, |
||||
|
alarmConfirmId: r.id, |
||||
|
projectCorrelationId: r.projectCorrelationId, |
||||
|
type: 4//告警确认
|
||||
|
} |
||||
|
}) |
||||
|
await models.LatestDynamicList.bulkCreate(dynamics); |
||||
|
|
||||
|
//TODO 消息推送到前端
|
||||
|
|
||||
|
|
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
alarmConfirmLog |
||||
|
}; |
@ -0,0 +1,98 @@ |
|||||
|
/* eslint-disable*/ |
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = dc => { |
||||
|
const DataTypes = dc.ORM; |
||||
|
const sequelize = dc.orm; |
||||
|
const AlarmHandleStatistics = sequelize.define("alarmHandleStatistics", { |
||||
|
id: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: true, |
||||
|
field: "id", |
||||
|
autoIncrement: true, |
||||
|
unique: "alarm_handle_statistics_id_uindex" |
||||
|
}, |
||||
|
time: { |
||||
|
type: DataTypes.DATE, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "time", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
projectCorrelationId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "project_correlation_id", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
day1: { |
||||
|
type: DataTypes.DOUBLE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "day1", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
day3: { |
||||
|
type: DataTypes.DOUBLE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "day3", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
day7: { |
||||
|
type: DataTypes.DOUBLE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "day7", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
day15: { |
||||
|
type: DataTypes.DOUBLE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "day15", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
day30: { |
||||
|
type: DataTypes.DOUBLE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "day30", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
day30m: { |
||||
|
type: DataTypes.DOUBLE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "day30m", |
||||
|
autoIncrement: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: "alarm_handle_statistics", |
||||
|
comment: "", |
||||
|
indexes: [] |
||||
|
}); |
||||
|
dc.models.AlarmHandleStatistics = AlarmHandleStatistics; |
||||
|
return AlarmHandleStatistics; |
||||
|
}; |
@ -0,0 +1,480 @@ |
|||||
|
'use strict'; |
||||
|
const moment = require('moment'); |
||||
|
|
||||
|
module.exports = function (app, opts) { |
||||
|
const { models } = app.fs.dc |
||||
|
const { clickHouse } = app.fs |
||||
|
const { database: anxinyun } = clickHouse.anxinyun.opts.config |
||||
|
const alarmHandleStatistics = app.fs.scheduleInit( |
||||
|
{ |
||||
|
interval: '0 58 9 * * *', |
||||
|
// immediate: true,
|
||||
|
//proRun: true,
|
||||
|
}, |
||||
|
async () => { |
||||
|
try { |
||||
|
let anxinStruc = await getAxyStructs() |
||||
|
let pomsProject = await pomsProjectRange() |
||||
|
if (anxinStruc.length) { |
||||
|
let dataAlarms = await getDataAlarms(anxinStruc);//数据告警
|
||||
|
let appAlarms = await getAppAlarms(pomsProject);//应用告警
|
||||
|
let videoAlarms = await getVideoAlarms(anxinStruc);//视频告警
|
||||
|
|
||||
|
|
||||
|
let time = moment().format() |
||||
|
//算全局
|
||||
|
let dataArrToSave = [] |
||||
|
let dataMap = calculate(dataAlarms, appAlarms, videoAlarms) |
||||
|
let sum = dataAlarms.length + appAlarms.length + videoAlarms.length; |
||||
|
if (sum) { |
||||
|
dataArrToSave.push({ |
||||
|
time: time, |
||||
|
projectCorrelationId: null,//全局
|
||||
|
day1: parseFloat((100 * dataMap.day1 / sum).toFixed(2)), |
||||
|
day3: parseFloat((100 * dataMap.day3 / sum).toFixed(2)), |
||||
|
day7: parseFloat((100 * dataMap.day7 / sum).toFixed(2)), |
||||
|
day15: parseFloat((100 * dataMap.day15 / sum).toFixed(2)), |
||||
|
day30: parseFloat((100 * dataMap.day30 / sum).toFixed(2)), |
||||
|
day30m: parseFloat((100 * dataMap.day30m / sum).toFixed(2)), |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//算单个项目
|
||||
|
pomsProject.map(p => { |
||||
|
let pid = p.id; |
||||
|
let pDataAlarms = dataAlarms.filter(da => da.pomsProject.indexOf(pid) != -1) |
||||
|
let pAppAlarms = appAlarms.filter(aa => aa.app.projectCorrelations.map(ap => ap.id).indexOf(pid) != -1) |
||||
|
let pVideoAlarms = videoAlarms.filter(va => va.pomsProject.indexOf(pid) != -1) |
||||
|
|
||||
|
let pDataMap = calculate(pDataAlarms, pAppAlarms, pVideoAlarms) |
||||
|
let sm = pDataAlarms.length + pAppAlarms.length + pVideoAlarms.length; |
||||
|
if (sm) { |
||||
|
dataArrToSave.push({ |
||||
|
time: time, |
||||
|
projectCorrelationId: pid,//单个项目
|
||||
|
day1: parseFloat((100 * pDataMap.day1 / sum).toFixed(2)), |
||||
|
day3: parseFloat((100 * pDataMap.day3 / sum).toFixed(2)), |
||||
|
day7: parseFloat((100 * pDataMap.day7 / sum).toFixed(2)), |
||||
|
day15: parseFloat((100 * pDataMap.day15 / sum).toFixed(2)), |
||||
|
day30: parseFloat((100 * pDataMap.day30 / sum).toFixed(2)), |
||||
|
day30m: parseFloat((100 * pDataMap.day30m / sum).toFixed(2)), |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
await models.AlarmHandleStatistics.bulkCreate(dataArrToSave) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
function calculate(dataAlarms, appAlarms, videoAlarms) { |
||||
|
try { |
||||
|
//算全局
|
||||
|
let dataMap = { |
||||
|
day1: 0,//当日处理
|
||||
|
day3: 0,//3日内
|
||||
|
day7: 0,//7日内
|
||||
|
day15: 0,//15日内
|
||||
|
day30: 0,//30日内
|
||||
|
day30m: 0//超过30日
|
||||
|
} |
||||
|
dataAlarms.filter(d => d.State > 3).map(da => { |
||||
|
let range = moment(da.confirmTime).diff(moment(da.StartTime), 'day') |
||||
|
if (range <= 1) { |
||||
|
dataMap.day1++ |
||||
|
} else if (range > 1 && range <= 3) { |
||||
|
dataMap.day3++ |
||||
|
} else if (range > 3 && range <= 7) { |
||||
|
dataMap.day7++ |
||||
|
} else if (range > 7 && range <= 15) { |
||||
|
dataMap.day15++ |
||||
|
} else if (range > 15 && range <= 30) { |
||||
|
dataMap.day30++ |
||||
|
} else if (range > 30) { |
||||
|
dataMap.day30m++ |
||||
|
} |
||||
|
}) |
||||
|
appAlarms.filter(d => d.confirmTime).map(da => { |
||||
|
let range = moment(da.confirmTime).diff(moment(da.createTime), 'day') |
||||
|
if (range < 1) { |
||||
|
dataMap.day1++ |
||||
|
} else if (range >= 1 && range < 3) { |
||||
|
dataMap.day3++ |
||||
|
} else if (range >= 3 && range < 7) { |
||||
|
dataMap.day7++ |
||||
|
} else if (range >= 7 && range < 15) { |
||||
|
dataMap.day15++ |
||||
|
} else if (range >= 15 && range < 30) { |
||||
|
dataMap.day30++ |
||||
|
} else if (range >= 30) { |
||||
|
dataMap.day30m++ |
||||
|
} |
||||
|
}) |
||||
|
videoAlarms.filter(d => d.confirmTime || d.autoRestore).map(da => { |
||||
|
let range = moment(da.confirmTime).diff(moment(da.createTime), 'day') |
||||
|
if (range < 1) { |
||||
|
dataMap.day1++ |
||||
|
} else if (range >= 1 && range < 3) { |
||||
|
dataMap.day3++ |
||||
|
} else if (range >= 3 && range < 7) { |
||||
|
dataMap.day7++ |
||||
|
} else if (range >= 7 && range < 15) { |
||||
|
dataMap.day15++ |
||||
|
} else if (range >= 15 && range < 30) { |
||||
|
dataMap.day30++ |
||||
|
} else if (range >= 30) { |
||||
|
dataMap.day30m++ |
||||
|
} |
||||
|
}) |
||||
|
return dataMap; |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function getDataAlarms(anxinStruc) { |
||||
|
try { |
||||
|
const anxinStrucIds = anxinStruc.map(a => a.strucId) |
||||
|
let whereOption = [] |
||||
|
whereOption.push(`alarms.StructureId IN (${anxinStrucIds.join(",")})`) |
||||
|
|
||||
|
let start = moment().add(-1, 'year').format('YYYY-MM-DD HH:mm:ss');//最近一年
|
||||
|
whereOption.push(`alarms.StartTime >= '${start}'`) |
||||
|
|
||||
|
let alarmQueryOptionStr = `FROM alarms
|
||||
|
LEFT JOIN ${anxinyun}.t_structure |
||||
|
ON ${anxinyun}.t_structure.id = alarms.StructureId |
||||
|
${whereOption.length ? 'WHERE ' + whereOption.join(' AND ') : ''}` |
||||
|
|
||||
|
console.log('开始查数据-数据-数据类告警---' + moment().format('YYYY-MM-DD HH:mm:ss')) |
||||
|
|
||||
|
const alarmRes = await clickHouse.dataAlarm.query(` |
||||
|
SELECT |
||||
|
alarms.AlarmId AS AlarmId, |
||||
|
alarms.State AS State, |
||||
|
alarms.StructureId AS StructureId, |
||||
|
StartTime, EndTime |
||||
|
${alarmQueryOptionStr}`).toPromise();
|
||||
|
|
||||
|
console.log('数据-数据-数据告警查询结束---' + moment().format('YYYY-MM-DD HH:mm:ss') + `---一共${alarmRes.length}条`) |
||||
|
|
||||
|
const confirmedAlarm = alarmRes.filter(ar => ar.State && ar.State > 2).map(ar => "'" + ar.AlarmId + "'"); |
||||
|
const confirmedAlarmDetailMax = confirmedAlarm.length ? |
||||
|
await clickHouse.dataAlarm.query(` |
||||
|
SELECT |
||||
|
max(Time) AS Time, AlarmId |
||||
|
FROM |
||||
|
alarm_details |
||||
|
WHERE |
||||
|
AlarmId IN (${confirmedAlarm.join(',')}) |
||||
|
GROUP BY AlarmId |
||||
|
`).toPromise() : [];
|
||||
|
|
||||
|
alarmRes.forEach(ar => { |
||||
|
ar.pomsProject = ( |
||||
|
anxinStruc.find(as => as.strucId == ar.StructureId) || |
||||
|
{ |
||||
|
pomsProject: [ |
||||
|
// TODO: 开发临时添加
|
||||
|
] |
||||
|
} |
||||
|
).pomsProject.map(p => p.id) |
||||
|
|
||||
|
// 最新告警详情 - 确认信息
|
||||
|
let corConfirmedData = (confirmedAlarmDetailMax.find(cdm => cdm.AlarmId == ar.AlarmId) || {}); |
||||
|
ar.confirmTime = corConfirmedData.Time || ar.EndTime |
||||
|
}) |
||||
|
return alarmRes; |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function getAppAlarms(pomsProject) { |
||||
|
try { |
||||
|
const pomsProjectIds = pomsProject.map(p => p.id) |
||||
|
let findOption = { |
||||
|
where: { |
||||
|
createTime: { $gte: moment().add(-1, 'year').format() },//最近一年
|
||||
|
}, |
||||
|
attributes: ['id', 'createTime', 'confirmTime'], |
||||
|
include: [{ |
||||
|
model: models.App, |
||||
|
attributes: ['id'], |
||||
|
include: [{ |
||||
|
model: models.ProjectCorrelation, |
||||
|
attributes: ['id'] |
||||
|
}] |
||||
|
}] |
||||
|
} |
||||
|
findOption.where['$app->projectCorrelations.id$'] = { |
||||
|
$in: pomsProjectIds |
||||
|
} |
||||
|
|
||||
|
console.log('开始查应用-应用-应用告警---' + moment().format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const listRes = await models.AppAlarm.findAll(findOption) |
||||
|
console.log('应用-应用-应用告警查询结束---' + moment().format('YYYY-MM-DD HH:mm:ss') + `---一共${listRes.length}条`) |
||||
|
|
||||
|
return listRes |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
async function getVideoAlarms(anxinStruc) { |
||||
|
try { |
||||
|
const anxinStrucIds = anxinStruc.map(a => a.strucId) |
||||
|
let statusAlarmWhereOption = [] |
||||
|
let start = moment().add(-1, 'year').format('YYYY-MM-DD HH:mm:ss');//最近一年
|
||||
|
statusAlarmWhereOption.push(`camera_status_alarm.create_time >= '${start}'`) |
||||
|
|
||||
|
console.log('开始查视频-视频-视频告警---' + moment().format('YYYY-MM-DD HH:mm:ss')) |
||||
|
const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query( |
||||
|
` |
||||
|
SELECT |
||||
|
cameraAlarm.cameraId AS cameraId, |
||||
|
cameraAlarm.alarmId AS alarmId, |
||||
|
cameraAlarm.createTime AS createTime, |
||||
|
cameraAlarm.confirmTime AS confirmTime, |
||||
|
${'cameraAlarm.autoRestore AS autoRestore,'} |
||||
|
anxinStruc.id AS strucId |
||||
|
FROM |
||||
|
( |
||||
|
SELECT |
||||
|
camera.id AS cameraId, |
||||
|
camera_status_alarm.id AS alarmId, |
||||
|
camera_status_alarm.create_time AS createTime, |
||||
|
camera_status_alarm.platform AS platform, |
||||
|
camera_status_alarm.status_id AS statusId, |
||||
|
camera_status_alarm.serial_no AS cameraSerialNo, |
||||
|
camera_status_alarm.channel_no AS cameraChannelNo, |
||||
|
${'camera_status_alarm.auto_restore AS autoRestore,'} |
||||
|
camera_status_alarm.confirm_time AS confirmTime |
||||
|
FROM camera_status_alarm |
||||
|
INNER JOIN camera |
||||
|
ON camera.serial_no = camera_status_alarm.serial_no |
||||
|
AND camera.channel_no = camera_status_alarm.channel_no |
||||
|
WHERE |
||||
|
camera.delete = false |
||||
|
AND camera.recycle_time is null |
||||
|
${statusAlarmWhereOption.length ? 'AND ' + statusAlarmWhereOption.join(' AND ') : ''} |
||||
|
AND alarmId IN ( |
||||
|
SELECT camera_status_alarm.id AS alarmId |
||||
|
FROM camera_status_alarm |
||||
|
RIGHT 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 |
||||
|
${`WHERE ${anxinyun}.t_video_ipc.structure IN (${anxinStrucIds.join(',')})`} |
||||
|
) |
||||
|
) AS cameraAlarm |
||||
|
LEFT JOIN camera_status |
||||
|
ON cameraAlarm.platform = camera_status.platform |
||||
|
AND cameraAlarm.statusId = camera_status.id |
||||
|
LEFT JOIN camera_status_resolve |
||||
|
ON camera_status_resolve.status_id = camera_status.id |
||||
|
|
||||
|
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 (${anxinStrucIds.join(',')}) |
||||
|
LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation |
||||
|
ON anxinIpcStation.ipc = anxinIpc.id |
||||
|
`).toPromise() : []
|
||||
|
|
||||
|
console.log('视频-视频-视频告警查询结束---' + moment().format('YYYY-MM-DD HH:mm:ss') + `---一共${alarmRes.length}条`) |
||||
|
let returnD = [] |
||||
|
let positionD = {} |
||||
|
// 每个设备一个告警
|
||||
|
for (let a of alarmRes) { |
||||
|
if (positionD[a.cameraId]) { |
||||
|
} else { |
||||
|
let d = { |
||||
|
cameraId: a.cameraId, |
||||
|
autoRestore: a.autoRestore, |
||||
|
createTime: a.createTime, |
||||
|
alarmId: a.alarmId, |
||||
|
confirmTime: a.confirmTime, |
||||
|
} |
||||
|
// pep 项目
|
||||
|
d.pomsProject = ( |
||||
|
anxinStruc.find(as => as.strucId == a.strucId) || |
||||
|
{ |
||||
|
pomsProject: [ |
||||
|
|
||||
|
] |
||||
|
} |
||||
|
).pomsProject.map(d => d.id) |
||||
|
returnD.push(d) |
||||
|
positionD[a.cameraId] = { |
||||
|
positionReturnD: returnD.length - 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return returnD; |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
async function pomsProjectRange() { |
||||
|
try { |
||||
|
const { pepProjectRes, bindRes } = await pomsWithPepRangeParams() |
||||
|
let pomsProject = [] |
||||
|
for (let b of bindRes) { |
||||
|
if (b.pepProjectId) { |
||||
|
let corPepProject = pepProjectRes.find(pp => pp.id == b.pepProjectId) || {} |
||||
|
pomsProject.push({ |
||||
|
...b.dataValues, |
||||
|
pepProject: corPepProject |
||||
|
}) |
||||
|
} else { |
||||
|
pomsProject.push({ |
||||
|
...b.dataValues |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
return pomsProject |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function pomsWithPepRangeParams() { |
||||
|
try { |
||||
|
const bindRes = await models.ProjectCorrelation.findAll({ where: { del: false } }); |
||||
|
// 获取不重复的 项企项目id
|
||||
|
let pepProjectIds = [] |
||||
|
for (let b of bindRes) { |
||||
|
if (b.pepProjectId) { |
||||
|
pepProjectIds.push(b.pepProjectId) |
||||
|
} |
||||
|
} |
||||
|
// 查询项企项目的信息
|
||||
|
const pepProjectRes = pepProjectIds.length ? |
||||
|
await clickHouse.projectManage.query( |
||||
|
`SELECT
|
||||
|
t_pim_project.id AS id, |
||||
|
t_pim_project.project_name AS projectName, |
||||
|
t_pim_project.isdelete AS isdelete, |
||||
|
t_pim_project_construction.construction_status_id AS constructionStatusId, |
||||
|
t_pim_project_state.construction_status AS constructionStatus |
||||
|
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() : []; |
||||
|
|
||||
|
return { |
||||
|
pepProjectRes, bindRes |
||||
|
} |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function getAxyStructs() { |
||||
|
try { |
||||
|
const { pepProjectRes, bindRes } = await pomsWithPepRangeParams() |
||||
|
// 获取不重复的 安心云项目 id
|
||||
|
const anxinProjectIds = [ |
||||
|
...(bindRes).reduce( |
||||
|
(arr, b) => { |
||||
|
for (let sid of b.anxinProjectId) { |
||||
|
arr.add(sid); |
||||
|
} |
||||
|
return arr; |
||||
|
}, |
||||
|
new Set() |
||||
|
) |
||||
|
] |
||||
|
// 查询安心云项目及结构物信息
|
||||
|
const undelStrucRes = anxinProjectIds.length ? |
||||
|
await clickHouse.anxinyun.query( |
||||
|
`SELECT
|
||||
|
t_project.id AS projectId, |
||||
|
t_structure.id AS strucId, |
||||
|
t_structure.name AS strucName, |
||||
|
project_state |
||||
|
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 (${anxinProjectIds.join(',')})`).toPromise() : []
|
||||
|
|
||||
|
// 构建安心云结构物和项企项目的关系
|
||||
|
// 并保存信息至数据
|
||||
|
let undelStruc = [] |
||||
|
for (let s of undelStrucRes) { |
||||
|
let corStruc = undelStruc.find(us => us.strucId == s.strucId) |
||||
|
if (corStruc) { |
||||
|
if (!corStruc.project.some(cp => cp.id == s.projectId)) { |
||||
|
corStruc.project.push({ |
||||
|
id: s.projectId |
||||
|
}) |
||||
|
} |
||||
|
} else { |
||||
|
corStruc = { |
||||
|
strucId: s.strucId, |
||||
|
strucName: s.strucName, |
||||
|
projectId: s.projectId, |
||||
|
project: [{ |
||||
|
id: s.projectId, |
||||
|
}], |
||||
|
pomsProject: [] |
||||
|
} |
||||
|
undelStruc.push(corStruc) |
||||
|
} |
||||
|
for (let { dataValues: br } of bindRes) { |
||||
|
if (br.anxinProjectId.some(braId => braId == s.projectId)) { |
||||
|
let corPepProject = pepProjectRes.find(pp => pp.id == br.pepProjectId) |
||||
|
let corPomsProject = corStruc.pomsProject.find(cp => cp.id == br.id) |
||||
|
|
||||
|
if (corPomsProject) { |
||||
|
// poms 的 project 和 pep 的 project 是一对一的关系 所以这个情况不用处理
|
||||
|
} else { |
||||
|
corStruc.pomsProject.push({ |
||||
|
...br, |
||||
|
pepProject: corPepProject |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return undelStruc |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
alarmHandleStatistics |
||||
|
} |
||||
|
} |
@ -0,0 +1,465 @@ |
|||||
|
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, |
||||
|
} |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const moment = require('moment') |
||||
|
const Core = require('@alicloud/pop-core'); |
||||
|
const nodemailer = require('nodemailer') |
||||
|
|
||||
|
module.exports = function (app, opts) { |
||||
|
const pushBySms = async ({ phone = [], templateCode, templateParam } = {}) => { |
||||
|
try { |
||||
|
if (phone.length) { |
||||
|
const client = new Core({ |
||||
|
accessKeyId: opts.sms.accessKey, |
||||
|
accessKeySecret: opts.sms.accessSecret, |
||||
|
endpoint: 'http://dysmsapi.aliyuncs.com',//固定
|
||||
|
apiVersion: '2017-05-25'//固定
|
||||
|
}); |
||||
|
const SendSmsRes = await client.request('SendSms', { |
||||
|
"PhoneNumbers": phone.join(','),//接收短信的手机号码。
|
||||
|
"SignName": "飞尚尚视",//短信签名名称。必须是已添加、并通过审核的短信签名。
|
||||
|
"TemplateCode": templateCode,//短信模板ID。必须是已添加、并通过审核的短信签名;且发送国际/港澳台消息时,请使用国际/港澳台短信模版。
|
||||
|
"TemplateParam": JSON.stringify(templateParam)//短信模板变量对应的实际值,JSON格式。
|
||||
|
}, { |
||||
|
method: 'POST' |
||||
|
}); |
||||
|
return SendSmsRes |
||||
|
} |
||||
|
} catch (error) { |
||||
|
throw error |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const pushByEmail = async ({ email = [], title, text = '', html = '', attachments = undefined, } = {}) => { |
||||
|
try { |
||||
|
let transporter = nodemailer.createTransport({ |
||||
|
host: opts.email.host, |
||||
|
port: opts.email.port, |
||||
|
secure: true, |
||||
|
auth: { |
||||
|
user: opts.email.sender.address, |
||||
|
pass: opts.email.sender.password, |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// send mail with defined transport object
|
||||
|
await transporter.sendMail({ |
||||
|
from: `${opts.email.sender.name}<${opts.email.sender.address}>`, // sender address
|
||||
|
to: email.join(','), // list of receivers 逗号分隔字符串
|
||||
|
subject: title, // Subject line
|
||||
|
text: text, // plain text body
|
||||
|
html: html, // html body
|
||||
|
attachments: attachments |
||||
|
}); |
||||
|
} catch (error) { |
||||
|
throw error |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
pushByEmail, |
||||
|
pushBySms, |
||||
|
} |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
|
||||
|
|
||||
|
create table alarm_confirm_log |
||||
|
( |
||||
|
id serial not null, |
||||
|
pep_user_id int null, |
||||
|
project_correlation_id int not null, |
||||
|
alarm_info json not null, |
||||
|
confirm_time timestamp not null, |
||||
|
confirm_content varchar not null |
||||
|
); |
||||
|
|
||||
|
comment on table alarm_confirm_log is '告警确认日志表'; |
||||
|
|
||||
|
create unique index alarm_confirm_log_id_uindex |
||||
|
on alarm_confirm_log (id); |
||||
|
|
||||
|
alter table alarm_confirm_log |
||||
|
add constraint alarm_confirm_log_pk |
||||
|
primary key (id); |
||||
|
|
||||
|
|
@ -0,0 +1,27 @@ |
|||||
|
|
||||
|
|
||||
|
create table if not exists email_send_log |
||||
|
( |
||||
|
id serial not null |
||||
|
constraint email_send_log_pk |
||||
|
primary key, |
||||
|
time timestamp not null, |
||||
|
push_config_id integer not null, |
||||
|
tactics varchar(32), |
||||
|
tactics_params jsonb, |
||||
|
project_correlation_id integer not null, |
||||
|
to_pep_user_ids integer[] not null |
||||
|
); |
||||
|
|
||||
|
comment on table email_send_log is 'EM推送日志'; |
||||
|
|
||||
|
alter table email_send_log owner to postgres; |
||||
|
|
||||
|
create unique index if not exists email_send_log_id_uindex |
||||
|
on email_send_log (id); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,23 @@ |
|||||
|
|
||||
|
|
||||
|
create table alarm_appear_record |
||||
|
( |
||||
|
id serial not null, |
||||
|
project_correlation_id int not null, |
||||
|
alarm_info json not null, |
||||
|
time timestamp, |
||||
|
type varchar null |
||||
|
); |
||||
|
|
||||
|
comment on table alarm_appear_record is '告警出现记录到日志'; |
||||
|
|
||||
|
create unique index alarm_appear_record_id_uindex |
||||
|
on alarm_appear_record (id); |
||||
|
|
||||
|
alter table alarm_appear_record |
||||
|
add constraint alarm_appear_record_pk |
||||
|
primary key (id); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,28 @@ |
|||||
|
|
||||
|
|
||||
|
create table latest_dynamic_list |
||||
|
( |
||||
|
id serial not null, |
||||
|
time timestamp not null, |
||||
|
project_correlation_id int not null, |
||||
|
alarm_appear_id int, |
||||
|
email_send_id int, |
||||
|
alarm_confirm_id int, |
||||
|
type int |
||||
|
); |
||||
|
|
||||
|
comment on table latest_dynamic_list is '最新动态表'; |
||||
|
comment on column latest_dynamic_list.type is '1:发现,2:通知,3:处置,4:确认'; |
||||
|
|
||||
|
create unique index latest_dynamic_list_id_uindex |
||||
|
on latest_dynamic_list (id); |
||||
|
|
||||
|
alter table latest_dynamic_list |
||||
|
add constraint latest_dynamic_list_pk |
||||
|
primary key (id); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,22 @@ |
|||||
|
|
||||
|
|
||||
|
create table alarm_handle_statistics |
||||
|
( |
||||
|
id serial not null, |
||||
|
time timestamp not null, |
||||
|
project_correlation_id int, |
||||
|
day1 float, |
||||
|
day3 float, |
||||
|
day7 float, |
||||
|
day15 float, |
||||
|
day30 float, |
||||
|
day30m float |
||||
|
); |
||||
|
|
||||
|
create unique index alarm_handle_statistics_id_uindex |
||||
|
on alarm_handle_statistics (id); |
||||
|
|
||||
|
alter table alarm_handle_statistics |
||||
|
add constraint alarm_handle_statistics_pk |
||||
|
primary key (id); |
||||
|
|
@ -0,0 +1,15 @@ |
|||||
|
alter table alarm_push_config |
||||
|
add struc_id int[] not null; |
||||
|
|
||||
|
alter table alarm_push_config |
||||
|
add tactics varchar(32); |
||||
|
|
||||
|
comment on column alarm_push_config.tactics is 'immediately 即时 / continue 持续 / abnormal_rate 异常率'; |
||||
|
|
||||
|
alter table alarm_push_config |
||||
|
add tactics_params jsonb; |
||||
|
|
||||
|
comment on column alarm_push_config.tactics_params is '推送策略 tactics 的参数'; |
||||
|
|
||||
|
alter table alarm_push_config |
||||
|
add del bool default false not null; |
@ -0,0 +1,93 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import { ApiTable, basicAction } from '$utils' |
||||
|
|
||||
|
export function getPush (query) { //获取推送配置列表
|
||||
|
return dispatch => basicAction({ |
||||
|
type: 'get', |
||||
|
dispatch: dispatch, |
||||
|
query: query, |
||||
|
actionType: 'GET_PUSH', |
||||
|
url: `${ApiTable.getPush}`, |
||||
|
msg: { error: '获取推送配置列表失败' }, |
||||
|
reducer: { name: '' } |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function postPush (data) {//添加/编辑成员
|
||||
|
let msg = '' |
||||
|
if (data) { |
||||
|
msg = data.msg |
||||
|
} |
||||
|
return (dispatch) => |
||||
|
basicAction({ |
||||
|
type: "post", |
||||
|
dispatch: dispatch, |
||||
|
data, |
||||
|
actionType: "POST_PUSH", |
||||
|
url: `${ApiTable.postPush}`, |
||||
|
msg: { option: msg }, //添加/编辑成员
|
||||
|
reducer: { name: "" }, |
||||
|
}); |
||||
|
} |
||||
|
export function getOrganizationUsers () { //获取全部未删除用户
|
||||
|
return dispatch => basicAction({ |
||||
|
type: 'get', |
||||
|
dispatch: dispatch, |
||||
|
actionType: 'GET_ORGANIZATION_USERS', |
||||
|
url: `${ApiTable.getOrganizationUsers}`, |
||||
|
msg: { error: '获取全部未删除用户' }, |
||||
|
reducer: { name: '' } |
||||
|
}); |
||||
|
} |
||||
|
export function getProjectPoms (query) {//获取已绑定项目
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_PROJECT_POMS", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getProjectPoms}`, |
||||
|
msg: { option: "获取已绑定项目" }, |
||||
|
reducer: { name: "ProjectPoms", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
export function getProjectStructure (query) {//获取绑定项目下结构物
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_PROJECT_STRUCTURE", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getProjectStructure}`, |
||||
|
msg: { option: "获取绑定项目下结构物" }, |
||||
|
reducer: { name: "ProjectStructure", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
export function getProjectStatus (query) {//获取项目状态列表
|
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_PROJECT_STATUS", |
||||
|
query: query, |
||||
|
url: `${ApiTable.getProjectStatus}`, |
||||
|
msg: { option: "获取项目状态列表" }, |
||||
|
reducer: { name: "ProjectStatus", params: { noClear: true } }, |
||||
|
}); |
||||
|
} |
||||
|
export function putPushPushId (data) {//更改推送配置状态(禁用或删除)
|
||||
|
let pushId = '' |
||||
|
let msg = '' |
||||
|
if (data) { |
||||
|
pushId = data.pushId |
||||
|
msg = data.msg |
||||
|
} |
||||
|
return (dispatch) => |
||||
|
basicAction({ |
||||
|
type: "put", |
||||
|
dispatch: dispatch, |
||||
|
data, |
||||
|
actionType: "PUT_PUSH_PUSHID", |
||||
|
url: `${ApiTable.putPushPushId.replace("{pushId}", pushId)}`, |
||||
|
msg: { option: msg }, //更改推送配置状态(禁用或删除)
|
||||
|
reducer: {}, |
||||
|
}); |
||||
|
} |
@ -1,14 +0,0 @@ |
|||||
'use strict'; |
|
||||
|
|
||||
import { ApiTable, basicAction } from '$utils' |
|
||||
|
|
||||
export function getPush () { //获取推送配置列表 |
|
||||
return dispatch => basicAction({ |
|
||||
type: 'get', |
|
||||
dispatch: dispatch, |
|
||||
actionType: 'GET_PUSH', |
|
||||
url: `${ApiTable.getPush}`, |
|
||||
msg: { error: '获取推送配置列表失败' }, |
|
||||
reducer: { name: '' } |
|
||||
}); |
|
||||
} |
|
@ -0,0 +1,10 @@ |
|||||
|
.pushInput{ |
||||
|
.semi-input-wrapper-default{ |
||||
|
height: 20px !important; |
||||
|
line-height: 20px !important; |
||||
|
} |
||||
|
.semi-input-default{ |
||||
|
height: 20px !important; |
||||
|
line-height: 20px !important; |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
.empush{ |
.myempush{ |
||||
.semi-input-wrapper{ |
.semi-input-wrapper{ |
||||
margin-bottom: 0px !important; |
margin-bottom: 0px !important; |
||||
} |
} |
||||
|
Loading…
Reference in new issue