From a1471ea9d3f734215f730a844ba8dc425fd80743 Mon Sep 17 00:00:00 2001 From: wuqun Date: Thu, 20 Oct 2022 15:30:29 +0800 Subject: [PATCH 1/4] =?UTF-8?q?(+)=E5=91=8A=E8=AD=A6=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=A1=A8model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/models/alarm_confirm_log.js | 71 +++++++++++++++++++++++++ api/package.json | 1 + 2 files changed, 72 insertions(+) create mode 100644 api/app/lib/models/alarm_confirm_log.js diff --git a/api/app/lib/models/alarm_confirm_log.js b/api/app/lib/models/alarm_confirm_log.js new file mode 100644 index 0000000..2419c82 --- /dev/null +++ b/api/app/lib/models/alarm_confirm_log.js @@ -0,0 +1,71 @@ +/* eslint-disable*/ + +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const AlarmConfirmLog = sequelize.define("alarmConfirmLog", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "alarm_confirm_log_id_uindex" + }, + pepUserId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "pep_user_id", + autoIncrement: false + }, + projectCorrelationId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "project_correlation_id", + autoIncrement: false + }, + alarmInfo: { + type: DataTypes.JSON, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "alarm_info", + autoIncrement: false + }, + confirmTime: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "confirm_time", + autoIncrement: false + }, + confirmContent: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "confirm_content", + autoIncrement: false + } + }, { + tableName: "alarm_confirm_log", + comment: "", + indexes: [] + }); + dc.models.AlarmConfirmLog = AlarmConfirmLog; + return AlarmConfirmLog; +}; \ No newline at end of file diff --git a/api/package.json b/api/package.json index cffb191..4d3e893 100644 --- a/api/package.json +++ b/api/package.json @@ -34,6 +34,7 @@ "pg": "^7.9.0", "redis": "^3.1.2", "request": "^2.88.2", + "sequelize-automate-freesun": "^1.2.2", "superagent": "^3.5.2", "uuid": "^3.3.2" }, From 0f9b1ac3e110e21d7430712f9112c98f00e57398 Mon Sep 17 00:00:00 2001 From: wuqun Date: Mon, 24 Oct 2022 10:52:10 +0800 Subject: [PATCH 2/4] =?UTF-8?q?(*)=E5=BA=94=E7=94=A8=E5=91=8A=E8=AD=A6,?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E5=91=8A=E8=AD=A6,=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E7=A1=AE=E8=AE=A4;=20=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=8F=B0=E6=9C=80=E6=96=B0=E5=8A=A8=E6=80=81=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=9A=82=E4=BA=A4?= 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/app.js | 38 ++++- api/app/lib/controllers/console/index.js | 153 ++++++++++++++++++ api/app/lib/index.js | 25 ++- api/app/lib/models/alarm_appear_record.js | 53 ++++++ api/app/lib/models/alarm_confirm_log.js | 2 +- api/app/lib/models/email_send_log.js | 62 +++++++ api/app/lib/models/latest_dynamic_list.js | 71 ++++++++ api/app/lib/routes/console/index.js | 23 +++ .../sections/problem/components/tableData.jsx | 12 +- .../sections/problem/containers/dataAlarm.jsx | 27 +++- 11 files changed, 445 insertions(+), 25 deletions(-) create mode 100644 api/app/lib/controllers/console/index.js create mode 100644 api/app/lib/models/alarm_appear_record.js create mode 100644 api/app/lib/models/email_send_log.js create mode 100644 api/app/lib/models/latest_dynamic_list.js create mode 100644 api/app/lib/routes/console/index.js diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index e7d0a75..5d040f2 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/app.js b/api/app/lib/controllers/alarm/app.js index 43d73fc..8307737 100644 --- a/api/app/lib/controllers/alarm/app.js +++ b/api/app/lib/controllers/alarm/app.js @@ -2,7 +2,7 @@ const moment = require('moment') -async function inspection (ctx) { +async function inspection(ctx) { // 巡查 try { const models = ctx.fs.dc.models; @@ -31,7 +31,7 @@ async function inspection (ctx) { } } -async function inspectionList (ctx) { +async function inspectionList(ctx) { try { const models = ctx.fs.dc.models; const { clickHouse } = ctx.app.fs @@ -116,7 +116,7 @@ async function inspectionList (ctx) { } } -async function notedInspection (ctx) { +async function notedInspection(ctx) { try { const models = ctx.fs.dc.models; const { inspectionId } = ctx.request.body @@ -141,7 +141,7 @@ async function notedInspection (ctx) { } } -async function apiError (ctx) { +async function apiError(ctx) { try { const models = ctx.fs.dc.models; const { projectAppId, alarmContent, router, statusCode, screenshot = '', type } = ctx.request.body @@ -212,7 +212,7 @@ async function apiError (ctx) { } } -async function apiErrorList (ctx) { +async function apiErrorList(ctx) { try { const models = ctx.fs.dc.models; const { clickHouse } = ctx.app.fs @@ -324,11 +324,11 @@ async function apiErrorList (ctx) { } } -async function confirmApiError (ctx) { +async function confirmApiError(ctx) { try { const models = ctx.fs.dc.models; - const { confirm, appAlarmId = [] } = ctx.request.body - + const { confirm, appAlarmId = [], confirmPost } = ctx.request.body + const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost await models.AppAlarm.update({ confirm, confirmTime: moment().format() @@ -338,6 +338,28 @@ 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: moment().format(), + alarmConfirmId: r.id + } + }) + await models.AlarmConfirmLog.bulkCreate(dynamics); + + ctx.status = 204; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); diff --git a/api/app/lib/controllers/console/index.js b/api/app/lib/controllers/console/index.js new file mode 100644 index 0000000..5fbe963 --- /dev/null +++ b/api/app/lib/controllers/console/index.js @@ -0,0 +1,153 @@ +'use strict'; +const moment = require('moment'); + +//工作台 +async function getWorkbench(ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { alarmId, limit, page } = ctx.query + 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 + } + } +} + +//项目概览 +async function getProjectsInfo(ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { alarmId, limit, page } = ctx.query + 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 getBiAnalysis(ctx) { + try { + const { models } = ctx.fs.dc; + const { clickHouse } = ctx.app.fs + const { alarmId, limit, page } = ctx.query + 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 + } + } +} + +//最新动态 +async function getLatestDynamic(ctx) { + try { + const { models } = ctx.fs.dc; + const { limit, page, projectCorrelationId } = ctx.query; + const { userInfo } = ctx.fs.api; + const { clickHouse } = ctx.app.fs; + + let where = {} + if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) { + where.projectCorrelationId = { $in: userInfo.correlationProject } + } + if (projectCorrelationId) {//查指定项目,控制台全局切换 + where.projectCorrelationId = projectCorrelationId + } + let news = await models.LatestDynamicList.findAll({//最新动态 + include: [{ + model: models.ProjectCorrelation, + where: { del: false }, + attributes: ['id', 'name', 'pepProjectId'], + }, { + model: models.AlarmAppearRecord + }, { + model: models.EmailSendLog + }, { + model: models.AlarmConfirmLog + }], + where: where, + offset: Number(page) * Number(limit), + limit: Number(limit), + order: [['time', 'desc']], + }); + + //查项目名称 查用户名 + let pepPojectIds = new Set(), notedUserIds = new Set(); + for (let p of news) { + pepPojectIds.add(p.projectCorrelation.pepProjectId); + + if (p.emailSendLog) { + notedUserIds.add(p.emailSendLog.toPepUserId);//通知 接收人 + } + if (p.alarmConfirmLog && p.alarmConfirmLog.pepUserId) { + notedUserIds.add(p.alarmConfirmLog.pepUserId);//确认 操作者 + } + } + let pepProjects = pepPojectIds.size ? await clickHouse.projectManage.query(` + SELECT id, project_name FROM t_pim_project WHERE id IN (${[...pepPojectIds]}) + `).toPromise() : []; + + let userPepRes = notedUserIds.size ? await clickHouse.pepEmis.query( + `SELECT DISTINCT user.id AS id, "user"."name" AS name FROM user WHERE user.id IN (${[...notedUserIds].join(',')}) + `).toPromise() : [] + + + let appear = [], notice = [], confirm = []; + news.map(d => { + let projectName = d.projectCorrelation.name || pepProjects.find(pp => pp.id == d.projectCorrelation.pepProjectId).project_name; + if (d.alarmAppearId) { + appear.push({ + projectName, + ...d.alarmAppearRecord + }); + } + if (d.emailSendId) { + notice.push({ + userName: userPepRes.find(u => u.id == d.emailSendLog.toPepUserId).name, + projectName, + ...d.emailSendLog + }); + } + if (d.alarmConfirmId) { + confirm.push({ + userName: d.alarmConfirmLog.pepUserId ? userPepRes.find(u => u.id == d.alarmConfirmLog.pepUserId).name : '自动恢复', + projectName, + ...d.alarmConfirmLog.dataValues + }); + } + }) + ctx.status = 200; + ctx.body = { + appear,//发现 + notice,//通知 + confirm//确认 + }; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +module.exports = { + getWorkbench, + getProjectsInfo, + getBiAnalysis, + getLatestDynamic +}; \ No newline at end of file diff --git a/api/app/lib/index.js b/api/app/lib/index.js index bd5cde2..10a1b33 100644 --- a/api/app/lib/index.js +++ b/api/app/lib/index.js @@ -58,7 +58,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq }); const { - AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App + AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App, AlarmAppearRecord, AlarmConfirmLog, EmailSendLog, LatestDynamicList } = dc.models; AppInspection.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' }); @@ -76,4 +76,27 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq AppAlarm.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' }); App.hasMany(AppAlarm, { foreignKey: 'projectAppId', sourceKey: 'id' }); + + + + AlarmAppearRecord.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' }); + ProjectCorrelation.hasMany(AlarmAppearRecord, { foreignKey: 'projectCorrelationId', sourceKey: 'id' }); + + AlarmConfirmLog.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' }); + ProjectCorrelation.hasMany(AlarmConfirmLog, { foreignKey: 'projectCorrelationId', sourceKey: 'id' }); + + EmailSendLog.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' }); + ProjectCorrelation.hasMany(EmailSendLog, { foreignKey: 'projectCorrelationId', sourceKey: 'id' }); + + LatestDynamicList.belongsTo(AlarmAppearRecord, { foreignKey: 'alarmAppearId', targetKey: 'id' }); + AlarmAppearRecord.hasMany(LatestDynamicList, { foreignKey: 'alarmAppearId', sourceKey: 'id' }); + + LatestDynamicList.belongsTo(EmailSendLog, { foreignKey: 'emailSendId', targetKey: 'id' }); + EmailSendLog.hasMany(LatestDynamicList, { foreignKey: 'emailSendId', sourceKey: 'id' }); + + LatestDynamicList.belongsTo(AlarmConfirmLog, { foreignKey: 'alarmConfirmId', targetKey: 'id' }); + AlarmConfirmLog.hasMany(LatestDynamicList, { foreignKey: 'alarmConfirmId', sourceKey: 'id' }); + + LatestDynamicList.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' }); + ProjectCorrelation.hasMany(LatestDynamicList, { foreignKey: 'projectCorrelationId', sourceKey: 'id' }); }; diff --git a/api/app/lib/models/alarm_appear_record.js b/api/app/lib/models/alarm_appear_record.js new file mode 100644 index 0000000..5bf40d6 --- /dev/null +++ b/api/app/lib/models/alarm_appear_record.js @@ -0,0 +1,53 @@ +/* eslint-disable*/ + +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const AlarmAppearRecord = sequelize.define("alarmAppearRecord", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "alarm_appear_record_id_uindex" + }, + projectCorrelationId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "project_correlation_id", + autoIncrement: false + }, + alarmInfo: { + type: DataTypes.JSON, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "alarm_info", + autoIncrement: false + }, + time: { + type: DataTypes.DATE, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "time", + autoIncrement: false + } + }, { + tableName: "alarm_appear_record", + comment: "", + indexes: [] + }); + dc.models.AlarmAppearRecord = AlarmAppearRecord; + return AlarmAppearRecord; +}; \ No newline at end of file diff --git a/api/app/lib/models/alarm_confirm_log.js b/api/app/lib/models/alarm_confirm_log.js index 2419c82..f850a83 100644 --- a/api/app/lib/models/alarm_confirm_log.js +++ b/api/app/lib/models/alarm_confirm_log.js @@ -18,7 +18,7 @@ module.exports = dc => { }, pepUserId: { type: DataTypes.INTEGER, - allowNull: false, + allowNull: true, defaultValue: null, comment: null, primaryKey: false, diff --git a/api/app/lib/models/email_send_log.js b/api/app/lib/models/email_send_log.js new file mode 100644 index 0000000..7f75ea1 --- /dev/null +++ b/api/app/lib/models/email_send_log.js @@ -0,0 +1,62 @@ +/* eslint-disable*/ + +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const EmailSendLog = sequelize.define("emailSendLog", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "email_send_log_id_uindex" + }, + projectCorrelationId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "project_correlation_id", + autoIncrement: false + }, + toPepUserId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "to_pep_user_id", + autoIncrement: false + }, + by: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "by", + autoIncrement: false + }, + time: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "time", + autoIncrement: false + } + }, { + tableName: "email_send_log", + comment: "", + indexes: [] + }); + dc.models.EmailSendLog = EmailSendLog; + return EmailSendLog; +}; \ No newline at end of file diff --git a/api/app/lib/models/latest_dynamic_list.js b/api/app/lib/models/latest_dynamic_list.js new file mode 100644 index 0000000..65ac6cd --- /dev/null +++ b/api/app/lib/models/latest_dynamic_list.js @@ -0,0 +1,71 @@ +/* eslint-disable*/ + +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const LatestDynamicList = sequelize.define("latestDynamicList", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "latest_dynamic_list_id_uindex" + }, + time: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "time", + autoIncrement: false + }, + alarmAppearId: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "alarm_appear_id", + autoIncrement: false + }, + emailSendId: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "email_send_id", + autoIncrement: false + }, + alarmConfirmId: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "alarm_confirm_id", + autoIncrement: false + }, + projectCorrelationId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "project_correlation_id", + autoIncrement: false + } + }, { + tableName: "latest_dynamic_list", + comment: "", + indexes: [] + }); + dc.models.LatestDynamicList = LatestDynamicList; + return LatestDynamicList; +}; \ No newline at end of file diff --git a/api/app/lib/routes/console/index.js b/api/app/lib/routes/console/index.js new file mode 100644 index 0000000..17f6fe2 --- /dev/null +++ b/api/app/lib/routes/console/index.js @@ -0,0 +1,23 @@ + + +'use strict'; + +const console = require('../../controllers/console/index'); + +module.exports = function (app, router, opts) { + //我的工作台 + app.fs.api.logAttr['GET/user/:userId/workbench'] = { content: '查询工作台问题', visible: false }; + router.get('/user/:userId/workbench', console.getWorkbench); + + //项目概览 + app.fs.api.logAttr['GET/user/:userId/projects/info'] = { content: '查询项目概览', visible: false }; + router.get('/user/:userId/projects/info', console.getProjectsInfo); + + //BI分析模块 + app.fs.api.logAttr['GET/user/:userId/bi/analysis'] = { content: '查询BI分析数据', visible: false }; + router.get('/user/:userId/bi/analysis', console.getBiAnalysis); + + //最新动态 + app.fs.api.logAttr['GET/user/:userId/latest/dynamic'] = { content: '查询最新动态', visible: false }; + router.get('/user/:userId/latest/dynamic', console.getLatestDynamic); +}; diff --git a/web/client/src/sections/problem/components/tableData.jsx b/web/client/src/sections/problem/components/tableData.jsx index a074e1d..1f4fa02 100644 --- a/web/client/src/sections/problem/components/tableData.jsx +++ b/web/client/src/sections/problem/components/tableData.jsx @@ -26,8 +26,8 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition let typeData = { element: "元素异常", apiError: "接口报错 ", timeout: "加载超时" } let tableDatas = res.payload.data?.rows.map(v => ({ key: v.id, - projectName: v.app?.projectCorrelations?.map(r => (r.name ? { name: r.name, state: 'PMOS' } : { - name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus + projectName: v.app?.projectCorrelations?.map(r => (r.name ? { id: r.id, name: r.name, state: 'PMOS' } : { + id: r.id, name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus }))?.filter(c => c), appName: v.app?.name, url: v.app?.url, @@ -59,8 +59,8 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition let tableDatas = res.payload.data?.map(v => ({ key: v.alarmId, StructureName: v.struc, - projectName: v.pomsProject?.map(r => (r.name ? { name: r.name, state: 'PMOS' } : { - name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus + projectName: v.pomsProject?.map(r => (r.name ? { id: r.id, name: r.name, state: 'PMOS' } : { + id: r.id, name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus }))?.filter(c => c), createTime: v.createTime ? moment(v.createTime).format("YYYY-MM-DD HH:mm:ss") : "", updateTime: v.updateTime ? moment(v.updateTime).format("YYYY-MM-DD HH:mm:ss") : "", @@ -107,8 +107,8 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition let tableDatas = res.payload.data?.rows?.map(v => ({ key: v.AlarmId, StructureName: v.StructureName, - projectName: v.pomsProject?.map(r => (r.name ? { name: r.name, state: 'PMOS' } : { - name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus + projectName: v.pomsProject?.map(r => (r.name ? { id: r.id, name: r.name, state: 'PMOS' } : { + id: r.id, name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus }))?.filter(c => c), createTime: v.StartTime ? moment(v.StartTime).format("YYYY-MM-DD HH:mm:ss") : "", updateTime: v.EndTime ? moment(v.EndTime).format("YYYY-MM-DD HH:mm:ss") : "", diff --git a/web/client/src/sections/problem/containers/dataAlarm.jsx b/web/client/src/sections/problem/containers/dataAlarm.jsx index 1703abb..04f45a1 100644 --- a/web/client/src/sections/problem/containers/dataAlarm.jsx +++ b/web/client/src/sections/problem/containers/dataAlarm.jsx @@ -37,7 +37,7 @@ 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('') @@ -309,6 +309,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb : r.State == 3 || r.autoRestore || r.confirmAuto ? : @@ -383,7 +384,19 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb // console.log(selected); - + const getAlarmConfirmItem = () => { + let source = route == 'useAbnormal' ? alarmToConfirm.appName : alarmToConfirm.SourceName; + let type = route == 'useAbnormal' ? alarmToConfirm.type : route == 'videoAbnormal' ? alarmToConfirm.AlarmContent : alarmToConfirm.AlarmGroupUnit; + return { + pepUserId: user.pomsUserInfo.pepUserId, + projectCorrelationIds: alarmToConfirm?.projectName?.map(p => p.id), + alarmInfo: { + id: alarmToConfirm.key, + source: source,//告警源 + type: type,//异常类型 + } + }; + } return ( // route=='dataLnterrupt'|| route=='dataAbnormal'|| route=='strategyHit'? @@ -444,10 +457,10 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb width={600} onCancel={() => setConfirm(false)} onOk={() => { - + let confirmPost = getAlarmConfirmItem(); if (route == 'useAbnormal') { TextAreaApi.current.validate().then((v) => { - dispatch(problem.postApiConfirm({ appAlarmId: selected, confirm: content })).then(res => { + dispatch(problem.postApiConfirm({ appAlarmId: selected, confirm: content, confirmPost })).then(res => { if (res.success) { setConfirm(false) setSelected([]) @@ -457,7 +470,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb }) } else if (route == 'videoAbnormal') { TextAreaApi.current.validate().then((v) => { - dispatch(problem.putAlarmVideoConfirm({ alarmId: selected, content: content })).then(res => { + dispatch(problem.putAlarmVideoConfirm({ alarmId: selected, content: content, confirmPost })).then(res => { if (res.success) { setConfirm(false) setSelected([]) @@ -476,7 +489,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb }) } else { TextAreaApi.current.validate().then((v) => { - dispatch(problem.putAlarmdataConfirm({ alarmId: selected, content: content })).then(res => { + dispatch(problem.putAlarmdataConfirm({ alarmId: selected, content: content, confirmPost })).then(res => { if (res.success) { setConfirm(false) let data = tableData?.map(v => { @@ -580,7 +593,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb ) } -function mapStateToProps (state) { +function mapStateToProps(state) { const { auth, global, members, webSocket } = state return { // loading: members.isRequesting, From 878eb3a1e42095d9748f3e9badd86255c256832a Mon Sep 17 00:00:00 2001 From: wuqun Date: Mon, 24 Oct 2022 14:35:20 +0800 Subject: [PATCH 3/4] =?UTF-8?q?(*)=E8=8E=B7=E5=8F=96=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=8F=B0=E6=9C=80=E6=96=B0=E5=8A=A8=E6=80=81=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=BF=AE=E6=94=B9,=E4=BC=A0=E9=80=89=E6=8B=A9=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/alarm/app.js | 8 +++++--- api/app/lib/controllers/console/index.js | 16 ++++++++++++---- api/app/lib/models/alarm_appear_record.js | 9 +++++++++ api/app/lib/models/latest_dynamic_list.js | 9 +++++++++ api/app/lib/routes/console/index.js | 4 ++-- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/api/app/lib/controllers/alarm/app.js b/api/app/lib/controllers/alarm/app.js index 8307737..5279241 100644 --- a/api/app/lib/controllers/alarm/app.js +++ b/api/app/lib/controllers/alarm/app.js @@ -353,11 +353,13 @@ async function confirmApiError(ctx) { //存最新动态 let dynamics = rslt.map(r => { return { - time: moment().format(), - alarmConfirmId: r.id + time: r.confirmTime, + alarmConfirmId: r.id, + projectCorrelationId: r.projectCorrelationId, + type: 4//告警确认 } }) - await models.AlarmConfirmLog.bulkCreate(dynamics); + await models.LatestDynamicList.bulkCreate(dynamics); ctx.status = 204; diff --git a/api/app/lib/controllers/console/index.js b/api/app/lib/controllers/console/index.js index 5fbe963..b12aabd 100644 --- a/api/app/lib/controllers/console/index.js +++ b/api/app/lib/controllers/console/index.js @@ -22,8 +22,16 @@ async function getWorkbench(ctx) { async function getProjectsInfo(ctx) { try { const { models } = ctx.fs.dc; - const { clickHouse } = ctx.app.fs - const { alarmId, limit, page } = ctx.query + const { clickHouse } = ctx.app.fs; + const { alarmId, limit, page, projectCorrelationId } = 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 + } ctx.status = 200; ctx.body = [] } catch (error) { @@ -56,11 +64,11 @@ async function getBiAnalysis(ctx) { async function getLatestDynamic(ctx) { try { const { models } = ctx.fs.dc; - const { limit, page, projectCorrelationId } = ctx.query; + const { limit, page, projectCorrelationId, types } = ctx.query; const { userInfo } = ctx.fs.api; const { clickHouse } = ctx.app.fs; - let where = {} + let where = { type: { $in: types.split(',') } }//传类型选择 if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) { where.projectCorrelationId = { $in: userInfo.correlationProject } } diff --git a/api/app/lib/models/alarm_appear_record.js b/api/app/lib/models/alarm_appear_record.js index 5bf40d6..3a6dcfa 100644 --- a/api/app/lib/models/alarm_appear_record.js +++ b/api/app/lib/models/alarm_appear_record.js @@ -42,6 +42,15 @@ module.exports = dc => { primaryKey: false, field: "time", autoIncrement: false + }, + type: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null, + comment: "数据告警:data,设备告警:device,应用告警:application", + primaryKey: false, + field: "type", + autoIncrement: false } }, { tableName: "alarm_appear_record", diff --git a/api/app/lib/models/latest_dynamic_list.js b/api/app/lib/models/latest_dynamic_list.js index 65ac6cd..5b53051 100644 --- a/api/app/lib/models/latest_dynamic_list.js +++ b/api/app/lib/models/latest_dynamic_list.js @@ -60,6 +60,15 @@ module.exports = dc => { primaryKey: false, field: "project_correlation_id", autoIncrement: false + }, + type: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: null, + comment: '1:发现,2:通知,3:处置,4:确认', + primaryKey: false, + field: "type", + autoIncrement: false } }, { tableName: "latest_dynamic_list", diff --git a/api/app/lib/routes/console/index.js b/api/app/lib/routes/console/index.js index 17f6fe2..a9ccc58 100644 --- a/api/app/lib/routes/console/index.js +++ b/api/app/lib/routes/console/index.js @@ -18,6 +18,6 @@ module.exports = function (app, router, opts) { router.get('/user/:userId/bi/analysis', console.getBiAnalysis); //最新动态 - app.fs.api.logAttr['GET/user/:userId/latest/dynamic'] = { content: '查询最新动态', visible: false }; - router.get('/user/:userId/latest/dynamic', console.getLatestDynamic); + app.fs.api.logAttr['GET/latest/dynamic'] = { content: '查询最新动态', visible: false }; + router.get('/latest/dynamic', console.getLatestDynamic); }; From 5fa4e98c157eca702e31b407c17eef46f7823f2f Mon Sep 17 00:00:00 2001 From: wuqun Date: Tue, 25 Oct 2022 09:54:36 +0800 Subject: [PATCH 4/4] =?UTF-8?q?(*)=E6=8E=A7=E5=88=B6=E5=8F=B0=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=20=E4=BF=AE=E6=94=B9=E5=88=B0control?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/alarm/data.js | 3 +- api/app/lib/controllers/alarm/video.js | 39 +++++++++++-- .../{console/index.js => control/data.js} | 58 +++++++++++++++---- api/app/lib/routes/console/index.js | 23 -------- api/app/lib/routes/control/index.js | 16 +++++ 5 files changed, 97 insertions(+), 42 deletions(-) rename api/app/lib/controllers/{console/index.js => control/data.js} (67%) delete mode 100644 api/app/lib/routes/console/index.js diff --git a/api/app/lib/controllers/alarm/data.js b/api/app/lib/controllers/alarm/data.js index ae135b5..595cd60 100644 --- a/api/app/lib/controllers/alarm/data.js +++ b/api/app/lib/controllers/alarm/data.js @@ -247,7 +247,8 @@ function confirm (opts) { const { models } = ctx.fs.dc; const { utils: { kfkSendAsync } } = ctx.app.fs const { clickHouse } = ctx.app.fs - const { content = '', alarmId } = ctx.request.body + const { content = '', alarmId, confirmPost } = ctx.request.body; + const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost; // 发送告警恢复通知 // Topic: alarm /* diff --git a/api/app/lib/controllers/alarm/video.js b/api/app/lib/controllers/alarm/video.js index 1263203..007b176 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) { +async function alarmList(ctx, agg) { try { const { models } = ctx.fs.dc; const { clickHouse } = ctx.app.fs @@ -69,7 +69,7 @@ async function alarmList (ctx) { } const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query( ` - SELECT + SELECT cameraAlarm.cameraId AS cameraId, cameraAlarm.cameraName AS cameraName, cameraAlarm.cameraKindId AS cameraKindId, @@ -250,8 +250,12 @@ async function alarmList (ctx) { } } - ctx.status = 200; - ctx.body = returnD + if (agg == 'day') {//控制台 按日聚集 + return returnD + } else { + ctx.status = 200; + ctx.body = returnD + } } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; @@ -264,8 +268,8 @@ async function alarmList (ctx) { async function confirm (ctx) { try { const { models } = ctx.fs.dc; - const { alarmId, content } = ctx.request.body; - + const { alarmId, content, confirmPost } = ctx.request.body; + const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost; // TODO: 以视频·应用的秘钥进行鉴权 await ctx.app.fs.vcmpRequest.put('status/alarm/confirm', { data: { @@ -273,6 +277,29 @@ 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); + ctx.status = 204; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); diff --git a/api/app/lib/controllers/console/index.js b/api/app/lib/controllers/control/data.js similarity index 67% rename from api/app/lib/controllers/console/index.js rename to api/app/lib/controllers/control/data.js index b12aabd..d875b5e 100644 --- a/api/app/lib/controllers/console/index.js +++ b/api/app/lib/controllers/control/data.js @@ -1,6 +1,6 @@ 'use strict'; const moment = require('moment'); - +const { alarmList } = require('../alarm/video'); //工作台 async function getWorkbench(ctx) { try { @@ -22,16 +22,40 @@ async function getWorkbench(ctx) { async function getProjectsInfo(ctx) { try { const { models } = ctx.fs.dc; - const { clickHouse } = ctx.app.fs; - const { alarmId, limit, page, projectCorrelationId } = ctx.query; + 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 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) { @@ -48,9 +72,19 @@ async function getBiAnalysis(ctx) { try { const { models } = ctx.fs.dc; const { clickHouse } = ctx.app.fs - const { alarmId, limit, page } = ctx.query + const { alarmId, limit, page } = ctx.query; + let videoAlarms = await alarmList(ctx, 'day'); + let aggDayMap = []; + for (let a of videoAlarms) { + let exist = aggDayMap.find(ad => ad.day == moment(a.createTime).format('YYYY-MM-DD')) + if (exist) { + exist.number++ + } else { + aggDayMap.push({ day: moment(a.createTime).format('YYYY-MM-DD'), number: 1 }) + } + } ctx.status = 200; - ctx.body = [] + ctx.body = aggDayMap; } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; diff --git a/api/app/lib/routes/console/index.js b/api/app/lib/routes/console/index.js deleted file mode 100644 index a9ccc58..0000000 --- a/api/app/lib/routes/console/index.js +++ /dev/null @@ -1,23 +0,0 @@ - - -'use strict'; - -const console = require('../../controllers/console/index'); - -module.exports = function (app, router, opts) { - //我的工作台 - app.fs.api.logAttr['GET/user/:userId/workbench'] = { content: '查询工作台问题', visible: false }; - router.get('/user/:userId/workbench', console.getWorkbench); - - //项目概览 - app.fs.api.logAttr['GET/user/:userId/projects/info'] = { content: '查询项目概览', visible: false }; - router.get('/user/:userId/projects/info', console.getProjectsInfo); - - //BI分析模块 - app.fs.api.logAttr['GET/user/:userId/bi/analysis'] = { content: '查询BI分析数据', visible: false }; - router.get('/user/:userId/bi/analysis', console.getBiAnalysis); - - //最新动态 - app.fs.api.logAttr['GET/latest/dynamic'] = { content: '查询最新动态', visible: false }; - router.get('/latest/dynamic', console.getLatestDynamic); -}; diff --git a/api/app/lib/routes/control/index.js b/api/app/lib/routes/control/index.js index 70b54c3..548c9d0 100644 --- a/api/app/lib/routes/control/index.js +++ b/api/app/lib/routes/control/index.js @@ -1,6 +1,7 @@ 'use strict'; const toolLink = require('../../controllers/control/toolLink'); +const csData = require('../../controllers/control/data'); module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/console/toollink'] = { content: '获取常用工具', visible: true }; @@ -14,4 +15,19 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/console/count'] = { content: '查询告警数量', visible: true }; router.get('/console/count', toolLink.count); + + + + + //项目概览 + app.fs.api.logAttr['GET/projects/info'] = { content: '查询项目概览', visible: false }; + router.get('/projects/info', csData.getProjectsInfo); + + //BI分析模块 + app.fs.api.logAttr['GET/bi/analysis'] = { content: '查询BI分析数据', visible: false }; + router.get('/bi/analysis', csData.getBiAnalysis); + + //最新动态 + app.fs.api.logAttr['GET/latest/dynamic'] = { content: '查询最新动态', visible: false }; + router.get('/latest/dynamic', csData.getLatestDynamic); }; \ No newline at end of file