wuqun 2 years ago
parent
commit
472da0d4df
  1. 4
      api/app/lib/controllers/push/config.js
  2. 205
      api/app/lib/schedule/alarms_push.js
  3. 10
      api/app/lib/utils/dataRange.js
  4. 62
      api/app/lib/utils/push.js
  5. 21
      web/client/src/sections/service/actions/emPush.js
  6. 209
      web/client/src/sections/service/components/pushModal.jsx
  7. 339
      web/client/src/sections/service/containers/emPush.jsx
  8. 10
      web/client/src/sections/service/style.less
  9. 1
      web/client/src/utils/webapi.js

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

@ -7,7 +7,7 @@ async function list (ctx) {
const sequelize = ctx.fs.dc.ORM;
const { clickHouse } = ctx.app.fs
const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
const { keyword, keywordTarget, alarmType, state, tactics, pomsProjectId } = ctx.request.body
const { keyword, keywordTarget, alarmType, state, tactics, pomsProjectId } = ctx.query
let findOption = {
where: {
@ -42,7 +42,7 @@ async function list (ctx) {
findOption.where.alarmType = { $contains: [alarmType] }
}
if (state) {
if (state == 'enable') {
if (state == 'enable' || state == 'notYet' || state == 'takeEffect') {
findOption.where.disable = false
} else if (state == 'disable') {
findOption.where.disable = true

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

@ -53,11 +53,11 @@ module.exports = function (app, opts) {
for (let { dataValues: c } of configListRes) {
if (c.tacticsParams && c.tactics) {
const { projectCorrelation, strucId, pomsProjectId } = c
const { projectCorrelation, strucId, pomsProjectId, } = c
const { interval, deviceProportion } = c.tacticsParams
if (
curMinOfYear % interval == 0
curMinOfYear % parseInt(interval) == 0
) {
const corPepProject = projectCorrelation.pepProjectId ?
pepProjectRes.find(p => p.id == projectCorrelation.pepProjectId)
@ -69,18 +69,63 @@ module.exports = function (app, opts) {
&& c.timeType.some(ct => ct == corPepProject.construction_status_id)
)
) {
// TODO 查当前 poms 下的结构物 并把不包含的去掉
const { anxinProjectId, pepProjectId } = projectCorrelation
const strucListRes = strucId.length ? await clickHouse.anxinyun.query(`
SELECT id, iota_thing_id, name
FROM t_structure
WHERE id IN (${strucId.join(',')})
`).toPromise() : []
// 查当前 poms 下的结构物 并把不包含的去掉
// 可能有结构物已解绑
const strucListRes = strucId.length && anxinProjectId.length ?
await clickHouse.anxinyun.query(
`
SELECT
DISTINCT id,
t_structure.id AS id,
t_structure.name AS name,
t_structure.iota_thing_id AS iotaThingId
FROM
t_project
LEFT JOIN
t_project_structure
ON t_project_structure.project = t_project.id
LEFT JOIN
t_project_structuregroup
ON t_project_structuregroup.project = t_project.id
LEFT JOIN
t_structuregroup_structure
ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup
LEFT JOIN
t_project_construction
ON t_project_construction.project = t_project.id
LEFT JOIN
t_structure_site
ON t_structure_site.siteid = t_project_construction.construction
RIGHT JOIN
t_structure
ON t_structure.id = t_project_structure.structure
OR t_structure.id = t_structuregroup_structure.structure
OR t_structure.id = t_structure_site.structid
WHERE
project_state != -1
AND t_project.id IN (${anxinProjectId.join(',')})
AND t_structure.id IN (${strucId.join(',')})
`
).toPromise() :
[]
let searchStrucIds = strucListRes.map(s => s.id)
if (searchStrucIds.length) {
searchStrucIds.unshift(-1)
}
let pepProjectName = pepProjectId ?
pepProjectRes.find(p => p.id == pepProjectId).project_name
: projectCorrelation.name
let emailTitle = `${pepProjectName}-${c.name}-`
let emailSubTitle = ''
let dataAlarmOption = []
let dataAlarmGroupOption = []
let dataAlarms = []
let videoAlarmWhereOption = []
let videoAlarms = []
let appAlarmWhereOption = {
@ -89,17 +134,26 @@ module.exports = function (app, opts) {
let appAlarms = []
let deviceCount = 0
let alarmDeviceCount = 0
let cameraCount = 0
let alarmCameraCount = 0
// 判断推送策略
let pointTime = moment().subtract(interval, 'minute').format('YYYY-MM-DD HH:mm:ss')
let nowTime = moment().startOf('minute')
let pointTime = moment().subtract(parseInt(interval), 'minute').startOf('minute').format('YYYY-MM-DD HH:mm:ss')
if (c.tactics == 'immediately') {
dataAlarmOption.push(`StartTime >= '${pointTime}'`);
appAlarmWhereOption.createTime = { $gte: pointTime }
// dataAlarmOption.push(`StartTime >= '${pointTime}'`);
// appAlarmWhereOption.createTime = { $gte: pointTime }
// videoAlarmWhereOption.push(`camera_status_alarm.create_time >= '${pointTime}'`)
emailTitle += `即时推送服务`
emailSubTitle += `截止${moment(pointTime).format('YYYY年MM月DD日 HH时mm分')}-${moment(nowTime).format('HH时mm分')}`
} else if (c.tactics == 'continue') {
dataAlarmOption.push(`StartTime <= '${pointTime}'`);
appAlarmWhereOption.createTime = { $lte: pointTime }
// dataAlarmOption.push(`StartTime <= '${pointTime}'`);
// appAlarmWhereOption.createTime = { $lte: pointTime }
// videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`)
emailTitle += `持续时长推送服务`
emailSubTitle += `告警持续时长超${interval}分钟的告警源,详情如下:`
} else if (c.tactics == 'abnormal_rate') {
dataAlarmOption.push(`StartTime <= '${pointTime}'`);
// dataAlarmOption.push(`StartTime <= '${pointTime}'`);
if (c.alarmType.includes('data_outages') || c.alarmType.includes('data_exception')) {
// 查了设备异常率 去安心云查当前项目下的设备数量
// TODO 等同步以太数据再查
@ -110,15 +164,19 @@ module.exports = function (app, opts) {
}
if (c.alarmType.includes('video_exception')) {
// 查了视频异常 去安心云查 接入的 萤石 设备数量
cameraCount = strucId.length ?
cameraCount = searchStrucIds.length ?
(await clickHouse.anxinyun.query(`
SELECT count(*) AS count FROM t_video_ipc
WHERE structure IN (${strucId.join(',')})
WHERE structure IN (${searchStrucIds.join(',')})
`).toPromise())[0].count
: 0
}
appAlarmWhereOption.createTime = { $lte: pointTime }
// appAlarmWhereOption.createTime = { $lte: pointTime }
// videoAlarmWhereOption.push(`camera_status_alarm.create_time <= '${pointTime}'`)
emailTitle += `异常率推送服务`
emailSubTitle += `持续产生时间超过${interval}分钟的异常设备数量${interval}个,异常率达到项目或结构物内设备总数量${deviceCount + cameraCount}个的${interval}%,详情如下`
}
emailTitle += '——POMS飞尚运维中台'
// 判断告警数据范围
if (c.alarmType.includes('data_outages')) {
@ -131,7 +189,7 @@ module.exports = function (app, opts) {
dataAlarmGroupOption.push(3)
}
if (c.alarmType.includes('video_exception')) {
videoAlarms = strucId.length ? await clickHouse.vcmp.query(
videoAlarms = searchStrucIds.length ? await clickHouse.vcmp.query(
`
SELECT
cameraAlarm.cameraId AS cameraId,
@ -167,13 +225,15 @@ module.exports = function (app, opts) {
INNER JOIN ${anxinyun}.t_video_ipc
ON toString(${anxinyun}.t_video_ipc.channel_no) = camera_status_alarm.channel_no
AND ${anxinyun}.t_video_ipc.serial_no = camera_status_alarm.serial_no
AND ${anxinyun}.t_video_ipc.structure IN (${strucId.join(',')})
AND ${anxinyun}.t_video_ipc.structure IN (${searchStrucIds.join(',')})
INNER JOIN camera
ON camera.serial_no = camera_status_alarm.serial_no
AND camera.channel_no = camera_status_alarm.channel_no
AND camera.delete = false
AND camera.recycle_time is null
WHERE camera_status_alarm.confirm_time IS null
WHERE
camera_status_alarm.confirm_time IS null
${videoAlarmWhereOption.length ? ` AND ${videoAlarmWhereOption.join(' AND ')}` : ''}
) AS cameraAlarm
LEFT JOIN camera_status
ON cameraAlarm.platform = camera_status.platform
@ -186,7 +246,7 @@ module.exports = function (app, opts) {
AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo
LEFT JOIN ${anxinyun}.t_structure AS anxinStruc
ON anxinStruc.id = anxinIpc.structure
AND anxinStruc.id IN (${strucId.join(',')})
AND anxinStruc.id IN (${searchStrucIds.join(',')})
LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation
ON anxinIpcStation.ipc = anxinIpc.id
LEFT JOIN ${anxinyun}.t_sensor AS anxinStation
@ -275,21 +335,120 @@ module.exports = function (app, opts) {
dataAlarmGroupOption.push(4)
dataAlarmGroupOption.push(5)
}
//
if (dataAlarmGroupOption.length) {
// 查数据告警 三警合一
if (dataAlarmGroupOption.length && searchStrucIds.length) {
dataAlarmGroupOption.push(-1)
dataAlarmOption.push(`AlarmGroup IN (${dataAlarmGroupOption.join(',')})`)
dataAlarms = await clickHouse.dataAlarm.query(`
SELECT * FROM alarms
WHERE
State NOT IN (3, 4)
AND StructureId IN (${searchStrucIds.join(',')})
${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''}
`).toPromise()
console.log(dataAlarms);
}
let dataAlarmTitle = [{
n: '项目',
k: '',
v: pepProjectName
}, {
n: '结构物',
k: '',
f: (d) => {
return (strucListRes.find(s => s.id == d.StructureId) || {}).name
}
}, {
n: '告警源名称',
k: 'SourceName'
}, {
n: '告警源类型',
k: '',
f: (d) => {
switch (d.SourceTypeId) {
case 0:
return 'DTU'
case 1:
return '传感器'
case 2:
return '测点'
default:
return ''
}
}
}, {
n: '告警信息',
k: 'AlarmContent'
}, {
n: '告警等级(当前)',
k: '',
f: (d) => {
switch (d.CurrentLevel) {
case 1:
return '一级'
case 2:
return '二级'
case 3:
return '三级'
default:
return ''
}
}
}, {
n: '持续时间',
k: '',
f: (d) => {
return d.StartTime ?
'超过' + moment().diff(moment(d.StartTime), 'minutes') + '分钟' : ''
}
},]
let html = ''
let tableTitlePostfix = ',详情如下:'
function packageTableTitle (tArr) {
}
if (c.alarmType.includes('data_outages')) {
let tableTitlePrefix = '数据中断告警源'
let newAddCount = 0
let alarmHtml = ''
if (c.tactics == 'immediately') {
for (let a of dataAlarms.filter(d => d.AlarmGroup == 1)) {
alarmHtml += 1
}
tableTitlePrefix += 1
} else if (c.tactics == 'continue') {
} else if (c.tactics == 'abnormal_rate') {
}
}
if (c.alarmType.includes('data_exception')) {
}
if (c.alarmType.includes('strategy_hit')) {
}
if (c.alarmType.includes('video_exception')) {
}
if (c.alarmType.includes('app_exception')) {
}
if (c.alarmType.includes('device_exception')) {
}
await pushByEmail({
email: ['1650192445@qq.com'],
title: emailTitle,
text: '',
html: ''
})
}
}
}
}

10
api/app/lib/utils/dataRange.js

@ -207,6 +207,16 @@ module.exports = function (app, opts) {
let pomsProject = []
for (let b of bindRes) {
if (
keywordTarget == 'pepProject' && keyword
&& (
!b.name && !(
b.pepProjectId && pepProjectRes.some(pp => pp.id == b.pepProjectId)
)
)
) {
continue
}
if (b.pepProjectId) {
let corPepProject = pepProjectRes.find(pp => pp.id == b.pepProjectId) || {}
pomsProject.push({

62
api/app/lib/utils/push.js

@ -0,0 +1,62 @@
'use strict';
const moment = require('moment')
const Core = require('@alicloud/pop-core');
const nodemailer = require('nodemailer')
module.exports = function (app, opts) {
const pushBySms = async ({ phone = [], templateCode, templateParam } = {}) => {
try {
if (phone.length) {
const client = new Core({
accessKeyId: opts.sms.accessKey,
accessKeySecret: opts.sms.accessSecret,
endpoint: 'http://dysmsapi.aliyuncs.com',//固定
apiVersion: '2017-05-25'//固定
});
const SendSmsRes = await client.request('SendSms', {
"PhoneNumbers": phone.join(','),//接收短信的手机号码。
"SignName": "飞尚尚视",//短信签名名称。必须是已添加、并通过审核的短信签名。
"TemplateCode": templateCode,//短信模板ID。必须是已添加、并通过审核的短信签名;且发送国际/港澳台消息时,请使用国际/港澳台短信模版。
"TemplateParam": JSON.stringify(templateParam)//短信模板变量对应的实际值,JSON格式。
}, {
method: 'POST'
});
return SendSmsRes
}
} catch (error) {
throw error
}
}
const pushByEmail = async ({ email = [], title, text = '', html = '', attachments = undefined, } = {}) => {
try {
let transporter = nodemailer.createTransport({
host: opts.email.host,
port: opts.email.port,
secure: true,
auth: {
user: opts.email.sender.address,
pass: opts.email.sender.password,
}
});
// send mail with defined transport object
await transporter.sendMail({
from: `${opts.email.sender.name}<${opts.email.sender.address}>`, // sender address
to: email.join(','), // list of receivers 逗号分隔字符串
subject: title, // Subject line
text: text, // plain text body
html: html, // html body
attachments: attachments
});
} catch (error) {
throw error
}
}
return {
pushByEmail,
pushBySms,
}
}

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

@ -2,10 +2,11 @@
import { ApiTable, basicAction } from '$utils'
export function getPush () { //获取推送配置列表
export function getPush (query) { //获取推送配置列表
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query,
actionType: 'GET_PUSH',
url: `${ApiTable.getPush}`,
msg: { error: '获取推送配置列表失败' },
@ -72,3 +73,21 @@ export function getProjectStatus (query) {//获取项目状态列表
reducer: { name: "ProjectStatus", params: { noClear: true } },
});
}
export function putPushPushId (data) {//更改推送配置状态(禁用或删除)
let pushId = ''
let msg = ''
if (data) {
pushId = data.pushId
msg = data.msg
}
return (dispatch) =>
basicAction({
type: "put",
dispatch: dispatch,
data,
actionType: "PUT_PUSH_PUSHID",
url: `${ApiTable.putPushPushId.replace("{pushId}", pushId)}`,
msg: { option: msg }, //更改推送配置状态(禁用或删除)
reducer: {},
});
}

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

@ -19,10 +19,6 @@ function pushModal (props) {
} = props;
const { service } = actions;
const form = useRef();//
const [disablePeople, setDisablePeople] = useState(true); //
const [peopleList, setPeopleList] = useState([]); //List
const [departmentId, setDepartmentId] = useState(); //id
const [peopleId, setPeopleId] = useState(); //id
const [abnormal, setAbnormal] = useState(false); //disable
const [usersList, setUsersList] = useState([]); //
const [structure, setStructure] = useState(true); //disable
@ -30,27 +26,38 @@ function pushModal (props) {
const [projectStructure, setProjectStructure] = useState([]); //
const [timeTypeDis, setTimeTypeDis] = useState(true); //disable
const [projectStatus, setProjectStatus] = useState([]); //
const timeTypePOMS = useRef([]);//
const [interval1, setInterval1] = useState(undefined); //
const [interval2, setInterval2] = useState(undefined); //
const [interval3, setInterval3] = useState(undefined); //
const [deviceProportion, setDeviceProportion] = useState(undefined); //
//
useEffect(() => {
// if (editObj.id) {
// let departmentList = []
// for (let i = 0; i < pepList.length; i++) {
// if (pepList[i].id == editObj.departments[0].id) {
// departmentList = pepList[i].users
// }
// }
// setPeopleList(departmentList)
// setDepartmentId(editObj.departments[0].id)
// setPeopleId(editObj.pepUserId)
// setDisablePeople(false)
// }
getOrganizationUsersList()//
getProjectPomsList()//
if (editObj.id) {
getProjectStructureList(editObj.pomsProjectId)
if (editObj.pomsProject?.pepProjectId) {
getProjectStatusList()//
}
else {
setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }])
timeTypePOMS.current = ['POMS']
}
if (editObj.tactics == 'immediately') {
setInterval1(editObj.tacticsParams?.interval)
} else if (editObj.tactics == 'continue') {
setInterval2(editObj.tacticsParams?.interval)
} else if (editObj.tactics == 'abnormal_rate') {
setInterval3(editObj.tacticsParams?.interval)
setDeviceProportion(editObj.tacticsParams?.deviceProportion)
}
}
}, []);
function getOrganizationUsersList () {//
dispatch(service.getOrganizationUsers()).then((res) => {
@ -100,7 +107,124 @@ function pushModal (props) {
.validate()
.then((values) => {
if (pushEdit) {
dispatch(service.postPush({ pushId: pushId, pepUserId: values.pepUserId, msg: '编辑推送配置' })).then((res) => {//(PEP)
let obj = JSON.parse(JSON.stringify(values))
if (obj.timeType[0] == 'POMS') {
obj.timeType = []
}
let regu = /^[0-9]*[1-9][0-9]*$/;
if (obj.tactics == 'immediately') {
if (obj.interval1) {
if (regu.test(obj.interval1)) {
if (obj.interval1 <= 1440) {
obj.tacticsParams = {
interval: obj.interval1
}
delete obj.interval1
delete obj.interval2
delete obj.interval3
delete obj.deviceProportion
} else {
Notification.error({
content: '即时推送时间不能大于1440分钟',
duration: 2,
})
}
} else {
Notification.error({
content: '即时推送时间应为正整数',
duration: 2,
})
}
} else {
Notification.error({
content: '即时推送时间应为正整数',
duration: 2,
})
}
}
else if (obj.tactics == 'continue') {
if (obj.interval2) {
if (regu.test(obj.interval2)) {
if (obj.interval2 <= 1440) {
obj.tacticsParams = {
interval: obj.interval2
}
delete obj.interval1
delete obj.interval2
delete obj.interval3
delete obj.deviceProportion
} else {
Notification.error({
content: '持续时长推送时间不能大于1440分钟',
duration: 2,
})
}
} else {
Notification.error({
content: '持续时长推送时间应为正整数',
duration: 2,
})
}
} else {
Notification.error({
content: '持续时长推送时间应为正整数',
duration: 2,
})
}
}
else {
if (regu.test(obj.interval3) && regu.test(obj.deviceProportion)) {
if (obj.interval3 <= 720 && obj.deviceProportion <= 100) {
obj.tacticsParams = {
interval: obj.interval3,
deviceProportion: obj.deviceProportion
}
delete obj.interval1
delete obj.interval2
delete obj.interval3
delete obj.deviceProportion
} else if (obj.interval3 <= 720 && obj.deviceProportion > 100) {
Notification.error({
content: '异常率推送异常率不能超过100%',
duration: 2,
})
} else if (obj.interval3 > 720 && obj.deviceProportion <= 100) {
Notification.error({
content: '异常率推送时间不能超过720小时',
duration: 2,
})
} else {
Notification.error({
content: '异常率推送时间不能超过720小时',
duration: 2,
})
Notification.error({
content: '异常率推送异常率不能超过100%',
duration: 2,
})
}
} else if (regu.test(obj.interval3) && !regu.test(obj.deviceProportion)) {
Notification.error({
content: '异常率推送异常率应为正整数',
duration: 2,
})
} else if (!regu.test(obj.interval3) && regu.test(obj.deviceProportion)) {
Notification.error({
content: '异常率推送时间应为正整数',
duration: 2,
})
} else {
Notification.error({
content: '异常率推送异常率应为正整数',
duration: 2,
})
Notification.error({
content: '异常率推送时间应为正整数',
duration: 2,
})
}
}
dispatch(service.postPush({ pushId: pushId, ...obj, msg: '编辑推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
}
@ -108,7 +232,7 @@ function pushModal (props) {
}
else {
let obj = JSON.parse(JSON.stringify(values))
if (obj.timeType[0] == null) {
if (obj.timeType[0] == 'POMS') {
obj.timeType = []
}
let regu = /^[0-9]*[1-9][0-9]*$/;
@ -224,7 +348,6 @@ function pushModal (props) {
})
}
}
console.log('obj', obj);
dispatch(service.postPush({ ...obj, msg: '新增推送配置' })).then((res) => {//(PEP)
if (res.success) {
close();
@ -274,35 +397,14 @@ function pushModal (props) {
getProjectStatusList()//
}
else {
setProjectStatus([{ construction_status: 'POMS', id: null }])
form.current.setValue('timeType', [null])
setProjectStatus([{ construction_status: 'POMS', id: 'POMS' }])
form.current.setValue('timeType', ['POMS'])
form.current.validate()
}
}
}
}
}
// for (var key in field) {
// if (key == 'department') {
// if (values.department >= 0) {
// let departmentList = []
// for (let i = 0; i < pepList.length; i++) {
// if (pepList[i].id == values.department) {
// departmentList = pepList[i].users
// }
// }
// setPeopleList(departmentList)
// setDisablePeople(false)
// form.current.setValue('pepUserId', undefined);
// }
// else {
// setPeopleList([])
// setDisablePeople(true)
// form.current.setValue('pepUserId', undefined);
// }
// }
// }
}}
getFormApi={(formApi) => (form.current = formApi)}
>
@ -314,7 +416,7 @@ function pushModal (props) {
field="name"
label='策略名称:'
style={{ width: 695 }}
// initValue={editObj?.name || ""}
initValue={editObj?.name || ""}
placeholder="请输入策略名称"
showClear
rules={[{ required: true, message: "请输入策略名称" }]} />
@ -326,7 +428,7 @@ function pushModal (props) {
placeholder="请选择项目"
style={{ width: 695 }}
rules={[{ required: true, message: "请选择项目" }]}
// initValue={departmentId || ""}
initValue={editObj?.pomsProjectId || ""}
filter
>
{
@ -347,7 +449,7 @@ function pushModal (props) {
placeholder="请选择结构物"
style={{ width: 695 }}
rules={[{ required: true, message: "请选择结构物" }]}
// initValue={departmentId || ""}
initValue={editObj?.strucId || []}
disabled={structure}
filter
multiple
@ -371,6 +473,7 @@ function pushModal (props) {
label='推送策略配置:'
type='card'
direction='horizontal'
initValue={editObj?.tactics || ''}
rules={[{ required: true, message: '请选择推送策略' }]}>
<Form.Radio
value='immediately'
@ -381,7 +484,7 @@ function pushModal (props) {
field="interval1"
pure
style={{ width: 60, height: 20, color: '#1859C1' }}
initValue={editObj?.interval || "10"}
initValue={interval1 || "10"}
// rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "" }]}
/>
分钟内有告警源新增则通过信鸽服务发送一条通知信息
@ -399,7 +502,7 @@ function pushModal (props) {
field="interval2"
pure
style={{ width: 60, height: 20, color: '#1859C1' }}
initValue={editObj?.interval || "10"}
initValue={interval2 || "10"}
// rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "" }]}
/>
分钟则通过信鸽服务发送一条通知信息
@ -417,7 +520,7 @@ function pushModal (props) {
field="deviceProportion"
pure
style={{ width: 60, height: 20, color: '#1859C1' }}
initValue={editObj?.deviceProportion || "40"}
initValue={deviceProportion || "40"}
// rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "" }]}
/>
%且持续时长超过
@ -425,7 +528,7 @@ function pushModal (props) {
field="interval3"
pure
style={{ width: 60, height: 20, color: '#1859C1' }}
initValue={editObj?.interval || "2"}
initValue={interval3 || "2"}
// rules={[{ pattern: "^[0-9]*[1-9][0-9]*$", message: "" }]}
/>
小时则通过信鸽服务发送一条通知信息
@ -442,7 +545,7 @@ function pushModal (props) {
field="alarmType"
style={{ width: 695 }}
rules={[{ required: true, message: "监听问题模块" }]}
// initValue={departmentId || ""}
initValue={editObj?.alarmType || []}
direction='horizontal'
showClear
>
@ -464,7 +567,7 @@ function pushModal (props) {
placeholder="请选择通知时效"
style={{ width: 285 }}
rules={[{ required: true, message: "请选择通知时效" }]}
// initValue={departmentId || ""}
initValue={editObj?.timeType.length > 0 ? editObj?.timeType : timeTypePOMS.current}
disabled={timeTypeDis}
multiple
maxTagCount={3}
@ -485,7 +588,7 @@ function pushModal (props) {
placeholder="请选择接收人"
style={{ width: 285 }}
rules={[{ required: true, message: "请选择接收人" }]}
initValue={[user.id]}
initValue={editObj?.receiverPepUserId || [user.id]}
filter
multiple
maxTagCount={3}
@ -511,7 +614,7 @@ function pushModal (props) {
pure
direction='horizontal'
style={{ display: 'flex', justifyContent: 'space-evenly' }}
initValue={false || false}
initValue={editObj?.disable || false}
rules={[{ required: true, }]}>
<Form.Radio value={false}>
启用

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

@ -1,6 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
import { connect } from 'react-redux';
import { Skeleton, Button, Pagination, Form, Popconfirm, Table, Tooltip } from '@douyinfe/semi-ui';
import { IconSearch } from '@douyinfe/semi-icons';
import { SkeletonScreen, } from "$components";
import moment from "moment";
import PushModal from '../components/pushModal'
@ -30,6 +31,7 @@ const EmPush = (props) => {
const [appArr, setAppArr] = useState([]) //
const [bindId, setBindId] = useState() //id
const [tableKey, setTableKey] = useState([]) //id
const [editObj, setEditObj] = useState({});//
const [projectStatus, setProjectStatus] = useState([]); //
const page = useRef(query.page);//
const EMPUSH = "empush";
@ -37,9 +39,9 @@ const EmPush = (props) => {
{
title: '推送信息',
list: [
{ name: "关联项目", value: "projectName" },
{ name: "策略名称", value: "name" },
{ name: "创建时间", value: "createTime" },
{ name: "关联项目", value: "projectName" },
{ name: "接收人", value: "receiverPepUser" },
{ name: "推送方式", value: "pushType" },
{ name: "监听问题模块", value: "alarmType" },
@ -77,7 +79,6 @@ const EmPush = (props) => {
return {};
}
}
const [tableData, setTableData] = useState([]) //
useEffect(() => {
@ -91,31 +92,28 @@ const EmPush = (props) => {
// dispatch(install.getProjectAppList(query)).then((res) => {//
// setAppList(res.payload.data)
// })
getProjectStatusList()
localStorage.getItem(EMPUSH) == null
? localStorage.setItem(
EMPUSH,
JSON.stringify(['name', 'createTime', 'projectName', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable'])
JSON.stringify(['projectName', 'name', 'createTime', 'receiverPepUser', 'alarmType', 'timeType', 'tactics', 'disable'])
)
: "";
attribute();
getProjectStatusList()
}, [])
useEffect(() => {
getPushList();
}, [query]);
function getPushList () {
dispatch(service.getPush(query)).then((res) => {//
let val = form.current.getValues()
// , ...query
dispatch(service.getPush({ ...val })).then((res) => {//
if (res.success) {
// let mytableData = JSON.parse(JSON.stringify(res.payload.data.rows));
// let mytableKey = []
// for (let index = 0; index < mytableData.length; index++) {
// mytableData[index].key = mytableData[index].id
// mytableKey.push(mytableData[index].id)
// }
// setTableKey(mytableKey)
// setTableData(mytableData)
setTableData(res.payload.data)
let mytableData = JSON.parse(JSON.stringify(res.payload.data));
for (let index = 0; index < mytableData.length; index++) {
mytableData[index].key = String(mytableData[index].id)
}
setTableData(mytableData)
setLimits(res.payload.data.length)
mylimits.current = res.payload.data.length
}
@ -125,13 +123,14 @@ const EmPush = (props) => {
dispatch(service.getProjectStatus()).then((res) => {
if (res.success) {
setProjectStatus(res.payload?.data)
attribute(res.payload?.data);
}
})
}
const [columns, setColumns] = useState([//
const columns = [//
{
title: "操作",
width: "20%",
width: "12%",
dataIndex: "text",
key: 'text',
render: (_, row) => {
@ -140,8 +139,9 @@ const EmPush = (props) => {
<Button
theme="borderless"
// disabled={row.pepProjectIsDelete == 1}
// onClick={() => {
// setPushModal(true);
onClick={() => {
setEditObj(row)
setPushModal(true);
// setPepname(row.name)
// if (!row.name) {
// setPepProjectId(row.pepProjectId)
@ -169,25 +169,51 @@ const EmPush = (props) => {
// setAppArr(myapparr)
// setAnxincloudArr(myanxinArr)
// setBindId(row.id)
// setPushEdit(true)
// }}
setPushEdit(true)
}}
>
修改
</Button>
<Button theme="borderless">禁用</Button>
{row?.disable ? (
<Button
theme="borderless"
style={{ color: '#F31C1C' }}
onClick={() => {
dispatch(service.putPushPushId({ pushId: row?.id, del: false, disable: false, msg: '更改推送配置状态' })).then(() => {
// setQuery({ limit: 10, page: page.current })
})
}}
>
已禁用
</Button>
) : (
<Popconfirm
title="禁用后,通知策略将会失效。"
arrowPointAtCenter={false}
showArrow={true}
position="topRight"
onConfirm={() => {
dispatch(service.putPushPushId({ pushId: row?.id, del: false, disable: true, msg: '更改推送配置状态' })).then(() => {
// setQuery({ limit: 10, page: page.current })
})
}}
>
<Button theme="borderless">已启用</Button>
</Popconfirm>
)}
<Popconfirm
title="删除后对应的项目全局将无法进入和显示,对应的信鸽服务也会被禁用"
title="删除后通知策略将会失效。"
arrowPointAtCenter={false}
showArrow={true}
position="topRight"
onConfirm={() => {
// dispatch(install.deleteProjectBind({ bindId: row?.id, msg: '' })).then(() => {
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 })
// }
// })
})
}}
>
<Button theme="borderless">删除</Button>
@ -196,30 +222,29 @@ const EmPush = (props) => {
);
},
},
])
]
function expandRowRender (record, index) {
return (
<div style={{ display: 'flex' }}>
结构物
{
record.structure?.map((item, index) => {
return (
<div key={index} style={{ marginRight: 5, padding: '1px 17px', color: '#0F7EFB', background: 'rgba(221,237,255,0.38)' }}>
{item.name}
</div>
)
})
}
</div>
)
}
//
function attribute () {
function attribute (val) {
const arr = localStorage.getItem(EMPUSH)
? JSON.parse(localStorage.getItem(EMPUSH))
: [];
const column = [
{
title: '策略名称',
dataIndex: "name",
key: 'name',
render: (_, row) => {
return row.name
}
},
{
title: "创建时间",
dataIndex: "createTime",
key: "createTime",
render: (_, r, index) => {
return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss');
},
},
{
title: '关联项目',
dataIndex: "projectName",
@ -250,10 +275,63 @@ const EmPush = (props) => {
</Tooltip>
</div>
}
{
row.pomsProject?.pepProject?.projectName ? (
<div style={{
height: 18, marginLeft: 4,
background: 'linear-gradient(180deg, #EBF5FF 0%, #EBF5FF 0%, #D3E8FF 100%)',
borderRadius: 2, display: 'flex', alignItems: 'center'
}}>
<div>
<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>
)
})
}
</div>
</div>
) : (
<div style={{
height: 18, marginLeft: 4,
background: 'linear-gradient(180deg, #99C7DD 0%, #3048FC 100%)',
borderRadius: 2, display: 'flex', alignItems: 'center'
}}>
<div>
<img src="/assets/images/install/icon_POMS.png" alt="" style={{ height: 10, width: 10, marginLeft: 4, marginRight: 9 }} />
</div>
<div style={{ color: '#FFFFFF', fontSize: 11, marginRight: 12 }}>
POMS
</div>
</div>
)
}
</div>
)
}
},
{
title: '策略名称',
dataIndex: "name",
key: 'name',
render: (_, row) => {
return row.name
}
},
{
title: "创建时间",
dataIndex: "createTime",
key: "createTime",
render: (_, r, index) => {
return moment(r.createTime).format('YYYY-MM-DD HH:mm:ss');
},
},
{
title: '接收人',
dataIndex: "receiverPepUser",
@ -280,7 +358,7 @@ const EmPush = (props) => {
{
row.receiverPepUser.map((item, index) => {
return (
<div key={index} style={{ color: '#005ABD' }}>
<div key={index}>
{item.name},
</div>
)
@ -314,7 +392,6 @@ const EmPush = (props) => {
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
{
// alarmTypeObj
row.alarmType.map((item, index) => {
return (
<div className='myseparator' key={index} style={{ display: 'flex', alignItems: 'center' }}>
@ -355,9 +432,81 @@ const EmPush = (props) => {
title: "生效项目节点",
dataIndex: "timeType",
key: "timeType",
render: (_, r, index) => {
// projectStatus
return r.timeType[0]
render: (_, row, index) => {
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
{
row.timeType.length > 0 ? (
row.timeType.map((item, index) => {
return (
<div key={index} style={{
height: 18, marginLeft: 4,
background: 'linear-gradient(180deg, #EBF5FF 0%, #EBF5FF 0%, #D3E8FF 100%)',
borderRadius: 2, display: index > 1 ? 'none' : 'flex', alignItems: 'center'
}}>
<div>
<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 == item ? ite.construction_status : ''}
</div>
)
})
}
</div>
</div>
)
})
) : (
<div style={{
height: 18, marginLeft: 4,
background: 'linear-gradient(180deg, #99C7DD 0%, #3048FC 100%)',
borderRadius: 2, display: 'flex', alignItems: 'center'
}}>
<div>
<img src="/assets/images/install/icon_POMS.png" alt="" style={{ height: 10, width: 10, marginLeft: 4, marginRight: 9 }} />
</div>
<div style={{ color: '#FFFFFF', fontSize: 11, marginRight: 12 }}>
POMS
</div>
</div>
)
}
{
row.timeType.length > 2 ? (
<Tooltip content={
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
{
row.timeType.map((item, index) => {
return (
<div key={index}>
{
val.map((ite, idx) => {
return (
<span key={idx}>
{ite.id == item ? ite.construction_status : ''}
</span>
)
})
},
</div>
)
})
}
</div>
} trigger="click" style={{ lineHeight: 2 }}>
<div style={{ fontSize: 14, color: '#005ABD', marginLeft: 8, cursor: "pointer", }}>
+{row.timeType.length - 2}
</div>
</Tooltip>
) : ('')
}
</div>
)
},
},
{
@ -372,9 +521,31 @@ const EmPush = (props) => {
title: "启用状态",
dataIndex: "disable",
key: "disable",
render: (_, r, index) => {
// projectStatus
return r.disable
render: (_, row, index) => {
let enableType = ''
if (row.disable) {
enableType = '禁用'
}
else {
if (row.timeType.length > 0) {
for (let i = 0; i < row.timeType.length; i++) {
if (row.timeType[i] == row.pomsProject?.pepProject?.constructionStatusId) {
enableType = '已生效'
break;
} else {
enableType = '未生效'
}
}
}
else {
enableType = '已生效'
}
}
return (
<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)', }}>
{enableType}
</div>
)
},
},
{
@ -382,7 +553,7 @@ const EmPush = (props) => {
dataIndex: "pushCount",
key: "pushCount",
render: (_, r, index) => {
return r.pushCount
return r.pushCount + '次'
},
},
];
@ -403,7 +574,7 @@ const EmPush = (props) => {
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>EM推送</div>
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>Em push</div>
</div>
<div style={{ marginRight: 20, display: 'flex', alignItems: 'center' }} className='empush'>
<div style={{ marginRight: 20, display: 'flex', alignItems: 'center' }} className='myempush'>
<Form
onSubmit={(values) => console.log(values)}
// onValueChange={values=>console.log(values)}
@ -411,47 +582,53 @@ const EmPush = (props) => {
layout="horizontal"
style={{ position: "relative", width: "100%", flex: 1 }}
>
<Form.Select
pure
field="keywordTarget"
placeholder="请选择搜索类型"
style={{ width: 200 }}
initValue={"pepProject"}
>
<Form.Select.Option value='pepProject'>项目</Form.Select.Option>
<Form.Select.Option value='struc'>结构物</Form.Select.Option>
<Form.Select.Option value='tactics'>策略名</Form.Select.Option>
</Form.Select>
<Form.Input
label='搜索:'
field='seacth'
maxLength="16"
placeholder="搜项目、结构物或推送策略名"
labelPosition="left"
style={{ width: 292, marginRight: 10, marginBottom: 16 }}
suffix={<IconSearch />}
field="keyword"
pure
showClear
style={{ width: 260, marginLeft: 12, marginRight: 12 }}
placeholder="请输入或选择关键词"
/>
<Form.Select
label='推送类型:'
labelPosition="left"
field='pushType'
field='alarmType'
style={{ width: 116, marginRight: 10, color: "#F9F9F9", }}
placeholder="全部"
filter
showClear
>
{/* {.map((item) => {
return (
<Form.Select.Option key={item.value} value={item.value}>
{item.name}
</Form.Select.Option>
);
})} */}
<Form.Select.Option value="data_outages">数据中断</Form.Select.Option>
<Form.Select.Option value="data_exception">数据异常</Form.Select.Option>
<Form.Select.Option value="strategy_hit">策略命中</Form.Select.Option>
<Form.Select.Option value="video_exception">视频异常</Form.Select.Option>
<Form.Select.Option value="app_exception">应用异常</Form.Select.Option>
<Form.Select.Option value="device_exception">设备异常</Form.Select.Option>
</Form.Select>
<Form.Select
label='启用状态:'
labelPosition="left"
field='enableType'
field='state'
style={{ width: 116, marginRight: 10, color: "#F9F9F9", }}
placeholder="全部"
filter
showClear
>
{/* {.map((item) => {
return (
<Form.Select.Option key={item.value} value={item.value}>
{item.name}
</Form.Select.Option>
);
})} */}
<Form.Select.Option value='takeEffect'>已生效</Form.Select.Option>
<Form.Select.Option value='notYet'>未生效</Form.Select.Option>
<Form.Select.Option value='disable'>禁用</Form.Select.Option>
</Form.Select>
</Form>
<Button
@ -466,7 +643,9 @@ const EmPush = (props) => {
color: '#005ABD',
border: '1px solid #005ABD'
}}
// onClick={() => { }}
onClick={() => {
getPushList()
}}
>
查询
</Button>
@ -502,10 +681,13 @@ const EmPush = (props) => {
placeholder={SkeletonScreen()}
>
<Table
rowKey="name"
columns={setupp.filter((s) => s)}
dataSource={tableData}
bordered={false}
hideExpandedColumn={false}
empty="暂无数据"
expandedRowRender={expandRowRender}
pagination={false}
onRow={handleRow}
/>
@ -543,6 +725,7 @@ const EmPush = (props) => {
<PushModal
visible={true}
pushEdit={pushEdit}
editObj={editObj}
cancel={() => {
setPushModal(false);
}}
@ -558,7 +741,7 @@ const EmPush = (props) => {
tableList={tableList}
close={() => {
setSetup(false);
attribute();
attribute(projectStatus);
}}
/>
) : (

10
web/client/src/sections/service/style.less

@ -1,5 +1,13 @@
.empush{
.myempush{
.semi-input-wrapper{
margin-bottom: 0px !important;
}
}
.emPushTable{
// .semi-table-row-cell:first-child{
// display: flex !important;
// }
// .semi-table-row-cell{
// display: flex !important;
// }
}

1
web/client/src/utils/webapi.js

@ -54,6 +54,7 @@ export const ApiTable = {
getOrganizationUsers: "organization/users", //获取全部未删除用户
getProjectStructure: "project/structure", //获取绑定项目下结构物
getProjectStatus: "project/status", //获取项目状态列表
putPushPushId: "push/{pushId}", //更改推送配置状态(禁用或删除)
//控制台
consoleToollink: 'console/toollink', //常用工具

Loading…
Cancel
Save