wuqun 2 years ago
parent
commit
3a916b4763
  1. 11
      api/.vscode/launch.json
  2. 38
      api/Dockerfile
  3. 17
      api/app/lib/controllers/alarm/data.js
  4. 16
      api/app/lib/controllers/alarm/video.js
  5. 15
      api/app/lib/controllers/auth/index.js
  6. 45
      api/app/lib/controllers/control/data.js
  7. 3
      api/app/lib/controllers/project/bind.js
  8. 83
      api/app/lib/controllers/project/index.js
  9. 36
      api/app/lib/controllers/push/config.js
  10. 4
      api/app/lib/index.js
  11. 20
      api/app/lib/models/alarm_push_config.js
  12. 2
      api/app/lib/models/email_send_log.js
  13. 2
      api/app/lib/models/latest_dynamic_list.js
  14. 504
      api/app/lib/schedule/alarms_push.js
  15. 2
      api/app/lib/utils/alarmHandle.js
  16. 5
      api/config.js
  17. 12
      jenkinsfile_poms_api
  18. 12
      jenkinsfile_poms_web
  19. 16
      script/0.10/schema/1.alter_alarm_push_config.sql
  20. 4
      script/0.11/schema/1.alter_alarm_push_config.sql
  21. 6
      script/0.11/schema/2.alter_email_send_log.sql
  22. 1
      script/0.11/schema/3.alert_latest_dynamic_list.sql
  23. 44
      web/Dockerfile
  24. BIN
      web/client/assets/images/background/B.png
  25. BIN
      web/client/assets/images/background/General.png
  26. 4
      web/client/index.html
  27. 2
      web/client/src/layout/actions/global.js
  28. 6
      web/client/src/layout/reducers/global.js
  29. 64
      web/client/src/sections/control/containers/control.jsx
  30. 4
      web/client/src/sections/control/containers/userCenter.jsx
  31. 28
      web/client/src/sections/facility/containers/monitor.jsx
  32. 1
      web/client/src/sections/facility/nav-item.jsx
  33. 2
      web/client/src/sections/install/containers/system.jsx
  34. 8
      web/client/src/sections/problem/actions/problem.jsx
  35. 20
      web/client/src/sections/problem/components/tableData.jsx
  36. 490
      web/client/src/sections/service/components/pushModal.jsx
  37. 99
      web/client/src/sections/service/containers/emPush.jsx
  38. 10
      web/client/src/sections/service/containers/reportManagement.jsx
  39. 8
      web/config.js
  40. 2
      web/package.json
  41. 4
      web/routes/attachment/index.js

11
api/.vscode/launch.json

@ -16,13 +16,14 @@
"-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 10.8.30.72:29092,10.8.30.73:29092,10.8.30.74:29092",
"--iotaProxy http://10.8.30.157:17007",
"--redisHost 10.8.30.112",
"--redisHost localhost",
"--redisPort 6379",
"--apMergeDeVeAnxinProjectId 1,2,3",
"--axyApiUrl http://127.0.0.1:4100",
// "--apiEmisUrl http://10.8.30.112:14000",
//
@ -43,11 +44,9 @@
"--clickHouseUrl http://10.8.30.161",
// "--clickHouseUrl https://clickhouse01.anxinyun.cn/play",
"--clickHousePort 30123",
// * 2
// "--clickHouseUser ",
// "--clickHousePassword ",
//
// "--clickHouseAnxincloud anxinyun",
// "--clickHousePepEmis pepca",
@ -55,7 +54,6 @@
// "--clickHouseVcmp video_accrss1",
// "--clickHouseDataAlarm default",
// "--clickHouseIot iot",
//
"--clickHouseAnxincloud anxinyun88",
"--clickHousePepEmis pepca8",
@ -63,7 +61,6 @@
"--clickHouseVcmp video_access_dev",
"--clickHouseDataAlarm default",
"--clickHouseIot iot",
"--confirmAlarmAnxinUserId 1",
"--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
"--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",

38
api/Dockerfile

@ -1,36 +1,36 @@
# FROM repository.anxinyun.cn/devops/node:12-dev as builder
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12-dev as builder
# COPY . /var/app
COPY . /var/app
# WORKDIR /var/app
WORKDIR /var/app
# EXPOSE 8080
EXPOSE 8080
# RUN npm config set registry=http://10.8.30.22:7000
# RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json
# RUN npm cache clean -f
# RUN rm -rf package-lock.json
# RUN npm install --registry http://10.8.30.22:7000
RUN npm config set registry=http://10.8.30.22:7000
RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json
RUN npm cache clean -f
RUN rm -rf package-lock.json
RUN npm install --registry http://10.8.30.22:7000
# FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12
# COPY --from=builder --chown=node /var/app /home/node/app
COPY --from=builder --chown=node /var/app /home/node/app
# WORKDIR /home/node/app
WORKDIR /home/node/app
# CMD ["node", "server.js"]
CMD ["node", "server.js"]
# 旧版本构建方式
FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
# FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
COPY . /var/app
# COPY . /var/app
WORKDIR /var/app
# WORKDIR /var/app
EXPOSE 8080
# EXPOSE 8080
CMD ["-u", "http://localhost:8088"]
# CMD ["-u", "http://localhost:8088"]
ENTRYPOINT [ "node", "server.js" ]
# ENTRYPOINT [ "node", "server.js" ]

17
api/app/lib/controllers/alarm/data.js

