From 16ab1cf6c11618270a3bc317bba1608962ee26e2 Mon Sep 17 00:00:00 2001 From: wuqun Date: Wed, 26 Oct 2022 16:25:29 +0800 Subject: [PATCH 01/22] =?UTF-8?q?(*)=E9=97=AE=E9=A2=98=E5=A4=84=E7=BD=AE?= =?UTF-8?q?=E6=95=88=E7=8E=87=E5=88=86=E6=9E=90=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=9A=82=E4=BA=A4;=20=E8=8E=B7=E5=8F=96=E5=A4=84?= =?UTF-8?q?=E7=BD=AE=E6=95=88=E7=8E=87=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/control/data.js | 22 + api/app/lib/models/alarm_handle_statistics.js | 98 ++++ api/app/lib/routes/control/index.js | 3 + .../lib/schedule/alarms_handle_statistics.js | 550 ++++++++++++++++++ 4 files changed, 673 insertions(+) create mode 100644 api/app/lib/models/alarm_handle_statistics.js create mode 100644 api/app/lib/schedule/alarms_handle_statistics.js diff --git a/api/app/lib/controllers/control/data.js b/api/app/lib/controllers/control/data.js index 26f1fcf..1cf6684 100644 --- a/api/app/lib/controllers/control/data.js +++ b/api/app/lib/controllers/control/data.js @@ -209,6 +209,26 @@ async function getVideoAlarmsAggDay(ctx) { } } +//BI分析-问题处置效率分析 +async function getAlarmsHandleStatistics(ctx) { + try { + const { projectCorrelationId } = ctx.query + const models = ctx.fs.dc.models; + const data = await models.AlarmHandleStatistics.findAll({ + order: [['time', 'DESC']], + where: projectCorrelationId ? { projectCorrelationId: projectCorrelationId } : {}, + limit: 1 + }) + ctx.status = 200; + ctx.body = data; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} //最新动态 async function getLatestDynamic(ctx) { try { @@ -308,5 +328,7 @@ module.exports = { getDataAlarmsAggDay, getAppAlarmsAggDay, getVideoAlarmsAggDay, + getAlarmsHandleStatistics, + getLatestDynamic }; \ No newline at end of file diff --git a/api/app/lib/models/alarm_handle_statistics.js b/api/app/lib/models/alarm_handle_statistics.js new file mode 100644 index 0000000..ad5ef7d --- /dev/null +++ b/api/app/lib/models/alarm_handle_statistics.js @@ -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; +}; \ No newline at end of file diff --git a/api/app/lib/routes/control/index.js b/api/app/lib/routes/control/index.js index 95aa8e5..bda94bc 100644 --- a/api/app/lib/routes/control/index.js +++ b/api/app/lib/routes/control/index.js @@ -36,6 +36,9 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/video/alarms/agg/day'] = { content: '查询BI分析数据-视频异常', visible: false }; router.get('/video/alarms/agg/day', csData.getVideoAlarmsAggDay); + app.fs.api.logAttr['GET/alarms/handle/statistics'] = { content: '查询BI分析数据-问题处理效率分析', visible: false }; + router.get('/alarms/handle/statistics', csData.getAlarmsHandleStatistics); + //最新动态 app.fs.api.logAttr['GET/latest/dynamic'] = { content: '查询最新动态', visible: false }; router.get('/latest/dynamic', csData.getLatestDynamic); diff --git a/api/app/lib/schedule/alarms_handle_statistics.js b/api/app/lib/schedule/alarms_handle_statistics.js new file mode 100644 index 0000000..f4e945f --- /dev/null +++ b/api/app/lib/schedule/alarms_handle_statistics.js @@ -0,0 +1,550 @@ +'use strict'; +const moment = require('moment'); +const request = require('superagent'); + +module.exports = function (app, opts) { + const { models } = app.fs.dc + const { clickHouse, utils: { anxinStrucIdRange } } = app.fs + const { database: anxinyun } = clickHouse.anxinyun.opts.config + const alarmHandleStatistics = app.fs.scheduleInit( + { + interval: '40 8 15 * * *', + // immediate: true, + //proRun: true, + }, + async () => { + try { + let time = moment().format() + 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 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 pid = 26 + let pDataAlarms = dataAlarms.filter(da => da.pomsProject.map(dap => dap.id).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.map(dap => dap.id).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 ') : ''}` + + const alarmRes = await clickHouse.dataAlarm.query(` + SELECT + alarms.AlarmId AS AlarmId, + alarms.State AS State, + alarms.StructureId AS StructureId, + SourceName, StartTime, EndTime + ${alarmQueryOptionStr}`).toPromise(); + + 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 , max(Content) AS Content + 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 + + // 最新告警详情 - 确认信息 + 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: { + exclude: ['projectId'] + }, + include: [{ + model: models.ProjectCorrelation, + attributes: ['id'] + }] + }] + } + findOption.where['$app->projectCorrelations.id$'] = { + $in: pomsProjectIds + } + const listRes = await models.AppAlarm.findAll(findOption) + 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}'`) + + const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query( + ` + SELECT + cameraAlarm.cameraId AS cameraId, + cameraAlarm.cameraName AS cameraName, + cameraAlarm.cameraKindId AS cameraKindId, + cameraAlarm.venderId AS venderId, + cameraAlarm.venderName AS venderName, + cameraAlarm.cameraSerialNo AS cameraSerialNo, + cameraAlarm.cameraChannelNo AS cameraChannelNo, + cameraAlarm.alarmId AS alarmId, + cameraAlarm.createTime AS createTime, + cameraAlarm.updateTime AS updateTime, + cameraAlarm.platform AS platform, + cameraAlarm.confirmContent AS confirmContent, + cameraAlarm.confirmTime AS confirmTime, + ${'cameraAlarm.autoRestore AS autoRestore,'} + camera_status_resolve.id AS resolveId, + camera_status.describe AS statusDescribe, + camera_status_resolve.resolve AS resolve, + "gbCamera".online AS cameraOnline, + secret_yingshi.token AS yingshiToken, + 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.kind_id AS cameraKindId, + camera.vender_id AS venderId, + camera.yingshi_secret_id AS yingshiSecretId, + vender.name AS venderName, + camera_status_alarm.id AS alarmId, + camera_status_alarm.create_time AS createTime, + camera_status_alarm.update_time AS updateTime, + 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.confirm AS confirmContent, + ${'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 + LEFT JOIN vender + ON vender.id = camera.vender_id + 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 "gbCamera" + ON "gbCamera".id = cameraAlarm.gbId + LEFT JOIN "secret_yingshi" + ON "secret_yingshi".id = cameraAlarm.yingshiSecretId + + 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 + LEFT JOIN ${anxinyun}.t_sensor AS anxinStation + ON anxinStation.id = anxinIpcStation.station + `).toPromise() : [] + + let returnD = [] + let positionD = {} + // 每个设备一个告警 + for (let a of alarmRes) { + if (positionD[a.cameraId]) { + let curD = returnD[positionD[a.cameraId].positionReturnD] + if (a.resolveId && !curD.resolve.some(r => r.id == a.resolveId)) { + curD.resolve.push({ + id: a.resolveId, + resolve: a.resolve + }) + } + 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, + + venderId: a.venderId, + venderName: a.venderName, + cameraKindId: a.cameraKindId, + yingshiToken: a.yingshiToken, + + resolve: [], + struc: [], + station: [] + } + + // pep 项目 + d.pomsProject = ( + anxinStruc.find(as => as.strucId == a.strucId) || + { + pomsProject: [ + + ] + } + ).pomsProject + + if (a.resolveId) { + d.resolve.push({ + id: a.resolveId, + resolve: a.resolve + }) + } + 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 + } + } + } + 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) { + if (!undelStruc.some(us => us.strucId == s.strucId)) { + let pomsProject = [] + for (let { dataValues: br } of bindRes) { + if (br.anxinProjectId.some(braId => braId == s.projectId)) { + let corPepProject = pepProjectRes.find(pp => pp.id == br.pepProjectId) + pomsProject.push({ + ...br, + pepProject: corPepProject + }) + } + } + undelStruc.push({ + strucId: s.strucId, + strucName: s.strucName, + projectId: s.projectId, + pomsProject: pomsProject + }) + } + } + return undelStruc + } catch (error) { + console.error(error); + } + } + return { + alarmHandleStatistics + } +} \ No newline at end of file From 518fb3d974c191b9c5e16c00e4573cbf01bb82a4 Mon Sep 17 00:00:00 2001 From: wuqun Date: Wed, 26 Oct 2022 17:44:35 +0800 Subject: [PATCH 02/22] =?UTF-8?q?(*)=E5=91=8A=E8=AD=A6=E5=A4=84=E7=BD=AE?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1,=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/schedule/alarms_handle_statistics.js | 168 +++++++----------- 1 file changed, 66 insertions(+), 102 deletions(-) diff --git a/api/app/lib/schedule/alarms_handle_statistics.js b/api/app/lib/schedule/alarms_handle_statistics.js index f4e945f..37097ae 100644 --- a/api/app/lib/schedule/alarms_handle_statistics.js +++ b/api/app/lib/schedule/alarms_handle_statistics.js @@ -8,7 +8,7 @@ module.exports = function (app, opts) { const { database: anxinyun } = clickHouse.anxinyun.opts.config const alarmHandleStatistics = app.fs.scheduleInit( { - interval: '40 8 15 * * *', + interval: '10 41 17 * * *', // immediate: true, //proRun: true, }, @@ -42,10 +42,10 @@ module.exports = function (app, opts) { //算单个项目 //pomsProject.map(p => { //let pid = p.id; - let pid = 26 - let pDataAlarms = dataAlarms.filter(da => da.pomsProject.map(dap => dap.id).indexOf(pid) != -1) + let pid = 22 + 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.map(dap => dap.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; @@ -154,14 +154,14 @@ module.exports = function (app, opts) { alarms.AlarmId AS AlarmId, alarms.State AS State, alarms.StructureId AS StructureId, - SourceName, StartTime, EndTime + StartTime, EndTime ${alarmQueryOptionStr}`).toPromise(); 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 , max(Content) AS Content + max(Time) AS Time, AlarmId FROM alarm_details WHERE @@ -177,7 +177,7 @@ module.exports = function (app, opts) { // TODO: 开发临时添加 ] } - ).pomsProject + ).pomsProject.map(p => p.id) // 最新告警详情 - 确认信息 let corConfirmedData = (confirmedAlarmDetailMax.find(cdm => cdm.AlarmId == ar.AlarmId) || {}); @@ -199,9 +199,7 @@ module.exports = function (app, opts) { attributes: ['id', 'createTime', 'confirmTime'], include: [{ model: models.App, - attributes: { - exclude: ['projectId'] - }, + attributes: ['id'], include: [{ model: models.ProjectCorrelation, attributes: ['id'] @@ -228,55 +226,27 @@ module.exports = function (app, opts) { ` SELECT cameraAlarm.cameraId AS cameraId, - cameraAlarm.cameraName AS cameraName, - cameraAlarm.cameraKindId AS cameraKindId, - cameraAlarm.venderId AS venderId, - cameraAlarm.venderName AS venderName, - cameraAlarm.cameraSerialNo AS cameraSerialNo, - cameraAlarm.cameraChannelNo AS cameraChannelNo, cameraAlarm.alarmId AS alarmId, cameraAlarm.createTime AS createTime, - cameraAlarm.updateTime AS updateTime, - cameraAlarm.platform AS platform, - cameraAlarm.confirmContent AS confirmContent, cameraAlarm.confirmTime AS confirmTime, ${'cameraAlarm.autoRestore AS autoRestore,'} - camera_status_resolve.id AS resolveId, - camera_status.describe AS statusDescribe, - camera_status_resolve.resolve AS resolve, - "gbCamera".online AS cameraOnline, - secret_yingshi.token AS yingshiToken, - anxinIpc.t_video_ipc.name AS anxinIpcPosition, - anxinStation.id AS anxinStationId, - anxinStation.name AS anxinStationName, - anxinStruc.name AS strucName, - anxinStruc.id AS strucId + anxinStruc.id AS strucId FROM ( SELECT camera.id AS cameraId, - camera.gb_id AS gbId, - camera.name AS cameraName, - camera.kind_id AS cameraKindId, - camera.vender_id AS venderId, - camera.yingshi_secret_id AS yingshiSecretId, - vender.name AS venderName, camera_status_alarm.id AS alarmId, camera_status_alarm.create_time AS createTime, - camera_status_alarm.update_time AS updateTime, 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.confirm AS confirmContent, ${'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 - LEFT JOIN vender - ON vender.id = camera.vender_id WHERE camera.delete = false AND camera.recycle_time is null @@ -295,10 +265,6 @@ module.exports = function (app, opts) { AND cameraAlarm.statusId = camera_status.id LEFT JOIN camera_status_resolve ON camera_status_resolve.status_id = camera_status.id - LEFT JOIN "gbCamera" - ON "gbCamera".id = cameraAlarm.gbId - LEFT JOIN "secret_yingshi" - ON "secret_yingshi".id = cameraAlarm.yingshiSecretId LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo @@ -308,8 +274,6 @@ module.exports = function (app, opts) { AND anxinStruc.id IN (${anxinStrucIds.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 = [] @@ -317,51 +281,51 @@ module.exports = function (app, opts) { // 每个设备一个告警 for (let a of alarmRes) { if (positionD[a.cameraId]) { - let curD = returnD[positionD[a.cameraId].positionReturnD] - if (a.resolveId && !curD.resolve.some(r => r.id == a.resolveId)) { - curD.resolve.push({ - id: a.resolveId, - resolve: a.resolve - }) - } - 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 - }) - } + //let curD = returnD[positionD[a.cameraId].positionReturnD] + // if (a.resolveId && !curD.resolve.some(r => r.id == a.resolveId)) { + // curD.resolve.push({ + // id: a.resolveId, + // resolve: a.resolve + // }) + // } + // 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, + //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, + //updateTime: a.updateTime, + //platform: a.platform, + //statusDescribe: a.statusDescribe, alarmId: a.alarmId, - confirmContent: a.confirmContent, + //confirmContent: a.confirmContent, confirmTime: a.confirmTime, - venderId: a.venderId, - venderName: a.venderName, - cameraKindId: a.cameraKindId, - yingshiToken: a.yingshiToken, + // venderId: a.venderId, + // venderName: a.venderName, + // cameraKindId: a.cameraKindId, + // yingshiToken: a.yingshiToken, - resolve: [], - struc: [], - station: [] + // resolve: [], + // struc: [], + // station: [] } // pep 项目 @@ -372,28 +336,28 @@ module.exports = function (app, opts) { ] } - ).pomsProject + ).pomsProject.map(d => d.id) - if (a.resolveId) { - d.resolve.push({ - id: a.resolveId, - resolve: a.resolve - }) - } - 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 - }) - } + // if (a.resolveId) { + // d.resolve.push({ + // id: a.resolveId, + // resolve: a.resolve + // }) + // } + // 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 From 50ebf6e493bb05b30b1a9266e9f18162908cf240 Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Wed, 26 Oct 2022 17:47:16 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/.vscode/launch.json | 4 +- api/app/lib/controllers/alarm/video.js | 182 ++++++++------- api/app/lib/controllers/organization/index.js | 26 ++- api/app/lib/controllers/project/index.js | 93 ++++++++ api/app/lib/controllers/push/config.js | 155 +++++++++--- api/app/lib/models/alarm_push_config.js | 220 ++++++++++-------- api/app/lib/models/email_send_log.js | 30 ++- api/app/lib/routes/organization/index.js | 3 + api/app/lib/routes/project/index.js | 6 + api/app/lib/routes/push/index.js | 2 +- api/sequelize-automate.config.js | 2 +- script/0.0.7/schema/1.update_alarm_push.sql | 15 ++ 12 files changed, 512 insertions(+), 226 deletions(-) create mode 100644 script/0.0.7/schema/1.update_alarm_push.sql diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index 3388416..f58978c 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -16,9 +16,9 @@ "-p 4600", "-f http://localhost:4600", // 研发 - // "-g postgres://postgres:123@10.8.30.32:5432/orational_service", + "-g postgres://postgres:123@10.8.30.32:5432/orational_service", // 测试 - "-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS", + // "-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS", "-k node35:6667,node36:6667,node37:6667", "--iotaProxy http://10.8.30.157:17007", "--redisHost 10.8.30.112", diff --git a/api/app/lib/controllers/alarm/video.js b/api/app/lib/controllers/alarm/video.js index 0e546cb..a0d0096 100644 --- a/api/app/lib/controllers/alarm/video.js +++ b/api/app/lib/controllers/alarm/video.js @@ -21,7 +21,7 @@ async function deviceType (ctx) { } } -async function alarmList(ctx, agg) { +async function alarmList (ctx, agg) { try { const { models } = ctx.fs.dc; const { clickHouse } = ctx.app.fs @@ -67,99 +67,101 @@ async function alarmList(ctx, agg) { ) `) } - const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query( - ` + + const queryStr = ` SELECT - cameraAlarm.cameraId AS cameraId, - cameraAlarm.cameraName AS cameraName, - cameraAlarm.cameraKindId AS cameraKindId, - cameraAlarm.venderId AS venderId, - cameraAlarm.venderName AS venderName, - cameraAlarm.cameraSerialNo AS cameraSerialNo, - cameraAlarm.cameraChannelNo AS cameraChannelNo, - cameraAlarm.alarmId AS alarmId, - cameraAlarm.createTime AS createTime, - cameraAlarm.updateTime AS updateTime, - cameraAlarm.platform AS platform, - cameraAlarm.confirmContent AS confirmContent, - cameraAlarm.confirmTime AS confirmTime, - ${'cameraAlarm.autoRestore AS autoRestore,'} - camera_status_resolve.id AS resolveId, - camera_status.describe AS statusDescribe, - camera_status_resolve.resolve AS resolve, - "gbCamera".online AS cameraOnline, - secret_yingshi.token AS yingshiToken, - 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.kind_id AS cameraKindId, - camera.vender_id AS venderId, - camera.yingshi_secret_id AS yingshiSecretId, - vender.name AS venderName, - camera_status_alarm.id AS alarmId, - camera_status_alarm.create_time AS createTime, - camera_status_alarm.update_time AS updateTime, - 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.confirm AS confirmContent, - ${'camera_status_alarm.auto_restore AS autoRestore,'} - camera_status_alarm.confirm_time AS confirmTime + cameraAlarm.cameraId AS cameraId, + cameraAlarm.cameraName AS cameraName, + cameraAlarm.cameraKindId AS cameraKindId, + cameraAlarm.venderId AS venderId, + cameraAlarm.venderName AS venderName, + cameraAlarm.cameraSerialNo AS cameraSerialNo, + cameraAlarm.cameraChannelNo AS cameraChannelNo, + cameraAlarm.alarmId AS alarmId, + cameraAlarm.createTime AS createTime, + cameraAlarm.updateTime AS updateTime, + cameraAlarm.platform AS platform, + cameraAlarm.confirmContent AS confirmContent, + cameraAlarm.confirmTime AS confirmTime, + ${'cameraAlarm.autoRestore AS autoRestore,'} + camera_status_resolve.id AS resolveId, + camera_status.describe AS statusDescribe, + camera_status_resolve.resolve AS resolve, + "gbCamera".online AS cameraOnline, + secret_yingshi.token AS yingshiToken, + 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.kind_id AS cameraKindId, + camera.vender_id AS venderId, + camera.yingshi_secret_id AS yingshiSecretId, + vender.name AS venderName, + camera_status_alarm.id AS alarmId, + camera_status_alarm.create_time AS createTime, + camera_status_alarm.update_time AS updateTime, + 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.confirm AS confirmContent, + ${'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 + ${cameraWhereOption.length ? 'AND ' + cameraWhereOption.join(' AND ') : ''} + LEFT JOIN vender + ON vender.id = camera.vender_id + 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 - INNER JOIN camera - ON camera.serial_no = camera_status_alarm.serial_no - AND camera.channel_no = camera_status_alarm.channel_no - ${cameraWhereOption.length ? 'AND ' + cameraWhereOption.join(' AND ') : ''} - LEFT JOIN vender - ON vender.id = camera.vender_id - 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(',')})`} - ) - ${limit ? 'LIMIT ' + limit : ''} - ${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''} - ) 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 "gbCamera" - ON "gbCamera".id = cameraAlarm.gbId - LEFT JOIN "secret_yingshi" - ON "secret_yingshi".id = cameraAlarm.yingshiSecretId + 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(',')})`} + ) + ${limit ? 'LIMIT ' + limit : ''} + ${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''} + ) 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 "gbCamera" + ON "gbCamera".id = cameraAlarm.gbId + LEFT JOIN "secret_yingshi" + ON "secret_yingshi".id = cameraAlarm.yingshiSecretId - 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(',')}) - ${keywordTarget == 'struc' && keyword ? `AND anxinStruc.name LIKE '%${keyword}%'` : ''} - 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 - ` + 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(',')}) + ${keywordTarget == 'struc' && keyword ? `AND anxinStruc.name LIKE '%${keyword}%'` : ''} + 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 + ` + const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query( + queryStr ).toPromise() : [] - + console.log(queryStr); let returnD = [] let positionD = {} // 每个设备一个告警 diff --git a/api/app/lib/controllers/organization/index.js b/api/app/lib/controllers/organization/index.js index 7643065..c6d048d 100644 --- a/api/app/lib/controllers/organization/index.js +++ b/api/app/lib/controllers/organization/index.js @@ -22,6 +22,27 @@ async function allDeps (ctx) { } } +async function allUsers (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + + const userRes = await clickHouse.pepEmis.query(` + SELECT id, name FROM user + WHERE delete = false + `).toPromise() + + ctx.status = 200; + ctx.body = userRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: error`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + async function editUser (ctx) { try { const models = ctx.fs.dc.models; @@ -183,7 +204,7 @@ async function user (ctx) { $not: { role: { $contained: ['SuperAdmin', 'admin'] } } - }, + }, sequelize.where(sequelize.fn('cardinality', sequelize.col('role')), 0)], // $not: { // role: { $contained: ['SuperAdmin', 'admin'] } @@ -254,7 +275,7 @@ async function user (ctx) { // 查对应的项企项目信息 let pepProjectRes = pepPojectIds.size ? await clickHouse.projectManage.query(` - SELECT id, project_name, isdelete FROM t_pim_project WHERE id IN (${[...pepPojectIds]}) + SELECT id, project_name isdelete FROM t_pim_project WHERE id IN (${[...pepPojectIds]}) `).toPromise() : [] @@ -311,4 +332,5 @@ module.exports = { putUser, delAdmin, user, + allUsers, }; \ No newline at end of file diff --git a/api/app/lib/controllers/project/index.js b/api/app/lib/controllers/project/index.js index 83797ba..0abf675 100644 --- a/api/app/lib/controllers/project/index.js +++ b/api/app/lib/controllers/project/index.js @@ -145,9 +145,102 @@ async function projectPManage (ctx) { } } +async function pepProjectConstrictionState (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + + const cRes = await clickHouse.projectManage.query(` + SELECT * FROM t_pim_project_state ORDER BY id + `).toPromise() + + ctx.status = 200; + ctx.body = cRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: error`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function strucWithPomsProject (ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { pomsProjectId } = ctx.query + + const bindRes = await models.ProjectCorrelation.findOne({ + where: { + id: pomsProjectId + } + }) + + let undelStruc = [] + if (bindRes) { + const undelStrucRes = bindRes.anxinProjectId.length ? + await clickHouse.anxinyun.query( + ` + SELECT + t_structure.id AS strucId, + t_structure.name AS strucName + 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 (${ bindRes.anxinProjectId.join(',')}) + ORDER BY strucId + ` + ).toPromise() : + [] + for (let s of undelStrucRes) { + if (!undelStruc.some(us => us.id == s.strucId)) { + undelStruc.push({ + id: s.strucId, + name: s.strucName, + }) + } + } + } + + ctx.status = 200; + ctx.body = undelStruc + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: error`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + module.exports = { appList, projectAnxincloud, projectPManage, pomsProject, + pepProjectConstrictionState, + strucWithPomsProject, }; \ No newline at end of file diff --git a/api/app/lib/controllers/push/config.js b/api/app/lib/controllers/push/config.js index f4cba4f..7e8325e 100644 --- a/api/app/lib/controllers/push/config.js +++ b/api/app/lib/controllers/push/config.js @@ -4,21 +4,40 @@ const moment = require('moment') async function list (ctx) { try { const models = ctx.fs.dc.models; - const { keyword, alarmType, state, } = ctx.request.body + const sequelize = ctx.fs.dc.ORM; + const { clickHouse } = ctx.app.fs + const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs + const { keyword, keywordTarget, alarmType, state, tactics, pomsProjectId } = ctx.request.body let findOption = { where: { - + del: false } } - if (keyword) { - findOption.where.$or = [ - { - name: { $like: `%${keyword}%` } - }, - ] + let anxinStrucsRange = await anxinStrucIdRange({ + ctx, pepProjectId: pomsProjectId, keywordTarget, keyword + }) + if (keywordTarget && keyword) { + if (keywordTarget == 'tactics') { + findOption.where.name = { $like: `%${keyword}%` } + } else if (keywordTarget == 'struc') { + let bindAnixinStrucRes = await clickHouse.anxinyun.query(` + SELECT id, name FROM t_structure + WHERE name LIKE '%${keyword}%' + `).toPromise() + let bindAnixinStrucIds = bindAnixinStrucRes.map(s => s.id) + findOption.where.strucId = { $contains: bindAnixinStrucIds } + } else if (keywordTarget == 'pepProject') { + + } } + const pomsProjectRes = await pomsProjectRange({ + ctx, pepProjectId: pomsProjectId, keywordTarget, keyword + }) + let pomsProjectIds = pomsProjectRes.map(p => p.id) + findOption.where.pomsProjectId = { $in: pomsProjectIds } + if (alarmType) { findOption.where.alarmType = { $contains: [alarmType] } } @@ -27,15 +46,99 @@ async function list (ctx) { findOption.where.disable = false } else if (state == 'disable') { findOption.where.disable = true - } else if (state == 'overtime') { + } + } + if (tactics) { + findOption.where.tactics = tactics + } + const listRes = await models.AlarmPushConfig.findAll(findOption) + let allStrucIds = new Set() + let allConfigId = [] + let allReceiverIds = new Set() + for (let p of listRes) { + for (let sid of p.strucId) { + allStrucIds.add(sid) + } + allConfigId.push(p.id) + for (let uid of p.receiverPepUserId) { + allReceiverIds.add(uid) } } + // 查配置所包含的所有结构物 + const allStrucRes = allStrucIds.size ? await clickHouse.anxinyun.query(` + SELECT id, name FROM t_structure + WHERE id IN (${[...allStrucIds].join(',')}) + `).toPromise() : [] + + // 查所有配置的推送的次数 + const pushLogCountRes = await models.EmailSendLog.findAll({ + attributes: [ + 'pushConfigId', + [sequelize.fn('COUNT', sequelize.col('push_config_id')), 'count'] + ], + where: { + pushConfigId: { $in: allConfigId } + }, + group: ['pushConfigId'] + }) + + // 查询所有的用户信息 + const userRes = allReceiverIds.size ? await clickHouse.pepEmis.query(` + SELECT id, name, delete FROM user + WHERE id IN (${[...allReceiverIds].join(',')}) + `).toPromise() : [] + + let returnD = [] + for (let { dataValues: p } of listRes) { + // 查对应的 poms 绑定的结构物绑定关系 + const corBind = pomsProjectRes.find(ppj => ppj.id == p.pomsProjectId) + if (corBind.pepProjectId) { + if (state == 'notYet') { + if (corBind.pepProject && p.timeType.some(pt => pt == corBind.pepProject.constructionStatusId)) { + continue + } + } else if (state == 'takeEffect') { + if (!corBind.pepProject || !p.timeType.some(pt => pt == corBind.pepProject.constructionStatusId)) { + continue + } + } + } + + // 结构物信息 + let returnStruc = [] + for (let sid of p.strucId) { + // 查这个结构物的信息 + let structure = allStrucRes.find(asr => asr.id == sid) + if (structure) { + // 检查当前结构物有没有从已绑定的项目里解绑 + // 查对应的可见的结构物信息 + const anxinStrucSeen = anxinStrucsRange.find(axs => axs.strucId == sid) + returnStruc.push({ + id: sid, + name: structure.name, + unbind: !anxinStrucSeen || !corBind.anxinProjectId.includes(anxinStrucSeen.projectId) + }) + } else { + // 这个结构物已删 + } + } - const listRes = await models.AlarmPushConfig.findAndCountAll(findOption) + // 查找日志数量 + const corLogCount = pushLogCountRes.find(plc => plc.pushConfigId == p.id) + // 查找接收人数据 + const corReceiver = userRes.filter(u => p.receiverPepUserId.some(prId => u.id == prId)) + returnD.push({ + ...p, + pomsProject: corBind, + structure: returnStruc, + pushCount: corLogCount ? corLogCount.count : 0, + receiverPepUser: corReceiver + }) + } ctx.status = 200; - ctx.body = listRes + ctx.body = returnD } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; @@ -49,10 +152,12 @@ async function edit (ctx) { try { const models = ctx.fs.dc.models; const { userId, pepUserId } = ctx.fs.api - const { pushId, name, pepProjectId = [], alarmType = [], receiverPepUserId = [], timeType = [], disable } = ctx.request.body + const { pushId, name, pomsProjectId, alarmType = [], receiverPepUserId = [], timeType = [], disable, + strucId = [], tactics, tacticsParams } = ctx.request.body let storageData = { - name, pepProjectId, alarmType, receiverPepUserId, timeType, disable + name, pomsProjectId, alarmType, receiverPepUserId, timeType, disable, + strucId, tactics, tacticsParams } if (pushId) { await models.AlarmPushConfig.update(storageData, { @@ -63,6 +168,7 @@ async function edit (ctx) { } else { storageData.createTime = moment().format() storageData.createUserId = userId + storageData.del = false await models.AlarmPushConfig.create(storageData) } @@ -82,22 +188,17 @@ async function state (ctx) { const { pushId } = ctx.params const { disable = undefined, del = undefined, } = ctx.request.body - if (del) { - await models.AlarmPushConfig.destroy({ - where: { - id: pushId - } - }) - } else { - await models.AlarmPushConfig.update({ - disable, - }, { - where: { - id: pushId - } - }) + let updateData = { + disable, + del, } + await models.AlarmPushConfig.update(updateData, { + where: { + id: pushId + } + }) + ctx.status = 204; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); diff --git a/api/app/lib/models/alarm_push_config.js b/api/app/lib/models/alarm_push_config.js index 296d53d..7ea7a19 100644 --- a/api/app/lib/models/alarm_push_config.js +++ b/api/app/lib/models/alarm_push_config.js @@ -2,96 +2,132 @@ 'use strict'; module.exports = dc => { - const DataTypes = dc.ORM; - const sequelize = dc.orm; - const AlarmPushConfig = sequelize.define("alarmPushConfig", { - id: { - type: DataTypes.INTEGER, - allowNull: false, - defaultValue: null, - comment: null, - primaryKey: true, - field: "id", - autoIncrement: true, - unique: "alarm_push_config_id_uindex" - }, - name: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: null, - comment: null, - primaryKey: false, - field: "name", - autoIncrement: false - }, - pepProjectId: { - type: DataTypes.ARRAY(DataTypes.INTEGER), - allowNull: false, - defaultValue: null, - comment: null, - primaryKey: false, - field: "pep_project_id", - autoIncrement: false - }, - alarmType: { - type: DataTypes.ARRAY(DataTypes.STRING), - allowNull: true, - defaultValue: null, - comment: "监听的告警类型", - primaryKey: false, - field: "alarm_type", - autoIncrement: false - }, - receiverPepUserId: { - type: DataTypes.ARRAY(DataTypes.INTEGER), - allowNull: true, - defaultValue: null, - comment: "接收人id 项企", - primaryKey: false, - field: "receiver_pep_user_id", - autoIncrement: false - }, - timeType: { - type: DataTypes.ARRAY(DataTypes.STRING), - allowNull: true, - defaultValue: null, - comment: "通知时效", - primaryKey: false, - field: "time_type", - autoIncrement: false - }, - createTime: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - comment: null, - primaryKey: false, - field: "create_time", - autoIncrement: false - }, - createUserId: { - type: DataTypes.INTEGER, - allowNull: false, - defaultValue: null, - comment: null, - primaryKey: false, - field: "create_user_id", - autoIncrement: false - }, - disable: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: null, - comment: null, - primaryKey: false, - field: "disable", - autoIncrement: false - } - }, { - tableName: "alarm_push_config", - comment: "", - indexes: [] - }); - dc.models.AlarmPushConfig = AlarmPushConfig; - return AlarmPushConfig; + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const AlarmPushConfig = sequelize.define("alarmPushConfig", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "alarm_push_config_id_uindex" + }, + name: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "name", + autoIncrement: false + }, + pomsProjectId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "poms_project_id", + autoIncrement: false + }, + alarmType: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + defaultValue: null, + comment: "监听的告警类型", + primaryKey: false, + field: "alarm_type", + autoIncrement: false + }, + receiverPepUserId: { + type: DataTypes.ARRAY(DataTypes.INTEGER), + allowNull: true, + defaultValue: null, + comment: "接收人id 项企", + primaryKey: false, + field: "receiver_pep_user_id", + autoIncrement: false + }, + timeType: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + defaultValue: null, + comment: "通知时效", + primaryKey: false, + field: "time_type", + autoIncrement: false + }, + createTime: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "create_time", + autoIncrement: false + }, + createUserId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "create_user_id", + autoIncrement: false + }, + disable: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "disable", + autoIncrement: false + }, + strucId: { + type: DataTypes.ARRAY(DataTypes.INTEGER), + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "struc_id", + autoIncrement: false + }, + tactics: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null, + comment: "immediately 即时 / continue 持续 / abnormal_rate 异常率", + primaryKey: false, + field: "tactics", + autoIncrement: false + }, + tacticsParams: { + type: DataTypes.JSONB, + allowNull: true, + defaultValue: null, + comment: "推送策略 tactics 的参数", + primaryKey: false, + field: "tactics_params", + autoIncrement: false + }, + del: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "del", + autoIncrement: false + } + }, { + tableName: "alarm_push_config", + comment: "", + indexes: [] + }); + dc.models.AlarmPushConfig = AlarmPushConfig; + return AlarmPushConfig; }; \ No newline at end of file diff --git a/api/app/lib/models/email_send_log.js b/api/app/lib/models/email_send_log.js index 7f75ea1..bab7a0b 100644 --- a/api/app/lib/models/email_send_log.js +++ b/api/app/lib/models/email_send_log.js @@ -1,5 +1,4 @@ /* eslint-disable*/ - 'use strict'; module.exports = dc => { @@ -16,40 +15,49 @@ module.exports = dc => { autoIncrement: true, unique: "email_send_log_id_uindex" }, - projectCorrelationId: { + toPepUserId: { type: DataTypes.INTEGER, allowNull: false, defaultValue: null, comment: null, primaryKey: false, - field: "project_correlation_id", + field: "to_pep_user_id", autoIncrement: false }, - toPepUserId: { + time: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "time", + autoIncrement: false + }, + pushConfigId: { type: DataTypes.INTEGER, allowNull: false, defaultValue: null, comment: null, primaryKey: false, - field: "to_pep_user_id", + field: "push_config_id", autoIncrement: false }, - by: { + tactics: { type: DataTypes.STRING, allowNull: true, defaultValue: null, comment: null, primaryKey: false, - field: "by", + field: "tactics", autoIncrement: false }, - time: { - type: DataTypes.DATE, - allowNull: false, + tacticsParams: { + type: DataTypes.JSONB, + allowNull: true, defaultValue: null, comment: null, primaryKey: false, - field: "time", + field: "tactics_params", autoIncrement: false } }, { diff --git a/api/app/lib/routes/organization/index.js b/api/app/lib/routes/organization/index.js index 4b26221..5c8106e 100644 --- a/api/app/lib/routes/organization/index.js +++ b/api/app/lib/routes/organization/index.js @@ -5,6 +5,9 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/organization/deps'] = { content: '获取全部部门及其下用户', visible: true }; router.get('/organization/deps', organization.allDeps); + app.fs.api.logAttr['GET/organization/users'] = { content: '获取全部未删除用户', visible: true }; + router.get('/organization/users', organization.allUsers); + app.fs.api.logAttr['POST/organization/user'] = { content: '编辑成员', visible: true }; router.post('/organization/user', organization.editUser); diff --git a/api/app/lib/routes/project/index.js b/api/app/lib/routes/project/index.js index 241aaf3..aa3a5a7 100644 --- a/api/app/lib/routes/project/index.js +++ b/api/app/lib/routes/project/index.js @@ -21,4 +21,10 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/project/poms'] = { content: '获取已绑定项目', visible: true }; router.get('/project/poms', project.pomsProject); + + app.fs.api.logAttr['GET/project/status'] = { content: '获取项目状态列表', visible: true }; + router.get('/project/status', project.pepProjectConstrictionState); + + app.fs.api.logAttr['GET/project/structure'] = { content: '获取绑定项目下结构物', visible: true }; + router.get('/project/structure', project.strucWithPomsProject); }; \ No newline at end of file diff --git a/api/app/lib/routes/push/index.js b/api/app/lib/routes/push/index.js index 560784d..f62393d 100644 --- a/api/app/lib/routes/push/index.js +++ b/api/app/lib/routes/push/index.js @@ -7,7 +7,7 @@ module.exports = function (app, router, opts) { router.get('/push', push.list); app.fs.api.logAttr['POST/push'] = { content: '新增/编辑推送配置', visible: true }; - router.get('/push', push.edit); + router.post('/push', push.edit); app.fs.api.logAttr['PUT/push/:pushId'] = { content: '更改推送配置状态(禁用或删除)', visible: true }; router.put('/push/:pushId', push.state); diff --git a/api/sequelize-automate.config.js b/api/sequelize-automate.config.js index e404aeb..0b063a4 100644 --- a/api/sequelize-automate.config.js +++ b/api/sequelize-automate.config.js @@ -26,7 +26,7 @@ module.exports = { dir: './app/lib/models', // 指定输出 models 文件的目录 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` - tables: ['quick_link'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 + tables: ['alarm_push_config'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 diff --git a/script/0.0.7/schema/1.update_alarm_push.sql b/script/0.0.7/schema/1.update_alarm_push.sql new file mode 100644 index 0000000..ddc1a89 --- /dev/null +++ b/script/0.0.7/schema/1.update_alarm_push.sql @@ -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; From 08d72e78292edf755c1a19f34ad1f1285e700a59 Mon Sep 17 00:00:00 2001 From: wuqun Date: Thu, 27 Oct 2022 09:37:13 +0800 Subject: [PATCH 04/22] =?UTF-8?q?(*)=E5=91=8A=E8=AD=A6=E5=A4=84=E7=BD=AE?= =?UTF-8?q?=E6=95=88=E7=8E=87=E6=9F=A5=E8=AF=A2=E5=A4=84=E7=90=86=E5=85=A5?= =?UTF-8?q?=E5=BA=93=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=20=E5=8A=A0?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/control/data.js | 51 ------------------- api/app/lib/routes/control/index.js | 5 -- .../lib/schedule/alarms_handle_statistics.js | 10 ++++ 3 files changed, 10 insertions(+), 56 deletions(-) diff --git a/api/app/lib/controllers/control/data.js b/api/app/lib/controllers/control/data.js index 1cf6684..6d97f7c 100644 --- a/api/app/lib/controllers/control/data.js +++ b/api/app/lib/controllers/control/data.js @@ -2,55 +2,6 @@ const moment = require('moment'); const { alarmList } = require('../alarm/video'); -//项目概览 -async function getProjectsInfo(ctx) { - try { - const { models } = ctx.fs.dc; - const { clickHouse, utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs - const { database: anxinyun } = clickHouse.anxinyun.opts.config - const { alarmId, limit, page, projectCorrelationId, pepProjectId, keywordTarget, keyword } = ctx.query; - const { userInfo } = ctx.fs.api; - // let where = {} - // if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) { - // where.projectCorrelationId = { $in: userInfo.correlationProject } - // } - // if (projectCorrelationId) {//查指定项目,控制台全局切换 - // where.projectCorrelationId = projectCorrelationId - // } - let anxinStruc = await anxinStrucIdRange({ - ctx, pepProjectId, keywordTarget, keyword - }) - const anxinStrucIds = anxinStruc.map(a => a.strucId); - //先查全部的摄像头 - const videoList = anxinStrucIds.length ? await clickHouse.vcmp.query( - `select camera.id, - camera.name, - camera.serial_no from camera where camera.delete=false and camera.recycle_time is null - - 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 - LEFT JOIN ${anxinyun}.t_sensor AS anxinStation - ON anxinStation.id = anxinIpcStation.station` - ).toPromise() : [] - - - ctx.status = 200; - ctx.body = [] - } catch (error) { - ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); - ctx.status = 400; - ctx.body = { - message: typeof error == 'string' ? error : undefined - } - } -} - //BI分析-数据 async function getDataAlarmsAggDay(ctx) { try { @@ -323,8 +274,6 @@ async function getLatestDynamic(ctx) { } module.exports = { - getProjectsInfo, - getDataAlarmsAggDay, getAppAlarmsAggDay, getVideoAlarmsAggDay, diff --git a/api/app/lib/routes/control/index.js b/api/app/lib/routes/control/index.js index b380664..23e4542 100644 --- a/api/app/lib/routes/control/index.js +++ b/api/app/lib/routes/control/index.js @@ -24,11 +24,6 @@ module.exports = function (app, router, opts) { router.get('/analysis/userlist', analysis.userlist); - - //项目概览 - app.fs.api.logAttr['GET/projects/info'] = { content: '查询项目概览', visible: false }; - router.get('/projects/info', csData.getProjectsInfo); - //BI分析模块 app.fs.api.logAttr['GET/data/alarms/agg/day'] = { content: '查询BI分析数据-数据', visible: false }; router.get('/data/alarms/agg/day', csData.getDataAlarmsAggDay); diff --git a/api/app/lib/schedule/alarms_handle_statistics.js b/api/app/lib/schedule/alarms_handle_statistics.js index 37097ae..7b02acd 100644 --- a/api/app/lib/schedule/alarms_handle_statistics.js +++ b/api/app/lib/schedule/alarms_handle_statistics.js @@ -149,6 +149,8 @@ module.exports = function (app, opts) { 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, @@ -157,6 +159,8 @@ module.exports = function (app, opts) { 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(` @@ -209,7 +213,11 @@ module.exports = function (app, opts) { 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); @@ -222,6 +230,7 @@ module.exports = function (app, opts) { 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 @@ -276,6 +285,7 @@ module.exports = function (app, opts) { ON anxinIpcStation.ipc = anxinIpc.id `).toPromise() : [] + console.log('视频-视频-视频告警查询结束---' + moment().format('YYYY-MM-DD HH:mm:ss') + `---一共${alarmRes.length}条`) let returnD = [] let positionD = {} // 每个设备一个告警 From 75ce97f64a5d44663469d9a2a82e1a5bd826d38f Mon Sep 17 00:00:00 2001 From: wuqun Date: Thu, 27 Oct 2022 11:28:36 +0800 Subject: [PATCH 05/22] =?UTF-8?q?(*)=E6=95=B0=E6=8D=AE=E5=91=8A=E8=AD=A6,?= =?UTF-8?q?=20=E5=BA=94=E7=94=A8=E5=91=8A=E8=AD=A6,=20=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E7=A1=AE=E8=AE=A4=E6=97=A5=E5=BF=97=E8=AE=B0?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/controllers/alarm/alarmConfirmLog.js | 49 +++++++++++++++++++ api/app/lib/controllers/alarm/app.js | 27 +--------- api/app/lib/controllers/alarm/data.js | 6 ++- api/app/lib/controllers/alarm/video.js | 29 ++--------- .../sections/problem/containers/dataAlarm.jsx | 47 +++++++++--------- 5 files changed, 83 insertions(+), 75 deletions(-) create mode 100644 api/app/lib/controllers/alarm/alarmConfirmLog.js diff --git a/api/app/lib/controllers/alarm/alarmConfirmLog.js b/api/app/lib/controllers/alarm/alarmConfirmLog.js new file mode 100644 index 0000000..5a91aea --- /dev/null +++ b/api/app/lib/controllers/alarm/alarmConfirmLog.js @@ -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 +}; \ No newline at end of file diff --git a/api/app/lib/controllers/alarm/app.js b/api/app/lib/controllers/alarm/app.js index 723435e..4472c04 100644 --- a/api/app/lib/controllers/alarm/app.js +++ b/api/app/lib/controllers/alarm/app.js @@ -1,7 +1,7 @@ 'use strict'; const moment = require('moment') - +const { alarmConfirmLog } = require('./alarmConfirmLog'); async function inspection(ctx) { // 巡查 try { @@ -328,7 +328,6 @@ async function confirmApiError(ctx) { try { const models = ctx.fs.dc.models; const { confirm, appAlarmId = [], confirmPost } = ctx.request.body - const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost await models.AppAlarm.update({ confirm, confirmTime: moment().format() @@ -338,29 +337,7 @@ async function confirmApiError(ctx) { } }) - //存日志 - let logDatas = projectCorrelationIds.map(id => { - return { - pepUserId, - projectCorrelationId: id, - alarmInfo,//包含告警id,type,source - confirmTime: moment().format(), - confirmContent: confirm - } - }) - 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); - + await alarmConfirmLog(ctx, confirmPost, confirm);//告警确认日志 ctx.status = 204; } catch (error) { diff --git a/api/app/lib/controllers/alarm/data.js b/api/app/lib/controllers/alarm/data.js index 595cd60..13f42fd 100644 --- a/api/app/lib/controllers/alarm/data.js +++ b/api/app/lib/controllers/alarm/data.js @@ -1,6 +1,6 @@ 'use strict'; const moment = require('moment'); - +const { alarmConfirmLog } = require('./alarmConfirmLog'); async function groupList (ctx) { try { const { models } = ctx.fs.dc; @@ -248,7 +248,6 @@ function confirm (opts) { const { utils: { kfkSendAsync } } = ctx.app.fs const { clickHouse } = ctx.app.fs const { content = '', alarmId, confirmPost } = ctx.request.body; - const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost; // 发送告警恢复通知 // Topic: alarm /* @@ -295,6 +294,9 @@ function confirm (opts) { await kfkSendAsync(payloads) } + + await alarmConfirmLog(ctx, confirmPost, content);//告警确认日志 + ctx.status = 204; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); diff --git a/api/app/lib/controllers/alarm/video.js b/api/app/lib/controllers/alarm/video.js index a0d0096..8adf26b 100644 --- a/api/app/lib/controllers/alarm/video.js +++ b/api/app/lib/controllers/alarm/video.js @@ -1,6 +1,6 @@ 'use strict'; const moment = require('moment') - +const { alarmConfirmLog } = require('./alarmConfirmLog'); async function deviceType (ctx) { try { const { models } = ctx.fs.dc; @@ -129,7 +129,7 @@ async function alarmList (ctx, agg) { 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总书记任期 + 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(',')})`} ) @@ -268,9 +268,7 @@ async function alarmList (ctx, agg) { async function confirm (ctx) { try { - const { models } = ctx.fs.dc; const { alarmId, content, confirmPost } = ctx.request.body; - const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost; // TODO: 以视频·应用的秘钥进行鉴权 await ctx.app.fs.vcmpRequest.put('status/alarm/confirm', { data: { @@ -278,28 +276,7 @@ async function confirm (ctx) { } }) - //存日志 - let logDatas = projectCorrelationIds.map(id => { - return { - 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); + await alarmConfirmLog(ctx, confirmPost, content);//告警确认日志 ctx.status = 204; } catch (error) { diff --git a/web/client/src/sections/problem/containers/dataAlarm.jsx b/web/client/src/sections/problem/containers/dataAlarm.jsx index 92689e8..04746d7 100644 --- a/web/client/src/sections/problem/containers/dataAlarm.jsx +++ b/web/client/src/sections/problem/containers/dataAlarm.jsx @@ -37,8 +37,6 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb const [videoModal, setVideoModal] = useState(false) //视频播放弹框 const [videoData, setVideoData] = useState({}) //视频播放参数 const [videoToken, setVideoToken] = useState() //视频token - const [alarmToConfirm, setAlarmToConfirm] = useState(null) //告警确认 - const TextAreaApi = useRef('') @@ -306,15 +304,14 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb name: '操作', sort: 25, value: 'text', render: (_, r, index) => { return
{r.State < 3 || route && ['videoAbnormal', 'useAbnormal'].includes(route) && !r.confirmTime ? - - : r.State == 3 || r.autoRestore || r.confirmAuto ? + + : r.State == 3 || r.autoRestore || r.confirmAuto ? : - - } + + } {route && ['dataLnterrupt', 'dataAbnormal', 'strategyHit', 'deviceAbnormal'].includes(route) ? <> {route == 'deviceAbnormal' ? "" :
) diff --git a/web/client/src/sections/service/actions/emPush.js b/web/client/src/sections/service/actions/emPush.js new file mode 100644 index 0000000..52db5a4 --- /dev/null +++ b/web/client/src/sections/service/actions/emPush.js @@ -0,0 +1,74 @@ +'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: '' } + }); +} + +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 } }, + }); +} \ No newline at end of file diff --git a/web/client/src/sections/service/actions/emPush.jsx b/web/client/src/sections/service/actions/emPush.jsx deleted file mode 100644 index d4dcd9e..0000000 --- a/web/client/src/sections/service/actions/emPush.jsx +++ /dev/null @@ -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: '' } - }); -} diff --git a/web/client/src/sections/service/components/pushModal.jsx b/web/client/src/sections/service/components/pushModal.jsx index b2c73cd..6cf391c 100644 --- a/web/client/src/sections/service/components/pushModal.jsx +++ b/web/client/src/sections/service/components/pushModal.jsx @@ -1,7 +1,8 @@ import React, { useState, useRef, useEffect } from "react"; import { connect } from "react-redux"; -import { Modal, Form } from "@douyinfe/semi-ui"; +import { Modal, Form, Notification } from "@douyinfe/semi-ui"; import { IconAlertCircle } from '@douyinfe/semi-icons'; +import './pushModal.less' function pushModal (props) { @@ -12,47 +13,219 @@ function pushModal (props) { dispatch, pepList, actions, - adminEdit,//是否是编辑 + pushEdit,//是否是编辑 editObj, + user } = props; - const { install } = actions; + const { service } = actions; const form = useRef();//表单 const [disablePeople, setDisablePeople] = useState(true); //页码信息 const [peopleList, setPeopleList] = useState([]); //人员List const [departmentId, setDepartmentId] = useState(); //部门id const [peopleId, setPeopleId] = useState(); //人员id + const [abnormal, setAbnormal] = useState(false); //异常率推送机制disable + const [usersList, setUsersList] = useState([]); //获取全部未删除用户 + const [structure, setStructure] = useState(true); //结构物disable + const [projectPoms, setProjectPoms] = useState([]); //获取已绑定项目 + const [projectStructure, setProjectStructure] = useState([]); //获取绑定项目下结构物 + const [timeTypeDis, setTimeTypeDis] = useState(true); //通知时效disable + const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 + + + + + //初始化 useEffect(() => { - if (editObj.id) { - let departmentList = [] - for (let i = 0; i < pepList.length; i++) { - if (pepList[i].id == editObj.departments[0].id) { - departmentList = pepList[i].users + // if (editObj.id) { + // let departmentList = [] + // for (let i = 0; i < pepList.length; i++) { + // if (pepList[i].id == editObj.departments[0].id) { + // departmentList = pepList[i].users + // } + // } + // setPeopleList(departmentList) + // setDepartmentId(editObj.departments[0].id) + // setPeopleId(editObj.pepUserId) + // setDisablePeople(false) + // } + getOrganizationUsersList()//获取全部未删除用户 + getProjectPomsList()//获取已绑定项目 + }, []); + function getOrganizationUsersList () {//获取全部未删除用户 + dispatch(service.getOrganizationUsers()).then((res) => { + if (res.success) { + setUsersList(res.payload.data) + } + }) + } + function getProjectPomsList () {//获取已绑定项目 + dispatch(service.getProjectPoms()).then((res) => { + if (res.success) { + setProjectPoms(res.payload?.data?.rows) + } + }) + } + function getProjectStructureList (value) {//获取绑定项目下结构物 + dispatch(service.getProjectStructure({ pomsProjectId: value })).then((res) => { + if (res.success) { + let mylist = [] + for (let i = 0; i < res.payload?.data.length; i++) { + mylist.push(res.payload?.data[i].id) } + setProjectStructure(res.payload?.data) + form.current.setValue('strucId', mylist) + form.current.validate(['strucId','timeType']) + setStructure(false) + setTimeTypeDis(false) } - setPeopleList(departmentList) - setDepartmentId(editObj.departments[0].id) - setPeopleId(editObj.pepUserId) - setDisablePeople(false) - } - }, []); - + }) + } + function getProjectStatusList () {//获取项目状态列表 + dispatch(service.getProjectStatus()).then((res) => { + if (res.success) { + setProjectStatus(res.payload?.data) + let mylist = [] + for (let i = 0; i < res.payload?.data.length; i++) { + mylist.push(res.payload?.data[i].id) + } + form.current.setValue('timeType', mylist) + form.current.validate(['strucId','timeType']) + } + }) + } function handleOk () { //点击弹框确定 右边按钮 form.current .validate() .then((values) => { - if (adminEdit) { - dispatch(install.deteleOrganizationAdmin({id:editObj.id,msg:''})).then( - dispatch(install.postOrganizationUser({ role: ['admin'], pepUserId: values.pepUserId, msg: '修改管理员' })).then((res) => {//获取项企(PEP)全部部门及其下用户 - if (res.success) { - close(); - } - }) - ) + if (pushEdit) { + dispatch(service.postPush({ pushId: pushId, pepUserId: values.pepUserId, msg: '编辑推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 + if (res.success) { + close(); + } + }) } else { - dispatch(install.postOrganizationUser({ role: ['admin'], pepUserId: values.pepUserId, msg: '新增管理员' })).then((res) => {//获取项企(PEP)全部部门及其下用户 + let obj = JSON.parse(JSON.stringify(values)) + if (obj.timeType[0] == null) { + obj.timeType = [] + } + let regu = /^[0-9]*[1-9][0-9]*$/; + if (obj.tactics == 'immediately') { + if (obj.interval1) { + if (regu.test(obj.interval1)) { + if (obj.interval1 <= 1440) { + obj.tacticsParams = { + interval: obj.interval1 + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else { + Notification.error({ + content: '即时推送时间不能大于1440分钟', + duration: 2, + }) + } + } else { + Notification.error({ + content: '即时推送时间应为正整数', + duration: 2, + }) + } + } else { + Notification.error({ + content: '即时推送时间应为正整数', + duration: 2, + }) + } + } + else if (obj.tactics == 'continue') { + if (obj.interval2) { + if (regu.test(obj.interval2)) { + if (obj.interval2 <= 1440) { + obj.tacticsParams = { + interval: obj.interval2 + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else { + Notification.error({ + content: '持续时长推送时间不能大于1440分钟', + duration: 2, + }) + } + } else { + Notification.error({ + content: '持续时长推送时间应为正整数', + duration: 2, + }) + } + } else { + Notification.error({ + content: '持续时长推送时间应为正整数', + duration: 2, + }) + } + } + else { + if (regu.test(obj.interval3) && regu.test(obj.deviceProportion)) { + if (obj.interval3 <= 720 && obj.deviceProportion <= 100) { + obj.tacticsParams = { + interval: obj.interval3, + deviceProportion: obj.deviceProportion + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else if (obj.interval3 <= 720 && obj.deviceProportion > 100) { + Notification.error({ + content: '异常率推送异常率不能超过100%', + duration: 2, + }) + } else if (obj.interval3 > 720 && obj.deviceProportion <= 100) { + Notification.error({ + content: '异常率推送时间不能超过720小时', + duration: 2, + }) + } else { + Notification.error({ + content: '异常率推送时间不能超过720小时', + duration: 2, + }) + Notification.error({ + content: '异常率推送异常率不能超过100%', + duration: 2, + }) + } + } else if (regu.test(obj.interval3) && !regu.test(obj.deviceProportion)) { + Notification.error({ + content: '异常率推送异常率应为正整数', + duration: 2, + }) + } else if (!regu.test(obj.interval3) && regu.test(obj.deviceProportion)) { + Notification.error({ + content: '异常率推送时间应为正整数', + duration: 2, + }) + } else { + Notification.error({ + content: '异常率推送异常率应为正整数', + duration: 2, + }) + Notification.error({ + content: '异常率推送时间应为正整数', + duration: 2, + }) + } + } + console.log('obj', obj); + dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 if (res.success) { close(); } @@ -67,60 +240,122 @@ function pushModal (props) { return ( <> -
-
-
-
成员成为管理员后,拥有平台所有权限和项目,成员的普通角色会被禁用。
-
+
{ + console.log('values', values); for (var key in field) { - if (key == 'department') { - if (values.department >= 0) { - let departmentList = [] - for (let i = 0; i < pepList.length; i++) { - if (pepList[i].id == values.department) { - departmentList = pepList[i].users - } - } - setPeopleList(departmentList) - setDisablePeople(false) - form.current.setValue('pepUserId', undefined); + if (key == 'tactics') { + if (values.tactics == 'abnormal_rate') { + form.current.setValue('alarmType', undefined) + setAbnormal(true) } else { - setPeopleList([]) - setDisablePeople(true) - form.current.setValue('pepUserId', undefined); + setAbnormal(false) + } + } + if (key == 'pomsProjectId') { + getProjectStructureList(values.pomsProjectId)//获取绑定项目下结构物 + for (let i = 0; i < projectPoms.length; i++) { + if (values.pomsProjectId == projectPoms[i].id) { + if (projectPoms[i].pepProjectId) { + getProjectStatusList()//获取项目状态列表 + } + else { + setProjectStatus([{ construction_status: 'POMS', id: null }]) + form.current.setValue('timeType', [null]) + form.current.validate() + } + } } } } + + // for (var key in field) { + // if (key == 'department') { + // if (values.department >= 0) { + // let departmentList = [] + // for (let i = 0; i < pepList.length; i++) { + // if (pepList[i].id == values.department) { + // departmentList = pepList[i].users + // } + // } + // setPeopleList(departmentList) + // setDisablePeople(false) + // form.current.setValue('pepUserId', undefined); + // } + // else { + // setPeopleList([]) + // setDisablePeople(true) + // form.current.setValue('pepUserId', undefined); + // } + // } + // } }} getFormApi={(formApi) => (form.current = formApi)} > +
+ 项目信息配置 +
+
+ +
+
+ + { + projectPoms.map((item, index) => { + return ( + + {item.pepProjectName || item.name} + + ) + }) + } + +
{ - pepList.map((item, index) => { + projectStructure.map((item, index) => { return ( {item.name} @@ -130,21 +365,136 @@ function pushModal (props) { }
+
+ + + 发现在 + + 分钟内,有告警源新增,则通过【信鸽服务】发送一条通知信息。 + + } + style={{ width: 198 }}> + 即时推送机制 + + + 告警源持续产生时间超过 + + 分钟,则通过【信鸽服务】发送一条通知信息。 + + } + style={{ width: 198 }}> + 持续时长推送机制 + + + 异常设备数量达到项目或结构物内设备总数量的 + + %,且持续时长超过 + + 小时,则通过【信鸽服务】发送一条通知信息。 + + } + style={{ width: 260 }}> + 异常率推送机制 + + +
+ + 数据中断 + 数据异常 + 策略命中 + 视频异常 + 应用异常 + 设备异常 + +
+
+ 接收信息配置 +
+
+ { + projectStatus.map((item, index) => { + return ( + + {item.construction_status} + + ) + }) + } + + { - peopleList.map((item, index) => { + usersList.map((item, index) => { return ( - + {item.name} ) @@ -152,9 +502,29 @@ function pushModal (props) { }
+
+ 不在项目节点的通知策略,会自动失效. +
+
+ + + 启用 + + + 禁用 + + +
- + ); } diff --git a/web/client/src/sections/service/components/pushModal.less b/web/client/src/sections/service/components/pushModal.less new file mode 100644 index 0000000..78a5cf8 --- /dev/null +++ b/web/client/src/sections/service/components/pushModal.less @@ -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; + } +} \ No newline at end of file diff --git a/web/client/src/sections/service/containers/emPush.jsx b/web/client/src/sections/service/containers/emPush.jsx index b93f90d..b988ae8 100644 --- a/web/client/src/sections/service/containers/emPush.jsx +++ b/web/client/src/sections/service/containers/emPush.jsx @@ -1,9 +1,9 @@ import React, { useEffect, useRef, useState } from 'react'; import { connect } from 'react-redux'; -import { Skeleton, Button, Pagination, Form, Popconfirm, Table } from '@douyinfe/semi-ui'; +import { Skeleton, Button, Pagination, Form, Popconfirm, Table, Tooltip } from '@douyinfe/semi-ui'; import { SkeletonScreen, } from "$components"; import moment from "moment"; -import pushModal from '../components/pushModal' +import PushModal from '../components/pushModal' import '../style.less' import { Setup } from "$components"; // import { set } from 'nprogress'; @@ -18,9 +18,8 @@ const EmPush = (props) => { const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 const [limits, setLimits] = useState()//每页实际条数 const mylimits = useRef(); //每页实际条数 - const [selected, setSelected] = useState([]) //表格被勾选项 const [pushModal, setPushModal] = useState(false) //信鸽弹框 - const [systemEdit, setSystemEdit] = useState(false) //是否是修改 + const [pushEdit, setPushEdit] = useState(false) //是否是修改 const [anxincloudList, setAnxincloudList] = useState([]) //安心云列表 const [peplist, setPeplist] = useState([]) //PEP项目管理项目列表 const [appList, setAppList] = useState([]) //应用列表 @@ -31,20 +30,39 @@ const EmPush = (props) => { const [appArr, setAppArr] = useState([]) //修改时添加应用 const [bindId, setBindId] = useState() //修改时绑定的id const [tableKey, setTableKey] = useState([]) //修改时绑定的id + const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 const page = useRef(query.page);//哪一页 const EMPUSH = "empush"; const tableList = [//表格属性 - { - title: '推送信息', - list: [ - { name: "策略类型", value: "pushWay" }, - { name: "推送机制", value: "noticeWay" }, - { name: "监听设备数量", value: "monitorCount" }, - { name: "累计推送次数", value: "logCount" }, - ] - }, -]; - + { + title: '推送信息', + list: [ + { name: "策略名称", value: "name" }, + { name: "创建时间", value: "createTime" }, + { name: "关联项目", value: "projectName" }, + { name: "接收人", value: "receiverPepUser" }, + { name: "推送方式", value: "pushType" }, + { name: "监听问题模块", value: "alarmType" }, + { name: "生效项目节点", value: "timeType" }, + { name: "推送机制", value: "tactics" }, + { name: "启用状态", value: "disable" }, + { name: "推送次数", value: "pushCount" }, + ] + }, + ]; + const alarmTypeObj = { + data_outages: '数据中断', + data_exception: '数据异常', + strategy_hit: '策略命中', + video_exception: '视频异常', + app_exception: '应用异常', + device_exception: '设备异常', + } + const tacticsObj = { + immediately: '即时推送机制', + continue: '持续时长推送机制', + abnormal_rate: '异常率推送机制', + } function handleRow (record, index) {//斑马条纹 @@ -63,61 +81,54 @@ const EmPush = (props) => { const [tableData, setTableData] = useState([]) //表格数据 useEffect(() => { - attribute(); - dispatch(service.getPush(query)).then((res) => {//获取推送配置列表 - // console.log('res.payload.datares.payload.data',res.payload.data); - // setAnxincloudList(res.payload.data) - }) + // 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) // }) + getProjectStatusList() localStorage.getItem(EMPUSH) == null - ? localStorage.setItem( - EMPUSH, - JSON.stringify(['pushWay','noticeWay','logCount','monitorCount']) - ) - : ""; + ? localStorage.setItem( + EMPUSH, + JSON.stringify(['name', 'createTime', 'projectName', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable']) + ) + : ""; + attribute(); }, []) useEffect(() => { - // getProjectPomsList(); + getPushList(); }, [query]); - function getProjectPomsList () { - // dispatch(install.getProjectPoms(query)).then((res) => {//获取已绑定项目 - // if (res.success) { - // let mytableData = JSON.parse(JSON.stringify(res.payload.data.rows)); - // let mytableKey = [] - // for (let index = 0; index < mytableData.length; index++) { - // mytableData[index].key = mytableData[index].id - // mytableKey.push(mytableData[index].id) - // } - // setTableKey(mytableKey) - // setTableData(mytableData) - // setLimits(res.payload.data.count) - // mylimits.current = res.payload.data.rows.length - // } - // }) + function getPushList () { + dispatch(service.getPush(query)).then((res) => {//获取已绑定项目 + if (res.success) { + // let mytableData = JSON.parse(JSON.stringify(res.payload.data.rows)); + // let mytableKey = [] + // for (let index = 0; index < mytableData.length; index++) { + // mytableData[index].key = mytableData[index].id + // mytableKey.push(mytableData[index].id) + // } + // setTableKey(mytableKey) + // setTableData(mytableData) + setTableData(res.payload.data) + setLimits(res.payload.data.length) + mylimits.current = res.payload.data.length + } + }) } - const [columns, setColumns] = useState([//表格属性 - { - title: "策略编号", - dataIndex: "index", - key: 'index', - render: (text, r, index) => { - return index + 1; - }, - }, - { - title: '策略名称', - dataIndex: "pepProjectName", - key: 'pepProjectName', - render: (_, row) => { - return row.pepProjectName + function getProjectStatusList () {//获取项目状态列表 + dispatch(service.getProjectStatus()).then((res) => { + if (res.success) { + setProjectStatus(res.payload?.data) } - }, + }) + } + const [columns, setColumns] = useState([//表格属性 { title: "操作", width: "20%", @@ -128,38 +139,38 @@ const EmPush = (props) => {
@@ -186,12 +197,6 @@ const EmPush = (props) => { }, }, ]) - const rowSelection = { - selectedRowKeys: selected, - onChange: (selectedRowKeys, selectedRows) => { - setSelected(selectedRows.map(v => v.key)) - }, - } //获取表格属性设置 function attribute () { const arr = localStorage.getItem(EMPUSH) @@ -200,59 +205,184 @@ const EmPush = (props) => { const column = [ { - title: "关联项目", - dataIndex: "noticeWay", - key: "noticeWay", - render: (_, r, index) => { - return r.noticeWay; - }, + title: '策略名称', + dataIndex: "name", + key: 'name', + render: (_, row) => { + return row.name + } }, { title: "创建时间", - dataIndex: "logCount", - key: "logCount", + dataIndex: "createTime", + key: "createTime", render: (_, r, index) => { - return (r.logCount + '次') + return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss'); }, }, { - title: "接收人", - dataIndex: "monitorCount", - key: "monitorCount", + title: '关联项目', + dataIndex: "projectName", + key: "projectName", + render: (_, row) => { + let anxinerror = false + let anxinerrorArr = '' + if (row.pomsProject.del == true) { + anxinerror = true + anxinerrorArr = row.pomsProject.pepProject?.projectName || row.pomsProject.name + } + return ( +
+ { + anxinerror ? ( + +
+ +
+
) : ('') + } + { +
+ +
7 || row.pomsProject?.name?.length > 7 ? '112px' : '', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', color: row.pomsProject.del ? '#F93920' : '' }}> + {row.pomsProject.pepProject?.projectName || row.pomsProject.name} +
+
+
+ } +
+ ) + } + }, + { + title: '接收人', + dataIndex: "receiverPepUser", + key: 'receiverPepUser', + render: (_, row) => { + return ( +
+ { + row.receiverPepUser.map((item, index) => { + return ( +
+
1 ? 'none' : '', color: '#005ABD' }}> + {item.name} +
+
0 ? 'none' : '' }}>
+
+ ) + }) + } + { + row.receiverPepUser.length > 2 ? ( + + { + row.receiverPepUser.map((item, index) => { + return ( +
+ {item.name}, +
+ ) + }) + } +
+ } trigger="click" style={{ lineHeight: 2 }}> +
+ +{row.receiverPepUser.length - 2} +
+ + ) : ('') + } +
+ ) + } + }, + { + title: "推送方式", + dataIndex: "pushType", + key: "pushType", render: (_, r, index) => { - return r.monitorCount + return '邮件通知'; }, }, { - title: "监听问题", - dataIndex: "pushWay", - key: "pushWay", + title: "监听问题模块", + dataIndex: "alarmType", + key: "alarmType", + render: (_, row) => { + return ( +
+ { + // alarmTypeObj + row.alarmType.map((item, index) => { + return ( +
+
1 ? 'none' : '' }}> + {alarmTypeObj[item]} +
+
0 ? 'none' : '' }}>
+
+ ) + }) + } + { + row.alarmType.length > 2 ? ( + + { + row.alarmType.map((item, index) => { + return ( +
+ {alarmTypeObj[item]}, +
+ ) + }) + } +
+ } trigger="click" style={{ lineHeight: 2 }}> +
+ +{row.alarmType.length - 2} +
+ + ) : ('') + } +
+ ) + } + }, + { + title: "生效项目节点", + dataIndex: "timeType", + key: "timeType", render: (_, r, index) => { - return r.pushWay=='email' ? '邮件通知' : '短信通知'; + // projectStatus + return r.timeType[0] }, }, { - title: "通知时效", - dataIndex: "text1", - key: "text1", + title: "推送机制", + dataIndex: "tactics", + key: "tactics", render: (_, r, index) => { - return r.text1 + return tacticsObj[r.tactics] }, }, { title: "启用状态", - dataIndex: "text2", - key: "text2", + dataIndex: "disable", + key: "disable", render: (_, r, index) => { - return r.text2 + // projectStatus + return r.disable }, }, { title: "推送次数", - dataIndex: "time", - key: "time", + dataIndex: "pushCount", + key: "pushCount", render: (_, r, index) => { - return r.time + return r.pushCount }, }, ]; @@ -260,7 +390,7 @@ const EmPush = (props) => { let colum = column.filter((item) => { return item.key === arr[i]; }); - columns.splice(i + 2, 0, colum[0]); + columns.splice(columns.length - 1, 0, colum[0]); } setSetupp(columns); } @@ -332,15 +462,15 @@ const EmPush = (props) => { height: 32, borderRadius: 2, marginRight: 32, - background:'#FFFFFF', - color:'#005ABD', - border:'1px solid #005ABD' + background: '#FFFFFF', + color: '#005ABD', + border: '1px solid #005ABD' }} - // onClick={() => { }} + // onClick={() => { }} > 查询 -
+
setSetup(true)} />
-
预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留预留
+
EM推送提供对映射关系组的项目、结构物问题的监听和通知服务,支持对设备异常率、问题持续时间、即时响应等策略定义的动态推送。
{ empty="暂无数据" pagination={false} onRow={handleRow} - rowSelection={rowSelection} />
{ }} >
-
勾选 {selected.length}条 信息
- - { - // dispatch(install.deleteProjectBind({ bindId: selected.join(','), msg: '删除安心云、项目管理项目绑定关系' })).then(() => { - // if (page.current > 0 && mylimits.current == selected.length) { - // setQuery({ limit: 10, page: page.current - 1 }) - // } else { - // setQuery({ limit: 10, page: page.current }) - // } - // setSelected([]) - // }) - }} - > - -
@@ -438,49 +532,38 @@ const EmPush = (props) => { onChange={(currentPage, pageSize) => { setQuery({ limit: pageSize, page: currentPage - 1 }); page.current = currentPage - 1 - setSelected([]) }} />
- {/* {//映射关系弹框 + {//推送配置弹框 pushModal ? { setPushModal(false); }} close={() => { setPushModal(false); - getProjectPomsList() + getPushList() }} > : '' - } */} + } {setup ? ( - { - setSetup(false); - attribute(); - // setcameraSetup(false); - }} - /> - ) : ( - "" - )} + { + setSetup(false); + attribute(); + }} + /> + ) : ( + "" + )} ) } diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index ca385b4..36b3e65 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -50,6 +50,10 @@ export const ApiTable = { //服务-信鸽服务 getPush: "push", //获取推送配置列表 + postPush: "push", //新增/编辑推送配置 + getOrganizationUsers: "organization/users", //获取全部未删除用户 + getProjectStructure: "project/structure", //获取绑定项目下结构物 + getProjectStatus: "project/status", //获取项目状态列表 //控制台 consoleToollink: 'console/toollink', //常用工具 From 8c861a7dd89cef41159190d035e9461f59ec5e68 Mon Sep 17 00:00:00 2001 From: wuqun Date: Fri, 28 Oct 2022 10:06:26 +0800 Subject: [PATCH 16/22] =?UTF-8?q?(*)=E5=91=8A=E8=AD=A6=E5=A4=84=E7=BD=AE?= =?UTF-8?q?=E6=95=88=E7=8E=87=E7=BB=9F=E8=AE=A1=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E4=B8=AD=E7=BB=93=E6=9E=84=E7=89=A9=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=90=8C=E6=AD=A5dataRange=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/schedule/alarms_handle_statistics.js | 103 +++++------------- 1 file changed, 30 insertions(+), 73 deletions(-) diff --git a/api/app/lib/schedule/alarms_handle_statistics.js b/api/app/lib/schedule/alarms_handle_statistics.js index a3b7d87..04fab12 100644 --- a/api/app/lib/schedule/alarms_handle_statistics.js +++ b/api/app/lib/schedule/alarms_handle_statistics.js @@ -7,7 +7,7 @@ module.exports = function (app, opts) { const { database: anxinyun } = clickHouse.anxinyun.opts.config const alarmHandleStatistics = app.fs.scheduleInit( { - interval: '30 59 14 * * *', + interval: '0 58 9 * * *', // immediate: true, //proRun: true, }, @@ -290,53 +290,14 @@ module.exports = function (app, opts) { // 每个设备一个告警 for (let a of alarmRes) { if (positionD[a.cameraId]) { - //let curD = returnD[positionD[a.cameraId].positionReturnD] - // if (a.resolveId && !curD.resolve.some(r => r.id == a.resolveId)) { - // curD.resolve.push({ - // id: a.resolveId, - // resolve: a.resolve - // }) - // } - // 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, - - // venderId: a.venderId, - // venderName: a.venderName, - // cameraKindId: a.cameraKindId, - // yingshiToken: a.yingshiToken, - - // resolve: [], - // struc: [], - // station: [] } - // pep 项目 d.pomsProject = ( anxinStruc.find(as => as.strucId == a.strucId) || @@ -346,27 +307,6 @@ module.exports = function (app, opts) { ] } ).pomsProject.map(d => d.id) - - // if (a.resolveId) { - // d.resolve.push({ - // id: a.resolveId, - // resolve: a.resolve - // }) - // } - // 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 @@ -493,23 +433,40 @@ module.exports = function (app, opts) { // 并保存信息至数据 let undelStruc = [] for (let s of undelStrucRes) { - if (!undelStruc.some(us => us.strucId == s.strucId)) { - let pomsProject = [] - for (let { dataValues: br } of bindRes) { - if (br.anxinProjectId.some(braId => braId == s.projectId)) { - let corPepProject = pepProjectRes.find(pp => pp.id == br.pepProjectId) - pomsProject.push({ + 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 }) } + } - undelStruc.push({ - strucId: s.strucId, - strucName: s.strucName, - projectId: s.projectId, - pomsProject: pomsProject - }) } } return undelStruc From 8dc209efec2ff81073f4b238d33c5bc2051eb483 Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Fri, 28 Oct 2022 11:44:47 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=E6=8E=A8=E9=80=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/push/config.js | 2 +- api/app/lib/schedule/alarms_push.js | 207 ++++++++++++++++++++++--- api/app/lib/utils/push.js | 62 ++++++++ 3 files changed, 246 insertions(+), 25 deletions(-) create mode 100644 api/app/lib/utils/push.js diff --git a/api/app/lib/controllers/push/config.js b/api/app/lib/controllers/push/config.js index 4564481..f915686 100644 --- a/api/app/lib/controllers/push/config.js +++ b/api/app/lib/controllers/push/config.js @@ -7,7 +7,7 @@ async function list (ctx) { const sequelize = ctx.fs.dc.ORM; const { clickHouse } = ctx.app.fs const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs - const { keyword, keywordTarget, alarmType, state, tactics, pomsProjectId } = ctx.request.body + const { keyword, keywordTarget, alarmType, state, tactics, pomsProjectId } = ctx.query let findOption = { where: { diff --git a/api/app/lib/schedule/alarms_push.js b/api/app/lib/schedule/alarms_push.js index a0d8918..eec2f63 100644 --- a/api/app/lib/schedule/alarms_push.js +++ b/api/app/lib/schedule/alarms_push.js @@ -53,11 +53,11 @@ module.exports = function (app, opts) { for (let { dataValues: c } of configListRes) { if (c.tacticsParams && c.tactics) { - const { projectCorrelation, strucId, pomsProjectId } = c + const { projectCorrelation, strucId, pomsProjectId, } = c const { interval, deviceProportion } = c.tacticsParams if ( - curMinOfYear % interval == 0 + curMinOfYear % parseInt(interval) == 0 ) { const corPepProject = projectCorrelation.pepProjectId ? pepProjectRes.find(p => p.id == projectCorrelation.pepProjectId) @@ -69,18 +69,63 @@ module.exports = function (app, opts) { && c.timeType.some(ct => ct == corPepProject.construction_status_id) ) ) { - // TODO 查当前 poms 下的结构物 并把不包含的去掉 + const { anxinProjectId, pepProjectId } = projectCorrelation - const strucListRes = strucId.length ? await clickHouse.anxinyun.query(` - SELECT id, iota_thing_id, name - FROM t_structure - WHERE id IN (${strucId.join(',')}) - `).toPromise() : [] + // 查当前 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 = { @@ -89,17 +134,26 @@ module.exports = function (app, opts) { let appAlarms = [] let deviceCount = 0 + let alarmDeviceCount = 0 let cameraCount = 0 + let alarmCameraCount = 0 // 判断推送策略 - let pointTime = moment().subtract(interval, 'minute').format('YYYY-MM-DD HH:mm:ss') + 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 } + // 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 } + // 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}'`); + // dataAlarmOption.push(`StartTime <= '${pointTime}'`); if (c.alarmType.includes('data_outages') || c.alarmType.includes('data_exception')) { // 查了设备异常率 去安心云查当前项目下的设备数量 // TODO 等同步以太数据再查 @@ -110,15 +164,19 @@ module.exports = function (app, opts) { } if (c.alarmType.includes('video_exception')) { // 查了视频异常 去安心云查 接入的 萤石 设备数量 - cameraCount = strucId.length ? + cameraCount = searchStrucIds.length ? (await clickHouse.anxinyun.query(` SELECT count(*) AS count FROM t_video_ipc - WHERE structure IN (${strucId.join(',')}) + WHERE structure IN (${searchStrucIds.join(',')}) `).toPromise())[0].count : 0 } - appAlarmWhereOption.createTime = { $lte: pointTime } + // 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')) { @@ -131,7 +189,7 @@ module.exports = function (app, opts) { dataAlarmGroupOption.push(3) } if (c.alarmType.includes('video_exception')) { - videoAlarms = strucId.length ? await clickHouse.vcmp.query( + videoAlarms = searchStrucIds.length ? await clickHouse.vcmp.query( ` SELECT cameraAlarm.cameraId AS cameraId, @@ -167,13 +225,15 @@ module.exports = function (app, opts) { 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 (${strucId.join(',')}) + 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 + 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 @@ -186,7 +246,7 @@ module.exports = function (app, opts) { AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo LEFT JOIN ${anxinyun}.t_structure AS anxinStruc ON anxinStruc.id = anxinIpc.structure - AND anxinStruc.id IN (${strucId.join(',')}) + 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 @@ -275,20 +335,119 @@ module.exports = function (app, opts) { dataAlarmGroupOption.push(4) dataAlarmGroupOption.push(5) } - // - if (dataAlarmGroupOption.length) { + + // 查数据告警 三警合一 + if (dataAlarmGroupOption.length && searchStrucIds.length) { dataAlarmGroupOption.push(-1) dataAlarmOption.push(`AlarmGroup IN (${dataAlarmGroupOption.join(',')})`) dataAlarms = await clickHouse.dataAlarm.query(` - SELECT * FROM alarms + 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: '' + }) } } } diff --git a/api/app/lib/utils/push.js b/api/app/lib/utils/push.js new file mode 100644 index 0000000..a0cf5ed --- /dev/null +++ b/api/app/lib/utils/push.js @@ -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, + } +} \ No newline at end of file From 978d12936d8bf72216d6fe373550d525c204031a Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Fri, 28 Oct 2022 15:15:53 +0800 Subject: [PATCH 18/22] =?UTF-8?q?fix=20=E7=8A=B6=E6=80=81=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/push/config.js | 2 +- api/app/lib/utils/dataRange.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/api/app/lib/controllers/push/config.js b/api/app/lib/controllers/push/config.js index f915686..d4b299e 100644 --- a/api/app/lib/controllers/push/config.js +++ b/api/app/lib/controllers/push/config.js @@ -42,7 +42,7 @@ async function list (ctx) { findOption.where.alarmType = { $contains: [alarmType] } } if (state) { - if (state == 'enable') { + if (state == 'enable' || state == 'notYet' || state == 'takeEffect') { findOption.where.disable = false } else if (state == 'disable') { findOption.where.disable = true diff --git a/api/app/lib/utils/dataRange.js b/api/app/lib/utils/dataRange.js index 14fcc6b..407c80e 100644 --- a/api/app/lib/utils/dataRange.js +++ b/api/app/lib/utils/dataRange.js @@ -207,6 +207,16 @@ module.exports = function (app, opts) { let pomsProject = [] for (let b of bindRes) { + if ( + keywordTarget == 'pepProject' && keyword + && ( + !b.name && !( + b.pepProjectId && pepProjectRes.some(pp => pp.id == b.pepProjectId) + ) + ) + ) { + continue + } if (b.pepProjectId) { let corPepProject = pepProjectRes.find(pp => pp.id == b.pepProjectId) || {} pomsProject.push({ From c96f714e6b203d45540e587c99eed7f1a6d73c66 Mon Sep 17 00:00:00 2001 From: deartibers <947466799@qq.com> Date: Fri, 28 Oct 2022 15:41:45 +0800 Subject: [PATCH 19/22] =?UTF-8?q?em=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/sections/service/actions/emPush.js | 21 +- .../sections/service/components/pushModal.jsx | 213 ++++++--- .../sections/service/containers/emPush.jsx | 405 +++++++++++++----- web/client/src/sections/service/style.less | 10 +- web/client/src/utils/webapi.js | 1 + 5 files changed, 482 insertions(+), 168 deletions(-) diff --git a/web/client/src/sections/service/actions/emPush.js b/web/client/src/sections/service/actions/emPush.js index 52db5a4..cadbb00 100644 --- a/web/client/src/sections/service/actions/emPush.js +++ b/web/client/src/sections/service/actions/emPush.js @@ -2,10 +2,11 @@ import { ApiTable, basicAction } from '$utils' -export function getPush () { //获取推送配置列表 +export function getPush (query) { //获取推送配置列表 return dispatch => basicAction({ type: 'get', dispatch: dispatch, + query: query, actionType: 'GET_PUSH', url: `${ApiTable.getPush}`, msg: { error: '获取推送配置列表失败' }, @@ -71,4 +72,22 @@ export function getProjectStatus (query) {//获取项目状态列表 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: {}, + }); } \ No newline at end of file diff --git a/web/client/src/sections/service/components/pushModal.jsx b/web/client/src/sections/service/components/pushModal.jsx index 6cf391c..de9199b 100644 --- a/web/client/src/sections/service/components/pushModal.jsx +++ b/web/client/src/sections/service/components/pushModal.jsx @@ -19,10 +19,6 @@ function pushModal (props) { } = props; const { service } = actions; const form = useRef();//表单 - const [disablePeople, setDisablePeople] = useState(true); //页码信息 - const [peopleList, setPeopleList] = useState([]); //人员List - const [departmentId, setDepartmentId] = useState(); //部门id - const [peopleId, setPeopleId] = useState(); //人员id const [abnormal, setAbnormal] = useState(false); //异常率推送机制disable const [usersList, setUsersList] = useState([]); //获取全部未删除用户 const [structure, setStructure] = useState(true); //结构物disable @@ -30,27 +26,38 @@ function pushModal (props) { const [projectStructure, setProjectStructure] = useState([]); //获取绑定项目下结构物 const [timeTypeDis, setTimeTypeDis] = useState(true); //通知时效disable const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 + const timeTypePOMS = useRef([]);//表单 + const [interval1, setInterval1] = useState(undefined); // + const [interval2, setInterval2] = useState(undefined); // + const [interval3, setInterval3] = useState(undefined); // + const [deviceProportion, setDeviceProportion] = useState(undefined); // //初始化 useEffect(() => { - // if (editObj.id) { - // let departmentList = [] - // for (let i = 0; i < pepList.length; i++) { - // if (pepList[i].id == editObj.departments[0].id) { - // departmentList = pepList[i].users - // } - // } - // setPeopleList(departmentList) - // setDepartmentId(editObj.departments[0].id) - // setPeopleId(editObj.pepUserId) - // setDisablePeople(false) - // } getOrganizationUsersList()//获取全部未删除用户 getProjectPomsList()//获取已绑定项目 + if (editObj.id) { + getProjectStructureList(editObj.pomsProjectId) + if (editObj.pomsProject?.pepProjectId) { + getProjectStatusList()//获取项目状态列表 + } + else { + setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }]) + timeTypePOMS.current = ['POMS'] + } + if (editObj.tactics == 'immediately') { + setInterval1(editObj.tacticsParams?.interval) + } else if (editObj.tactics == 'continue') { + setInterval2(editObj.tacticsParams?.interval) + } else if (editObj.tactics == 'abnormal_rate') { + setInterval3(editObj.tacticsParams?.interval) + setDeviceProportion(editObj.tacticsParams?.deviceProportion) + } + } }, []); function getOrganizationUsersList () {//获取全部未删除用户 dispatch(service.getOrganizationUsers()).then((res) => { @@ -75,7 +82,7 @@ function pushModal (props) { } setProjectStructure(res.payload?.data) form.current.setValue('strucId', mylist) - form.current.validate(['strucId','timeType']) + form.current.validate(['strucId', 'timeType']) setStructure(false) setTimeTypeDis(false) } @@ -90,7 +97,7 @@ function pushModal (props) { mylist.push(res.payload?.data[i].id) } form.current.setValue('timeType', mylist) - form.current.validate(['strucId','timeType']) + form.current.validate(['strucId', 'timeType']) } }) } @@ -100,7 +107,124 @@ function pushModal (props) { .validate() .then((values) => { if (pushEdit) { - dispatch(service.postPush({ pushId: pushId, pepUserId: values.pepUserId, msg: '编辑推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 + let obj = JSON.parse(JSON.stringify(values)) + if (obj.timeType[0] == 'POMS') { + obj.timeType = [] + } + let regu = /^[0-9]*[1-9][0-9]*$/; + if (obj.tactics == 'immediately') { + if (obj.interval1) { + if (regu.test(obj.interval1)) { + if (obj.interval1 <= 1440) { + obj.tacticsParams = { + interval: obj.interval1 + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else { + Notification.error({ + content: '即时推送时间不能大于1440分钟', + duration: 2, + }) + } + } else { + Notification.error({ + content: '即时推送时间应为正整数', + duration: 2, + }) + } + } else { + Notification.error({ + content: '即时推送时间应为正整数', + duration: 2, + }) + } + } + else if (obj.tactics == 'continue') { + if (obj.interval2) { + if (regu.test(obj.interval2)) { + if (obj.interval2 <= 1440) { + obj.tacticsParams = { + interval: obj.interval2 + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else { + Notification.error({ + content: '持续时长推送时间不能大于1440分钟', + duration: 2, + }) + } + } else { + Notification.error({ + content: '持续时长推送时间应为正整数', + duration: 2, + }) + } + } else { + Notification.error({ + content: '持续时长推送时间应为正整数', + duration: 2, + }) + } + } + else { + if (regu.test(obj.interval3) && regu.test(obj.deviceProportion)) { + if (obj.interval3 <= 720 && obj.deviceProportion <= 100) { + obj.tacticsParams = { + interval: obj.interval3, + deviceProportion: obj.deviceProportion + } + delete obj.interval1 + delete obj.interval2 + delete obj.interval3 + delete obj.deviceProportion + } else if (obj.interval3 <= 720 && obj.deviceProportion > 100) { + Notification.error({ + content: '异常率推送异常率不能超过100%', + duration: 2, + }) + } else if (obj.interval3 > 720 && obj.deviceProportion <= 100) { + Notification.error({ + content: '异常率推送时间不能超过720小时', + duration: 2, + }) + } else { + Notification.error({ + content: '异常率推送时间不能超过720小时', + duration: 2, + }) + Notification.error({ + content: '异常率推送异常率不能超过100%', + duration: 2, + }) + } + } else if (regu.test(obj.interval3) && !regu.test(obj.deviceProportion)) { + Notification.error({ + content: '异常率推送异常率应为正整数', + duration: 2, + }) + } else if (!regu.test(obj.interval3) && regu.test(obj.deviceProportion)) { + Notification.error({ + content: '异常率推送时间应为正整数', + duration: 2, + }) + } else { + Notification.error({ + content: '异常率推送异常率应为正整数', + duration: 2, + }) + Notification.error({ + content: '异常率推送时间应为正整数', + duration: 2, + }) + } + } + dispatch(service.postPush({ pushId: pushId, ...obj, msg: '编辑推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 if (res.success) { close(); } @@ -108,7 +232,7 @@ function pushModal (props) { } else { let obj = JSON.parse(JSON.stringify(values)) - if (obj.timeType[0] == null) { + if (obj.timeType[0] == 'POMS') { obj.timeType = [] } let regu = /^[0-9]*[1-9][0-9]*$/; @@ -224,7 +348,6 @@ function pushModal (props) { }) } } - console.log('obj', obj); dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 if (res.success) { close(); @@ -274,35 +397,14 @@ function pushModal (props) { getProjectStatusList()//获取项目状态列表 } else { - setProjectStatus([{ construction_status: 'POMS', id: null }]) - form.current.setValue('timeType', [null]) + setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }]) + form.current.setValue('timeType', ['POMS']) form.current.validate() } } } } } - - // for (var key in field) { - // if (key == 'department') { - // if (values.department >= 0) { - // let departmentList = [] - // for (let i = 0; i < pepList.length; i++) { - // if (pepList[i].id == values.department) { - // departmentList = pepList[i].users - // } - // } - // setPeopleList(departmentList) - // setDisablePeople(false) - // form.current.setValue('pepUserId', undefined); - // } - // else { - // setPeopleList([]) - // setDisablePeople(true) - // form.current.setValue('pepUserId', undefined); - // } - // } - // } }} getFormApi={(formApi) => (form.current = formApi)} > @@ -314,7 +416,7 @@ function pushModal (props) { field="name" label='策略名称:' style={{ width: 695 }} - // initValue={editObj?.name || ""} + initValue={editObj?.name || ""} placeholder="请输入策略名称" showClear rules={[{ required: true, message: "请输入策略名称" }]} /> @@ -326,7 +428,7 @@ function pushModal (props) { placeholder="请选择项目" style={{ width: 695 }} rules={[{ required: true, message: "请选择项目" }]} - // initValue={departmentId || ""} + initValue={editObj?.pomsProjectId || ""} filter > { @@ -347,7 +449,7 @@ function pushModal (props) { placeholder="请选择结构物" style={{ width: 695 }} rules={[{ required: true, message: "请选择结构物" }]} - // initValue={departmentId || ""} + initValue={editObj?.strucId || []} disabled={structure} filter multiple @@ -371,6 +473,7 @@ function pushModal (props) { label='推送策略配置:' type='card' direction='horizontal' + initValue={editObj?.tactics || ''} rules={[{ required: true, message: '请选择推送策略' }]}> 分钟内,有告警源新增,则通过【信鸽服务】发送一条通知信息。 @@ -399,7 +502,7 @@ function pushModal (props) { field="interval2" pure style={{ width: 60, height: 20, color: '#1859C1' }} - initValue={editObj?.interval || "10"} + initValue={interval2 || "10"} // rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "请输入正整数" }]} /> 分钟,则通过【信鸽服务】发送一条通知信息。 @@ -417,7 +520,7 @@ function pushModal (props) { field="deviceProportion" pure style={{ width: 60, height: 20, color: '#1859C1' }} - initValue={editObj?.deviceProportion || "40"} + initValue={deviceProportion || "40"} // rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "请输入正整数" }]} /> %,且持续时长超过 @@ -425,7 +528,7 @@ function pushModal (props) { field="interval3" pure style={{ width: 60, height: 20, color: '#1859C1' }} - initValue={editObj?.interval || "2"} + initValue={interval3 || "2"} // rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "请输入正整数" }]} /> 小时,则通过【信鸽服务】发送一条通知信息。 @@ -442,7 +545,7 @@ function pushModal (props) { field="alarmType" style={{ width: 695 }} rules={[{ required: true, message: "监听问题模块" }]} - // initValue={departmentId || ""} + initValue={editObj?.alarmType || []} direction='horizontal' showClear > @@ -464,7 +567,7 @@ function pushModal (props) { placeholder="请选择通知时效" style={{ width: 285 }} rules={[{ required: true, message: "请选择通知时效" }]} - // initValue={departmentId || ""} + initValue={editObj?.timeType.length > 0 ? editObj?.timeType : timeTypePOMS.current} disabled={timeTypeDis} multiple maxTagCount={3} @@ -485,7 +588,7 @@ function pushModal (props) { placeholder="请选择接收人" style={{ width: 285 }} rules={[{ required: true, message: "请选择接收人" }]} - initValue={[user.id]} + initValue={editObj?.receiverPepUserId || [user.id]} filter multiple maxTagCount={3} @@ -511,7 +614,7 @@ function pushModal (props) { pure direction='horizontal' style={{ display: 'flex', justifyContent: 'space-evenly' }} - initValue={false || false} + initValue={editObj?.disable || false} rules={[{ required: true, }]}> 启用 diff --git a/web/client/src/sections/service/containers/emPush.jsx b/web/client/src/sections/service/containers/emPush.jsx index b988ae8..3772b80 100644 --- a/web/client/src/sections/service/containers/emPush.jsx +++ b/web/client/src/sections/service/containers/emPush.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { connect } from 'react-redux'; import { Skeleton, Button, Pagination, Form, Popconfirm, Table, Tooltip } from '@douyinfe/semi-ui'; +import { IconSearch } from '@douyinfe/semi-icons'; import { SkeletonScreen, } from "$components"; import moment from "moment"; import PushModal from '../components/pushModal' @@ -30,6 +31,7 @@ const EmPush = (props) => { const [appArr, setAppArr] = useState([]) //修改时添加应用 const [bindId, setBindId] = useState() //修改时绑定的id const [tableKey, setTableKey] = useState([]) //修改时绑定的id + const [editObj, setEditObj] = useState({});//管理员弹框修改内容 const [projectStatus, setProjectStatus] = useState([]); //获取项目状态列表 const page = useRef(query.page);//哪一页 const EMPUSH = "empush"; @@ -37,9 +39,9 @@ const EmPush = (props) => { { title: '推送信息', list: [ + { name: "关联项目", value: "projectName" }, { name: "策略名称", value: "name" }, { name: "创建时间", value: "createTime" }, - { name: "关联项目", value: "projectName" }, { name: "接收人", value: "receiverPepUser" }, { name: "推送方式", value: "pushType" }, { name: "监听问题模块", value: "alarmType" }, @@ -77,7 +79,6 @@ const EmPush = (props) => { return {}; } } - const [tableData, setTableData] = useState([]) //表格数据 useEffect(() => { @@ -91,31 +92,28 @@ const EmPush = (props) => { // dispatch(install.getProjectAppList(query)).then((res) => {//获取应用列表 // setAppList(res.payload.data) // }) - getProjectStatusList() localStorage.getItem(EMPUSH) == null ? localStorage.setItem( EMPUSH, - JSON.stringify(['name', 'createTime', 'projectName', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable']) + JSON.stringify(['projectName', 'name', 'createTime', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable']) ) : ""; - attribute(); + getProjectStatusList() }, []) useEffect(() => { getPushList(); }, [query]); function getPushList () { - dispatch(service.getPush(query)).then((res) => {//获取已绑定项目 + let val = form.current.getValues() + // , ...query + dispatch(service.getPush({ ...val })).then((res) => {//获取已绑定项目 if (res.success) { - // let mytableData = JSON.parse(JSON.stringify(res.payload.data.rows)); - // let mytableKey = [] - // for (let index = 0; index < mytableData.length; index++) { - // mytableData[index].key = mytableData[index].id - // mytableKey.push(mytableData[index].id) - // } - // setTableKey(mytableKey) - // setTableData(mytableData) - setTableData(res.payload.data) + 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) setLimits(res.payload.data.length) mylimits.current = res.payload.data.length } @@ -125,13 +123,14 @@ const EmPush = (props) => { dispatch(service.getProjectStatus()).then((res) => { if (res.success) { setProjectStatus(res.payload?.data) + attribute(res.payload?.data); } }) } - const [columns, setColumns] = useState([//表格属性 + const columns = [//表格属性 { title: "操作", - width: "20%", + width: "12%", dataIndex: "text", key: 'text', render: (_, row) => { @@ -139,55 +138,82 @@ const EmPush = (props) => {
- + {row?.disable ? ( + + ) : ( + { + dispatch(service.putPushPushId({ pushId: row?.id, del: false, disable: true, msg: '更改推送配置状态' })).then(() => { + // setQuery({ limit: 10, page: page.current }) + }) + }} + > + + + )} { - // dispatch(install.deleteProjectBind({ bindId: row?.id, msg: '删除安心云、项目管理项目绑定关系' })).then(() => { - // if (page.current > 0 && mylimits.current < 2) { - // setQuery({ limit: 10, page: page.current - 1 }) - // } else { - // setQuery({ limit: 10, page: page.current }) - // } - // }) + dispatch(service.putPushPushId({ pushId: row?.id, del: true, disable: false, msg: '删除推送配置' })).then(() => { + // if (page.current > 0 && mylimits.current < 2) { + // setQuery({ limit: 10, page: page.current - 1 }) + // } else { + // setQuery({ limit: 10, page: page.current }) + // } + }) }} > @@ -196,30 +222,29 @@ const EmPush = (props) => { ); }, }, - ]) + ] + function expandRowRender (record, index) { + return ( +
+ 结构物: + { + record.structure?.map((item, index) => { + return ( +
+ {item.name} +
+ ) + }) + } +
+ ) + } //获取表格属性设置 - function attribute () { + function attribute (val) { const arr = localStorage.getItem(EMPUSH) ? JSON.parse(localStorage.getItem(EMPUSH)) : []; - const column = [ - { - title: '策略名称', - dataIndex: "name", - key: 'name', - render: (_, row) => { - return row.name - } - }, - { - title: "创建时间", - dataIndex: "createTime", - key: "createTime", - render: (_, r, index) => { - return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss'); - }, - }, { title: '关联项目', dataIndex: "projectName", @@ -250,10 +275,63 @@ const EmPush = (props) => {
} + { + row.pomsProject?.pepProject?.projectName ? ( +
+
+ +
+
+ { + val.map((ite, idx) => { + return ( +
+ {ite.id == row.pomsProject?.pepProject.constructionStatusId ? ite.construction_status : ''} +
+ ) + }) + } +
+
+ ) : ( +
+
+ +
+
+ POMS +
+
+ ) + } ) } }, + { + title: '策略名称', + dataIndex: "name", + key: 'name', + render: (_, row) => { + return row.name + } + }, + { + title: "创建时间", + dataIndex: "createTime", + key: "createTime", + render: (_, r, index) => { + return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss'); + }, + }, { title: '接收人', dataIndex: "receiverPepUser", @@ -280,7 +358,7 @@ const EmPush = (props) => { { row.receiverPepUser.map((item, index) => { return ( -
+
{item.name},
) @@ -314,7 +392,6 @@ const EmPush = (props) => { return (
{ - // alarmTypeObj row.alarmType.map((item, index) => { return (
@@ -355,9 +432,81 @@ const EmPush = (props) => { title: "生效项目节点", dataIndex: "timeType", key: "timeType", - render: (_, r, index) => { - // projectStatus - return r.timeType[0] + render: (_, row, index) => { + return ( +
+ { + row.timeType.length > 0 ? ( + row.timeType.map((item, index) => { + return ( +
1 ? 'none' : 'flex', alignItems: 'center' + }}> +
+ +
+
+ { + val.map((ite, idx) => { + return ( +
+ {ite.id == item ? ite.construction_status : ''} +
+ ) + }) + } +
+
+ ) + }) + ) : ( +
+
+ +
+
+ POMS +
+
+ ) + } + { + row.timeType.length > 2 ? ( + + { + row.timeType.map((item, index) => { + return ( +
+ { + val.map((ite, idx) => { + return ( + + {ite.id == item ? ite.construction_status : ''} + + ) + }) + }, +
+ ) + }) + } +
+ } trigger="click" style={{ lineHeight: 2 }}> +
+ +{row.timeType.length - 2} +
+ + ) : ('') + } +
+ ) }, }, { @@ -372,9 +521,31 @@ const EmPush = (props) => { title: "启用状态", dataIndex: "disable", key: "disable", - render: (_, r, index) => { - // projectStatus - return r.disable + render: (_, row, index) => { + let enableType = '' + if (row.disable) { + enableType = '禁用' + } + else { + if (row.timeType.length > 0) { + for (let i = 0; i < row.timeType.length; i++) { + if (row.timeType[i] == row.pomsProject?.pepProject?.constructionStatusId) { + enableType = '已生效' + break; + } else { + enableType = '未生效' + } + } + } + else { + enableType = '已生效' + } + } + return ( +
+ {enableType} +
+ ) }, }, { @@ -382,7 +553,7 @@ const EmPush = (props) => { dataIndex: "pushCount", key: "pushCount", render: (_, r, index) => { - return r.pushCount + return r.pushCount + '次' }, }, ]; @@ -403,7 +574,7 @@ const EmPush = (props) => {
EM推送
Em push
-
+
console.log(values)} // onValueChange={values=>console.log(values)} @@ -411,47 +582,53 @@ const EmPush = (props) => { layout="horizontal" style={{ position: "relative", width: "100%", flex: 1 }} > + + 项目 + 结构物 + 策略名 + } + field="keyword" + pure + showClear + style={{ width: 260, marginLeft: 12, marginRight: 12 }} + placeholder="请输入或选择关键词" /> - {/* {.map((item) => { - return ( - - {item.name} - - ); - })} */} + 数据中断 + 数据异常 + 策略命中 + 视频异常 + 应用异常 + 设备异常 - {/* {.map((item) => { - return ( - - {item.name} - - ); - })} */} + 已生效 + 未生效 + 禁用 @@ -502,10 +681,13 @@ const EmPush = (props) => { placeholder={SkeletonScreen()} > s)} dataSource={tableData} bordered={false} + hideExpandedColumn={false} empty="暂无数据" + expandedRowRender={expandRowRender} pagination={false} onRow={handleRow} /> @@ -543,6 +725,7 @@ const EmPush = (props) => { { setPushModal(false); }} @@ -558,7 +741,7 @@ const EmPush = (props) => { tableList={tableList} close={() => { setSetup(false); - attribute(); + attribute(projectStatus); }} /> ) : ( diff --git a/web/client/src/sections/service/style.less b/web/client/src/sections/service/style.less index b98d242..7f2491d 100644 --- a/web/client/src/sections/service/style.less +++ b/web/client/src/sections/service/style.less @@ -1,5 +1,13 @@ -.empush{ +.myempush{ .semi-input-wrapper{ margin-bottom: 0px !important; } +} +.emPushTable{ + // .semi-table-row-cell:first-child{ + // display: flex !important; + // } + // .semi-table-row-cell{ + // display: flex !important; + // } } \ No newline at end of file diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index 36b3e65..e420840 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -54,6 +54,7 @@ export const ApiTable = { getOrganizationUsers: "organization/users", //获取全部未删除用户 getProjectStructure: "project/structure", //获取绑定项目下结构物 getProjectStatus: "project/status", //获取项目状态列表 + putPushPushId: "push/{pushId}", //更改推送配置状态(禁用或删除) //控制台 consoleToollink: 'console/toollink', //常用工具 From 4237144e6ad74ae762de5c4db04af64172c6220d Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Fri, 28 Oct 2022 15:55:03 +0800 Subject: [PATCH 20/22] =?UTF-8?q?fix=20=E7=8A=B6=E6=80=81=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/push/config.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/api/app/lib/controllers/push/config.js b/api/app/lib/controllers/push/config.js index d4b299e..c6382c8 100644 --- a/api/app/lib/controllers/push/config.js +++ b/api/app/lib/controllers/push/config.js @@ -12,7 +12,15 @@ async function list (ctx) { let findOption = { where: { del: false - } + }, + include: [{ + model: models.ProjectCorrelation, + where: { + del: false, + pepProjectId: state == 'notYet' ? { $ne: null } : undefined + }, + required: true + }] } let anxinStrucsRange = await anxinStrucIdRange({ From 645c69fa446fca9c7d7b14845a2d3f2a8ef900f7 Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Fri, 28 Oct 2022 16:06:53 +0800 Subject: [PATCH 21/22] =?UTF-8?q?fix=20=E7=8A=B6=E6=80=81=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/push/config.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/api/app/lib/controllers/push/config.js b/api/app/lib/controllers/push/config.js index c6382c8..3feedea 100644 --- a/api/app/lib/controllers/push/config.js +++ b/api/app/lib/controllers/push/config.js @@ -9,16 +9,19 @@ async function list (ctx) { const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs const { keyword, keywordTarget, alarmType, state, tactics, pomsProjectId } = ctx.query + let projectCorrelationWhere = { + del: false, + } + if (state == 'notYet') { + projectCorrelationWhere.pepProjectId = { $ne: null } + } let findOption = { where: { del: false }, include: [{ model: models.ProjectCorrelation, - where: { - del: false, - pepProjectId: state == 'notYet' ? { $ne: null } : undefined - }, + where: projectCorrelationWhere, required: true }] } From ab6f9fbd288633c3d8b6534becffe83f88fc593a Mon Sep 17 00:00:00 2001 From: deartibers <947466799@qq.com> Date: Fri, 28 Oct 2022 17:28:15 +0800 Subject: [PATCH 22/22] =?UTF-8?q?em=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/client/index.ejs | 2 +- web/client/index.html | 2 +- web/client/src/sections/install/containers/system.jsx | 2 +- web/client/src/sections/service/components/pushModal.jsx | 4 ++-- web/client/src/sections/service/containers/emPush.jsx | 3 ++- web/client/src/sections/service/nav-item.jsx | 2 +- web/client/src/sections/service/style.less | 8 -------- 7 files changed, 8 insertions(+), 15 deletions(-) diff --git a/web/client/index.ejs b/web/client/index.ejs index 9cbe876..71b028e 100644 --- a/web/client/index.ejs +++ b/web/client/index.ejs @@ -11,7 +11,7 @@ - + diff --git a/web/client/index.html b/web/client/index.html index 162fe74..302a7e4 100644 --- a/web/client/index.html +++ b/web/client/index.html @@ -11,7 +11,7 @@ + src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_19077_11.559b91c217b8ddc76c0c4b1397d84d48.es5.js"> diff --git a/web/client/src/sections/install/containers/system.jsx b/web/client/src/sections/install/containers/system.jsx index b33458f..69a80d1 100644 --- a/web/client/src/sections/install/containers/system.jsx +++ b/web/client/src/sections/install/containers/system.jsx @@ -273,7 +273,7 @@ const Example = (props) => { 修改 {//获取项企(PEP)全部部门及其下用户 + dispatch(service.postPush({ pushId: editObj.id, ...obj, msg: '编辑推送配置' })).then((res) => {//获取项企(PEP)全部部门及其下用户 if (res.success) { close(); } @@ -567,7 +567,7 @@ function pushModal (props) { placeholder="请选择通知时效" style={{ width: 285 }} rules={[{ required: true, message: "请选择通知时效" }]} - initValue={editObj?.timeType.length > 0 ? editObj?.timeType : timeTypePOMS.current} + initValue={editObj?.timeType?.length > 0 ? editObj?.timeType : timeTypePOMS.current} disabled={timeTypeDis} multiple maxTagCount={3} diff --git a/web/client/src/sections/service/containers/emPush.jsx b/web/client/src/sections/service/containers/emPush.jsx index 3772b80..1e1354a 100644 --- a/web/client/src/sections/service/containers/emPush.jsx +++ b/web/client/src/sections/service/containers/emPush.jsx @@ -662,6 +662,7 @@ const EmPush = (props) => { marginLeft: 32 }} onClick={() => { + setEditObj({}) setPushModal(true); setPushEdit(false); }} @@ -688,7 +689,7 @@ const EmPush = (props) => { hideExpandedColumn={false} empty="暂无数据" expandedRowRender={expandRowRender} - pagination={false} + // pagination={false} onRow={handleRow} /> diff --git a/web/client/src/sections/service/nav-item.jsx b/web/client/src/sections/service/nav-item.jsx index 694b817..cd770ad 100644 --- a/web/client/src/sections/service/nav-item.jsx +++ b/web/client/src/sections/service/nav-item.jsx @@ -30,7 +30,7 @@ export function getNavItem (user, dispatch) { }, { itemKey: 'carrierPigeon', text: '信鸽服务', - icon: , + icon: , to: '/service/carrierPigeon/emPush', items: [{ itemKey: 'emPush', to: '/service/carrierPigeon/emPush', text: 'EM推送' diff --git a/web/client/src/sections/service/style.less b/web/client/src/sections/service/style.less index 7f2491d..d26536b 100644 --- a/web/client/src/sections/service/style.less +++ b/web/client/src/sections/service/style.less @@ -2,12 +2,4 @@ .semi-input-wrapper{ margin-bottom: 0px !important; } -} -.emPushTable{ - // .semi-table-row-cell:first-child{ - // display: flex !important; - // } - // .semi-table-row-cell{ - // display: flex !important; - // } } \ No newline at end of file