'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, } = ctx.query let anxinStruc = await anxinStrucIdRange({ ctx, pepProjectId, keywordTarget, keyword }) let whereOption = [] // TODO: 1 开发临时增加 if (anxinStruc.length || 1) { 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() let momentEnd = moment(sustainTimeEnd).format() 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}%'`) } 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 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 ${whereOption.length ? 'WHERE ' + whereOption.join(' AND ') : ''} ORDER BY alarms.StartTime DESC ${limit ? 'LIMIT ' + limit : ''} ${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''} `).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 FROM alarm_details WHERE AlarmId IN (${confirmedAlarm.join(',')}) GROUP BY AlarmId `).toPromise() : []; // State = 3 是 自动恢复 / 4 是 人工恢复 / 其他数字 是 需要恢复 // state = 2 是 等级提升 / 1 是持续产生 / 0 是首次产生 // SourceType 0: 'DTU' / 1: '传感器' / 2: '测点' alarmRes.forEach(ar => { ar.pepProject = (anxinStruc.find(as => as.strucId == ar.StructureId) || { pepProject: [ // TODO: 开发临时添加 { id: 999, projectName: '这是假的开发の数据,看到请拨打110', isdelete: 0, constructionStatusId: 1, constructionStatus: '建设中', } ] }).pepProject 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' }) ctx.body = alarmRes } else { ctx.body = [] } 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 = '${alarmId}' `).toPromise(); if (!alarmRes.length) { throw '没有查询到对应的告警信息' } const [corAlarm] = alarmRes if ([3, 4].some(s => s == corAlarm.State)) { throw '告警信息已确认' } 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, };