wenlele 2 years ago
parent
commit
d1265d3d0f
  1. 3
      api/.vscode/launch.json
  2. 7
      api/app/lib/controllers/alarm/alarmConfirmLog.js
  3. 41
      api/app/lib/controllers/alarm/app.js
  4. 51
      api/app/lib/controllers/alarm/video.js
  5. 3
      api/app/lib/routes/alarm/index.js
  6. 360
      api/app/lib/schedule/alarms_push.js
  7. 169
      api/app/lib/service/kafka.js
  8. 132
      api/app/lib/utils/alarmHandle.js
  9. 159
      api/app/lib/utils/helper.js
  10. 10
      api/config.js
  11. 2
      api/package.json
  12. 21
      web/client/src/sections/control/containers/control.jsx
  13. 2
      web/client/src/sections/service/actions/emPush.js
  14. 1
      web/client/src/sections/service/components/pushModal.jsx
  15. 99
      web/client/src/sections/service/containers/emPush.jsx

3
api/.vscode/launch.json

@ -19,7 +19,7 @@
// "-g postgres://postgres:123@10.8.30.32:5432/orational_service",
//
"-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS",
"-k node35:6667,node36:6667,node37:6667",
"-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",
"--redisPort 6379",
@ -61,6 +61,7 @@
"--clickHouseProjectManage peppm8",
"--clickHouseVcmp video_access_dev",
"--clickHouseDataAlarm default",
"--clickHouseIot iot",
"--confirmAlarmAnxinUserId 1",
"--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",

7
api/app/lib/controllers/alarm/alarmConfirmLog.js

