|
|
|
'use strict';
|
|
|
|
const moment = require('moment');
|
|
|
|
|
|
|
|
async function groupList (ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { clickHouse } = ctx.app.fs
|
|
|
|
|
|
|
|
const groupRes = await clickHouse.anxinyun.query(`
|
|
|
|
SELECT * FROM t_alarm_group
|
|
|
|
`).toPromise();
|
|
|
|
|
|
|
|
for (let g of groupRes) {
|
|
|
|
g.unit = await await clickHouse.anxinyun.query(`
|
|
|
|
SELECT * FROM t_alarm_group_unit WHERE group_id = ${g.id}
|
|
|
|
`).toPromise();
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.body = groupRes
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function list (ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { clickHouse } = ctx.app.fs
|
|
|
|
const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs
|
|
|
|
const { database: anxinyun } = clickHouse.anxinyun.opts.config
|
|
|
|
|
|
|
|
const { pepProjectId, keywordTarget, keyword, groupId, groupUnitId, sustainTimeStart, sustainTimeEnd, limit, page, state } = ctx.query
|
|
|
|
|
|
|
|
let anxinStruc = await anxinStrucIdRange({
|
|
|
|
ctx, pepProjectId, keywordTarget, keyword
|
|
|
|
})
|
|
|
|
let whereOption = []
|
|
|
|
// TODO: 1 开发临时增加
|
|
|
|
if (anxinStruc.length) {
|
|
|
|
const anxinStrucIds = anxinStruc.map(a => a.strucId)
|
|
|
|
// TODO: 开发临时注释
|
|
|
|
whereOption.push(`alarms.StructureId IN (${anxinStrucIds.join(",")})`)
|
|
|
|
|
|
|
|
if (groupId) {
|
|
|
|
whereOption.push(`alarms.AlarmGroup IN (${groupId})`)
|
|
|
|
}
|
|
|
|
if (groupUnitId) {
|
|
|
|
whereOption.push(`alarms.AlarmGroupUnit=${groupUnitId}`)
|
|
|
|
}
|
|
|
|
if (sustainTimeStart && sustainTimeEnd) {
|
|
|
|
let momentStart = moment(sustainTimeStart).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
let momentEnd = moment(sustainTimeEnd).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
whereOption.push(`
|
|
|
|
(
|
|
|
|
alarms."StartTime"
|
|
|
|
BETWEEN '${momentStart}' AND '${momentEnd}'
|
|
|
|
OR
|
|
|
|
"alarms"."EndTime" BETWEEN '${momentStart}' AND '${momentEnd}'
|
|
|
|
OR (
|
|
|
|
"alarms"."StartTime" <= '${momentStart}'
|
|
|
|
AND
|
|
|
|
"alarms"."EndTime" >= '${momentEnd}'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
`)
|
|
|
|
}
|
|
|
|
if (keywordTarget == 'source' && keyword) {
|
|
|
|
whereOption.push(`SourceName LIKE '%${keyword}%'`)
|
|
|
|
}
|
|
|
|
if (state) {
|
|
|
|
if (state == 'new') {
|
|
|
|
whereOption.push(`alarms.State < 3`)
|
|
|
|
} else if (state == 'histroy') {
|
|
|
|
whereOption.push(`alarms.State >= 3`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let alarmQueryOptionStr = `
|
|
|
|
FROM
|
|
|
|
alarms
|
|
|
|
LEFT JOIN ${anxinyun}.t_structure
|
|
|
|
ON ${anxinyun}.t_structure.id = alarms.StructureId
|
|
|
|
LEFT JOIN ${anxinyun}.t_alarm_code
|
|
|
|
ON ${anxinyun}.t_alarm_code.code = alarms.AlarmTypeCode
|
|
|
|
LEFT JOIN ${anxinyun}.t_alarm_type
|
|
|
|
ON ${anxinyun}.t_alarm_type.id = alarms.AlarmTypeId
|
|
|
|
${whereOption.length ? 'WHERE ' + whereOption.join(' AND ') : ''}
|
|
|
|
`
|
|
|
|
const alarmRes = await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT
|
|
|
|
alarms.AlarmId AS AlarmId,
|
|
|
|
alarms.State AS State,
|
|
|
|
SourceName, StartTime, EndTime,
|
|
|
|
alarms.CurrentLevel AS CurrentLevel,
|
|
|
|
SourceTypeId,
|
|
|
|
AlarmAdviceProblem, AlarmGroup, AlarmGroupUnit, AlarmAdviceProblem,
|
|
|
|
alarms.StructureId AS StructureId,
|
|
|
|
${anxinyun}.t_structure.name AS StructureName,
|
|
|
|
${anxinyun}.t_alarm_code.name AS AlarmCodeName,
|
|
|
|
AlarmContent
|
|
|
|
${alarmQueryOptionStr}
|
|
|
|
ORDER BY alarms.StartTime DESC
|
|
|
|
${limit ? 'LIMIT ' + limit : ''}
|
|
|
|
${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''}
|
|
|
|
`).toPromise();
|
|
|
|
|
|
|
|
// ,
|
|
|
|
// ${anxinyun}.t_alarm_type.old_name AS alarmTypeOldName
|
|
|
|
|
|
|
|
// State = 3 是 自动恢复 / 4 是 人工恢复 / 其他数字 是 需要恢复
|
|
|
|
// state = 2 是 等级提升 / 1 是持续产生 / 0 是首次产生
|
|
|
|
|
|
|
|
// SourceType 0: 'DTU' / 1: '传感器' / 2: '测点'
|
|
|
|
|
|
|
|
const countAlarm = await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT
|
|
|
|
count(alarms.AlarmId) AS count
|
|
|
|
${alarmQueryOptionStr}
|
|
|
|
`).toPromise();
|
|
|
|
|
|
|
|
const confirmedAlarm = alarmRes
|
|
|
|
// TODO: 开发临时注释
|
|
|
|
.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 , max(Content) AS Content
|
|
|
|
FROM
|
|
|
|
alarm_details
|
|
|
|
WHERE
|
|
|
|
AlarmId IN (${confirmedAlarm.join(',')})
|
|
|
|
GROUP BY AlarmId
|
|
|
|
`).toPromise() :
|
|
|
|
[];
|
|
|
|
|
|
|
|
const detailCountAlarm = alarmRes
|
|
|
|
.map(ar => "'" + ar.AlarmId + "'")
|
|
|
|
const alarmDetailCount = detailCountAlarm.length ? await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT
|
|
|
|
count(Time) AS count, AlarmId
|
|
|
|
FROM
|
|
|
|
alarm_details
|
|
|
|
WHERE
|
|
|
|
AlarmId IN (${detailCountAlarm.join(',')})
|
|
|
|
AND AlarmState < 3
|
|
|
|
GROUP BY AlarmId
|
|
|
|
`).toPromise() :
|
|
|
|
[]
|
|
|
|
|
|
|
|
|
|
|
|
alarmRes.forEach(ar => {
|
|
|
|
ar.pomsProject = (
|
|
|
|
anxinStruc.find(as => as.strucId == ar.StructureId) ||
|
|
|
|
{
|
|
|
|
pomsProject: [
|
|
|
|
// TODO: 开发临时添加
|
|
|
|
]
|
|
|
|
}
|
|
|
|
).pomsProject
|
|
|
|
|
|
|
|
// 最新告警详情 - 确认信息
|
|
|
|
let corConfirmedData = (confirmedAlarmDetailMax.find(cdm => cdm.AlarmId == ar.AlarmId) || {});
|
|
|
|
|
|
|
|
// if (corConfirmedData.AlarmState && corConfirmedData.AlarmState > 2) {
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
// corConfirmedData = {}
|
|
|
|
// }
|
|
|
|
ar.confirmedContent = corConfirmedData.Content
|
|
|
|
ar.confirmedTime = corConfirmedData.Time
|
|
|
|
|
|
|
|
// 告警详情的数量
|
|
|
|
ar.detailCount = (alarmDetailCount.find(adc => adc.AlarmId == ar.AlarmId) || { count: 0 }).count
|
|
|
|
})
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.body = {
|
|
|
|
count: countAlarm[0].count,
|
|
|
|
rows: alarmRes
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ctx.body = {
|
|
|
|
count: 0,
|
|
|
|
rows: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctx.status = 200;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function detail (ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { clickHouse } = ctx.app.fs
|
|
|
|
const { alarmId } = ctx.query
|
|
|
|
|
|
|
|
const detailRes = await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT * FROM alarm_details WHERE AlarmId = '${alarmId}' ORDER BY Time ASC
|
|
|
|
`).toPromise()
|
|
|
|
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.body = detailRes
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function confirm (opts) {
|
|
|
|
return async function (ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { utils: { kfkSendAsync } } = ctx.app.fs
|
|
|
|
const { clickHouse } = ctx.app.fs
|
|
|
|
const { content = '', alarmId } = ctx.request.body
|
|
|
|
// 发送告警恢复通知
|
|
|
|
// Topic: alarm
|
|
|
|
/*
|
|
|
|
* {
|
|
|
|
* messageMode: "AlarmManElimination",
|
|
|
|
* sourceId: "",
|
|
|
|
* alarmTypeCode: "",
|
|
|
|
* sponsor: userId,
|
|
|
|
* content: "确认消息",
|
|
|
|
* time: "YYYY-MM-DDTHH:mm:ss.SSSZ"
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
|
|
|
|
const alarmRes = await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT * FROM alarms WHERE AlarmId IN (${alarmId.map(a => `'${a}'`).join(',')})
|
|
|
|
`).toPromise();
|
|
|
|
|
|
|
|
if (!alarmRes.length) {
|
|
|
|
throw '没有查询到对应的告警信息'
|
|
|
|
}
|
|
|
|
|
|
|
|
const [corAlarm] = alarmRes
|
|
|
|
if ([3, 4].some(s => s == corAlarm.State)) {
|
|
|
|
throw '告警信息已确认'
|
|
|
|
}
|
|
|
|
for (let corAlarm of alarmRes) {
|
|
|
|
if ([3, 4].some(s => s == corAlarm.State)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
const message = {
|
|
|
|
messageMode: "AlarmManElimination",
|
|
|
|
sourceId: corAlarm.SourceId,
|
|
|
|
alarmTypeCode: corAlarm.AlarmTypeCode,
|
|
|
|
sponsor: opts.anxinCloud.confirmAlarmAnxinUserId,
|
|
|
|
content: content,
|
|
|
|
time: moment().toISOString()
|
|
|
|
};
|
|
|
|
|
|
|
|
const payloads = [{
|
|
|
|
topic: `${opts.kafka.topicPrefix}_alarm`,
|
|
|
|
messages: [JSON.stringify(message)],
|
|
|
|
partition: 0
|
|
|
|
}];
|
|
|
|
|
|
|
|
await kfkSendAsync(payloads)
|
|
|
|
}
|
|
|
|
ctx.status = 204;
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function detailAggregation (ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { alarmId } = ctx.query
|
|
|
|
const { clickHouse } = ctx.app.fs
|
|
|
|
|
|
|
|
const alarmDetailAggRes = await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT
|
|
|
|
formatDateTime(Time,'%F %H') hours, count(AlarmId) count
|
|
|
|
FROM
|
|
|
|
alarm_details
|
|
|
|
WHERE
|
|
|
|
AlarmId='${alarmId}'
|
|
|
|
GROUP BY
|
|
|
|
hours;
|
|
|
|
`).toPromise();
|
|
|
|
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.body = alarmDetailAggRes
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function alarmCount (ctx) {
|
|
|
|
try {
|
|
|
|
const { models } = ctx.fs.dc;
|
|
|
|
const { clickHouse } = ctx.app.fs
|
|
|
|
|
|
|
|
const alarmUnconfirmedAggRes = await clickHouse.dataAlarm.query(`
|
|
|
|
SELECT count(AlarmId) count, AlarmGroup from alarms GROUP BY AlarmGroup;
|
|
|
|
`).toPromise();
|
|
|
|
|
|
|
|
ctx.status = 200;
|
|
|
|
ctx.body = alarmUnconfirmedAggRes
|
|
|
|
} catch (error) {
|
|
|
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|
|
|
ctx.status = 400;
|
|
|
|
ctx.body = {
|
|
|
|
message: typeof error == 'string' ? error : undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
list,
|
|
|
|
detail,
|
|
|
|
groupList,
|
|
|
|
confirm,
|
|
|
|
detailAggregation,
|
|
|
|
alarmCount,
|
|
|
|
};
|