wuqun 2 years ago
parent
commit
2b6b367e6c
  1. 273
      api/app/lib/controllers/control/analysis.js
  2. 7
      api/app/lib/routes/control/index.js
  3. 16
      web/client/src/sections/control/actions/control.js
  4. 22
      web/client/src/sections/control/containers/control.jsx
  5. 1
      web/client/src/sections/problem/components/tableData.jsx
  6. 4
      web/client/src/utils/webapi.js

273
api/app/lib/controllers/control/analysis.js

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const moment = require('moment'); const moment = require('moment');
async function dataList (ctx) { async function dataList (ctx) {
try { try {
const { models } = ctx.fs.dc; const { models } = ctx.fs.dc;
@ -82,12 +81,12 @@ async function dataList (ctx) {
} }
} }
async function userlist (ctx) { async function personnelApp (ctx) {
try { try {
const models = ctx.fs.dc.models; const models = ctx.fs.dc.models;
const { clickHouse } = ctx.app.fs const { clickHouse } = ctx.app.fs
const sequelize = ctx.fs.dc.orm; const sequelize = ctx.fs.dc.orm;
const { userId, pepUserId, userInfo, pepUserInfo } = ctx.fs.api
const { pepId } = ctx.query const { pepId } = ctx.query
const excludeField = ['lastInTime', 'inTimes', 'onlineDuration', 'lastInAddress', 'deleted', 'updateTime'] const excludeField = ['lastInTime', 'inTimes', 'onlineDuration', 'lastInAddress', 'deleted', 'updateTime']
@ -202,6 +201,80 @@ async function userlist (ctx) {
}) })
} }
let findOptions = {
where: {
del: false
},
order: [['updateTime', 'desc']],
attributes: ['id', 'pepProjectId', 'name', 'anxinProjectId'],
distinct: true,
include: {
model: models.App,
}
}
if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) {
findOptions.where.id = { $in: userInfo.correlationProject }
}
if (pepId) {
findOption.where.id = pepId
}
const proRes = await models.ProjectCorrelation.findAndCountAll(findOptions)
let pepProjectIds = new Set()
let anxinProjectIds = new Set()
for (let p of proRes.rows) {
if (p.pepProjectId) {
pepProjectIds.add(p.pepProjectId)
}
for (let ap of p.anxinProjectId) {
if (ap) {
anxinProjectIds.add(ap)
}
}
}
const pepProjectRess = pepProjectIds.size ?
await clickHouse.projectManage.query(
`
SELECT
t_pim_project.id AS id,
t_pim_project.project_name AS project_name,
t_pim_project.isdelete AS isdelete,
t_pim_project_construction.construction_status_id AS construction_status_id,
t_pim_project_state.construction_status AS construction_status
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() :
[]
for (let p of proRes.rows) {
const corPro = pepProjectRess.find(pp => pp.id == p.pepProjectId) || {}
p.dataValues.pepProjectName = corPro.project_name
p.dataValues.pepProjectIsDelete = corPro.isdelete
delete p.dataValues.anxinProjectId
}
let webApp = []
let appproRes = proRes.rows.filter(v => v.dataValues.pepProjectIsDelete != 1).map(r => {
if (r.dataValues.apps.length > 0) {
r.dataValues.apps.map(vv => {
if (webApp.map(n => n.name).indexOf(vv.dataValues.name)) {
webApp.push({ name: vv.dataValues.name, url: vv.dataValues.url })
}
})
}
})
let personnel = userRes.rows.filter(r => r.correlationProject.length > 0) let personnel = userRes.rows.filter(r => r.correlationProject.length > 0)
if (pepId) { if (pepId) {
@ -210,7 +283,8 @@ async function userlist (ctx) {
ctx.status = 200 ctx.status = 200
ctx.body = { ctx.body = {
personnel: personnel.map(v => v.dataValues.name) personnel: personnel.map(v => v.dataValues.name),
webApp: webApp
} }
} catch (error) { } catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
@ -221,10 +295,197 @@ async function userlist (ctx) {
} }
} }
async function problem (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const { utils: { pomsProjectRange, anxinStrucIdRange } } = ctx.app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { pepProjectId, limit = 50, page = 0 } = ctx.query
let anxinStruc = await anxinStrucIdRange({
ctx, pepProjectId
})
let pomsProject = await pomsProjectRange({
ctx, pepProjectId,
})
const pomsProjectIds = pomsProject.map(p => p.id)
let whereOption = []
if (anxinStruc.length) {
const anxinStrucIds = anxinStruc.map(a => a.strucId)
whereOption.push(`alarms.StructureId IN (${anxinStrucIds.join(",")})`)
const alarmRes = await clickHouse.dataAlarm.query(`
SELECT
AlarmId,State,AlarmGroup,AlarmGroupUnit,SourceName,StartTime,${anxinyun}.t_alarm_group_unit.name AS typeName
FROM alarms
LEFT JOIN ${anxinyun}.t_alarm_group_unit
ON ${anxinyun}.t_alarm_group_unit.id = alarms.AlarmGroupUnit
${whereOption.length ? 'WHERE ' + whereOption.join(' AND ') + ' AND ' + 'alarms.State < 3' : ''}
${limit ? 'LIMIT ' + limit : ''}
${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''}
`).toPromise();
alarmRes.forEach(ar => {
switch (ar.AlarmGroup) {
case 1:
ar.groupName = '数据中断'
ar.url = '/problem/dataAlarm/dataLnterrupt'
break;
case 2:
ar.groupName = '数据异常'
ar.url = '/problem/dataAlarm/dataAbnormal'
break;
case 3:
ar.groupName = '策略命中'
ar.url = '/problem/dataAlarm/strategyHit'
break;
default:
ar.groupName = '设备异常'
ar.url = '/problem/deviceAlarm/deviceAbnormal'
break;
}
})
const video = anxinStrucIds.length ? await clickHouse.vcmp.query(
`
SELECT
cameraAlarm.cameraId AS cameraId,
cameraAlarm.cameraName AS cameraName,
cameraAlarm.alarmId AS alarmId,
cameraAlarm.createTime AS createTime,
cameraAlarm.confirmTime AS confirmTime
FROM
(
SELECT
camera.id AS cameraId,
camera.name AS cameraName,
camera_status_alarm.id AS alarmId,
camera_status_alarm.create_time AS createTime,
camera_status_alarm.platform AS platform,
camera_status_alarm.status_id AS statusId,
camera_status_alarm.serial_no AS cameraSerialNo,
camera_status_alarm.channel_no AS cameraChannelNo,
camera_status_alarm.confirm_time AS confirmTime
FROM camera_status_alarm
INNER JOIN camera
ON camera.serial_no = camera_status_alarm.serial_no
AND camera.channel_no = camera_status_alarm.channel_no
WHERE
camera.delete = false
AND camera.recycle_time is null
AND alarmId IN (
SELECT camera_status_alarm.id AS alarmId
FROM camera_status_alarm
RIGHT JOIN ${anxinyun}.t_video_ipc
ON toString(${anxinyun}.t_video_ipc.channel_no) = camera_status_alarm.channel_no
AND ${anxinyun}.t_video_ipc.serial_no = camera_status_alarm.serial_no
${`WHERE ${anxinyun}.t_video_ipc.structure IN (${anxinStrucIds.join(',')})`}
)
${limit ? 'LIMIT ' + limit : ''}
${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''}
) AS cameraAlarm
LEFT JOIN camera_status
ON cameraAlarm.platform = camera_status.platform
AND cameraAlarm.statusId = camera_status.id
LEFT JOIN camera_status_resolve
ON camera_status_resolve.status_id = camera_status.id
LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc
ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo
AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo
LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation
ON anxinIpcStation.ipc = anxinIpc.id
WHERE
cameraAlarm.confirmTime is null
`
).toPromise() : []
let returnD = []
let positionD = {}
// 每个设备一个告警
for (let a of video) {
if (!positionD[a.cameraId]) {
let d = {
cameraId: a.cameraId,
SourceName: a.cameraName,
StartTime: a.createTime,
alarmId: a.alarmId,
}
returnD.push(d)
positionD[a.cameraId] = {
positionReturnD: returnD.length - 1
}
}
}
returnD.forEach(v => {
v.groupName = '视频异常'
v.url = '/problem/dataAlarm/videoAbnormal'
})
let findOption = {
where: {
'$app->projectCorrelations.id$': {
$in: pomsProjectIds
},
confirmTime: null
},
attributes: ['createTime', 'type'],
include: [{
model: models.App,
where: {
},
attributes: ['id', 'name'],
include: [{
model: models.ProjectCorrelation,
where: {
},
attributes: ['id'],
}]
}]
}
const listRes = await models.AppAlarm.findAndCountAll(findOption)
let app = listRes.rows.map(v => ({ StartTime: v.createTime, SourceName: v.app.name, type: v.type }))
let typeData = { element: '元素异常', apiError: "接口报错", timeout: '加载超时' }
app.forEach(v => {
v.groupName = '应用异常'
v.url = '/problem/useAlarm/useAbnormal',
v.typeName = typeData[v.type]
})
let sum = [...alarmRes, ...returnD, ...app]
sum.sort((a, b) => {
if (moment(a.StartTime).isBefore(b.StartTime)) {
return 1
} else {
return -1
}
})
ctx.status = 200;
ctx.body = sum
} else {
ctx.body =[]
}
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
}
}
}
module.exports = { module.exports = {
dataList, dataList,
userlist personnelApp,
}; problem
}

7
api/app/lib/routes/control/index.js

@ -20,8 +20,11 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/analysis/dataList'] = { content: '查询数据告警产生,确认数量', visible: true }; app.fs.api.logAttr['GET/analysis/dataList'] = { content: '查询数据告警产生,确认数量', visible: true };
router.get('/analysis/dataList', analysis.dataList); router.get('/analysis/dataList', analysis.dataList);
app.fs.api.logAttr['GET/analysis/userlist'] = { content: '查询关联人员', visible: true }; app.fs.api.logAttr['GET/analysis/userlist'] = { content: '查询关联人员,web应用', visible: true };
router.get('/analysis/userlist', analysis.userlist); router.get('/analysis/userlist', analysis.personnelApp);
app.fs.api.logAttr['GET/analysis/problem'] = { content: '查询异常&问题', visible: true };
router.get('/analysis/problem', analysis.problem);
//BI分析模块 //BI分析模块

16
web/client/src/sections/control/actions/control.js

@ -35,14 +35,26 @@ export function deleteConsoleToollink (orgId) { //删除常用工具
}); });
} }
export function geteteConsoleCount (query) { //工作台数量查询 export function getConsoleCount (query) { //工作台数量查询
return dispatch => basicAction({ return dispatch => basicAction({
type: 'get', type: 'get',
dispatch: dispatch, dispatch: dispatch,
query, query,
actionType: 'GET_CONSLE_COUNT', actionType: 'GET_CONSLE_COUNT',
url: `${ApiTable.geteteConsoleCount}`, url: `${ApiTable.getConsoleCount}`,
msg: { option: '工作台数量查询' }, msg: { option: '工作台数量查询' },
reducer: { name: '' } reducer: { name: '' }
}); });
} }
export function getConsoleAbnormal (query) { //项目概览异常查询
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query,
actionType: 'GET_CONSLE_ABNORMAL',
url: `${ApiTable.getConsoleAbnormal}`,
msg: { option: '项目概览异常查询' },
reducer: { name: '' }
});
}

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

@ -63,9 +63,12 @@ const Control = (props) => {
}, []) }, [])
useEffect(() => { useEffect(() => {
dispatch(control.geteteConsoleCount({ pepProjectId: pepProjectId })).then(res => { dispatch(control.getConsoleCount({ pepProjectId: pepProjectId })).then(res => {
if (res.success) setWorkData(res.payload.data) if (res.success) setWorkData(res.payload.data)
}) })
dispatch(getConsoleAbnormal({ pepProjectId: pepProjectId })).then(res => {
if (res.success) setProblemsList(...res.payload.data,...res.payload.data)
})
}, [pepProjectId]) }, [pepProjectId])
useEffect(() => { useEffect(() => {
@ -145,7 +148,6 @@ const Control = (props) => {
// dispatch(actions.example.getMembers(user.orgId)) // dispatch(actions.example.getMembers(user.orgId))
}) })
const consoleToollink = () => { const consoleToollink = () => {
dispatch(control.getConsoleToollink()).then(res => { dispatch(control.getConsoleToollink()).then(res => {
if (res.success) setToolShow(res.payload.data) if (res.success) setToolShow(res.payload.data)
@ -177,13 +179,13 @@ const Control = (props) => {
{ name: '关联web应用', sort: 4, key: 'web', }, { name: '关联web应用', sort: 4, key: 'web', },
{ name: '异常&问题', sort: 5, key: 'problem', }, { name: '异常&问题', sort: 5, key: 'problem', },
{ name: '数据中断', sort: 5, key: 'dataInterrupt', }, { name: '数据中断', sort: 1, key: 'dataInterrupt', },
{ name: '数据异常', sort: 5, key: 'dataAnomaly', }, { name: '数据异常', sort: 2, key: 'dataAnomaly', },
{ name: '策略命中', sort: 5, key: 'strategyHit', }, { name: '策略命中', sort: 3, key: 'strategyHit', },
{ name: '视频异常', sort: 5, key: 'videoException', }, { name: '视频异常', sort: 4, key: 'videoException', },
{ name: '应用异常', sort: 5, key: 'appAbnormal', }, { name: '应用异常', sort: 5, key: 'appAbnormal', },
{ name: '设备异常', sort: 5, key: 'unitException', }, { name: '设备异常', sort: 6, key: 'unitException', },
{ name: '问题处置效率分析', sort: 5, key: 'problemAnalysis', }, { name: '问题处置效率分析', sort: 7, key: 'problemAnalysis', },
] ]
useEffect(() => { useEffect(() => {
@ -202,12 +204,11 @@ const Control = (props) => {
TableDisplay.sort((a, b) => a.sort - b.sort) TableDisplay.sort((a, b) => a.sort - b.sort)
exhibition.current = { ...exhibition.current, [title]: TableDisplay } exhibition.current = { ...exhibition.current, [title]: TableDisplay }
setTableSetup([{ list: data }]) setTableSetup([{ list: data }])
} }
return ( return (
11 ? <img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative", top: -12, left: -8, }} /> : // 11 ? <img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative", top: -12, left: -8, }} /> :
<> <>
<div style={{ padding: '0px 40px', width: '100%' }} className='console'> <div style={{ padding: '0px 40px', width: '100%' }} className='console'>
{/* 头部 */} {/* 头部 */}
@ -516,7 +517,6 @@ const Control = (props) => {
</div> </div>
<div style={{ width: '100%', height: '100%' }}> <div style={{ width: '100%', height: '100%' }}>
{exhibition.current?.analyse?.map((v, index) => { {exhibition.current?.analyse?.map((v, index) => {
console.log(exhibition.current?.analyse);
return <div id={'ReactECharts' + index} style={{ marginTop: 20, padding: 10, width: '50%', display: "inline-block" }}> return <div id={'ReactECharts' + index} style={{ marginTop: 20, padding: 10, width: '50%', display: "inline-block" }}>
<ReactECharts <ReactECharts
option={{ option={{

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

@ -87,7 +87,6 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
default: default:
dispatch(problem.getAlarmDataGroup()).then((res) => { dispatch(problem.getAlarmDataGroup()).then((res) => {
if (res.success) { if (res.success) {
let routeData = { dataLnterrupt: '数据中断', dataAbnormal: '数据异常', strategyHit: '策略命中', deviceAbnormal: '' }
let data let data
if (route == 'dataLnterrupt') data = res.payload.data?.filter(v => v.desc == '数据中断') if (route == 'dataLnterrupt') data = res.payload.data?.filter(v => v.desc == '数据中断')
if (route == 'dataAbnormal') data = res.payload.data?.filter(v => v.desc == '数据异常') if (route == 'dataAbnormal') data = res.payload.data?.filter(v => v.desc == '数据异常')

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

@ -59,8 +59,8 @@ export const ApiTable = {
//控制台 //控制台
consoleToollink: 'console/toollink', //常用工具 consoleToollink: 'console/toollink', //常用工具
deleteConsoleToollink: 'console/toollink/{linkId}', //删除常用工具 deleteConsoleToollink: 'console/toollink/{linkId}', //删除常用工具
geteteConsoleCount: 'console/count', //工作台数量查询 getConsoleCount: 'console/count', //工作台数量查询
getConsoleAbnormal: 'analysis/problem', //项目概览异常查询
}; };
export const RouteTable = { export const RouteTable = {

Loading…
Cancel
Save