@ -5,6 +5,7 @@ const moment = require('moment')
async function alarmConfirmLog(ctx, confirmPost, content) {
try {
const { models } = ctx.fs.dc;
const { utils: { sendConfirmToWeb } } = ctx.app.fs
//存日志
let logDatas = [];
confirmPost.map(cp => {
@ -33,9 +34,9 @@ async function alarmConfirmLog(ctx, confirmPost, content) {
await models.LatestDynamicList.bulkCreate(dynamics);
//消息推送到前端
//ctx.app.socket.emit('TEST', { someProperty: '【广播】呼叫青铜时代号!!!', })
if (logDatas.length) {
await sendConfirmToWeb(logDatas, false);
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;

41
api/app/lib/controllers/alarm/app.js

@ -143,6 +143,7 @@ async function notedInspection(ctx) {
async function apiError(ctx) {
try {
const { utils: { sendAppearToWeb } } = ctx.app.fs
const models = ctx.fs.dc.models;
const { projectAppId, alarmContent, router, statusCode, screenshot = '', type } = ctx.request.body
const now = moment().format()
@ -198,6 +199,46 @@ async function apiError(ctx) {
storageData.createTime = now
storageData.screenshot = screenshot
await models.AppAlarm.create(storageData)
//存告警记录
let constTypes = { 'element': "元素异常", 'apiError': "接口报错 ", 'timeout': "加载超时" }
let belongsTo = await models.ProjectApp.findOne({
where: {
id: projectAppId
},
include: [{
model: models.ProjectCorrelation,
attributes: ['id'],
where: { del: false }
}]
})
if (belongsTo) {
let appName = await models.App.findOne({
where: {
id: belongsTo.appId
},
attributes: ['name'],
})
let pId = belongsTo.projectCorrelation.dataValues.id;//归属项目
let data = {
projectCorrelationId: pId,
alarmInfo: { messageMode: 'AlarmGeneration', sourceName: appName.name, content: alarmContent, type },//AlarmGeneration代表告警首次产生
time: now,
type: constTypes[type]
}
let r = await models.AlarmAppearRecord.create(data, { returning: true });
let dynamic = {
time: r.dataValues.time,
alarmAppearId: r.dataValues.id,
projectCorrelationId: r.dataValues.projectCorrelationId,
type: 1//发现
}
await models.LatestDynamicList.create(dynamic);
//消息推送到前端
await sendAppearToWeb([data], 'app');
}
}
}

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

@ -284,6 +284,56 @@ async function confirm (ctx) {
}
}
async function alarmAdded(ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const { utils: { anxinStrucIdRange, sendAppearToWeb } } = ctx.app.fs
let anxinStruc = await anxinStrucIdRange({ ctx })
const { serial_no, channel_no, create_time, description, status_id } = ctx.request.body;
let belongToStruct = await clickHouse.anxinyun.query(
`SELECT name, structure FROM t_video_ipc WHERE serial_no='${serial_no}' and channel_no='${channel_no}'`).toPromise()
let structId = belongToStruct.length ? belongToStruct[0].structure : null
if (structId) {
let exist = anxinStruc.find(s => s.strucId == structId);
let projects = exist.pomsProject.filter(d => !d.del).map(p => p.id);
let datas = projects.map(d => {//需要 项目,告警源,异常类型,时间
return {
projectCorrelationId: d,
alarmInfo: { messageMode: 'AlarmGeneration', sourceName: belongToStruct[0].name, status_id, content: description },//AlarmGeneration代表告警首次产生
time: create_time,
type: description
}
})
let rslt = await models.AlarmAppearRecord.bulkCreate(datas, { returning: true });
let dynamics = rslt.map(r => {
return {
time: r.time,
alarmAppearId: r.id,
projectCorrelationId: r.projectCorrelationId,
type: 1//发现
}
})
await models.LatestDynamicList.bulkCreate(dynamics);
//消息推送到前端
if (datas.length) {
await sendAppearToWeb(datas, 'video');
}
}
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function vcmpAppAuthToken(ctx) {
try {
const { models } = ctx.fs.dc;
@ -306,5 +356,6 @@ module.exports = {
deviceType,
alarmList,
confirm,
alarmAdded,
vcmpAppAuthToken
};

3
api/app/lib/routes/alarm/index.js

@ -55,6 +55,9 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['PUT/alarm/video/confirm'] = { content: '确认视频告警信息', visible: true };
router.put('/alarm/video/confirm', videoAlarm.confirm);
app.fs.api.logAttr['POST/alarm/video/added_log'] = { content: '新增视频告警记录', visible: true };
router.post('/alarm/video/added_log', videoAlarm.alarmAdded);
app.fs.api.logAttr['GET/vcmp/auth'] = { content: '获取视频平台应用鉴权token', visible: true };
router.get('/vcmp/auth', videoAlarm.vcmpAppAuthToken);
};

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

@ -4,7 +4,7 @@ module.exports = function (app, opts) {
const alarmsPush = app.fs.scheduleInit(
{
interval: '12 */1 * * * *',
immediate: true, // dev
// immediate: true, // dev
proRun: true,
},
async () => {
@ -12,19 +12,20 @@ module.exports = function (app, opts) {
const { models, ORM: sequelize } = app.fs.dc
const { clickHouse } = app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { pushBySms, pushByEmail } = app.fs.utils
const { pushBySms, pushByEmail, sendNoticeToWeb } = app.fs.utils
const curMinOfYear = moment().diff(moment().startOf('year'), 'minutes')
const configListRes = await models.AlarmPushConfig.findAll({
where: {
del: false
},
order: ['id'],
include: [{
model: models.ProjectCorrelation,
where: {
del: false
},
required: true
}]
}],
})
let pepProjectIds = new Set()
for (let { dataValues: c } of configListRes) {
@ -111,7 +112,17 @@ module.exports = function (app, opts) {
`
).toPromise() :
[]
let searchStrucIds = strucListRes.map(s => s.id)
let strucThingId = []
let searchStrucIds = strucListRes.map(s => {
if (s.iotaThingId) {
strucThingId.push(s.iotaThingId)
}
return s.id
})
// 开发测试用的数据
// searchStrucIds = searchStrucIds.concat([991, 1052, 700])
if (searchStrucIds.length) {
searchStrucIds.unshift(-1)
}
@ -139,28 +150,47 @@ module.exports = function (app, opts) {
let alarmCameraCount = 0
// 判断推送策略
let nowTime = moment().startOf('minute')
let pointTime = moment().subtract(parseInt(interval), 'minute').startOf('minute').format('YYYY-MM-DD HH:mm:ss')
let pointTime =
moment()
.subtract(
parseInt(interval),
// + 1440 * 365,
'minute'
)
.startOf('minute')
.format('YYYY-MM-DD HH:mm:ss')
let newAddStartTime = pointTime
let newAddEndTime = nowTime.clone()
if (c.tactics == 'immediately') {
// !查所有未解决告警 所以时间范围大可不必
// dataAlarmOption.push(`StartTime >= '${pointTime}'`);
// appAlarmWhereOption.createTime = { $gte: pointTime }
// videoAlarmWhereOption.push(`camera_status_alarm.create_time >= '${pointTime}'`)
emailTitle += `即时推送服务`
emailSubTitle += `截止${moment(pointTime).format('YYYY年MM月DD日 HH时mm分')}-${moment(nowTime).format('HH时mm分')}`
} else if (c.tactics == 'continue') {
// dataAlarmOption.push(`StartTime <= '${pointTime}'`);
// appAlarmWhereOption.createTime = { $lte: pointTime }
// videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`)
} else if (c.tactics == 'continue' || c.tactics == 'abnormal_rate') {
// 新增的应该是上一个时间节点到上上个节点之间
dataAlarmOption.push(`StartTime <= '${pointTime}'`);
appAlarmWhereOption.createTime = { $lte: pointTime }
videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`)
// 新增的应该是上一个时间节点到上上个节点之间
newAddStartTime = moment(pointTime).subtract(parseInt(interval)).format('YYYY-MM-DD HH:mm:ss')
newAddEndTime = pointTime
if (c.tactics == 'continue') {
emailTitle += `持续时长推送服务`
emailSubTitle += `告警持续时长超${interval}分钟的告警源,详情如下:`
} else if (c.tactics == 'abnormal_rate') {
// dataAlarmOption.push(`StartTime <= '${pointTime}'`);
} else {
if (c.alarmType.includes('data_outages') || c.alarmType.includes('data_exception')) {
// 查了设备异常率 去安心云查当前项目下的设备数量
// TODO 等同步以太数据再查
deviceCount = 9999
// await clickHouse.anxinyun.query(`
// SELECT count(*) FROM
// `).toPromise()
let deviceCountRes =
strucThingId.length ?
await clickHouse.iot.query(`
SELECT count(DeviceId) AS count FROM device WHERE ThingId IN (${strucThingId.map(t => `'${t}'`).join(',')}, '-1')
`).toPromise()
: []
deviceCount = deviceCountRes.length ? deviceCountRes[0].count : 0
}
if (c.alarmType.includes('video_exception')) {
// 查了视频异常 去安心云查 接入的 萤石 设备数量
@ -171,10 +201,9 @@ module.exports = function (app, opts) {
`).toPromise())[0].count
: 0
}
// appAlarmWhereOption.createTime = { $lte: pointTime }
// videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`)
emailTitle += `异常率推送服务`
emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${deviceCount + cameraCount}个的${interval}%,详情如下`
emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${parseInt(deviceCount) + parseInt(cameraCount)}个的 --%,详情如下`
}
}
emailTitle += '——POMS飞尚运维中台'
@ -202,6 +231,7 @@ module.exports = function (app, opts) {
cameraAlarm.confirmTime AS confirmTime,
cameraAlarm.autoRestore AS autoRestore,
camera_status.describe AS statusDescribe,
camera_kind.kind AS cameraKind,
"gbCamera".online AS cameraOnline,
anxinIpc.t_video_ipc.name AS anxinIpcPosition,
anxinStation.id AS anxinStationId,
@ -213,6 +243,7 @@ module.exports = function (app, opts) {
camera.id AS cameraId,
camera.gb_id AS gbId,
camera.name AS cameraName,
camera.kind_id AS cameraKindId,
camera_status_alarm.id AS alarmId,
camera_status_alarm.create_time AS createTime,
camera_status_alarm.status_id AS statusId,
@ -240,7 +271,8 @@ module.exports = function (app, opts) {
AND cameraAlarm.statusId = camera_status.id
LEFT JOIN "gbCamera"
ON "gbCamera".id = cameraAlarm.gbId
LEFT JOIN camera_kind
ON camera_kind.id = cameraAlarm.cameraKindId
LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc
ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo
AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo
@ -251,6 +283,7 @@ module.exports = function (app, opts) {
ON anxinIpcStation.ipc = anxinIpc.id
LEFT JOIN ${anxinyun}.t_sensor AS anxinStation
ON anxinStation.id = anxinIpcStation.station
ORDER BY cameraAlarm.createTime DESC
`
).toPromise() : []
@ -290,6 +323,7 @@ module.exports = function (app, opts) {
alarmId: a.alarmId,
confirmContent: a.confirmContent,
confirmTime: a.confirmTime,
cameraKind: a.cameraKind,
struc: [],
station: []
}
@ -315,10 +349,12 @@ module.exports = function (app, opts) {
}
}
let p = 1
videoAlarms = returnD
}
if (c.alarmType.includes('app_exception')) {
appAlarms = await models.AppAlarm.findAll({
where: appAlarmWhereOption,
order: [['createTime', 'DESC']],
include: [{
model: models.App,
include: [{
@ -329,7 +365,6 @@ module.exports = function (app, opts) {
}]
}]
})
let a = 2
}
if (c.alarmType.includes('device_exception')) {
dataAlarmGroupOption.push(4)
@ -343,9 +378,10 @@ module.exports = function (app, opts) {
dataAlarms = await clickHouse.dataAlarm.query(`
SELECT * FROM alarms
WHERE
State NOT IN (3, 4)
AND StructureId IN (${searchStrucIds.join(',')})
${`State NOT IN (3, 4) AND `}
StructureId IN (${searchStrucIds.join(',')})
${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''}
ORDER BY StartTime DESC
`).toPromise()
console.log(dataAlarms);
}
@ -358,7 +394,7 @@ module.exports = function (app, opts) {
n: '结构物',
k: '',
f: (d) => {
return (strucListRes.find(s => s.id == d.StructureId) || {}).name
return (strucListRes.find(s => s.id == d.StructureId) || { name: '' }).name
}
}, {
n: '告警源名称',
@ -405,49 +441,289 @@ module.exports = function (app, opts) {
}
},]
let html = ''
let videoAlarmTitle = [{
n: '项目',
k: '',
v: pepProjectName
}, {
n: '结构物',
k: '',
f: (d) => {
return d.struc.map(ds => ds.name).join('、')
}
}, {
n: '告警源名称',
k: 'cameraName'
}, {
n: '告警源类型',
k: 'cameraKind'
}, {
n: '序列号',
k: 'cameraSerialNo'
}, {
n: '通道号',
k: 'cameraChannelNo'
}, {
n: '测点',
k: '',
f: (d) => {
return d.station.map(ds => ds.name).join('、')
}
}, {
n: '位置',
k: '',
f: (d) => {
return d.station.map(ds => ds.position).join('、')
}
}, {
n: '告警信息',
k: 'statusDescribe'
}, {
n: '持续时间',
k: '',
f: (d) => {
return d.createTime ?
'超过' + moment().diff(moment(d.createTime), 'minutes') + '分钟' : ''
}
},]
let appAlarmTitle = [{
n: '项目',
k: '',
v: pepProjectName
}, {
n: '应用名称',
k: '',
f: (d) => {
return d.app ? d.app.name : ''
}
}, {
n: '异常类型',
k: '',
f: (d) => {
if (d.type == 'element') {
return '元素异常'
} else if (d.type == 'apiError') {
return '接口报错'
} else {
return ''
}
}
}, {
n: '异常信息',
k: 'alarmContent'
}, {
n: 'URL地址',
k: 'cameraName',
f: (d) => {
return d.app && d.app.url ? `<a href="${d.app.url}">${d.app.url}</a>` : ''
}
}, {
n: '持续时间',
k: '',
f: (d) => {
return d.createTime ?
'超过' + moment().diff(moment(d.createTime), 'minutes') + '分钟' : ''
}
},]
let ifEmailSend = false
let tableTitlePostfix = ',详情如下:'
function packageTableTitle (tArr) {
function packageTableTitle (titleArr) {
let tableTitle = '<tr>'
for (let t of titleArr) {
tableTitle += `<th style="background-color:#8faadc; color:#fff; text-align:left">${t.n}</th>`
}
tableTitle += '</tr>'
return tableTitle
}
function packageTableData (data, titleArr) {
let tableData = '<tr>'
for (let t of titleArr) {
if (t.v) {
tableData += `<td>${t.v}</td>`
} else if (t.f) {
tableData += `<td>${t.f(data)}</td>`
} else if (t.k) {
tableData += `<td>${data[t.k]}</td>`
} else {
tableData += `<td></td>`
}
if (c.alarmType.includes('data_outages')) {
let tableTitlePrefix = '数据中断告警源'
}
tableData += '</tr>'
return tableData
}
function packageAlarmData2Table (titlePrefix, alarmData, alarmTitleArr, keyOfStartTime = 'StartTime') {
if (!alarmData.length) {
return ''
}
ifEmailSend = true
let tableTitlePrefix = titlePrefix + '告警源'
let newAddCount = 0
let alarmHtml = ''
let alarmHtml = '<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)
if (c.tactics == 'immediately') {
for (let a of dataAlarms.filter(d => d.AlarmGroup == 1)) {
alarmHtml += 1
for (let a of alarmData) {
alarmContent += packageTableData(a, alarmTitleArr)
if (a[keyOfStartTime] && moment(a[keyOfStartTime]).isBetween(newAddStartTime, newAddEndTime)) {
newAddCount++
}
}
tableTitlePrefix +=
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
}
tableTitlePrefix += 1
} else if (c.tactics == 'continue') {
} else if (c.tactics == 'abnormal_rate') {
let dataAlarmG1 = [];
let dataAlarmG2 = [];
let dataAlarmG3 = [];
let dataAlarmG45 = [];
let deviceStatistic = new Set()
for (let d of dataAlarms) {
if (d.AlarmGroup == 1) {
dataAlarmG1.push(d)
} else if (d.AlarmGroup == 2) {
dataAlarmG2.push(d)
} else if (d.AlarmGroup == 3) {
dataAlarmG3.push(d)
} else if (d.AlarmGroup == 4 || d.AlarmGroup == 5) {
dataAlarmG45.push(d)
}
deviceStatistic.add(d.SourceId)
}
if (c.tactics == 'abnormal_rate') {
let rate = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount)));
if (rate < parseFloat(deviceProportion)) {
continue
}
emailSubTitle = emailSubTitle.replace('--%', rate.toFixed(1) + '%')
}
let html = `
<html>
<head>
<meta charset="utf-8">
<style>
table {
border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;
}
th,td{
border-right:1px solid #888;border-bottom:1px solid #888;padding:5px 15px;
}
th{
font-weight:bold;
}
</style>
</head>
</html>
<h5 style="margin-bottom:12px">${emailSubTitle}</h5>
`
if (c.alarmType.includes('data_outages')) {
html += packageAlarmData2Table(
'数据中断',
dataAlarmG1,
dataAlarmTitle,
)
}
if (c.alarmType.includes('data_exception')) {
html += packageAlarmData2Table(
'数据异常',
dataAlarmG2,
dataAlarmTitle,
)
}
if (c.alarmType.includes('strategy_hit')) {
html += packageAlarmData2Table(
'策略命中',
dataAlarmG3,
dataAlarmTitle,
)
}
if (c.alarmType.includes('video_exception')) {
html += packageAlarmData2Table(
'视频异常',
videoAlarms,
videoAlarmTitle,
'createTime',
)
}
if (c.alarmType.includes('app_exception')) {
html += packageAlarmData2Table(
'应用异常',
appAlarms,
appAlarmTitle,
'createTime',
)
}
if (c.alarmType.includes('device_exception')) {
html += packageAlarmData2Table(
'设备异常',
dataAlarmG45,
dataAlarmTitle,
)
}
if (ifEmailSend) {
// 查接收人的信息
const receiverRes =
c.receiverPepUserId.length ?
await clickHouse.pepEmis.query(`
SELECT id, name, email FROM user WHERE id IN (${c.receiverPepUserId.join(',')},-1)
`).toPromise()
: []
let receiverId = []
const emails = receiverRes.reduce((arr, r) => {
if (r.email) {
arr.push(r.email)
receiverId.push({ id: r.id, name: r.name })
}
return arr
}, [])
if (emails.length) {
await pushByEmail({
email: ['1650192445@qq.com'],
email: emails,
title: emailTitle,
text: '',
html: ''
html: html
})
//存日志 存动态 socket到前端
let dataToSave = {
time: moment().format(),
pushConfigId: c.id,
cfgName: c.name,//策略名称
tactics: c.tactics,
tacticsParams: c.tacticsParams,
projectCorrelationId: pomsProjectId,
toPepUserIds: receiverId.map(r => r.id)
}
let r = await models.EmailSendLog.create(dataToSave, { returning: true })
let dynamic = {
time: r.dataValues.time,
emailSendId: r.dataValues.id,
projectCorrelationId: r.dataValues.projectCorrelationId,
type: 2//通知
}
await models.LatestDynamicList.create(dynamic);
//消息推送到前端
await sendNoticeToWeb(receiverId, dataToSave);
}
}
}
}
}

169
api/app/lib/service/kafka.js

@ -1,20 +1,171 @@
'use strict';
const moment = require('moment');
const Kafka = require('kafka-node');
module.exports = async function factory(app, opts) {
const client = new Kafka.KafkaClient({ kafkaHost: opts.kafka.rootURL });
try {
const client = new Kafka.KafkaClient({ kafkaHost: opts.kafka.rootURL, fromOffset: true });
const producer = new Kafka.HighLevelProducer(client);
producer.on('error', function (err) {
app.fs.logger.log('error', "[FS-KAFKA]", err);
});
producer.on("ready", function () {
console.log('111111 ready 666666666666')
})
const kafka = {
producer: producer,
configUpdateMessage: opts.configUpdateMessage || {}
};
// const kafka = {
// producer: producer,
// configUpdateMessage: opts.configUpdateMessage || {}
// };
app.fs.kafka = kafka;
// app.fs.kafka = kafka;
app.fs.logger.log('debug', "[FS-KAFKA]", "Init.Success");
// const topics = [{ topic: 'anxinyun_alarm', partition: 0 }]
// const options = {
// // groupId: 'topic-test-one',
// autoCommit: false,
// //fromOffset: true,
// fromOffset: 'latest'
// }
// const consumer = new Kafka.Consumer(client, topics, options)
// consumer.on("ready", function () {
// console.log('consumer ready 666666666666')
// })
// // consumer.on("message", function (w) {
// // console.log('consumer ready 666666666666')
// // })
// consumer.on('message', function (message) {
// const decodedMessage = JSON.parse(message.value)
// console.log('decodedMessage: ', decodedMessage)
// })
// consumer.on('error', function (err) {
// console.log('consumer err:')
// console.log(err);
// });
let consumer = new Kafka.ConsumerGroup(Object.assign({}, { groupId: 'yunwei-platform-api', fromOffset: 'latest' }, { kafkaHost: opts.kafka.rootURL }), ['anxinyun_alarm'])
consumer.on('message', async function (message) {
let msg = JSON.parse(message.value)
console.log('kafka consumer----------接收到消息');
await savePullAlarm(msg);
})
// let offset = new Kafka.Offset(client);
// consumer.on('offsetOutOfRange', function (topic) {
// console.log('offsetOutOfRange')
// // consumer.setOffset('topic', 0, 0);
// // topic.maxNum = 1;
// offset.fetch([topic], function (err, offsets) {
// if (err) {
// return console.error(err);
// }
// try {
// const max = Math.max.apply(null, offsets[topic.topic][topic.partition]);
// consumer.setOffset(topic.topic, topic.partition, max);
// } catch (error) {
// console.log(error);
// }
// });
// });
let structsAche = {
dataList: [],
expireTime: null//10分钟更新一次结构物列表
}
async function getStructsAche() {
const { utils: { getAxyStructs } } = app.fs
try {
if (!structsAche.dataList.length || moment() > moment(structsAche.expireTime)) {
let structList = await getAxyStructs();
structsAche.dataList = structList;
structsAche.expireTime = moment().add(10, 'minute').format('YYYY-MM-DD HH:mm:ss');
}
return structsAche;
} catch (err) {
console.log(`获取结构物列表失败, error: ${err}`);
}
}
//保存告警[发现]
async function savePullAlarm(msg) {
const { clickHouse, utils: { sendAppearToWeb, sendConfirmToWeb } } = app.fs
try {
let { messageMode, structureId, sourceName, alarmTypeCode, alarmCode, content, time } = msg;
let structsAche = await getStructsAche();
if (structsAche) {
let structs = structsAche.dataList;//结构物列表
const { models } = app.fs.dc
let exist = structs.find(s => s.strucId == structureId);
if (exist) {
let alarmType = await clickHouse.anxinyun.query(
`SELECT name FROM t_alarm_type WHERE code='${alarmTypeCode}'`).toPromise()
let type = alarmType.length ? alarmType[0].name : ''
let projects = exist.pomsProject.filter(d => !d.del).map(p => p.id);
if (messageMode == 'AlarmGeneration') {//告警产生--------------------------------------------------1
let datas = projects.map(d => {//需要 项目,告警源,异常类型,时间
return {
projectCorrelationId: d,
alarmInfo: { messageMode, sourceName, alarmTypeCode, content },
time: time,
type//异常类型
}
})
let rslt = await models.AlarmAppearRecord.bulkCreate(datas, { returning: true });
let dynamics = rslt.map(r => {
return {
time: r.time,
alarmAppearId: r.id,
projectCorrelationId: r.projectCorrelationId,
type: 1//发现
}
})
await models.LatestDynamicList.bulkCreate(dynamics);
//消息推送到前端
if (datas.length) {
await sendAppearToWeb(datas, 'data');
}
} else if (messageMode == 'AlarmAutoElimination') {//告警自动恢复------------------------------------2
let datas = projects.map(d => {
return {
pepUserId: null,
projectCorrelationId: d,
alarmInfo: { source: sourceName, type },//包含告警id,type,source
confirmTime: time,
confirmContent: '自动恢复'
}
})
let rslt = await models.AlarmConfirmLog.bulkCreate(datas, { returning: true });
let dynamics = rslt.map(r => {
return {
time: r.confirmTime,
alarmConfirmId: r.id,
projectCorrelationId: r.projectCorrelationId,
type: 4//告警确认
}
})
await models.LatestDynamicList.bulkCreate(dynamics);
//消息推送到前端
if (datas.length) {
await sendConfirmToWeb(datas, true);
}
}
}
} else {
console.log(`获取结构物列表失败, error: ${err}`);
}
} catch (error) {
console.error(error);
}
}
} catch (error) {
console.log(error);
}
}

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

@ -0,0 +1,132 @@
'use strict';
module.exports = function (app, opts) {
const { models } = app.fs.dc
const { clickHouse } = app.fs
let constAlarmGroups = {
1: '数据中断',
2: '数据异常',
3: '策略命中',
4: '设备异常',
5: '设备异常',
'video': '视频异常',
'app': '应用异常'
}
async function sendAppearToWeb(datas, ttype) {
try {
//告警类型
let alarmGroup = null
//项目信息
let { projects, pepProjects } = await getProjectsInfo(datas);
//数据类区分alarmGroup
if (ttype == 'data') {
let alarm_group = await clickHouse.anxinyun.query(
`SELECT alarm_group FROM t_alarm_code WHERE code='${datas[0].alarmInfo.alarmTypeCode}'`).toPromise();
alarmGroup = alarm_group.length ? constAlarmGroups[alarm_group[0].alarm_group] : null
} else {
alarmGroup = constAlarmGroups[ttype]
}
let sendData = []
datas.map(ld => {
let pepPId = projects.find(p => p.id == ld.projectCorrelationId).pepProjectId;
sendData.push({
projectCorrelationId: ld.projectCorrelationId,
project: projects.find(p => p.id == ld.projectCorrelationId).name || pepProjects.find(pp => pp.id == pepPId).project_name,//前者为自定义项目名称
source: ld.alarmInfo.sourceName,
type: ld.type,
time: ld.time,
alarmGroup//告警类型
})
})
app.socket.emit('alarmSendSocket', { type: 'alarmAppear', sendData })
} catch (err) {
console.log(`告警(发现)推送失败, error: ${err}`);
}
}
async function sendConfirmToWeb(logDatas, isAuto) {
try {
//用户信息
let userName = null
if (!isAuto) {
let userPepRes = await clickHouse.pepEmis.query(
`SELECT DISTINCT user.id AS id, "user"."name" AS name FROM user WHERE user.id=${logDatas[0].pepUserId}`).toPromise();
userName = userPepRes.length ? userPepRes[0].name : null
}
//项目信息
let { projects, pepProjects } = await getProjectsInfo(logDatas);
let sendData = []
logDatas.map(ld => {
let pepPId = projects.find(p => p.id == ld.projectCorrelationId).pepProjectId;
sendData.push({
user: userName,
projectCorrelationId: ld.projectCorrelationId,
project: projects.find(p => p.id == ld.projectCorrelationId).name || pepProjects.find(pp => pp.id == pepPId).project_name,//前者为自定义项目名称
source: ld.alarmInfo.source,
type: ld.alarmInfo.type,
time: ld.confirmTime,
isAuto//是否为自动恢复,自动恢复时user为null
})
})
app.socket.emit('alarmSendSocket', { type: 'alarmConfirm', sendData })//小飞(处理人) 确认并关闭了A项目(项目) DTU设备(告警源) 状态异常(异常类型)的问题
} catch (err) {
console.log(`告警(确认)推送失败, error: ${err}`);
}
}
async function getProjectsInfo(datas) {
try {
let pIds = datas.map(l => l.projectCorrelationId);//所有的项目的id
let projects = await models.ProjectCorrelation.findAll({
where: { id: { $in: pIds } },
attributes: ['id', 'name', 'pepProjectId']
});
let pepPojectIds = new Set();
for (let p of projects) {
pepPojectIds.add(p.pepProjectId);
}
let pepProjects = pepPojectIds.size ? await clickHouse.projectManage.query(`
SELECT id, project_name FROM t_pim_project WHERE id IN (${[...pepPojectIds]})`).toPromise() : [];
return { projects, pepProjects };
} catch (err) {
console.log(`获取项目信息失败, error: ${err}`);
}
}
async function sendNoticeToWeb(pepUsers, data) {
try {
let { cfgName, tactics, tacticsParams, projectCorrelationId, time } = data;
//项目信息
let { projects, pepProjects } = await getProjectsInfo([data]);
let pepPId = projects.find(p => p.id == projectCorrelationId).pepProjectId;
//需要 策略名称 处理人 项目 策略和参数 时间
let sendData = {
pushConfig: { cfgName, tactics, tacticsParams },//策略信息
pepUsers,
projectCorrelationId: projectCorrelationId,
project: projects.find(p => p.id == projectCorrelationId).name || pepProjects.find(pp => pp.id == pepPId).project_name,//前者为自定义项目名称
time
}
app.socket.emit('alarmSendSocket', { type: 'alarmNotice', sendData })
} catch (err) {
console.log(`推送通知失败, error: ${err}`);
}
}
return {
sendAppearToWeb,//推送告警发现
sendConfirmToWeb,//推送告警确认
sendNoticeToWeb//推送通知
}
}

159
api/app/lib/utils/helper.js

@ -0,0 +1,159 @@
'use strict';
const moment = require('moment')
module.exports = function (app, opts) {
const { models } = app.fs.dc
const { clickHouse } = app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
async function getAxyStructs(pepProjectId) {
try {
try {
const { pepProjectRes, bindRes } = await pomsWithPepRangeParams(pepProjectId)
// 获取不重复的 安心云项目 id
const anxinProjectIds = [
...(bindRes).reduce(
(arr, b) => {
for (let sid of b.anxinProjectId) {
arr.add(sid);
}
return arr;
},
new Set()
)
]
// 查询安心云项目及结构物信息
const undelStrucRes = anxinProjectIds.length ?
await clickHouse.anxinyun.query(
`SELECT
t_project.id AS projectId,
t_structure.id AS strucId,
t_structure.name AS strucName,
project_state
FROM
t_project
LEFT JOIN
t_project_structure
ON t_project_structure.project = t_project.id
LEFT JOIN
t_project_structuregroup
ON t_project_structuregroup.project = t_project.id
LEFT JOIN
t_structuregroup_structure
ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup
LEFT JOIN
t_project_construction
ON t_project_construction.project = t_project.id
LEFT JOIN
t_structure_site
ON t_structure_site.siteid = t_project_construction.construction
RIGHT JOIN
t_structure
ON t_structure.id = t_project_structure.structure
OR t_structure.id = t_structuregroup_structure.structure
OR t_structure.id = t_structure_site.structid
WHERE
project_state != -1
AND
t_project.id IN (${anxinProjectIds.join(',')})`).toPromise() : []
// 构建安心云结构物和项企项目的关系
// 并保存信息至数据
let undelStruc = []
for (let s of undelStrucRes) {
let corStruc = undelStruc.find(us => us.strucId == s.strucId)
if (corStruc) {
if (!corStruc.project.some(cp => cp.id == s.projectId)) {
corStruc.project.push({
id: s.projectId
})
}
} else {
corStruc = {
strucId: s.strucId,
strucName: s.strucName,
projectId: s.projectId,
project: [{
id: s.projectId,
}],
pomsProject: []
}
undelStruc.push(corStruc)
}
for (let { dataValues: br } of bindRes) {
if (br.anxinProjectId.some(braId => braId == s.projectId)) {
let corPepProject = pepProjectRes.find(pp => pp.id == br.pepProjectId)
let corPomsProject = corStruc.pomsProject.find(cp => cp.id == br.id)
if (corPomsProject) {
// poms 的 project 和 pep 的 project 是一对一的关系 所以这个情况不用处理
} else {
corStruc.pomsProject.push({
...br,
pepProject: corPepProject
})
}
}
}
}
return undelStruc
} catch (error) {
console.error(error);
}
} catch (error) {
console.log(error)
}
}
async function pomsWithPepRangeParams(pepProjectId) {
try {
let findOption = {
where: {
del: false
}
}
if (pepProjectId) {
// 有 特定的项目id 就按此查询
findOption.where.id = pepProjectId
}
const bindRes = await models.ProjectCorrelation.findAll(findOption);
// 获取不重复的 项企项目id
let pepProjectIds = []
for (let b of bindRes) {
if (b.pepProjectId) {
pepProjectIds.push(b.pepProjectId)
}
}
// 查询项企项目的信息
const pepProjectRes = pepProjectIds.length ?
await clickHouse.projectManage.query(
`SELECT
t_pim_project.id AS id,
t_pim_project.project_name AS projectName,
t_pim_project.isdelete AS isdelete,
t_pim_project_construction.construction_status_id AS constructionStatusId,
t_pim_project_state.construction_status AS constructionStatus
FROM
t_pim_project
LEFT JOIN t_pim_project_construction
ON t_pim_project.id = t_pim_project_construction.project_id
LEFT JOIN t_pim_project_state
ON t_pim_project_construction.construction_status_id = t_pim_project_state.id
WHERE
id IN (${pepProjectIds.join(',')})`
).toPromise() : [];
return {
pepProjectRes, bindRes
}
} catch (error) {
console.error(error);
}
}
return {
getAxyStructs
}
}

10
api/config.js

@ -43,6 +43,7 @@ args.option('clickHousePepEmis', 'clickHouse 项企数据库名称');
args.option('clickHouseProjectManage', 'clickHouse 项目管理数据库名称');
args.option('clickHouseVcmp', 'clickHouse 视频平台数据库名称');
args.option('clickHouseDataAlarm', 'clickHouse 视频平台数据告警库名称');
args.option('clickHouseIot', 'clickHouse IOT平台设备信息库名称');
args.option('confirmAlarmAnxinUserId', '确认告警时保存到 ES 的安心云的用户的 id');
@ -98,6 +99,7 @@ const CLICKHOUST_PEP_EMIS = process.env.CLICKHOUST_PEP_EMIS || flags.clickHouseP
const CLICKHOUST_PROJECT_MANAGE = process.env.CLICKHOUST_PROJECT_MANAGE || flags.clickHouseProjectManage
const CLICKHOUST_VCMP = process.env.CLICKHOUST_VCMP || flags.clickHouseVcmp
const CLICKHOUST_DATA_ALARM = process.env.CLICKHOUST_DATA_ALARM || flags.clickHouseDataAlarm
const CLICKHOUST_IOT = process.env.CLICKHOUST_IOT || flags.clickHouseIot
const CONFIRM_ALARM_ANXIN_USER_ID = process.env.CONFIRM_ALARM_ANXIN_USER_ID || flags.confirmAlarmAnxinUserId
@ -118,7 +120,7 @@ if (
|| !API_IOT_AUTH
|| !QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK
|| !CLICKHOUST_URL || !CLICKHOUST_PORT
|| !CLICKHOUST_ANXINCLOUD || !CLICKHOUST_PEP_EMIS || !CLICKHOUST_PROJECT_MANAGE || !CLICKHOUST_VCMP || !CLICKHOUST_DATA_ALARM
|| !CLICKHOUST_ANXINCLOUD || !CLICKHOUST_PEP_EMIS || !CLICKHOUST_PROJECT_MANAGE || !CLICKHOUST_VCMP || !CLICKHOUST_DATA_ALARM || !CLICKHOUST_IOT
|| !CONFIRM_ALARM_ANXIN_USER_ID
|| !VCMP_APP_ID || !VCMP_APP_SECRET
) {
@ -150,7 +152,8 @@ const product = {
{ p: '/attachments/:p', o: 'POST' },
{ p: '/alarm/application/inspection', o: 'POST' },
{ p: '/project/app_list', o: 'GET' },
{ p: '/alarm/application/api', o: 'POST' }
{ p: '/alarm/application/api', o: 'POST' },
{ p: '/alarm/video/added_log', o: 'POST' }
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
anxinCloud: {
confirmAlarmAnxinUserId: CONFIRM_ALARM_ANXIN_USER_ID
@ -233,6 +236,9 @@ const product = {
}, {
name: 'dataAlarm',
db: CLICKHOUST_DATA_ALARM
}, {
name: 'iot',
db: CLICKHOUST_IOT
}
]
}

2
api/package.json

@ -22,7 +22,7 @@
"file-saver": "^2.0.2",
"fs-web-server-scaffold": "^2.0.2",
"ioredis": "^5.0.4",
"kafka-node": "^2.2.3",
"kafka-node": "^5.0.0",
"koa-convert": "^1.2.0",
"koa-proxy": "^0.9.0",
"moment": "^2.24.0",

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

@ -45,6 +45,25 @@ const Control = (props) => {
const exhibition = useRef({ workbench: [], statistical: [] }) //
const FormApi = useRef()
// websocket 使
useEffect(() => {
if (socket) {
socket.on('alarmSendSocket', function (msg) {
//console.info(msg);
if (msg.type == "alarmAppear") {//
} else if (msg.type == "alarmConfirm") {//
} else if (msg.type == "alarmNotice") {//
}
});
return () => {
socket.off("alarmSendSocket");
}
}
}, [socket])
useEffect(() => {
consoleToollink()
@ -928,7 +947,7 @@ function mapStateToProps (state) {
actions: global.actions,
pepProjectId: global.pepProjectId,
// members: members.data,
// socket: webSocket.socket
socket: webSocket.socket
};
}

2
web/client/src/sections/service/actions/emPush.js

@ -10,7 +10,7 @@ export function getPush (query) { //获取推送配置列表
actionType: 'GET_PUSH',
url: `${ApiTable.getPush}`,
msg: { error: '获取推送配置列表失败' },
reducer: { name: '' }
reducer: { name: "getPush", params: { noClear: true } },
});
}

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

@ -378,7 +378,6 @@ function pushModal (props) {
labelAlign="right"
labelWidth="120px"
onValueChange={(values, field) => {
console.log('values', values);
for (var key in field) {
if (key == 'tactics') {
if (values.tactics == 'abnormal_rate') {

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

@ -7,7 +7,6 @@ import moment from "moment";
import PushModal from '../components/pushModal'
import '../style.less'
import { Setup } from "$components";
// import { set } from 'nprogress';
const EmPush = (props) => {
const form = useRef();//
@ -15,22 +14,13 @@ const EmPush = (props) => {
const { service } = actions;
const [setup, setSetup] = useState(false); //
const [setupp, setSetupp] = useState([]);//
const [tableSetup, setTableSetup] = useState([]); //
const [query, setQuery] = useState({ limit: 10, page: 0 }); //
const [limits, setLimits] = useState()//
const mylimits = useRef(); //
const [pushModal, setPushModal] = useState(false) //
const [pushEdit, setPushEdit] = useState(false) //
const [anxincloudList, setAnxincloudList] = useState([]) //
const [peplist, setPeplist] = useState([]) //PEP
const [appList, setAppList] = useState([]) //
const [pepProjectId, setPepProjectId] = useState() //id
const [anxincloudArr, setAnxincloudArr] = useState([]) //
const [pepname, setPepname] = useState() //
const [anxinDelete, setAnxinDelete] = useState([]) //,
const [appArr, setAppArr] = useState([]) //
const [bindId, setBindId] = useState() //id
const [tableKey, setTableKey] = useState([]) //id
const [change, setChange] = useState(false) //
const [allTableData, setAllTableData] = useState([]) //
const [editObj, setEditObj] = useState({});//
const [projectStatus, setProjectStatus] = useState([]); //
const page = useRef(query.page);//
@ -82,16 +72,6 @@ const EmPush = (props) => {
const [tableData, setTableData] = useState([]) //
useEffect(() => {
// dispatch(service.getPush(query)).then((res) => {//
// console.log('res.payload.datares.payload.data',res.payload.data);
// setAnxincloudList(res.payload.data)
// })
// dispatch(install.getProjectPmanage(query)).then((res) => {//PEP
// setPeplist(res.payload.data)
// })
// dispatch(install.getProjectAppList(query)).then((res) => {//
// setAppList(res.payload.data)
// })
localStorage.getItem(EMPUSH) == null
? localStorage.setItem(
EMPUSH,
@ -99,23 +79,28 @@ const EmPush = (props) => {
)
: "";
getProjectStatusList()
getPushList(query);
}, [])
useEffect(() => {
getPushList();
}, [query]);
let showTableData = JSON.parse(JSON.stringify(allTableData)).slice(query.page * query.limit, (query.page + 1) * query.limit)
setTableData(showTableData)
mylimits.current = showTableData.length
}, [change]);
function getPushList () {
function getPushList (query) {
let val = form.current.getValues()
// , ...query
dispatch(service.getPush({ ...val })).then((res) => {//
if (res.success) {
let mytableData = JSON.parse(JSON.stringify(res.payload.data));
for (let index = 0; index < mytableData.length; index++) {
mytableData[index].key = String(mytableData[index].id)
}
setTableData(mytableData)
setAllTableData(mytableData)
let showTableData = mytableData.slice(query.page * query.limit, (query.page + 1) * query.limit)
setTableData(showTableData)
setQuery(query)
setLimits(res.payload.data.length)
mylimits.current = res.payload.data.length
mylimits.current = showTableData.length
}
})
}
@ -138,37 +123,9 @@ const EmPush = (props) => {
<div style={{ display: "flex" }}>
<Button
theme="borderless"
// disabled={row.pepProjectIsDelete == 1}
onClick={() => {
setEditObj(row)
setPushModal(true);
// setPepname(row.name)
// if (!row.name) {
// setPepProjectId(row.pepProjectId)
// }
// else {
// setPepProjectId()
// }
// let myanxinArr = []
// let anxinErrorList = []
// for (let i = 0; i < row.anxinProject.length; i++) {
// if (row.anxinProject[i].projectState !== -1) {
// myanxinArr.push(row.anxinProject[i].id)
// }
// else {
// anxinErrorList.push(row.anxinProject[i].name)
// }
// }
// setAnxinDelete(anxinErrorList)
// let myapparr = []
// let myarrarrList = JSON.parse(JSON.stringify(row.apps))
// for (let j = 0; j < row.apps.length; j++) {
// delete myarrarrList[j].projectApp
// myapparr.push(JSON.stringify(myarrarrList[j]))
// }
// setAppArr(myapparr)
// setAnxincloudArr(myanxinArr)
// setBindId(row.id)
setPushEdit(true)
}}
>
@ -180,7 +137,7 @@ const EmPush = (props) => {
style={{ color: '#F31C1C' }}
onClick={() => {
dispatch(service.putPushPushId({ pushId: row?.id, del: false, disable: false, msg: '更改推送配置状态' })).then(() => {
// setQuery({ limit: 10, page: page.current })
getPushList({ limit: query.limit, page: page.current });
})
}}
>
@ -194,7 +151,7 @@ const EmPush = (props) => {
position="topRight"
onConfirm={() => {
dispatch(service.putPushPushId({ pushId: row?.id, del: false, disable: true, msg: '更改推送配置状态' })).then(() => {
// setQuery({ limit: 10, page: page.current })
getPushList({ limit: query.limit, page: page.current });
})
}}
>
@ -208,11 +165,11 @@ const EmPush = (props) => {
position="topRight"
onConfirm={() => {
dispatch(service.putPushPushId({ pushId: row?.id, del: true, disable: false, msg: '删除推送配置' })).then(() => {
// if (page.current > 0 && mylimits.current < 2) {
// setQuery({ limit: 10, page: page.current - 1 })
// } else {
// setQuery({ limit: 10, page: page.current })
// }
if (page.current > 0 && mylimits.current < 2) {
getPushList({ limit: query.limit, page: page.current - 1 });
} else {
getPushList({ limit: query.limit, page: page.current });
}
})
}}
>
@ -577,7 +534,6 @@ const EmPush = (props) => {
<div style={{ marginRight: 20, display: 'flex', alignItems: 'center' }} className='myempush'>
<Form
onSubmit={(values) => console.log(values)}
// onValueChange={values=>console.log(values)}
getFormApi={(formApi) => (form.current = formApi)}
layout="horizontal"
style={{ position: "relative", width: "100%", flex: 1 }}
@ -644,7 +600,7 @@ const EmPush = (props) => {
border: '1px solid #005ABD'
}}
onClick={() => {
getPushList()
getPushList({ limit: query.limit, page: 0 })
}}
>
查询
@ -676,8 +632,8 @@ const EmPush = (props) => {
</div>
<div style={{ marginTop: 20 }}>
<Skeleton
// loading={loading}
loading={false}
loading={loading}
// loading={false}
active={true}
placeholder={SkeletonScreen()}
>
@ -689,7 +645,7 @@ const EmPush = (props) => {
hideExpandedColumn={false}
empty="暂无数据"
expandedRowRender={expandRowRender}
// pagination={false}
pagination={false}
onRow={handleRow}
/>
</Skeleton>
@ -715,6 +671,7 @@ const EmPush = (props) => {
onChange={(currentPage, pageSize) => {
setQuery({ limit: pageSize, page: currentPage - 1 });
page.current = currentPage - 1
setChange(!change)
}}
/>
</div>
@ -732,7 +689,7 @@ const EmPush = (props) => {
}}
close={() => {
setPushModal(false);
getPushList()
getPushList(query)
}} >
</PushModal> : ''
}
@ -753,9 +710,9 @@ const EmPush = (props) => {
}
function mapStateToProps (state) {
const { auth, global, ProjectPoms } = state;
const { auth, global, getPush } = state;
return {
// loading: ProjectPoms.isRequesting,
loading: getPush.isRequesting,
user: auth.user,
actions: global.actions,
// members: members.data,

Loading…
Cancel
Save