@ -8,17 +8,23 @@ async function groupList (ctx) {
const { clickHouse } = ctx.app.fs
const { database: dataAlarm } = clickHouse.dataAlarm.opts.config
const { showAll } = ctx.query
const groupRes = await clickHouse.anxinyun.query(`
SELECT * FROM t_alarm_group
`).toPromise();
let whereOption = []
if (!showAll) {
whereOption.push(` INNER JOIN ${dataAlarm}.alarms
ON t_alarm_group_unit.id = ${dataAlarm}.alarms.AlarmGroupUnit`)
}
for (let g of groupRes) {
g.unit = await await clickHouse.anxinyun.query(`
SELECT DISTINCT
t_alarm_group_unit.id AS id,t_alarm_group_unit.name AS name,t_alarm_group_unit.group_id AS groupId
FROM t_alarm_group_unit
INNER JOIN ${dataAlarm}.alarms
ON t_alarm_group_unit.id = ${dataAlarm}.alarms.AlarmGroupUnit
${whereOption}
WHERE group_id = ${g.id}
`).toPromise();
}
@ -143,7 +149,6 @@ async function list (ctx) {
`).toPromise();
const confirmedAlarm = alarmRes
// TODO: 开发临时注释
.filter(ar => ar.State && ar.State > 2)
.map(ar => "'" + ar.AlarmId + "'")
const confirmedAlarmDetailMax = confirmedAlarm.length ?
@ -218,7 +223,7 @@ async function list (ctx) {
}
}
async function getAlarmGroups(ctx) {
async function getAlarmGroups (ctx) {
try {
const { clickHouse } = ctx.app.fs
const groupRes = await clickHouse.anxinyun.query(`
@ -239,7 +244,7 @@ async function getAlarmGroups(ctx) {
}
}
}
async function exportDataAlarms(ctx, alarmList, groupId) {
async function exportDataAlarms (ctx, alarmList, groupId) {
try {
const { utils: { simpleExcelDown, getExportAlarmHeader } } = ctx.app.fs;
let header = await getExportAlarmHeader(groupId);
@ -303,7 +308,7 @@ async function exportDataAlarms(ctx, alarmList, groupId) {
}
}
}
async function detail(ctx) {
async function detail (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs

16
api/app/lib/controllers/alarm/video.js

@ -8,11 +8,18 @@ async function deviceType (ctx) {
const { clickHouse } = ctx.app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs
const { showAll } = ctx.query
let anxinStruc = await anxinStrucIdRange({
ctx,
})
const anxinStrucIds = anxinStruc.map(a => a.strucId)
let whereOption = []
if (!showAll) {
whereOption.push(`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
${`WHERE ${anxinyun}.t_video_ipc.structure IN (${anxinStrucIds.join(',')})`}`)
}
const kindRes = await clickHouse.vcmp.query(`
SELECT DISTINCT
camera_kind.id AS id,camera_kind.kind AS kind
@ -22,10 +29,7 @@ async function deviceType (ctx) {
INNER JOIN camera_status_alarm
ON camera.channel_no = camera_status_alarm.channel_no
AND camera.serial_no = camera_status_alarm.serial_no
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
${`WHERE ${anxinyun}.t_video_ipc.structure IN (${anxinStrucIds.join(',')})`}
${whereOption}
`).toPromise()
ctx.status = 200;
ctx.body = kindRes
@ -38,7 +42,7 @@ async function deviceType (ctx) {
}
}
async function exceptionType(ctx) {
async function exceptionType (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs

15
api/app/lib/controllers/auth/index.js

@ -10,9 +10,18 @@ async function login (ctx, next) {
const models = ctx.fs.dc.models;
const params = ctx.request.body;
const emisLoginRes = await ctx.app.fs.emisRequest.post('login', {
data: params
let emisLoginRes = null
if (params.username && params.password) {
emisLoginRes = await ctx.app.fs.emisRequest.post('login', {
data: { ...params, code: 'POMS' }
})
} else if (params.token) {
emisLoginRes = await ctx.app.fs.emisRequest.get('user-info', {
query: {
token: params.token, code: 'POMS'
}
})
}
if (!emisLoginRes) {
throw "无此用户,请使用正确的登录信息"
@ -70,7 +79,7 @@ async function login (ctx, next) {
await ctx.redis.hmset(emisLoginRes.token, {
expired: moment().add(1, 'day'),
userInfo:JSON.stringify(emisLoginRes)
userInfo: JSON.stringify(emisLoginRes)
});
ctx.status = 200;

45
api/app/lib/controllers/control/data.js

@ -2,7 +2,7 @@
const moment = require('moment');
//BI分析-数据
async function getDataAlarmsAggDay(ctx) {
async function getDataAlarmsAggDay (ctx) {
try {
const { utils: { anxinStrucIdRange } } = ctx.app.fs
const { pepProjectId } = ctx.query
@ -12,10 +12,8 @@ async function getDataAlarmsAggDay(ctx) {
ctx, pepProjectId
})
let whereOption = []
// ! 1 开发临时增加
if (anxinStruc.length) {
const anxinStrucIds = anxinStruc.map(a => a.strucId)
// ! 开发临时注释
whereOption.push(`alarms.StructureId IN (${anxinStrucIds.join(",")})`)
let start = moment().add(-1, 'year').format('YYYY-MM-DD HH:mm:ss');//最近一年
@ -41,7 +39,7 @@ async function getDataAlarmsAggDay(ctx) {
}
}
async function queryAlarm(ctx, alarmQueryOptionStr, type) {
async function queryAlarm (ctx, alarmQueryOptionStr, type) {
const { clickHouse } = ctx.app.fs
try {
const alarmRes = await clickHouse.dataAlarm.query(`
@ -76,7 +74,7 @@ async function queryAlarm(ctx, alarmQueryOptionStr, type) {
}
//BI分析-应用异常
async function getAppAlarmsAggDay(ctx) {
async function getAppAlarmsAggDay (ctx) {
try {
const models = ctx.fs.dc.models;
const { utils: { pomsProjectRange } } = ctx.app.fs
@ -129,7 +127,7 @@ async function getAppAlarmsAggDay(ctx) {
}
//BI分析-视频异常
async function getVideoAlarmsAggDay(ctx) {
async function getVideoAlarmsAggDay (ctx) {
try {
const { clickHouse, utils: { anxinStrucIdRange } } = ctx.app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
@ -246,7 +244,7 @@ async function getVideoAlarmsAggDay(ctx) {
}
//BI分析-问题处置效率分析
async function getAlarmsHandleStatistics(ctx) {
async function getAlarmsHandleStatistics (ctx) {
try {
const { projectCorrelationId } = ctx.query
const models = ctx.fs.dc.models;
@ -266,7 +264,7 @@ async function getAlarmsHandleStatistics(ctx) {
}
}
//最新动态
async function getLatestDynamic(ctx) {
async function getLatestDynamic (ctx) {
try {
const { models } = ctx.fs.dc;
const { limit, page, projectCorrelationId, types } = ctx.query;
@ -303,9 +301,9 @@ async function getLatestDynamic(ctx) {
});
//查项目名称 查用户名
let pepPojectIds = new Set(), notedUserIds = new Set();
let pepPojectIds = new Set(), notedUserIds = new Set(), emailSendPomsProjectIds = new Set();
for (let p of news) {
if(p.projectCorrelation && p.projectCorrelation.pepProjectId){
if (p.projectCorrelation && p.projectCorrelation.pepProjectId) {
pepPojectIds.add(p.projectCorrelation.pepProjectId);
}
@ -313,11 +311,28 @@ async function getLatestDynamic(ctx) {
p.emailSendLog.toPepUserIds.map(u => {
notedUserIds.add(u);//通知 接收人
})
p.emailSendLog.projectCorrelationId.forEach(pid => emailSendPomsProjectIds.add(pid))
}
if (p.alarmConfirmLog && p.alarmConfirmLog.pepUserId) {
notedUserIds.add(p.alarmConfirmLog.pepUserId);//确认 操作者
}
}
// EM 推送的特殊处理
// 查找对应的 projectCorrelation
const emailLogProjectCorrelationRes =
emailSendPomsProjectIds.size ?
await models.ProjectCorrelation.findAll({
where: {
id: { $in: [...emailSendPomsProjectIds] }
}
}) : []
for (let { dataValues: p } of emailLogProjectCorrelationRes) {
if (p.pepProjectId) {
pepPojectIds.add(p.pepProjectId)
}
}
let pepProjects = pepPojectIds.size ? await clickHouse.projectManage.query(`
SELECT id, project_name FROM t_pim_project WHERE id IN (${[...pepPojectIds].join(',')},-1)
`).toPromise() : [];
@ -339,7 +354,15 @@ async function getLatestDynamic(ctx) {
if (d.emailSendId) {
notice.push({
userName: userPepRes.filter(u => d.emailSendLog.toPepUserIds.indexOf(u.id) != -1),
projectName,
projectName: d.emailSendLog.projectCorrelationId.map(p => {
let projectName = ''
if (p.pepProjectId) {
projectName = pepProjects.find(pp => pp.id == p.pepProjectId).project_name
} else {
projectName = p.name
}
return projectName
}).join('、'),
...d.emailSendLog.dataValues
});
}

3
api/app/lib/controllers/project/bind.js

@ -105,6 +105,9 @@ async function bindAnxin2pep (ctx) {
// 但是有之前的数据
if (existRes.del) {
// 不过之前的删除了
storageData.createUser = userId
storageData.updateTime = now.format()
storageData.createTime = now.format()
await models.ProjectCorrelation.update(storageData, {
where: {
pepProjectId: pepProjectId

83
api/app/lib/controllers/project/index.js

@ -57,7 +57,7 @@ async function pomsProject (ctx) {
if (p.pepProjectId) {
pepProjectIds.add(p.pepProjectId)
}
if(p.createUser){
if (p.createUser) {
createUsers.add(p.createUser)
}
for (let ap of p.anxinProjectId) {
@ -68,7 +68,7 @@ async function pomsProject (ctx) {
}
const pomsUser = await models.User.findAll({
where: {
id: { $in: [...createUsers]}
id: { $in: [...createUsers] }
}
})
let pepUserIds = new Set()
@ -112,7 +112,7 @@ async function pomsProject (ctx) {
for (let p of proRes.rows) {
const corPro = pepProjectRes.find(pp => pp.id == p.pepProjectId) || {}
const pepUserName = (pepcaUser.find(qq => qq.id == (pomsUser.find(oo => oo.id == p.createUser)||{}).pepUserId)||{}).name ||''
const pepUserName = (pepcaUser.find(qq => qq.id == (pomsUser.find(oo => oo.id == p.createUser) || {}).pepUserId) || {}).name || ''
p.dataValues.pepProjectName = corPro.project_name
p.dataValues.pepProjectIsDelete = corPro.isdelete
p.dataValues.constructionStatusId = corPro.construction_status_id
@ -126,7 +126,7 @@ async function pomsProject (ctx) {
ctx.status = 200;
ctx.body = proRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
@ -210,20 +210,33 @@ async function strucWithPomsProject (ctx) {
const { clickHouse } = ctx.app.fs
const { pomsProjectId } = ctx.query
const bindRes = await models.ProjectCorrelation.findOne({
const bindRes = await models.ProjectCorrelation.findAll({
where: {
id: pomsProjectId
id: { $in: pomsProjectId.split(',') }
}
})
let anxinProjectIds = new Set()
for (let b of bindRes) {
if (b.anxinProjectId.length) {
for (let aid of b.anxinProjectId) {
anxinProjectIds.add(aid)
}
}
}
let undelStruc = []
if (bindRes) {
const undelStrucRes = bindRes.anxinProjectId.length ?
const undelStrucRes = anxinProjectIds.size ?
await clickHouse.anxinyun.query(
`
SELECT
t_structure.id AS strucId,
t_structure.name AS strucName
t_structure.name AS strucName,
t_factor.id AS factorId,
t_factor.name AS factorName,
t_factor.proto AS factorProto,
t_factor_proto_item.name AS factorItemName,
t_factor_proto_item.id AS factorItemId
FROM
t_project
LEFT JOIN
@ -246,20 +259,70 @@ async function strucWithPomsProject (ctx) {
ON t_structure.id = t_project_structure.structure
OR t_structure.id = t_structuregroup_structure.structure
OR t_structure.id = t_structure_site.structid
LEFT JOIN t_structure_factor
ON t_structure_factor.structure = t_structure.id
LEFT JOIN t_factor
ON t_structure_factor.factor = t_factor.id
LEFT JOIN t_factor_proto_item
ON t_factor_proto_item.proto = t_factor.proto
WHERE
project_state != -1
AND
t_project.id IN (${bindRes.anxinProjectId.join(',')})
t_project.id IN (${[...anxinProjectIds].join(',')}, -1)
ORDER BY strucId
`
).toPromise() :
[]
for (let s of undelStrucRes) {
if (!undelStruc.some(us => us.id == s.strucId)) {
let corStrut = undelStruc.find(us => us.id == s.strucId)
if (!corStrut) {
let nextFacor = []
if (s.factorId) {
let nextFactorItem = []
if (s.factorItemId) {
nextFactorItem.push({
id: s.factorItemId,
name: s.factorItemName,
})
}
nextFacor.push({
id: s.factorId,
name: s.factorName,
proto: s.factorProto,
item: nextFactorItem,
})
}
undelStruc.push({
id: s.strucId,
name: s.strucName,
factor: nextFacor
})
} else {
if (s.factorId) {
let corFactor = corStrut.factor.find(v => v.id == s.factorId)
let nextFactorItem = null
if (s.factorItemId) {
nextFactorItem = {
id: s.factorItemId,
name: s.factorItemName,
}
}
if (corFactor) {
if (!corFactor.item.some(fi => fi.id == s.factorItemId) && nextFactorItem) {
corFactor.item.push(nextFactorItem)
}
} else {
corStrut.factor.push({
id: s.factorId,
name: s.factorName,
proto: s.factorProto,
item: nextFactorItem ? [{
id: s.factorItemId,
name: s.factorItemName,
}] : [],
})
}
}
}
}
}

36
api/app/lib/controllers/push/config.js

@ -8,23 +8,11 @@ async function list (ctx) {
const { clickHouse } = ctx.app.fs
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
},
order: [['id', 'desc']],
include: [{
model: models.ProjectCorrelation,
where: projectCorrelationWhere,
required: true
}]
}
let anxinStrucsRange = await anxinStrucIdRange({
@ -48,7 +36,7 @@ async function list (ctx) {
ctx, pepProjectId: pomsProjectId, keywordTarget, keyword
})
let pomsProjectIds = pomsProjectRes.map(p => p.id)
findOption.where.pomsProjectId = { $in: pomsProjectIds }
findOption.where.pomsProjectId = { $overlap: pomsProjectIds }
if (alarmType) {
findOption.where.alarmType = { $contains: [alarmType] }
@ -65,6 +53,7 @@ async function list (ctx) {
}
const listRes = await models.AlarmPushConfig.findAll(findOption)
// const listRes = await models.AlarmPushConfig.findAll({})
let allStrucIds = new Set()
let allConfigId = []
let allReceiverIds = new Set()
@ -104,7 +93,11 @@ async function list (ctx) {
let returnD = []
for (let { dataValues: p } of listRes) {
// 查对应的 poms 绑定的结构物绑定关系
const corBind = pomsProjectRes.find(ppj => ppj.id == p.pomsProjectId)
const corBinds = pomsProjectRes.filter(ppj => p.pomsProjectId.includes(ppj.id))
let filterBinds = []
for (let corBind of corBinds) {
if (corBind.pepProjectId) {
if (state == 'notYet') {
if (corBind.pepProject && p.timeType.some(pt => pt == corBind.pepProject.constructionStatusId)) {
@ -115,6 +108,13 @@ async function list (ctx) {
continue
}
}
} else if (state == 'notYet') {
continue
}
filterBinds.push(corBind)
}
if (!filterBinds.length) {
continue
}
// 结构物信息
@ -129,7 +129,7 @@ async function list (ctx) {
returnStruc.push({
id: sid,
name: structure.name,
unbind: !anxinStrucSeen || !corBind.anxinProjectId.includes(anxinStrucSeen.projectId)
unbind: !anxinStrucSeen || corBinds.every(corBinds => !corBinds.anxinProjectId.includes(anxinStrucSeen.projectId))
})
} else {
// 这个结构物已删
@ -142,7 +142,7 @@ async function list (ctx) {
const corReceiver = userRes.filter(u => p.receiverPepUserId.some(prId => u.id == prId))
returnD.push({
...p,
pomsProject: corBind,
pomsProject: corBinds,
structure: returnStruc,
pushCount: corLogCount ? corLogCount.dataValues.count : 0,
receiverPepUser: corReceiver
@ -165,11 +165,11 @@ async function edit (ctx) {
const models = ctx.fs.dc.models;
const { userId, pepUserId } = ctx.fs.api
const { pushId, name, pomsProjectId, alarmType = [], receiverPepUserId = [], timeType = [], disable,
strucId = [], tactics, tacticsParams } = ctx.request.body
strucId = [], tactics, tacticsParams, alarmSubType = {}, pomsStrucFactorId = {} } = ctx.request.body
let storageData = {
name, pomsProjectId, alarmType, receiverPepUserId, timeType, disable,
strucId, tactics, tacticsParams
strucId, tactics, tacticsParams, alarmSubType, pomsStrucFactorId
}
let repeatOption = {

4
api/app/lib/index.js

@ -77,8 +77,8 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
AppAlarm.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' });
App.hasMany(AppAlarm, { foreignKey: 'projectAppId', sourceKey: 'id' });
AlarmPushConfig.belongsTo(ProjectCorrelation, { foreignKey: 'pomsProjectId', targetKey: 'id' });
ProjectCorrelation.hasMany(AlarmPushConfig, { foreignKey: 'pomsProjectId', sourceKey: 'id' });
// AlarmPushConfig.belongsTo(ProjectCorrelation, { foreignKey: 'pomsProjectId', targetKey: 'id' });
// ProjectCorrelation.hasMany(AlarmPushConfig, { foreignKey: 'pomsProjectId', sourceKey: 'id' });
AlarmAppearRecord.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' });
ProjectCorrelation.hasMany(AlarmAppearRecord, { foreignKey: 'projectCorrelationId', sourceKey: 'id' });

20
api/app/lib/models/alarm_push_config.js

@ -25,7 +25,7 @@ module.exports = dc => {
autoIncrement: false
},
pomsProjectId: {
type: DataTypes.INTEGER,
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: false,
defaultValue: null,
comment: null,
@ -42,6 +42,24 @@ module.exports = dc => {
field: "alarm_type",
autoIncrement: false
},
alarmSubType: {
type: DataTypes.JSONB,
allowNull: true,
defaultValue: null,
comment: "监听的告警类型的子类id的关联",
primaryKey: false,
field: "alarm_sub_type",
autoIncrement: false
},
pomsStrucFactorId: {
type: DataTypes.JSONB,
allowNull: true,
defaultValue: null,
comment: "结构物与监测项id的关联",
primaryKey: false,
field: "poms_struc_factor_id",
autoIncrement: false
},
receiverPepUserId: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: true,

2
api/app/lib/models/email_send_log.js

@ -53,7 +53,7 @@ module.exports = dc => {
autoIncrement: false
},
projectCorrelationId: {
type: DataTypes.INTEGER,
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: false,
defaultValue: null,
comment: null,

2
api/app/lib/models/latest_dynamic_list.js

@ -54,7 +54,7 @@ module.exports = dc => {
},
projectCorrelationId: {
type: DataTypes.INTEGER,
allowNull: false,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,

504
api/app/lib/schedule/alarms_push.js

@ -1,15 +1,22 @@
const moment = require('moment')
let isDev = false
// isDev = true
let proDebug = false
proDebug = true
module.exports = function (app, opts) {
const alarmsPush = app.fs.scheduleInit(
{
interval: '12 */1 * * * *',
// immediate: true, // dev
proRun: true,
immediate: isDev,
proRun: !isDev,
},
async () => {
try {
const { models, ORM: sequelize } = app.fs.dc
const { apMergeDeVeAnxinProjectId = '' } = opts
const { clickHouse } = app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { pushBySms, pushByEmail, sendNoticeToWeb } = app.fs.utils
@ -20,18 +27,22 @@ module.exports = function (app, opts) {
disable: false
},
order: ['id'],
include: [{
model: models.ProjectCorrelation,
where: {
del: false,
},
required: true
}],
})
let pomsProjectId = new Set()
let pepProjectIds = new Set()
for (let { dataValues: c } of configListRes) {
if (c.projectCorrelation.pepProjectId) {
pepProjectIds.add(c.projectCorrelation.pepProjectId)
if (c.pomsProjectId) {
c.pomsProjectId.forEach(pid => pomsProjectId.add(pid))
}
}
const pomsProjectRes = pomsProjectId.size ? await models.ProjectCorrelation.findAll({
where: {
id: { $in: [...pomsProjectId] }
}
}) : []
for (let { dataValues: c } of pomsProjectRes) {
if (c.pepProjectId) {
pepProjectIds.add(c.pepProjectId)
}
}
const pepProjectRes = pepProjectIds.size ?
@ -55,24 +66,42 @@ module.exports = function (app, opts) {
for (let { dataValues: c } of configListRes) {
if (c.tacticsParams && c.tactics) {
const { projectCorrelation, strucId, pomsProjectId, } = c
if (proDebug) {
console.log(`当前运行EM配置:id=${c.id} name=${c.name}`);
}
// pomsProjectId 是个数组 []
const { strucId, pomsProjectId, pomsStrucFactorId } = c
const { interval, deviceProportion } = c.tacticsParams
if (
curMinOfYear % parseInt(interval) == 0
curMinOfYear % parseInt(interval) == 0 || isDev
) {
const corPepProject = projectCorrelation.pepProjectId ?
pepProjectRes.find(p => p.id == projectCorrelation.pepProjectId)
: null
if (
!projectCorrelation.pepProjectId
|| (
corPepProject
&& c.timeType.some(ct => ct == corPepProject.construction_status_id)
)
) {
const { anxinProjectId, pepProjectId } = projectCorrelation
const corPomsProject = pomsProjectRes.filter(poms => pomsProjectId.includes(poms.id))
let curAnxinProjectId = new Set()
let pepProjectName_ = []
for (let { dataValues: poms } of corPomsProject) {
if (poms.pepProjectId) {
// 找对应的项企项目
const corPepProject =
pepProjectRes.find(p => p.id == poms.pepProjectId)
if (corPepProject && c.timeType.some(ct => ct == corPepProject.construction_status_id)) {
pepProjectName_.push(corPepProject.project_name)
} else {
// 不符合当前项目的时间节点
continue
}
} else {
// 是自定义项目
poms.name ? pepProjectName_.push(poms.name) : null
}
// 筛选全部的 anxinProjectId pepProjectId
for (let axId of poms.anxinProjectId) {
curAnxinProjectId.add(axId)
}
}
const anxinProjectId = [...curAnxinProjectId]
// 查当前 poms 下的结构物 并把不包含的去掉
// 可能有结构物已解绑
const strucListRes = strucId.length && anxinProjectId.length ?
@ -82,7 +111,8 @@ module.exports = function (app, opts) {
DISTINCT id,
t_structure.id AS id,
t_structure.name AS name,
t_structure.iota_thing_id AS iotaThingId
t_structure.iota_thing_id AS iotaThingId,
t_project.id AS projectId
FROM
t_project
LEFT JOIN
@ -109,32 +139,45 @@ module.exports = function (app, opts) {
project_state != -1
AND t_project.id IN (${anxinProjectId.join(',')})
AND t_structure.id IN (${strucId.join(',')})
`
).toPromise() :
[]
let strucThingId = []
let strucMap = {}
let searchStrucIds = strucListRes.map(s => {
if (s.iotaThingId) {
strucThingId.push(s.iotaThingId)
}
strucMap[s.id] = {
...s
}
return s.id
})
// 开发测试用的数据
// searchStrucIds = searchStrucIds.concat([991, 1052, 700])
// !开发测试用的数据
if (isDev) {
searchStrucIds = searchStrucIds.concat([991, 1052, 700])
}
if (searchStrucIds.length) {
searchStrucIds.unshift(-1)
} else {
// 没有结构物可查
continue
}
let pepProjectName = pepProjectId ?
pepProjectRes.find(p => p.id == pepProjectId).project_name
: projectCorrelation.name
let emailTitle = `${pepProjectName}-${c.name}-`
let pepProjectName =
pepProjectName_.length ?
pepProjectName_.join('<br/>')
: ''
let emailTitle = `${pepProjectName_.length ?
pepProjectName_.join('、')
: ''}-${c.name}-`
let emailSubTitle = ''
let dataAlarmOption = []
let dataAlarmGroupOption = []
let dataAlarmSubType = []
let dataAlarms = []
let videoAlarmWhereOption = []
@ -211,15 +254,26 @@ module.exports = function (app, opts) {
// 判断告警数据范围
if (c.alarmType.includes('data_outages')) {
dataAlarmGroupOption.push(1)
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['data_outages'])
}
if (c.alarmType.includes('data_exception')) {
dataAlarmGroupOption.push(2)
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['data_exception'])
}
if (c.alarmType.includes('strategy_hit')) {
dataAlarmGroupOption.push(3)
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['strategy_hit'])
}
if (c.alarmType.includes('video_exception')) {
videoAlarms = searchStrucIds.length ? await clickHouse.vcmp.query(
let videoAlarmSubType = c.alarmSubType ? c.alarmSubType['video_exception'] : []
if (videoAlarmSubType.length == 1) {
videoAlarmSubType.push(-1)
}
videoAlarms =
searchStrucIds.length && (
!c.alarmSubType || videoAlarmSubType.length > 0
) ?
await clickHouse.vcmp.query(
`
SELECT
cameraAlarm.cameraId AS cameraId,
@ -237,6 +291,7 @@ module.exports = function (app, opts) {
anxinIpc.t_video_ipc.name AS anxinIpcPosition,
anxinStation.id AS anxinStationId,
anxinStation.name AS anxinStationName,
anxinStation.factor AS anxinStationFactorId,
anxinStruc.name AS strucName,
anxinStruc.id AS strucId
FROM (
@ -263,6 +318,7 @@ module.exports = function (app, opts) {
AND camera.channel_no = camera_status_alarm.channel_no
AND camera.delete = false
AND camera.recycle_time is null
${c.alarmSubType ? `AND camera.kind_id in (${videoAlarmSubType.join(',')})` : ""}
WHERE
camera_status_alarm.confirm_time IS null
${videoAlarmWhereOption.length ? ` AND ${videoAlarmWhereOption.join(' AND ')}` : ''}
@ -310,6 +366,21 @@ module.exports = function (app, opts) {
})
}
} else {
/**按监测因素 factor 筛选告警*/
// if (pomsStrucFactorId) {
// if (!a.strucId || !a.anxinStationFactorId) {
// // 当前告警没有绑定结构物或者摄像头没有绑定测点
// continue
// } else if (!pomsStrucFactorId[a.strucId]) {
// // 推送配置没配置这个结构物
// continue
// } else if (!pomsStrucFactorId[a.strucId].includes(a.anxinStationFactorId)) {
// // 不包含这个监测因素
// continue
// }
// }
let d = {
cameraId: a.cameraId,
cameraName: a.cameraName,
@ -325,6 +396,7 @@ module.exports = function (app, opts) {
confirmContent: a.confirmContent,
confirmTime: a.confirmTime,
cameraKind: a.cameraKind,
factorId: a.anxinStationFactorId,
struc: [],
station: []
}
@ -349,11 +421,14 @@ module.exports = function (app, opts) {
}
}
}
let p = 1
videoAlarms = returnD
}
if (c.alarmType.includes('app_exception')) {
appAlarms = await models.AppAlarm.findAll({
if (c.alarmSubType) {
appAlarmWhereOption.type = { $in: c.alarmSubType['app_exception'] || [] }
}
appAlarms = c.alarmSubType && c.alarmSubType['app_exception'].length ?
await models.AppAlarm.findAll({
where: appAlarmWhereOption,
order: [['createTime', 'DESC']],
include: [{
@ -362,31 +437,41 @@ module.exports = function (app, opts) {
include: [{
model: models.ProjectApp,
where: {
projectId: pomsProjectId
projectId: { $in: pomsProjectId }
},
required: true,
}]
}]
})
}) : []
}
if (c.alarmType.includes('device_exception')) {
dataAlarmGroupOption.push(4)
dataAlarmGroupOption.push(5)
if (c.alarmSubType) dataAlarmSubType = dataAlarmSubType.concat(c.alarmSubType['device_exception'])
}
// 查数据告警 三警合一
if (dataAlarmGroupOption.length && searchStrucIds.length) {
dataAlarmGroupOption.push(-1)
dataAlarmOption.push(`AlarmGroup IN (${dataAlarmGroupOption.join(',')})`)
dataAlarms = await clickHouse.dataAlarm.query(`
if (c.alarmSubType && dataAlarmSubType.length) {
dataAlarmSubType.push(-1)
dataAlarmOption.push(`AlarmGroupUnit IN (${dataAlarmSubType.join(',')})`)
}
dataAlarms =
!c.alarmSubType || dataAlarmSubType.length ?
await clickHouse.dataAlarm.query(`
SELECT * FROM alarms
LEFT JOIN ${anxinyun}.t_sensor
AS anxinStation
ON toString(anxinStation.id) = alarms.SourceId
AND alarms.SourceTypeId = 2
WHERE
${`State NOT IN (3, 4) AND `}
StructureId IN (${searchStrucIds.join(',')})
${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''}
ORDER BY StartTime DESC
`).toPromise()
console.log(dataAlarms);
`).toPromise() : []
}
let dataAlarmTitle = [{
@ -397,7 +482,8 @@ module.exports = function (app, opts) {
n: '结构物',
k: '',
f: (d) => {
return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name
return (strucMap[d.StructureId] || { name: '' }).name
// return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name
}
}, {
n: '告警源名称',
@ -470,13 +556,13 @@ module.exports = function (app, opts) {
n: '测点',
k: '',
f: (d) => {
return d.station.map(ds => ds.name).join('、')
return d.station ? d.station.map(ds => ds.name).join('、') : ''
}
}, {
n: '位置',
k: '',
f: (d) => {
return d.station.map(ds => ds.position).join('、')
return d.station ? d.station.map(ds => ds.position).join('、') : ''
}
}, {
n: '告警信息',
@ -542,15 +628,15 @@ module.exports = function (app, opts) {
tableTitle += '</tr>'
return tableTitle
}
function packageTableData (data, titleArr) {
function packageTableData ({ data, titleArr }) {
let tableData = '<tr>'
for (let t of titleArr) {
if (t.v) {
tableData += `<td>${t.v}</td>`
tableData += `<td>${t.v || ''}</td>`
} else if (t.f) {
tableData += `<td>${t.f(data)}</td>`
tableData += `<td>${t.f(data) || ''}</td>`
} else if (t.k) {
tableData += `<td>${data[t.k]}</td>`
tableData += `<td>${data[t.k] || ''}</td>`
} else {
tableData += `<td></td>`
}
@ -559,33 +645,13 @@ module.exports = function (app, opts) {
return tableData
}
function packageAlarmData2Table (titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime') {
if (!alarmData.length) {
return ''
}
ifEmailSend = true
let tableTitlePrefix = titlePrefix + '告警源'
let newAddCount = 0
let alarmHtml = '<table border="1" cellspacing="0" style="border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;">'
let alarmContent = ''
let alarmHtmlTitle = packageTableTitle(alarmTitleArr)
for (let a of alarmData) {
alarmContent += packageTableData(a, alarmTitleArr)
if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) {
newAddCount++
}
}
tableTitlePrefix +=
c.tactics == 'abnormal_rate' ?
`${alarmData.length}` :
`新增${newAddCount}个,未解决累计${alarmData.length}` + tableTitlePostfix
alarmHtml += `<tr><td colspan="${alarmTitleArr.length}" style="background-color:#ffff00">` + tableTitlePrefix + '</td></tr>'
alarmHtml += alarmHtmlTitle
alarmHtml += alarmContent
alarmHtml += '</table><br/>'
return alarmHtml
let apMergeDeVeAnxinProjectId_ = apMergeDeVeAnxinProjectId ?
apMergeDeVeAnxinProjectId.split(',') : [];
let apMergeDeVeAlarms = {
// 结构物id :{
// data_exception 数据异常告警:[],
// video_exception 视频异常告警:[]
// }
}
let dataAlarmG1 = [];
@ -593,12 +659,114 @@ module.exports = function (app, opts) {
let dataAlarmG3 = [];
let dataAlarmG45 = [];
let deviceStatistic = new Set()
let dataAlarmDetails = []
if (proDebug) {
console.log(`查得数据告警 ${dataAlarms.length}`);
console.log(dataAlarms);
}
if (dataAlarms.length) {
const alarmIds = dataAlarms
.map(ar => "'" + ar.AlarmId + "'")
dataAlarmDetails =
await clickHouse.dataAlarm.query(`
SELECT * FROM alarm_details
WHERE AlarmId IN (${alarmIds.join(',')}, '-1')
AND AlarmState = 0
`).toPromise()
}
if (proDebug) {
console.log(`查得数据告警详情数据 ${dataAlarmDetails.length}`);
}
if (proDebug) {
console.log(`pomsStrucFactorId:`, pomsStrucFactorId);
}
let deviceIds = new Set()
for (let d of dataAlarms) {
d = { ...d, stationId: d.id }
/** 按监测因素筛选 且为测点告警 */
// if (pomsStrucFactorId && d.SourceTypeId == 2) {
// // 做了监测因素筛选 且当前告警有监测因素
// if (!d.factor || d.factor == 0) {
// // 监测因素不对劲
// continue
// } else if (!d.StructureId) {
// // 当前告警没有绑定结构物
// continue
// } else if (!pomsStrucFactorId[d.StructureId]) {
// // 推送配置没配置这个结构物
// continue
// } else if (!pomsStrucFactorId[d.StructureId].includes(d.factor)) {
// // 不包含这个监测因素
// continue
// }
// }
if (d.AlarmGroup == 1) {
dataAlarmG1.push(d)
} else if (d.AlarmGroup == 2) {
dataAlarmG2.push(d)
/** 1
* 根据指定的安心云结构物把数据异常和视频告警合并在一起的代码
* 还要指定是扬尘设备
* 以测点关联
*/
if (
isDev ||
(
apMergeDeVeAnxinProjectId_.length &&
d.SourceName && d.SourceName.includes('扬尘') &&
apMergeDeVeAnxinProjectId_.some(pid => pid == (strucMap[d.StructureId] || {}).projectId)
)
) {
// SourceTypeId 0: 'DTU' / 1: '传感器' / 2: '测点'
if (d.SourceTypeId != 2) {
deviceIds.add(d.SourceId)
}
if (apMergeDeVeAlarms[d.StructureId]) {
apMergeDeVeAlarms[d.StructureId].data_exception.push(d)
} else {
apMergeDeVeAlarms[d.StructureId] = {
data_exception: [d],
video_exception: []
}
}
}
// 注1 END
} else if (d.AlarmGroup == 3) {
/** 按监测因项 factor-item 的名称筛选*/
if (pomsStrucFactorId) {
// 兼容之前的设置 有这个信息才进行判断
if (!d.StructureId) {
// 当前告警没有绑定结构物
continue
} else if (!pomsStrucFactorId[d.StructureId]) {
// 推送配置没配置这个结构物
continue
} else {
let corDetail = dataAlarmDetails.find(da => da.AlarmId == d.AlarmId)
if (proDebug) {
console.log(`相应告警详情(策略命中):`, corDetail);
}
if (corDetail) {
// 判断告警详情信息里有没有监测项关键字
if (!pomsStrucFactorId[d.StructureId].some(factorItemName => {
return corDetail.Content.includes(factorItemName)
})) {
continue
}
} else {
continue
}
}
}
if (proDebug) {
console.log(`符合条件的策略命中 + 1`, d);
}
dataAlarmG3.push(d)
} else if (d.AlarmGroup == 4 || d.AlarmGroup == 5) {
dataAlarmG45.push(d)
@ -609,11 +777,23 @@ module.exports = function (app, opts) {
let rate = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount)));
if (rate < parseFloat(deviceProportion)) {
// 设备异常率低于设定值
continue
}
emailSubTitle = emailSubTitle.replace('--%', rate.toFixed(1) + '%')
}
// 注1
if (apMergeDeVeAnxinProjectId_.length || isDev) {
for (let a of videoAlarms) {
let existStruc = a.struc.find(asc => apMergeDeVeAlarms[asc.id] || isDev)
if (existStruc) {
apMergeDeVeAlarms[existStruc.id].video_exception.push(a)
}
}
}
// 注1 END
let html = `
<html>
<head>
@ -633,49 +813,151 @@ module.exports = function (app, opts) {
</html>
<h5 style="margin-bottom:12px">${emailSubTitle}</h5>
`
function packageAlarmData2Table ({
titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime'
}) {
if (!alarmData.length) {
return ''
}
ifEmailSend = true
let tableTitlePrefix = titlePrefix + '告警源'
let newAddCount = 0
let alarmHtml = '<table border="1" cellspacing="0" style="border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;">'
let alarmContent = ''
let alarmHtmlTitle = packageTableTitle(alarmTitleArr)
for (let [index, a] of alarmData.entries()) {
alarmContent += packageTableData({ data: a, titleArr: alarmTitleArr })
if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) {
newAddCount++
}
}
tableTitlePrefix +=
titlePrefix != '数据异常&视频异常' ?
c.tactics == 'abnormal_rate' ?
`${alarmData.length}` :
`新增${newAddCount}个,未解决累计${alarmData.length}` + tableTitlePostfix
: ''
alarmHtml += `<tr><td colspan="${alarmTitleArr.length}" style="background-color:#ffff00">` + tableTitlePrefix + '</td></tr>'
alarmHtml += alarmHtmlTitle
alarmHtml += alarmContent
alarmHtml += '</table><br/>'
return alarmHtml
}
if (c.alarmType.includes('data_outages')) {
html += packageAlarmData2Table(
'数据中断',
dataAlarmG1,
dataAlarmTitle,
)
html += packageAlarmData2Table({
titlePrefix: '数据中断',
alarmData: dataAlarmG1,
alarmTitleArr: dataAlarmTitle,
})
}
if (c.alarmType.includes('data_exception')) {
html += packageAlarmData2Table(
'数据异常',
dataAlarmG2,
dataAlarmTitle,
)
html += packageAlarmData2Table({
titlePrefix: '数据异常',
alarmData: dataAlarmG2,
alarmTitleArr: dataAlarmTitle,
})
}
if (c.alarmType.includes('strategy_hit')) {
html += packageAlarmData2Table(
'策略命中',
dataAlarmG3,
dataAlarmTitle,
)
html += packageAlarmData2Table({
titlePrefix: '策略命中',
alarmData: dataAlarmG3,
alarmTitleArr: dataAlarmTitle,
})
}
if (c.alarmType.includes('video_exception')) {
html += packageAlarmData2Table(
'视频异常',
videoAlarms,
videoAlarmTitle,
'createTime',
)
html += packageAlarmData2Table({
titlePrefix: '视频异常',
alarmData: videoAlarms,
alarmTitleArr: videoAlarmTitle,
keyOfStartTime: 'createTime',
})
}
if (c.alarmType.includes('app_exception')) {
html += packageAlarmData2Table(
'应用异常',
appAlarms,
appAlarmTitle,
'createTime',
)
html += packageAlarmData2Table({
titlePrefix: '应用异常',
alarmData: appAlarms,
alarmTitleArr: appAlarmTitle,
keyOfStartTime: 'createTime',
})
}
if (c.alarmType.includes('device_exception')) {
html += packageAlarmData2Table(
'设备异常',
dataAlarmG45,
dataAlarmTitle,
html += packageAlarmData2Table({
titlePrefix: '设备异常',
alarmData: dataAlarmG45,
alarmTitleArr: dataAlarmTitle,
})
}
if (Object.keys(apMergeDeVeAlarms).length) {
if (proDebug) {
console.log(`查得数据异常、视频异常合并の告警:`);
console.log(apMergeDeVeAlarms);
}
let deviceSensorRes = []
if (deviceIds.size) {
const device4Search = [...deviceIds]
.map(id => "'" + id + "'")
deviceSensorRes = await clickHouse.anxinyun.query(`
SELECT
iota_device_id, sensor
FROM t_device_sensor
WHERE iota_device_id
${device4Search.length > 1 ? `IN (${device4Search.join(',')})` : `= ${device4Search[0]}`}
`).toPromise()
}
if (proDebug) {
console.log(`相关设备及测点信息:`);
console.log(deviceSensorRes);
}
let alarmTitle = dataAlarmTitle.concat(
videoAlarmTitle.slice(2).map(v => {
return {
...v,
n:
v.n == '持续时间' ?
'摄像头告警' + v.n :
'摄像头' + v.n
}
})
)
let alarmData = []
for (let aKey in apMergeDeVeAlarms) {
let curStrucAlarm = apMergeDeVeAlarms[aKey]
for (let de of curStrucAlarm.data_exception) {
if (!de.id) {
let corSensor = deviceSensorRes.find(ds => ds.iota_device_id == de.SourceId)
if (corSensor) {
de.id = corSensor.sensor
}
}
let corVideoException = curStrucAlarm.video_exception.filter(v => {
// ! de.id 是告警信息关联查出来的测点的id
return v.station.some(vs => vs.id == de.id)
})
if (!corVideoException.length) {
// 构造一个长度 以便在for循环里把 data_exception 放进去
corVideoException.push({})
}
for (let ve of corVideoException) {
alarmData.push({
...de,
...ve
})
}
}
}
html += packageAlarmData2Table({
titlePrefix: '数据异常&视频异常',
alarmData: alarmData,
alarmTitleArr: alarmTitle,
})
}
if (ifEmailSend) {
@ -687,13 +969,19 @@ module.exports = function (app, opts) {
`).toPromise()
: []
let receiverId = []
const emails = receiverRes.reduce((arr, r) => {
let emails = receiverRes.reduce((arr, r) => {
if (r.email) {
arr.push(r.email)
receiverId.push({ id: r.id, name: r.name })
}
return arr
}, [])
if (isDev) {
// !开发测试用的数据
emails = ['1650192445@qq.com']
}
if (emails.length) {
await pushByEmail({
email: emails,
@ -717,12 +1005,11 @@ module.exports = function (app, opts) {
let dynamic = {
time: r.dataValues.time,
emailSendId: r.dataValues.id,
projectCorrelationId: r.dataValues.projectCorrelationId,
// projectCorrelationId: r.dataValues.projectCorrelationId,
type: 2//通知
}
await models.LatestDynamicList.create(dynamic);
//消息推送到前端
await sendNoticeToWeb(receiverId, dataToSave);
}
@ -730,7 +1017,6 @@ module.exports = function (app, opts) {
}
}
}
}
} catch (error) {
console.error(error);

2
api/app/lib/utils/alarmHandle.js

@ -110,7 +110,7 @@ module.exports = function (app, opts) {
}
let pepProjects = pepPojectIds.size ? await clickHouse.projectManage.query(`
SELECT id, project_name FROM t_pim_project WHERE id IN (${[...pepPojectIds]}, -1)`
SELECT id, project_name FROM t_pim_project WHERE id IN (${[...pepPojectIds].join(',')}, -1)`
).toPromise() : [];
return { projects, pepProjects };

5
api/config.js

@ -19,6 +19,8 @@ args.option('redisHost', 'redisHost');
args.option('redisPort', 'redisPort');
args.option('redisPswd', 'redisPassword');
args.option('apMergeDeVeAnxinProjectId', '告警推送自定义の合并数据异常(De)视频异常(Ve)的指定的结构物id');
args.option('axyApiUrl', '安心云 api');
args.option('apiEmisUrl', '企业管理 api');
args.option('apiVcmpUrl', '视频平台 api');
@ -67,6 +69,8 @@ const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redis
const IOTA_REDIS_SERVER_PORT = process.env.IOTA_REDIS_SERVER_PORT || flags.redisPort || "6379";//redis 端口
const IOTA_REDIS_SERVER_PWD = process.env.IOTA_REDIS_SERVER_PWD || flags.redisPswd || "";//redis 密码
const AP_MERGE_DEVE_ANXINPROJECT_ID = process.env.AP_MERGE_DEVE_ANXINPROJECT_ID || flags.apMergeDeVeAnxinProjectId || "";
// 安心云api
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.axyApiUrl;
// 企业管理 api
@ -155,6 +159,7 @@ const product = {
{ p: '/alarm/application/api', o: 'POST' },
{ p: '/alarm/video/added_log', o: 'POST' }
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
apMergeDeVeAnxinProjectId: AP_MERGE_DEVE_ANXINPROJECT_ID,
anxinCloud: {
confirmAlarmAnxinUserId: CONFIRM_ALARM_ANXIN_USER_ID
},

12
jenkinsfile_poms_api

@ -6,13 +6,13 @@ pipeline {
}
stages {
stage('Testing poms ......') {
stage('Testing site......') {
steps {
sh 'switch-auth.sh anxinyun'
buildName "#${BUILD_NUMBER} ~/fs-cloud/${JOB_NAME}:${IMAGE_VERSION}"
buildDescription "registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION}"
sh 'docker build -t registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION} ./api'
sh 'docker push registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION}'
buildName "#${BUILD_NUMBER} ~/iot/${JOB_NAME}:${IMAGE_VERSION}"
buildDescription "harbor.anxinyun.cn/iot/${JOB_NAME}:${IMAGE_VERSION}"
sh 'nerdctl build -t harbor.anxinyun.cn/iot/${JOB_NAME}:${IMAGE_VERSION} ./api'
sh 'nerdctl push harbor.anxinyun.cn/iot/${JOB_NAME}:${IMAGE_VERSION}'
}
}
}

12
jenkinsfile_poms_web

@ -6,13 +6,13 @@ pipeline {
}
stages {
stage('Testing poms ......') {
stage('Testing site......') {
steps {
sh 'switch-auth.sh anxinyun'
buildName "#${BUILD_NUMBER} ~/fs-cloud/${JOB_NAME}:${IMAGE_VERSION}"
buildDescription "registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION}"
sh 'docker build -t registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION} ./web'
sh 'docker push registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION}'
buildName "#${BUILD_NUMBER} ~/iot/${JOB_NAME}:${IMAGE_VERSION}"
buildDescription "harbor.anxinyun.cn/iot/${JOB_NAME}:${IMAGE_VERSION}"
sh 'nerdctl build -t harbor.anxinyun.cn/iot/${JOB_NAME}:${IMAGE_VERSION} ./web'
sh 'nerdctl push harbor.anxinyun.cn/iot/${JOB_NAME}:${IMAGE_VERSION}'
}
}
}

16
script/0.10/schema/1.alter_alarm_push_config.sql

@ -0,0 +1,16 @@
alter table alarm_push_config
add alarm_sub_type jsonb;
comment on column alarm_push_config.alarm_sub_type is '存对应监听模块(alarm_type)的子类(
alarms.AlarmGroupUnit,
camera.kind_id,
app_alarm.errType
)';
ALTER TABLE alarm_push_config ALTER COLUMN poms_project_id TYPE INTEGER []
USING CASE
WHEN poms_project_id is NULL
then NULL
ELSE array [poms_project_id]
END ::INTEGER [];

4
script/0.11/schema/1.alter_alarm_push_config.sql

@ -0,0 +1,4 @@
alter table alarm_push_config
add poms_struc_factor_id jsonb;
comment on column alarm_push_config.poms_struc_factor_id is '结构物对应监测项 id';

6
script/0.11/schema/2.alter_email_send_log.sql

@ -0,0 +1,6 @@
ALTER TABLE email_send_log ALTER COLUMN project_correlation_id TYPE INTEGER []
USING CASE
WHEN project_correlation_id is NULL
then NULL
ELSE array [project_correlation_id]
END ::INTEGER [];

1
script/0.11/schema/3.alert_latest_dynamic_list.sql

@ -0,0 +1 @@
alter table latest_dynamic_list alter column project_correlation_id drop not null;

44
web/Dockerfile

@ -1,40 +1,40 @@
# FROM repository.anxinyun.cn/devops/node:12-dev as builder
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12-dev as builder
# COPY . /var/app
COPY . /var/app
# WORKDIR /var/app
WORKDIR /var/app
# EXPOSE 8080
EXPOSE 8080
# RUN npm config set registry=http://10.8.30.22:7000
# RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json
# RUN npm cache clean -f
# RUN npm install --registry http://10.8.30.22:7000
# RUN npm run build
# RUN rm -rf client/src
# RUN rm -rf node_modules
# RUN npm install --production --registry http://10.8.30.22:7000
RUN npm config set registry=http://10.8.30.22:7000
RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json
RUN npm cache clean -f
RUN npm install --registry http://10.8.30.22:7000
RUN npm run build
RUN rm -rf client/src
RUN rm -rf node_modules
RUN npm install --production --registry http://10.8.30.22:7000
# FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node-16:7.22-06-20
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node-16:7.22-06-20
# COPY --from=builder --chown=node /var/app /home/node/app
COPY --from=builder --chown=node /var/app /home/node/app
# WORKDIR /home/node/app
WORKDIR /home/node/app
# CMD ["node", "server.js"]
CMD ["node", "server.js"]
# 旧版本构建方式
FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
# FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
COPY . /var/app
# COPY . /var/app
WORKDIR /var/app
# WORKDIR /var/app
EXPOSE 8080
# EXPOSE 8080
CMD ["-u", "http://localhost:8088"]
# CMD ["-u", "http://localhost:8088"]
ENTRYPOINT [ "node", "server.js" ]
# ENTRYPOINT [ "node", "server.js" ]

BIN
web/client/assets/images/background/B.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
web/client/assets/images/background/General.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

4
web/client/index.html

@ -12,9 +12,9 @@
<script
src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_19077_11.559b91c217b8ddc76c0c4b1397d84d48.es5.js"></script>
</head >
</head>
<body>
<body>
<div id='PomsApp' style="height: 100%;"></div>
<!-- Webpack -->

2
web/client/src/layout/actions/global.js

@ -39,6 +39,8 @@ export function initApiRoot () {
payload: {
apiRoot: res.root,
iotVcmpWeb:res.iotVcmpWeb,
pomsMonitor:res.pomsMonitor,
dcWeb:res.dcWeb,
}
})
});

6
web/client/src/layout/reducers/global.js

@ -12,6 +12,8 @@ function global (state = {
clientWidth: 1024,
apiRoot: '',
iotVcmpWeb: '',
pomsMonitor:'',
dcWeb:'',
}, action) {
const payload = action.payload;
switch (action.type) {
@ -31,7 +33,9 @@ function global (state = {
case INIT_API_ROOT:
return Immutable.fromJS(state).merge({
apiRoot: payload.apiRoot,
iotVcmpWeb: payload.iotVcmpWeb
iotVcmpWeb: payload.iotVcmpWeb,
pomsMonitor:payload.pomsMonitor,
dcWeb:payload.dcWeb,
}).toJS();
case PEPPROJECTID:
return Immutable.fromJS(state).merge({

64
web/client/src/sections/control/containers/control.jsx

@ -8,7 +8,6 @@ import repairFQA from '../../means/containers/repairFQA';
import { Setup, OutHidden } from "$components";
import ReactECharts from 'echarts-for-react';
import moment from "moment";
import { log } from 'ezuikit-js';
let newScrollbar;
let overviewScrollbar;
@ -43,7 +42,7 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const [videoBI, setVideoBI] = useState([]); //BI-
const [appBI, setAppBI] = useState([]); //BI-
const [efficiencyBI, setEfficiencyBI] = useState({}); //BI-
const [query, setQuery] = useState({ limit: 10, page: 0, projectCorrelationId: '', types: '1' }); //
const [query, setQuery] = useState({ limit: 23, page: 0, projectCorrelationId: '', types: '1' }); //
const [querydata1, setQueryData1] = useState([]); //
const [long, setLong] = useState(''); //
const [pomsList, setPomsList] = useState([]); //
@ -398,6 +397,7 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
}))
}
if (exhibition?.current?.dynamic?.find(v => v.key == 'notice')) {
// EM
res.payload.data?.notice?.map(v => data.push({
seed: 'notice',
time: v.time,
@ -444,14 +444,26 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const line = document.getElementById("line")
const news = document.getElementById("news")
if (line && news) {
news.onscroll = () => {
news.onscroll = (e) => {
e.stopPropagation();
if ((line.clientHeight - 578) < news.scrollTop + 10) {
setQuery({ ...query, page: query.page + 1 })
if (exhibition?.current?.dynamic?.length > 0) {
dispatch(control.getLatestDynamic({ ...query, projectCorrelationId: pepProjectId, page: query.page + 1 })).then(res => {
news.scrollTop = news.scrollTop - 640
// news.scrollTop = news.scrollTop - 640
let data = querydata.current
if (res.success) {
let returnJudge = true
for (let k in res.payload.data) {
if (res.payload.data[k].length) {
returnJudge = false
break
}
}
if (returnJudge) {
return
}
if (exhibition?.current?.dynamic?.find(v => v.key == 'discovery')) {
res.payload.data?.appear?.map(v => data.push({
seed: 'discovery',
@ -503,9 +515,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject = document.getElementById("news");
if (domProject) {
// newScrollbar = new PerfectScrollbar("#news", {
// suppressScrollX: true,
// });
if (domProject && newScrollbar) {
newScrollbar.update();
}
@ -513,9 +522,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const pomsList = document.getElementById("pomsList");
if (pomsList) {
// pomsListScrollbar = new PerfectScrollbar("#pomsList", {
// suppressScrollX: true,
// });
if (pomsList && pomsListScrollbar) {
pomsListScrollbar.update();
}
@ -523,9 +529,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject1 = document.getElementById("overviewCalc");
if (domProject1) {
// overviewScrollbar = new PerfectScrollbar("#overviewCalc", {
// suppressScrollY: true,
// });
if (domProject1 && overviewScrollbar) {
overviewScrollbar.update();
}
@ -533,9 +536,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject2 = document.getElementById("member");
if (domProject2) {
// memberScrollbar = new PerfectScrollbar("#member", {
// suppressScrollX: true,
// });
if (domProject2 && memberScrollbar) {
memberScrollbar.update();
}
@ -543,9 +543,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject3 = document.getElementById("equipment");
if (domProject3) {
// equipmentScrollbar = new PerfectScrollbar("#equipment", {
// suppressScrollX: true,
// });
if (domProject3 && equipmentScrollbar) {
equipmentScrollbar.update();
}
@ -553,9 +550,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject4 = document.getElementById("web");
if (domProject4) {
// webScrollbar = new PerfectScrollbar("#web", {
// suppressScrollX: true,
// })
if (domProject4 && webScrollbar) {
webScrollbar.update();
}
@ -563,9 +557,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject5 = document.getElementById("problems");
if (domProject5) {
// problemsScrollbar = new PerfectScrollbar("#problems", {
// suppressScrollX: true,
// });
if (domProject5 && problemsScrollbar) {
problemsScrollbar.update();
}
@ -573,17 +564,11 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
const domProject6 = document.getElementById("alarm");
if (domProject6) {
// alarmScrollbar = new PerfectScrollbar("#alarm", {
// suppressScrollY: true,
// });
if (domProject6 && alarmScrollbar) {
alarmScrollbar.update();
}
}
// ACTION
// dispatch(actions.example.getMembers(user.orgId))
})
const consoleToollink = () => {
@ -625,8 +610,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
let Select = {
overall: ['workbench', 'statistical', 'analyse', 'dynamic', 'tool'],
workbench: ['project', 'data', 'app', 'device'],
@ -672,8 +655,6 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
{ name: 'BI分析模块', sort: 3, key: 'analyse', },
{ name: '最新动态', sort: 4, key: 'dynamic', },
{ name: '我常用的工具', sort: 5, key: 'tool', },
]
useEffect(() => {
@ -1245,8 +1226,8 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
}} />
</div>
</div>
<div id='news' style={{ height: 578, position: 'relative', marginTop: 10, }}>
<div id='line' style={{ width: '100%' }}>
<div id='news' style={{ height: 578, position: 'relative', marginTop: 10, overscrollBehavior: 'contain', }}>
<div id='line' style={{ width: '100%', }}>
<Timeline mode="center" style={{ marginLeft: '-56px', width: 400 }}>
{querydata.current?.map((v, index) => {
let title = ''
@ -1256,8 +1237,13 @@ const Control = ({ dispatch, actions, user, history, loading, socket, pepProject
title = v.userName ? (v.userName + '确认并关闭' + v.project + '【' + v.sources + '】' + v.type + '的问题') :
v.project + '【' + v.sources + '】' + v.type + '已恢复'
} else {
title = '【信鸽-' + v.alarmPushConfig + '】已邮件通知' +
v.userName?.map((u, i) => (i > 0 ? ',' + u : u)) + '【' + v.project + '】【' +
title =
'【信鸽-' + v.alarmPushConfig + '】已邮件通知'
+
v.userName?.map((u, i) => (i > 0 ? ',' + u : u))
+ '【' + v.project
+ '】【'
+
(v.tactics == 'immediately' ?
'发现在' + v.interval + '分钟内,有告警源新增' :
(v.tactics == 'continue' ? '告警源持续产生时间超过' + v.interval + '分钟' : '异常设备数量达到项目或结构物内设备总数量的' +

4
web/client/src/sections/control/containers/userCenter.jsx

@ -16,10 +16,10 @@ const UserCenter = (props) => {
<div style={{ margin: '4px 0px 14px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ color: '#4A4A4A', fontSize: 14 }}>
HI欢迎回来行业服务部
HI欢迎回来{user?.department[0]?.name}
</div>
<div style={{ fontFamily: 'YouSheBiaoTiHei', fontSize: 20, color: '#005ABD', marginLeft: 4 }}>
刘昊然
{user?.name}
</div>
</div>
</div>

28
web/client/src/sections/facility/containers/monitor.jsx

@ -4,7 +4,7 @@ import { connect } from 'react-redux';
const Server = (props) => {
const { dispatch, actions, user, loading, socket } = props
const { dispatch, actions, user, loading, socket, pomsMonitor } = props
useEffect(() => {
@ -13,13 +13,32 @@ const Server = (props) => {
return (
<>
<div>
<img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative", top: -12, left: -8, }} />
<div style={{
height: 'calc(100vh - 60px)', width: 'calc(100% + 16px)',
backgroundImage: "url('/assets/images/background/General.png')",
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
position: 'relative',
top: -12, left: -8,
}}>
<div style={{ position: 'absolute', top: 'calc(50%)', left: 'calc(10%)', }}>
<div style={{
width: 184, height: 48, fontWeight: 400, color: '#FFFFFF', lineHeight: '48px', backgroundImage: "url('/assets/images/background/B.png')", textAlign: 'center',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
}}>
<a target='_blank' href={pomsMonitor}
style={{width:'100%',height:'100%',display: 'block'}}
> 进入Grafana</a>
</div>
<div style={{ width: 679, height: 40, fontSize: 28, fontWeight: 400, color: '#146AD7', lineHeight: '40px', marginTop: 40 }}>
点击可跳转服务器监控网站Grafana</div>
</div>
{/* <iframe frameBorder="0" src={pomsMonitor} style={{height: 'calc(100vh - 80px)', width: 'calc(100%)'}} /> */}
</div>
</>
)
}
function mapStateToProps (state) {
const { auth, global, members, webSocket } = state;
return {
@ -28,6 +47,7 @@ function mapStateToProps (state) {
// actions: global.actions,
// members: members.data,
// socket: webSocket.socket
pomsMonitor: global.pomsMonitor,
};
}

1
web/client/src/sections/facility/nav-item.jsx

@ -41,3 +41,4 @@ export function getNavItem (user, dispatch) {
]
);
}

2
web/client/src/sections/install/containers/system.jsx

@ -258,7 +258,7 @@ const Example = (props) => {
},
{
title: "管理",
width: "20%",
width: "150px",
dataIndex: "text",
key: 'text',
render: (_, row) => {

8
web/client/src/sections/problem/actions/problem.jsx

@ -64,10 +64,11 @@ export function postApiConfirm (data) { //确认应用接口/元素错误信
});
}
export function getAlarmDataGroup () { //
export function getAlarmDataGroup (query) { //
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query,
actionType: 'GET_ALARM_DATA_GROUP',
url: `${ApiTable.getAlarmDataGroup}`,
msg: { option: '获取数据告警分类' },
@ -138,10 +139,11 @@ export function getAlarmVideoList (query) { //查询视频告警列表
export function getAlarmVideoDeviceKind () { //
export function getAlarmVideoDeviceKind (query) { //
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query,
actionType: 'GET_ALARM_VIDEO_DEVICE_KIND',
url: `${ApiTable.getAlarmVideoDeviceKind}`,
msg: { option: '查询视频设备类型' },
@ -158,7 +160,7 @@ export function getAlarmVideoExceptionType () { //查询视频异常类型
msg: { option: '查询视频异常类型' },
reducer: { name: '' }
});
}
}
export function putAlarmVideoConfirm (data) { //
return dispatch => basicAction({

20
web/client/src/sections/problem/components/tableData.jsx

@ -379,12 +379,23 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
placeholder={SkeletonScreen()}
>
{(() => {
// console.log(tableData);
console.log(tableData);
console.log((route == 'useAbnormal' || route == 'videoAbnormal') ?
(tableData?.slice(query.page * query.limit, (query.page + 1) * query.limit) || [])
: tableData);
return <Table
columns={exhibition}
dataSource={route == 'useAbnormal' || route == 'videoAbnormal' ? tableData.slice(query.page * query.limit, (query.page + 1) * query.limit) || [] : tableData}
dataSource={
(route == 'useAbnormal' || route == 'videoAbnormal') ?
(tableData?.slice(query.page * query.limit, (query.page + 1) * query.limit) || [])
: tableData
}
bordered={false}
empty={<div><img src="/assets/images/problem/shield.png" style={{ width: 20 }} />暂无告警数据</div>}
empty={
<div>
<img src="/assets/images/problem/shield.png" style={{ width: 20 }} />暂无告警数据
</div>
}
style={{}}
pagination={false}
onRow={(record, index) => {
@ -457,7 +468,6 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => {
setQuery({ limit: pageSize, page: currentPage - 1 });
}}
/>
</div> : ""}
@ -472,7 +482,7 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
}
function mapStateToProps(state) {
function mapStateToProps (state) {
const { auth, global, members } = state;
// console.log(global);
return {

490
web/client/src/sections/service/components/pushModal.jsx

@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from "react";
import { connect } from "react-redux";
import { Modal, Form, Notification } from "@douyinfe/semi-ui";
import { Modal, Form, Notification, Tooltip } from "@douyinfe/semi-ui";
import { IconAlertCircle } from '@douyinfe/semi-icons';
import './pushModal.less'
@ -15,9 +15,10 @@ function pushModal (props) {
actions,
pushEdit,//
editObj,
user
user,
subTypeData
} = props;
const { service } = actions;
const { service, problem } = actions;
const form = useRef();//
const [abnormal, setAbnormal] = useState(false); //disable
const [usersList, setUsersList] = useState([]); //
@ -32,22 +33,24 @@ function pushModal (props) {
const [interval2, setInterval2] = useState(undefined); //
const [interval3, setInterval3] = useState(undefined); //
const [deviceProportion, setDeviceProportion] = useState(undefined); //
const [subType, setSubType] = useState([]); //
const [factorShow, setFactorShow] = useState([]); //
const [firstPass, setFirstPass] = useState(true)
//
useEffect(() => {
if (editObj?.alarmType) setSubType(editObj?.alarmType)
getOrganizationUsersList()//
getProjectPomsList()//
if (editObj.id) {
getProjectStructureList(editObj.pomsProjectId)
if (editObj.pomsProject?.pepProjectId) {
getProjectStatusList()//
}
else {
getProjectStructureList(editObj.pomsProjectId, editObj)
let division = editObj?.pomsProject?.map(v => (v.pepProject?.id || 'POMS'))
if (division.length == 1 && division?.includes('POMS')) {
setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }])
timeTypePOMS.current = ['POMS']
} else {
getProjectStatusList()//
}
if (editObj.tactics == 'immediately') {
setInterval1(editObj.tacticsParams?.interval)
@ -58,7 +61,7 @@ function pushModal (props) {
setDeviceProportion(editObj.tacticsParams?.deviceProportion)
}
}
}, []);
}, [])
function getOrganizationUsersList () {//
dispatch(service.getOrganizationUsers()).then((res) => {
if (res.success) {
@ -73,15 +76,34 @@ function pushModal (props) {
}
})
}
function getProjectStructureList (value) {//
dispatch(service.getProjectStructure({ pomsProjectId: value })).then((res) => {
function getProjectStructureList (value, alter) {//
dispatch(service.getProjectStructure({ pomsProjectId: value.join(',') })).then((res) => {
if (res.success) {
let mylist = []
for (let i = 0; i < res.payload?.data.length; i++) {
mylist.push(res.payload?.data[i].id)
let data = []
let ProjectId = []
let factorId = []
res.payload?.data.map(v => {
if (ProjectId.includes(v.id)) {
} else {
ProjectId.push(v.id)
data.push(v)
}
})
setProjectStructure(data)
if (editObj.id && firstPass) {
let FactorId = []
if (alter?.pomsStrucFactorId) {
for (let key in alter?.pomsStrucFactorId) {
FactorId.push(key)
}
setFactorShow(FactorId?.map(Number))
} else {
setFactorShow(ProjectId)
}
setFirstPass(false)
} else {
form.current.setValue('strucId', ProjectId)
}
setProjectStructure(res.payload?.data)
form.current.setValue('strucId', mylist)
form.current.validate(['strucId', 'timeType'])
setStructure(false)
setTimeTypeDis(false)
@ -93,286 +115,88 @@ function pushModal (props) {
if (res.success) {
setProjectStatus(res.payload?.data)
let mylist = []
if (editObj?.id) {
mylist = editObj?.timeType.map(Number) || []
} else {
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 (pushEdit) {
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
dispatch(service.postPush({ pushId: editObj.id, ...obj, msg: '编辑推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
} 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
dispatch(service.postPush({ pushId: editObj.id, ...obj, msg: '编辑推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
} 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
dispatch(service.postPush({ pushId: editObj.id, ...obj, msg: '编辑推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
} 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,
})
}
}
}
else {
let obj = JSON.parse(JSON.stringify(values))
if (obj.timeType[0] == 'POMS') {
obj.timeType = []
}
function caution (tactics, interval, deviceProportion, data) {
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
dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
} else {
let title = tactics == 'immediately' ? '即时' : tactics == 'continue' ? '持续时长' : '异常率'
if (!regu.test(interval) || (tactics == 'abnormal_rate' && interval > 720) || interval > 1440) {
Notification.error({
content: '即时推送时间不能大于1440分钟',
content: title + (interval ? `推送时间不能大于${tactics == 'abnormal_rate' ? 720 : 1440}分钟` : '推送时间应为正整数'),
duration: 2,
})
return false
}
} else {
Notification.error({
content: '即时推送时间应为正整数',
duration: 2,
})
}
} else {
if (!deviceProportion || deviceProportion > 100) {
Notification.error({
content: '即时推送时间应为正整数',
content: '异常率推送异常率应为正整数且不超过100%',
duration: 2,
})
return false
}
if (data.alarmType?.includes('strategy_hit')) {
let factorItem = []
for (let key in data.pomsStrucFactorId) {
factorItem = [...factorItem, ...data.pomsStrucFactorId[key]]
}
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
dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
} else {
Notification.error({
content: '持续时长推送时间不能大于1440分钟',
duration: 2,
})
}
} else {
Notification.error({
content: '持续时长推送时间应为正整数',
duration: 2,
})
}
if (factorItem.length > 0 && data.alarmSubType['strategy_hit']?.length > 0) {
return true
} else {
Notification.error({
content: '持续时长推送时间应为正整数',
duration: 2,
content: '勾选策略命中监听模块后,策略命中细项与监测项都必须勾选一项',
duration: 3,
})
}
return false
}
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
return true
}
delete obj.interval1
delete obj.interval2
delete obj.interval3
delete obj.deviceProportion
dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//(PEP)
function handleOk () {
//
form.current
.validate()
.then((v) => {
let data = {
name: v.name,
pomsProjectId: v.pomsProjectId,
strucId: v.strucId || [],
tactics: v.tactics,
tacticsParams: {
interval: v.tactics == 'immediately' ? v.interval1 : (v.tactics == 'continue' ? v.interval2 : v.interval3),
deviceProportion: v.deviceProportion
},
alarmType: v.alarmType,
timeType: v.timeType[0] == 'POMS' ? [] : v.timeType,
receiverPepUserId: v.receiverPepUserId || [],
disable: v.disable,
alarmSubType: {},
pomsStrucFactorId: {}
}
for (let key in v) {
if (['app_exception', 'data_exception', 'data_outages', 'device_exception', 'video_exception', 'strategy_hit'].includes(key)) {
data.alarmSubType = { ...data.alarmSubType, [key]: v[key] }
} else if (key.indexOf('factor') == 0) {
data.pomsStrucFactorId = { ...data.pomsStrucFactorId, [key.slice(6)]: v[key] }
}
}
if (caution(data.tactics, data.tacticsParams.interval, data.tacticsParams.deviceProportion, data)) {
dispatch(service.postPush({ pushId: editObj.id, ...data, msg: pushEdit ? '编辑推送配置' : "新增推送策略" })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
} 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,
})
}
}
}
})
}
@ -393,35 +217,46 @@ function pushModal (props) {
>
<div>
<Form
allowEmpty
labelPosition="left"
labelAlign="right"
labelWidth="120px"
labelWidth="128px"
onValueChange={(values, field) => {
for (var key in field) {
if (key == 'tactics') {
if (values.tactics == 'abnormal_rate') {
form.current.setValue('alarmType', undefined)
setAbnormal(true)
}
else {
} else {
setAbnormal(false)
}
}
if (key == 'pomsProjectId') {
} else if (key == 'pomsProjectId') {
if (values.pomsProjectId.length > 0) {
getProjectStructureList(values.pomsProjectId)//
for (let i = 0; i < projectPoms.length; i++) {
if (values.pomsProjectId == projectPoms[i].id) {
if (projectPoms[i].pepProjectId) {
getProjectStatusList()//
let pepProjectId = []
let projectData = values.pomsProjectId?.map(v => {
let data = projectPoms?.filter(u => u.id == v) || []
if (data.length) {
pepProjectId.push(data[0].pepProjectId || 'POMS')
}
else {
})
if (pepProjectId.length == 1 && pepProjectId.includes('POMS')) {
setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }])
form.current.setValue('timeType', ['POMS'])
form.current.validate()
} else {
getProjectStatusList()//
}
} else {
setProjectStructure([])
form.current.setValue('strucId', [])
setProjectStatus([])
form.current.setValue('timeType', [])
}
}
} else if (key == 'strucId') {
setFactorShow(values.strucId)
} else if (key == 'alarmType') {
setSubType(field['alarmType'])
}
}
}}
@ -436,7 +271,7 @@ function pushModal (props) {
label='策略名称:'
maxLength={15}
disabled={pushEdit}
style={{ width: 695 }}
style={{ width: 678 }}
initValue={editObj?.name || ""}
placeholder="请输入策略名称"
showClear
@ -447,9 +282,10 @@ function pushModal (props) {
label="请选择项目:"
field="pomsProjectId"
placeholder="请选择项目"
style={{ width: 695 }}
style={{ width: 678 }}
rules={[{ required: true, message: "请选择项目" }]}
initValue={editObj?.pomsProjectId || ""}
multiple
filter
>
{
@ -465,10 +301,13 @@ function pushModal (props) {
</div>
<div>
<Form.Select
label="请选择结构物:"
label={<>请选择结构物
<Tooltip content={'选择结构物,若该结构物下没有监测项则下方不展示,反之展示结构物对应的监测项'} style={{ lineHeight: 2 }}>
<img src="/assets/images/install/table_question.png" alt="" style={{ height: 14, width: 14 }} />
</Tooltip>:</>}
field="strucId"
placeholder="请选择结构物"
style={{ width: 695 }}
style={{ width: 678 }}
rules={[{ required: true, message: "请选择结构物" }]}
initValue={editObj?.strucId || []}
disabled={structure}
@ -487,6 +326,27 @@ function pushModal (props) {
})
}
</Form.Select>
{projectStructure?.filter(v => (factorShow?.includes(v.id) && v.factor?.length > 0))?.map((u, index) => {
let factorItem = []
u.factor?.map(v => {
v.item.map(vv => factorItem.push(vv.name))
})
return <Form.CheckboxGroup
label={u.name + ':'}
key={u.name + index}
field={'factor' + u.id}
style={{ width: 695 }}
initValue={editObj?.pomsStrucFactorId ? editObj?.pomsStrucFactorId[u.id] : []}
direction='horizontal'
showClear
>
{
[...new Set(factorItem)].map((v, index) =>
<Form.Checkbox value={v} key={v} style={{ width: 150 }}>{v}</Form.Checkbox>)
}
</Form.CheckboxGroup>
})
}
</div>
<div className='pushInput'>
<Form.RadioGroup
@ -496,11 +356,12 @@ function pushModal (props) {
direction='horizontal'
initValue={editObj?.tactics || ''}
rules={[{ required: true, message: '请选择推送策略' }]}>
<div style={{ display: 'flex', alignItems: 'flex-start' }}>
<Form.Radio
value='immediately'
extra={
<span style={{ fontSize: 13 }}>
发现在
中台每分钟查询若有告警源新增则每
<Form.Input
field="interval1"
pure
@ -508,10 +369,10 @@ function pushModal (props) {
initValue={interval1 || "10"}
// rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "" }]}
/>
分钟有告警源新增通过信鸽服务发送一条通知信息
分钟通过信鸽服务发送一条通知信息
</span>
}
style={{ width: 198 }}>
style={{ width: 217 }}>
即时推送机制
</Form.Radio>
<Form.Radio
@ -529,7 +390,7 @@ function pushModal (props) {
分钟则通过信鸽服务发送一条通知信息
</span>
}
style={{ width: 198 }}>
style={{ width: 173 }}>
持续时长推送机制
</Form.Radio>
<Form.Radio
@ -558,6 +419,7 @@ function pushModal (props) {
style={{ width: 260 }}>
异常率推送机制
</Form.Radio>
</div>
</Form.RadioGroup>
</div>
<div>
@ -570,13 +432,49 @@ function pushModal (props) {
direction='horizontal'
showClear
>
<Form.Checkbox value="data_outages">数据中断</Form.Checkbox>
<Form.Checkbox value="data_exception">数据异常</Form.Checkbox>
<Form.Checkbox value="strategy_hit" disabled={abnormal}>策略命中</Form.Checkbox>
<Form.Checkbox value="video_exception">视频异常</Form.Checkbox>
<Form.Checkbox value="app_exception" disabled={abnormal}>应用异常</Form.Checkbox>
<Form.Checkbox value="device_exception">设备异常</Form.Checkbox>
{
[
{ name: '数据中断', value: "data_outages" },
{ name: '数据异常', value: "data_exception" },
{ name: '策略命中', value: "strategy_hit" },
{ name: '视频异常', value: "video_exception" },
{ name: '应用异常', value: "app_exception" },
{ name: '设备异常', value: "device_exception" },
].map((v, index) =>
<Form.Checkbox value={v.value}
key={v.value}
disabled={index == 2 || index == 4 ? abnormal : false}>
{v.name}</Form.Checkbox>)
}
</Form.CheckboxGroup>
{[
{ name: '数据中断', value: "data_outages", data: subTypeData['data_outages'][0] },
{ name: '数据异常', value: "data_exception", data: subTypeData['data_exception'][0] },
{ name: '策略命中', value: "strategy_hit", data: subTypeData['strategy_hit'][0] },
{ name: '视频异常', value: "video_exception", data: subTypeData['video_exception'][0]?.map(v => ({ id: v.id, name: v.kind })) },
{ name: '应用异常', value: "app_exception", data: subTypeData['app_exception'][0] },
{ name: '设备异常', value: "device_exception", data: [...subTypeData['device_exception'][0], ...subTypeData['device_exception'][1]] },
].filter(v => subType?.includes(v.value))?.map((u, index) => {
return <Form.CheckboxGroup
label={u.name + ':'}
key={u.name + u.index}
field={u.value}
style={{ width: 695 }}
initValue={
editObj?.id ?
editObj?.alarmSubType ?
editObj?.alarmSubType[u.value] : u.data.map(v => v.id)
: []}
direction='horizontal'
showClear
>
{
u.data?.map((v, index) =>
<Form.Checkbox value={v.id} key={v.id} style={{ width: 150 }}>{v.name}</Form.Checkbox>)
}
</Form.CheckboxGroup>
})
}
</div>
<div style={{ color: '#4A4A4A', fontSize: 14, fontWeight: 600, marginLeft: 6 }}>
接收信息配置

99
web/client/src/sections/service/containers/emPush.jsx

@ -11,7 +11,7 @@ import { Setup } from "$components";
const EmPush = (props) => {
const form = useRef();//
const { dispatch, actions, user, loading, socket } = props
const { service } = actions;
const { service, problem } = actions;
const [setup, setSetup] = useState(false); //
const [setupp, setSetupp] = useState([]);//
const [query, setQuery] = useState({ limit: 10, page: 0 }); //
@ -23,6 +23,14 @@ const EmPush = (props) => {
const [allTableData, setAllTableData] = useState([]) //
const [editObj, setEditObj] = useState({});//
const [projectStatus, setProjectStatus] = useState([]); //
const [subTypeData, setSubTypedata] = useState({
data_outages: [],
data_exception: [],
strategy_hit: [],
video_exception: [],
app_exception: [],
device_exception: [],
}); //
const page = useRef(query.page);//
const EMPUSH = "empush";
const tableList = [//
@ -80,7 +88,34 @@ const EmPush = (props) => {
: "";
getProjectStatusList()
getPushList(query);
//
dispatch(problem.getAlarmDataGroup({ showAll: 'true' })).then((res) => {
if (res.success) {
let data = { ...subTypeData }
res.payload.data?.map(v => {
if (v.id === 1) {
data['data_outages'].push(v.unit)
} else if (v.id === 2) {
data['data_exception'].push(v.unit)
} else if (v.id == 3) {
data['strategy_hit'].push(v.unit)
} else {
data['device_exception'].push(v.unit)
}
})
//
dispatch(problem.getAlarmVideoDeviceKind({ showAll: true })).then((res) => {
if (res.success) {
data['video_exception'].push(res.payload.data)
}
})
data['app_exception'].push([{ id: 'apiError', name: "接口报错", }, { id: 'element', name: "元素异常", }, { id: 'timeout', name: "加载超时", },])
setSubTypedata(data)
}
})
}, [])
useEffect(() => {
let showTableData = JSON.parse(JSON.stringify(allTableData)).slice(query.page * query.limit, (query.page + 1) * query.limit)
setTableData(showTableData)
@ -210,17 +245,27 @@ const EmPush = (props) => {
dataIndex: "projectName",
key: "projectName",
render: (_, row) => {
let projectData = []
row.pomsProject?.map(v => {
projectData.push({
projectName: v.pepProject?.projectName,
name: v.name,
anxinerror: v.del,
constructionStatus: v.pepProject?.constructionStatus,
})
})
let anxinerror = false
let anxinerrorArr = ''
if (row.pomsProject.del == true) {
anxinerror = true
anxinerrorArr = row.pomsProject.pepProject?.projectName || row.pomsProject.name
}
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
projectData.map((u, index) => <div key={'projecname' + index} style={{ display: 'flex', alignItems: 'center' }}>
{
anxinerror ? (
<Tooltip content={anxinerrorArr + ',项目已在【项企PEP】或【映射关系】中被删除,请重选项目!'}>
u.anxinerror ? (
<Tooltip content={(u.projectName || u.name) + ',项目已在【项企PEP】或【映射关系】中被删除,请重选项目!'}>
<div style={{ marginRight: 5 }}>
<img src="/assets/images/install/risk.png" alt="" style={{ height: 24, width: 24, }} />
</div>
@ -228,17 +273,17 @@ const EmPush = (props) => {
}
{
<div className='myseparator' style={{ display: 'flex', alignItems: 'center' }}>
<Tooltip content={row.pomsProject.pepProject?.projectName || row.pomsProject.name}>
<div style={{ width: row.pomsProject?.pepProject?.projectName?.length > 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}
<Tooltip content={(u.projectName || u.name)}>
<div style={{ width: u.projectName?.length > 7 || u.name?.length > 7 ? '112px' : '', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', color: row.pomsProject.del ? '#F93920' : '' }}>
{u.projectName || u.name}
</div>
</Tooltip>
</div>
}
{
row.pomsProject?.pepProject?.projectName ? (
u.projectName ? (
<div style={{
height: 18, marginLeft: 4,
height: 18, marginLeft: 4, width: 76,
background: 'linear-gradient(180deg, #EBF5FF 0%, #EBF5FF 0%, #D3E8FF 100%)',
borderRadius: 2, display: 'flex', alignItems: 'center'
}}>
@ -246,15 +291,7 @@ const EmPush = (props) => {
<img src="/assets/images/install/icon_zhengque.png" alt="" style={{ height: 10, width: 10, marginLeft: 4, marginRight: 9 }} />
</div>
<div style={{ color: '#0F7EFB', fontSize: 11, marginRight: 12 }}>
{
val.map((ite, idx) => {
return (
<div key={idx}>
{ite.id == row.pomsProject?.pepProject.constructionStatusId ? ite.construction_status : ''}
</div>
)
})
}
{u.constructionStatus}
</div>
</div>
) : (
@ -272,7 +309,7 @@ const EmPush = (props) => {
</div>
)
}
</div>
</div>)
)
}
},
@ -485,21 +522,22 @@ const EmPush = (props) => {
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) {
} else {
let construcId = row.pomsProject?.map(v => (v.pepProject?.constructionStatusId || 'POMS')) || []
if (construcId?.includes('POMS')) {
enableType = '已生效'
} else {
let timeType = row.timeType?.map(Number) || []
for (let i = 0; i < timeType.length; i++) {
if (construcId?.includes(timeType[i])) {
enableType = '已生效'
break;
break
} else {
enableType = '未生效'
}
}
}
else {
enableType = '已生效'
}
}
return (
<div style={{ textAlign: 'center', padding: '1px 17px', color: enableType == '禁用' ? '#FB0F0F' : enableType == '已生效' ? '#0F7EFB' : '#646566', background: enableType == '禁用' ? 'rgba(255,221,221,0.38)' : enableType == '已生效' ? 'rgba(221,237,255,0.38)' : 'rgba(192,192,192,0.38)', }}>
@ -513,7 +551,7 @@ const EmPush = (props) => {
dataIndex: "pushCount",
key: "pushCount",
render: (_, r, index) => {
return (r.pushCount||0) + '次'
return (r.pushCount || 0) + '次'
},
},
];
@ -618,9 +656,9 @@ const EmPush = (props) => {
marginLeft: 32
}}
onClick={() => {
setEditObj({})
setPushModal(true);
setPushEdit(false);
setEditObj({})
}}
>
添加推送策略
@ -684,6 +722,7 @@ const EmPush = (props) => {
visible={true}
pushEdit={pushEdit}
editObj={editObj}
subTypeData={subTypeData}
cancel={() => {
setPushModal(false);
}}

10
web/client/src/sections/service/containers/reportManagement.jsx

@ -4,7 +4,7 @@ import { connect } from 'react-redux';
const Rest = (props) => {
const { dispatch, actions, user, loading, socket } = props
const { dispatch, actions, user, loading, socket, dcWeb } = props
useEffect(() => {
@ -13,8 +13,9 @@ const Rest = (props) => {
return (
<>
<div>
<img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative", top: -12, left: -8, }} />
{/* style={{height: '100%', width: '100%',margin: '8px 12px', padding: '20px 20px 0px 20px'}} */}
<div >
<iframe frameBorder="0" src={`${dcWeb}/dataService/download?pcode=`} style={{ height: 'calc(100vh - 80px)', width: 'calc(100%)' }} />
</div>
</>
)
@ -27,7 +28,8 @@ function mapStateToProps (state) {
// user: auth.user,
// actions: global.actions,
// members: members.data,
// socket: webSocket.socket
// socket: webSocket.socket,
dcWeb: global.dcWeb,
};
}

8
web/config.js

@ -16,6 +16,9 @@ args.option('apiPomsUrl', 'webapi的URL 外网可访问');
args.option('apiAnxinyunUrl', '安心云 api');
args.option('apiEmisUrl', '企业管理 api');
args.option('iotVcmpWeb', 'IOT 视频服务');
args.option('pomsMonitor', '运维监控 web');
args.option('dcWeb', '报表中心web');
// 七牛
args.option('qnak', 'qiniuAccessKey');
@ -30,6 +33,8 @@ const API_POMS_URL = process.env.API_POMS_URL || flags.apiPomsUrl;
const API_EMIS_URL = process.env.API_EMIS_URL || flags.apiEmisUrl;
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.apiAnxinyunUrl;
const IOT_VIDEO_WEB = process.env.IOT_VIDEO_WEB || flags.iotVcmpWeb;
const POMS_MONITOR = process.env.POMS_MONITOR || flags.pomsMonitor;
const DC_WEB = process.env.DC_WEB || flags.dcWeb;
// 七牛
const ANXINCLOUD_QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
@ -41,6 +46,7 @@ const ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMA
if (
!API_URL
|| !API_ANXINYUN_URL
|| !POMS_MONITOR || !DC_WEB
|| !ANXINCLOUD_QINIU_AK || !ANXINCLOUD_QINIU_SK || !ANXINCLOUD_QINIU_BUCKET_RESOURCE || !ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || !IOT_VIDEO_WEB) {
console.log('缺少启动参数,异常退出');
args.showHelp();
@ -85,6 +91,8 @@ const product = {
opts: {
apiUrl: API_POMS_URL,
iotVcmpWeb: IOT_VIDEO_WEB,
pomsMonitor: POMS_MONITOR,
dcWeb: DC_WEB,
staticRoot: './client',
qiniu: {
fetchUrl: '/_file-server',

2
web/package.json

@ -7,7 +7,7 @@
"test": "mocha",
"start-vite": "cross-env NODE_ENV=developmentVite npm run start-params",
"start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5600 -u http://localhost:4600 --apiPomsUrl http://localhost:4600 --apiAnxinyunUrl http://10.8.30.112:4100 --apiEmisUrl http://10.8.30.112:14000 --qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5 --qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa --qnbkt dev-highways4good --qndmn http://rhvqdivo5.hn-bkt.clouddn.com --iotVcmpWeb https://mediaconsole.ngaiot.com",
"start-params": "node server -p 5600 -u http://localhost:4600 --apiPomsUrl http://localhost:4600 --apiAnxinyunUrl http://10.8.30.112:4100 --apiEmisUrl http://10.8.30.112:14000 --qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5 --qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa --qnbkt dev-highways4good --qndmn http://rhvqdivo5.hn-bkt.clouddn.com --iotVcmpWeb https://mediaconsole.ngaiot.com --pomsMonitor https://monitor.anxinyun.cn --dcWeb https://fsiot-oamss.anxinyun.cn",
"deploy": "export NODE_ENV=production&& npm run build && node server",
"build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js"

4
web/routes/attachment/index.js

@ -19,12 +19,14 @@ module.exports = {
entry: function (app, router, opts) {
const getApiRoot = async function (ctx) {
const { apiUrl, iotVcmpWeb } = opts;
const { apiUrl, iotVcmpWeb, pomsMonitor, dcWeb } = opts;
ctx.status = 200;
ctx.body = {
root: apiUrl,
iotVcmpWeb: iotVcmpWeb,
pomsMonitor: pomsMonitor,
dcWeb: dcWeb,
};
};

Loading…
Cancel
Save