diff --git a/api/app/lib/schedule/alarms_push.js b/api/app/lib/schedule/alarms_push.js index eec2f63..71f02e0 100644 --- a/api/app/lib/schedule/alarms_push.js +++ b/api/app/lib/schedule/alarms_push.js @@ -18,13 +18,14 @@ module.exports = function (app, opts) { where: { del: false }, + order: ['id'], include: [{ model: models.ProjectCorrelation, where: { del: false }, required: true - }] + }], }) let pepProjectIds = new Set() for (let { dataValues: c } of configListRes) { @@ -112,6 +113,9 @@ module.exports = function (app, opts) { ).toPromise() : [] let searchStrucIds = strucListRes.map(s => s.id) + + searchStrucIds = searchStrucIds.concat([991, 1052, 700]) + if (searchStrucIds.length) { searchStrucIds.unshift(-1) } @@ -139,42 +143,58 @@ module.exports = function (app, opts) { let alarmCameraCount = 0 // 判断推送策略 let nowTime = moment().startOf('minute') - let pointTime = moment().subtract(parseInt(interval), 'minute').startOf('minute').format('YYYY-MM-DD HH:mm:ss') + 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') { - // 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 + } 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')) { + // 查了设备异常率 去安心云查当前项目下的设备数量 + // 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 + } + emailTitle += `异常率推送服务` + emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${parseInt(deviceCount) + parseInt(cameraCount)}个的 --%,详情如下` } - // appAlarmWhereOption.createTime = { $lte: pointTime } - // videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`) - emailTitle += `异常率推送服务` - emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${deviceCount + cameraCount}个的${interval}%,详情如下` } emailTitle += '——POMS飞尚运维中台' @@ -202,6 +222,7 @@ module.exports = function (app, opts) { cameraAlarm.confirmTime AS confirmTime, cameraAlarm.autoRestore AS autoRestore, camera_status.describe AS statusDescribe, + camera_kind.kind AS cameraKind, "gbCamera".online AS cameraOnline, anxinIpc.t_video_ipc.name AS anxinIpcPosition, anxinStation.id AS anxinStationId, @@ -213,6 +234,7 @@ module.exports = function (app, opts) { camera.id AS cameraId, camera.gb_id AS gbId, camera.name AS cameraName, + camera.kind_id AS cameraKindId, camera_status_alarm.id AS alarmId, camera_status_alarm.create_time AS createTime, camera_status_alarm.status_id AS statusId, @@ -240,7 +262,8 @@ module.exports = function (app, opts) { AND cameraAlarm.statusId = camera_status.id LEFT JOIN "gbCamera" ON "gbCamera".id = cameraAlarm.gbId - + LEFT JOIN camera_kind + ON camera_kind.id = cameraAlarm.cameraKindId LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo @@ -251,6 +274,7 @@ module.exports = function (app, opts) { ON anxinIpcStation.ipc = anxinIpc.id LEFT JOIN ${anxinyun}.t_sensor AS anxinStation ON anxinStation.id = anxinIpcStation.station + ORDER BY cameraAlarm.createTime DESC ` ).toPromise() : [] @@ -290,6 +314,7 @@ module.exports = function (app, opts) { alarmId: a.alarmId, confirmContent: a.confirmContent, confirmTime: a.confirmTime, + cameraKind: a.cameraKind, struc: [], station: [] } @@ -315,10 +340,12 @@ module.exports = function (app, opts) { } } let p = 1 + videoAlarms = returnD } if (c.alarmType.includes('app_exception')) { appAlarms = await models.AppAlarm.findAll({ where: appAlarmWhereOption, + order: [['createTime', 'DESC']], include: [{ model: models.App, include: [{ @@ -329,7 +356,6 @@ module.exports = function (app, opts) { }] }] }) - let a = 2 } if (c.alarmType.includes('device_exception')) { dataAlarmGroupOption.push(4) @@ -343,9 +369,10 @@ module.exports = function (app, opts) { dataAlarms = await clickHouse.dataAlarm.query(` SELECT * FROM alarms WHERE - State NOT IN (3, 4) - AND StructureId IN (${searchStrucIds.join(',')}) + ${/*`'State NOT IN (3, 4) AND '`*/''} + StructureId IN (${searchStrucIds.join(',')}) ${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''} + ORDER BY StartTime DESC `).toPromise() console.log(dataAlarms); } @@ -358,7 +385,7 @@ module.exports = function (app, opts) { n: '结构物', k: '', f: (d) => { - return (strucListRes.find(s => s.id == d.StructureId) || {}).name + return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name } }, { n: '告警源名称', @@ -405,49 +432,258 @@ module.exports = function (app, opts) { } },] - let html = '' + 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.map(ds => ds.name).join('、') + } + }, { + n: '位置', + k: '', + f: (d) => { + return d.station.map(ds => ds.position).join('、') + } + }, { + n: '告警信息', + k: 'statusDescribe' + }, { + n: '持续时间', + k: '', + f: (d) => { + return d.createTime ? + '超过' + 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 ? `${d.app.url}` : '' + } + }, { + n: '持续时间', + k: '', + f: (d) => { + return d.createTime ? + '超过' + moment().diff(moment(d.createTime), 'minutes') + '分钟' : '' + } + },] + + + let ifEmailSend = false let tableTitlePostfix = ',详情如下:' - function packageTableTitle (tArr) { + function packageTableTitle (titleArr) { + let tableTitle = '' + for (let t of titleArr) { + tableTitle += `${t.n}` + } + tableTitle += '' + return tableTitle } - if (c.alarmType.includes('data_outages')) { - let tableTitlePrefix = '数据中断告警源' + function packageTableData (data, titleArr) { + let tableData = '' + for (let t of titleArr) { + if (t.v) { + tableData += `${t.v}` + } else if (t.f) { + tableData += `${t.f(data)}` + } else if (t.k) { + tableData += `${data[t.k]}` + } else { + tableData += `` + } + } + tableData += '' + return tableData + } + + function packageAlarmData2Table (titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime') { + if (!alarmData.length) { + return '' + } + ifEmailSend = true + let tableTitlePrefix = titlePrefix + '告警源' let newAddCount = 0 - let alarmHtml = '' + let alarmHtml = '' + let alarmContent = '' + let alarmHtmlTitle = packageTableTitle(alarmTitleArr) - if (c.tactics == 'immediately') { - for (let a of dataAlarms.filter(d => d.AlarmGroup == 1)) { - alarmHtml += 1 + for (let a of alarmData) { + alarmContent += packageTableData(a, alarmTitleArr) + if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) { + newAddCount++ } - tableTitlePrefix += 1 - } else if (c.tactics == 'continue') { + } + tableTitlePrefix += + c.tactics == 'abnormal_rate' ? + `${alarmData.length}个` : + `新增${newAddCount}个,未解决累计${alarmData.length}个` + tableTitlePostfix + alarmHtml += `' + alarmHtml += alarmHtmlTitle + alarmHtml += alarmContent + alarmHtml += '
` + tableTitlePrefix + '

' - } else if (c.tactics == 'abnormal_rate') { + return alarmHtml + } + let dataAlarmG1 = []; + let dataAlarmG2 = []; + let dataAlarmG3 = []; + let dataAlarmG45 = []; + let deviceStatistic = new Set() + for (let d of dataAlarms) { + if (d.AlarmGroup == 1) { + dataAlarmG1.push(d) + } else if (d.AlarmGroup == 2) { + dataAlarmG2.push(d) + } else if (d.AlarmGroup == 3) { + dataAlarmG3.push(d) + } else if (d.AlarmGroup == 4 || d.AlarmGroup == 5) { + dataAlarmG45.push(d) } + deviceStatistic.add(d.SourceId) + } + if (c.tactics == 'abnormal_rate') { + let a = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))).toFixed(1) + '%' + emailSubTitle = emailSubTitle.replace('--%', ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))).toFixed(1) + '%') + } + let html = ` + + + + + + +
${emailSubTitle}
+ ` + if (c.alarmType.includes('data_outages')) { + html += packageAlarmData2Table( + '数据中断', + dataAlarmG1, + dataAlarmTitle, + ) } if (c.alarmType.includes('data_exception')) { - + html += packageAlarmData2Table( + '数据异常', + dataAlarmG2, + dataAlarmTitle, + ) } if (c.alarmType.includes('strategy_hit')) { - + html += packageAlarmData2Table( + '策略命中', + dataAlarmG3, + dataAlarmTitle, + ) } if (c.alarmType.includes('video_exception')) { - + html += packageAlarmData2Table( + '视频异常', + videoAlarms, + videoAlarmTitle, + 'createTime', + ) } if (c.alarmType.includes('app_exception')) { - + html += packageAlarmData2Table( + '应用异常', + appAlarms, + appAlarmTitle, + 'createTime', + ) } if (c.alarmType.includes('device_exception')) { - + html += packageAlarmData2Table( + '设备异常', + dataAlarmG45, + dataAlarmTitle, + ) } - await pushByEmail({ - email: ['1650192445@qq.com'], - title: emailTitle, - text: '', - html: '' - }) + if (ifEmailSend) { + // 查接收人的信息 + const receiverRes = + c.receiverPepUserId.length ? + await clickHouse.pepEmis.query(` + SELECT email FROM user WHERE id IN (${c.receiverPepUserId.join(',')},-1) + `).toPromise() + : [] + const emails = receiverRes.reduce((arr, r) => { + if (r.email) { + arr.push(r.email) + } + return arr + }, []) + if (emails.length || 1) { + await pushByEmail({ + email: ['1650192445@qq.com', '777y'], + title: emailTitle, + text: '', + html: html + }) + } + } } } } diff --git a/web/client/src/sections/service/actions/emPush.js b/web/client/src/sections/service/actions/emPush.js index cadbb00..b560310 100644 --- a/web/client/src/sections/service/actions/emPush.js +++ b/web/client/src/sections/service/actions/emPush.js @@ -10,7 +10,7 @@ export function getPush (query) { //获取推送配置列表 actionType: 'GET_PUSH', url: `${ApiTable.getPush}`, msg: { error: '获取推送配置列表失败' }, - reducer: { name: '' } + reducer: { name: "getPush", params: { noClear: true } }, }); } diff --git a/web/client/src/sections/service/components/pushModal.jsx b/web/client/src/sections/service/components/pushModal.jsx index 3c089c9..d271001 100644 --- a/web/client/src/sections/service/components/pushModal.jsx +++ b/web/client/src/sections/service/components/pushModal.jsx @@ -378,7 +378,6 @@ function pushModal (props) { labelAlign="right" labelWidth="120px" onValueChange={(values, field) => { - console.log('values', values); for (var key in field) { if (key == 'tactics') { if (values.tactics == 'abnormal_rate') { diff --git a/web/client/src/sections/service/containers/emPush.jsx b/web/client/src/sections/service/containers/emPush.jsx index 1e1354a..8a4795d 100644 --- a/web/client/src/sections/service/containers/emPush.jsx +++ b/web/client/src/sections/service/containers/emPush.jsx @@ -7,7 +7,6 @@ import moment from "moment"; import PushModal from '../components/pushModal' import '../style.less' import { Setup } from "$components"; -// import { set } from 'nprogress'; const EmPush = (props) => { const form = useRef();//表单 @@ -15,22 +14,13 @@ const EmPush = (props) => { const { service } = actions; const [setup, setSetup] = useState(false); //表格设置是否显现 const [setupp, setSetupp] = useState([]);//实际显示的表格列表 - const [tableSetup, setTableSetup] = useState([]); //单一表格设置信息 const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 const [limits, setLimits] = useState()//每页实际条数 const mylimits = useRef(); //每页实际条数 const [pushModal, setPushModal] = useState(false) //信鸽弹框 const [pushEdit, setPushEdit] = useState(false) //是否是修改 - const [anxincloudList, setAnxincloudList] = useState([]) //安心云列表 - const [peplist, setPeplist] = useState([]) //PEP项目管理项目列表 - const [appList, setAppList] = useState([]) //应用列表 - const [pepProjectId, setPepProjectId] = useState() //修改时项企id - const [anxincloudArr, setAnxincloudArr] = useState([]) //修改时已经选择的安心云列表 - const [pepname, setPepname] = useState() //修改时自定义项目名称 - const [anxinDelete, setAnxinDelete] = useState([]) //修改时安心云项目有删除,显示提示信息 - const [appArr, setAppArr] = useState([]) //修改时添加应用 - const [bindId, setBindId] = useState() //修改时绑定的id - const [tableKey, setTableKey] = useState([]) //修改时绑定的id + const [change, setChange] = useState(false) //是否改变 + const [allTableData, setAllTableData] = useState([]) //获取到的所有表格信息 const [editObj, setEditObj] = useState({});//管理员弹框修改内容 const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 const page = useRef(query.page);//哪一页 @@ -82,16 +72,6 @@ const EmPush = (props) => { const [tableData, setTableData] = useState([]) //表格数据 useEffect(() => { - // dispatch(service.getPush(query)).then((res) => {//获取推送配置列表 - // console.log('res.payload.datares.payload.data',res.payload.data); - // setAnxincloudList(res.payload.data) - // }) - // dispatch(install.getProjectPmanage(query)).then((res) => {//获取PEP项目管理项目 - // setPeplist(res.payload.data) - // }) - // dispatch(install.getProjectAppList(query)).then((res) => {//获取应用列表 - // setAppList(res.payload.data) - // }) localStorage.getItem(EMPUSH) == null ? localStorage.setItem( EMPUSH, @@ -99,23 +79,28 @@ const EmPush = (props) => { ) : ""; getProjectStatusList() + getPushList(query); }, []) useEffect(() => { - getPushList(); - }, [query]); + let showTableData = JSON.parse(JSON.stringify(allTableData)).slice(query.page * query.limit, (query.page + 1) * query.limit) + setTableData(showTableData) + mylimits.current = showTableData.length + }, [change]); - function getPushList () { + function getPushList (query) { let val = form.current.getValues() - // , ...query dispatch(service.getPush({ ...val })).then((res) => {//获取已绑定项目 if (res.success) { let mytableData = JSON.parse(JSON.stringify(res.payload.data)); for (let index = 0; index < mytableData.length; index++) { mytableData[index].key = String(mytableData[index].id) } - setTableData(mytableData) + setAllTableData(mytableData) + let showTableData = mytableData.slice(query.page * query.limit, (query.page + 1) * query.limit) + setTableData(showTableData) + setQuery(query) setLimits(res.payload.data.length) - mylimits.current = res.payload.data.length + mylimits.current = showTableData.length } }) } @@ -138,37 +123,9 @@ const EmPush = (props) => {