diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index 7a3b8d3..f87c4f7 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -16,9 +16,9 @@ "-p 4600", "-f http://localhost:4600", // 研发 - "-g postgres://postgres:123@10.8.30.32:5432/orational_service", + // "-g postgres://postgres:123@10.8.30.32:5432/orational_service", // 测试 - // "-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS", + "-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS", "-k 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", diff --git a/api/app/lib/controllers/control/analysis.js b/api/app/lib/controllers/control/analysis.js index e097bfb..5d26317 100644 --- a/api/app/lib/controllers/control/analysis.js +++ b/api/app/lib/controllers/control/analysis.js @@ -1,86 +1,6 @@ 'use strict'; const moment = require('moment'); -async function dataList (ctx) { - try { - const { models } = ctx.fs.dc; - const { userId, pepUserId, userInfo = {}, pepUserInfo } = ctx.fs.api - const { clickHouse } = ctx.app.fs - const { utils: { judgeSuper, anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs - const { database: anxinyun } = clickHouse.anxinyun.opts.config - const { pepProjectId } = ctx.request.query - - - let anxinStruc = await anxinStrucIdRange({ - ctx, pepProjectId - }) - let pomsProject = await pomsProjectRange({ - ctx, pepProjectId, - }) - const pomsProjectIds = pomsProject.map(p => p.id) - - if (anxinStruc.length) { - - const anxinStrucIds = anxinStruc.map(a => a.strucId) || [] - - const dataAlarm = await clickHouse.dataAlarm.query(` - SELECT - formatDateTime(alarmData.StartTime,'%F %H') hours, count(AlarmId) count - FROM - ( SELECT - AlarmId,State,StartTime - FROM - alarms - WHERE - alarms.StructureId IN (${anxinStrucIds.join(",")}) - AND - AlarmGroup = 3) AS alarmData - GROUP BY hours - `).toPromise(); - - // const confirmedAlarm = dataAlarm - // // TODO: 开发临时注释 - // .filter(ar => ar.State && ar.State > 2) - // .map(ar => "'" + ar.AlarmId + "'") - - // // formatDateTime(Time,'%F %H') hours, count(AlarmId) count - - // const dataConfirme = confirmedAlarm.length ? - // await clickHouse.dataAlarm.query(` - // SELECT - // max(Time) AS Time, AlarmId - // FROM - // alarm_details - // WHERE - // AlarmId IN (${confirmedAlarm.join(',')}) - // GROUP BY AlarmId - // `).toPromise() : - // []; - - - // dataAlarm.forEach(ar => { - // ar.confirme = - // dataConfirme.find(as => as.AlarmId == ar.AlarmId) || {} - - // }) - ctx.status = 200 - ctx.body = dataAlarm - } else { - ctx.status = 200 - ctx.body = { - dataAlarm: 0, - } - } - - } catch (error) { - ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); - ctx.status = 400; - ctx.body = { - message: typeof error == 'string' ? error : undefined - } - } -} - async function personnelApp (ctx) { try { const models = ctx.fs.dc.models; @@ -219,7 +139,7 @@ async function personnelApp (ctx) { findOptions.where.id = { $in: userInfo.correlationProject } } if (pepId) { - findOption.where.id = pepId + findOptions.where.id = pepId } const proRes = await models.ProjectCorrelation.findAndCountAll(findOptions) @@ -267,7 +187,7 @@ async function personnelApp (ctx) { 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)) { + if (!webApp.map(n => n.name).includes(vv.dataValues.name)) { webApp.push({ name: vv.dataValues.name, url: vv.dataValues.url }) } }) @@ -283,7 +203,7 @@ async function personnelApp (ctx) { ctx.status = 200 ctx.body = { - personnel: personnel.map(v => v.dataValues.name), + personnel: personnel.map(v => ({ name: v.dataValues.name, department: v.dataValues.departments.map(r => r.name) })), webApp: webApp } } catch (error) { @@ -471,7 +391,7 @@ async function problem (ctx) { ctx.status = 200; ctx.body = sum } else { - ctx.body =[] + ctx.body = [] } ctx.status = 200; } catch (error) { @@ -485,7 +405,6 @@ async function problem (ctx) { module.exports = { - dataList, personnelApp, problem } \ No newline at end of file diff --git a/api/app/lib/routes/control/index.js b/api/app/lib/routes/control/index.js index a129002..be4e73c 100644 --- a/api/app/lib/routes/control/index.js +++ b/api/app/lib/routes/control/index.js @@ -14,12 +14,9 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['DEL/console/toollink'] = { content: '删除常用工具', visible: true }; router.del('/console/toollink/:linkId', toolLink.del); - app.fs.api.logAttr['GET/console/count'] = { content: '查询告警数量', visible: true }; + app.fs.api.logAttr['GET/console/count'] = { content: '工作台查询告警数量', visible: true }; router.get('/console/count', toolLink.count); - app.fs.api.logAttr['GET/analysis/dataList'] = { content: '查询数据告警产生,确认数量', visible: true }; - router.get('/analysis/dataList', analysis.dataList); - app.fs.api.logAttr['GET/analysis/userlist'] = { content: '查询关联人员,web应用', visible: true }; router.get('/analysis/userlist', analysis.personnelApp); diff --git a/api/app/lib/schedule/alarms_push.js b/api/app/lib/schedule/alarms_push.js index c6a1a37..9b2c556 100644 --- a/api/app/lib/schedule/alarms_push.js +++ b/api/app/lib/schedule/alarms_push.js @@ -120,7 +120,8 @@ module.exports = function (app, opts) { return s.id }) - searchStrucIds = searchStrucIds.concat([991, 1052, 700]) + // 开发测试用的数据 + // searchStrucIds = searchStrucIds.concat([991, 1052, 700]) if (searchStrucIds.length) { searchStrucIds.unshift(-1) @@ -377,7 +378,7 @@ module.exports = function (app, opts) { dataAlarms = await clickHouse.dataAlarm.query(` SELECT * FROM alarms WHERE - ${`'State NOT IN (3, 4) AND '`} + ${`State NOT IN (3, 4) AND `} StructureId IN (${searchStrucIds.join(',')}) ${dataAlarmOption.length ? ' AND ' + dataAlarmOption.join(' AND ') : ''} ORDER BY StartTime DESC @@ -602,8 +603,13 @@ module.exports = function (app, opts) { deviceStatistic.add(d.SourceId) } if (c.tactics == 'abnormal_rate') { - let a = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))).toFixed(1) + '%' - emailSubTitle = emailSubTitle.replace('--%', ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))).toFixed(1) + '%') + let rate = ((deviceStatistic.size + videoAlarms.length) / (parseInt(deviceCount) + parseInt(cameraCount))); + + if (rate < parseFloat(deviceProportion)) { + continue + } + + emailSubTitle = emailSubTitle.replace('--%', rate.toFixed(1) + '%') } let html = ` diff --git a/web/client/src/sections/control/actions/control.js b/web/client/src/sections/control/actions/control.js index edcf54f..310021e 100644 --- a/web/client/src/sections/control/actions/control.js +++ b/web/client/src/sections/control/actions/control.js @@ -47,6 +47,18 @@ export function getConsoleCount (query) { //工作台数量查询 }); } +export function getConsoleUser (query) { //查询关联人员,web应用 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_CONSLE_USER', + url: `${ApiTable.getConsoleUser}`, + msg: { option: '查询关联人员,web应用' }, + reducer: { name: '' } + }); +} + export function getConsoleAbnormal (query) { //项目概览异常查询 return dispatch => basicAction({ type: 'get', @@ -57,4 +69,40 @@ export function getConsoleAbnormal (query) { //项目概览异常查询 msg: { option: '项目概览异常查询' }, reducer: { name: '' } }); +} + +export function getDataAlarmsAggDay (query) { //查询BI分析数据-数据 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_DATA_ALARMS_AGG_DAY', + url: `${ApiTable.getDataAlarmsAggDay}`, + msg: { option: '查询BI分析数据' }, + reducer: { name: '' } + }); +} + +export function getVideoAlarmsAggDay (query) { //查询BI分析数据-视频异常 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_VIDEO_ALARMS_AGG_DAY', + url: `${ApiTable.getVideoAlarmsAggDay}`, + msg: { option: '查询BI分析视频数据' }, + reducer: { name: '' } + }); +} + +export function getAppAlarmsAggDay (query) { //查询BI分析数据-应用 + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + query, + actionType: 'GET_APP_ALARMS_AGG_DAY', + url: `${ApiTable.getAppAlarmsAggDay}`, + msg: { option: '查询BI分析应用数据' }, + reducer: { name: '' } + }); } \ No newline at end of file diff --git a/web/client/src/sections/control/containers/control.jsx b/web/client/src/sections/control/containers/control.jsx index 537c465..f7ceae9 100644 --- a/web/client/src/sections/control/containers/control.jsx +++ b/web/client/src/sections/control/containers/control.jsx @@ -7,7 +7,7 @@ import PerfectScrollbar from "perfect-scrollbar"; import repairFQA from '../../means/containers/repairFQA'; import { Setup, OutHidden } from "$components"; import ReactECharts from 'echarts-for-react'; -const { Meta } = Card; +import moment from "moment"; let newScrollbar; let overviewScrollbar; @@ -21,21 +21,15 @@ let alarmScrollbar; const Control = (props) => { const { dispatch, actions, user, loading, socket, pepProjectId } = props const { control } = actions - const stationList = [ - 'url(/assets/images/console/lan_1.png)', - 'url(/assets/images/console/lv_1.png)', - 'url(/assets/images/console/huang_1.png)', - 'url(/assets/images/console/hong_1.png)', - ] const [timelineList, setTimelineList] = useState(['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''])//最新动态列表 - const [memberList, setMemberList] = useState(['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''])//相关成员列表 + const [memberList, setMemberList] = useState([])//相关成员列表 const [equipmentList, setEquipmentList] = useState(['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''])//平台设备接入列表 - const [webList, setWebList] = useState(['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''])//关联web应用列表 + const [webList, setWebList] = useState([])//关联web应用列表 - const [problemsList, setProblemsList] = useState(['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''])//异常&问题列表 + const [problemsList, setProblemsList] = useState([])//异常&问题列表 const [setup, setSetup] = useState(false); //设置是否显现 const [tableType, setTableType] = useState(''); //localStorage存储名 const [tool, setTool] = useState(false); //工具添加修改弹窗 @@ -43,7 +37,10 @@ const Control = (props) => { const [compile, setCompile] = useState({}); //工具编辑的内容 const [toolShow, setToolShow] = useState([]); //工具展示 const [tableSetup, setTableSetup] = useState([]); //单一表格设置信息 - const [workData, setWorkData] = useState(); //我的工作台数据 + const [workData, setWorkData] = useState({}); //我的工作台数据 + const [dataBI, setDataBI] = useState({}); //查询BI分析数据-数据 + const [videoBI, setVideoBI] = useState([]); //查询BI分析数据-视频 + const [appBI, setAppBI] = useState([]); //查询BI分析数据-应用 const exhibition = useRef({ workbench: [], statistical: [] }) //页面结构 const FormApi = useRef() @@ -69,22 +66,60 @@ const Control = (props) => { data.map(v => { localStorage.getItem(v) == null ? localStorage.setItem(v, JSON.stringify(show[v])) - : ""; + : "" attribute(v) }) }, []) useEffect(() => { + // 工作台数据请求 dispatch(control.getConsoleCount({ pepProjectId: pepProjectId })).then(res => { if (res.success) setWorkData(res.payload.data) }) + // 统计概览--异常&问题 dispatch(control.getConsoleAbnormal({ pepProjectId: pepProjectId })).then(res => { - console.log(res.payload.data); - if (res.success) setProblemsList([...res.payload.data,...res.payload.data]) + if (res.success) { + if (res.payload.data?.length > 4) { + setProblemsList([...res.payload.data, ...res.payload.data]) + startmarquee(500, 2000, 'problems') + } else { + setProblemsList(res.payload.data) + } + } + }) + // 统计概览--相关成员与web应用 + dispatch(control.getConsoleUser({ pepId: pepProjectId })).then(res => { + if (res.success) { + if (res.payload.data?.personnel?.length > 5) { + setMemberList([...res.payload.data?.personnel, ...res.payload.data?.personnel]) + startmarquee(600, 2000, 'member') + } else { + setMemberList(res.payload.data?.personnel) + } + if (res.payload.data?.webApp?.length > 3) { + setWebList([...res.payload.data?.webApp, ...res.payload.data?.webApp]) + startmarquee(600, 2000, 'web') + } else { + setWebList(res.payload.data?.webApp) + } + } + }) + // 查询BI分析数据-数据 + dispatch(control.getDataAlarmsAggDay({ pepProjectId: pepProjectId })).then(res => { + if (res.success) setDataBI(res.payload.data) + }) + //查询BI分析数据-视频异常 + dispatch(control.getVideoAlarmsAggDay({ pepProjectId: pepProjectId })).then(res => { + if (res.success) setVideoBI(res.payload.data) + }) + //查询BI分析数据-应用 + dispatch(control.getAppAlarmsAggDay({ pepProjectId: pepProjectId })).then(res => { + // console.log(res.payload.data); + if (res.success) setAppBI(res.payload.data) }) - }, [pepProjectId]) + }, [pepProjectId]) useEffect(() => { const domProject = document.getElementById("news"); @@ -167,25 +202,55 @@ const Control = (props) => { if (res.success) setToolShow(res.payload.data) }) } + function startmarquee (speed, delay, name) { + /* + 函数startmarquee的参数: + lh:文字一次向上滚动的距离或高度; + speed:滚动速度; + delay:滚动停顿的时间间隔; + index:可以使封装后的函数应用于页面当中不同的元素; + */ + var t; + var p = false; + let top = 0 + var o = document.getElementById(name); + if (o) { + o.onmouseover = () => p = true + o.onmouseout = () => p = false + o.scrollTop = 0; + const start = () => { + t = setInterval(() => { + if (!p) (top += 10, o.scrollTop = top) + if (p) (clearInterval(t), setTimeout(start, delay)) + if (o.scrollTop >= o.scrollHeight / 2) (top = 0, o.scrollTop = 0) + }, speed); + } + setTimeout(start, 1000); + } + } + + + + let Select = { workbench: ['project', 'data', 'app', 'device'], statistical: ['milestone', 'personnel', 'DeviceAccess', 'web', 'problem'], - analyse: ['dataInterrupt', 'dataAnomaly', 'strategyHit', 'videoException', 'appAbnormal', 'unitException', 'problemAnalysis'], + analyse: ['dataInterrupt', 'dataAbnormal', 'policyHit', 'videoException', 'appAbnormal', 'deviceAbnormal', 'problemAnalysis'], dynamic: [], } let show = { workbench: ['project', 'data', 'app', 'device'], statistical: ['milestone', 'personnel', 'DeviceAccess', 'web', 'problem'], - analyse: ['dataInterrupt', 'dataAnomaly', 'strategyHit', 'videoException', 'appAbnormal', 'unitException', 'problemAnalysis'], + analyse: ['dataInterrupt', 'dataAbnormal', 'policyHit', 'videoException', 'appAbnormal', 'deviceAbnormal', 'problemAnalysis'], dynamic: [], } let listAll = [ { name: '关注的项目', sort: 1, key: 'project', data: workData?.projects || 0, img: 'url(/assets/images/console/lan_1.png)' }, - { name: '数据告警', sort: 2, key: 'data', data: workData?.dataSurplus || 0, img: 'url(/assets/images/console/lv_1.png)' }, - { name: '应用告警', sort: 2, key: 'app', data: workData?.appSurplus || 0, img: 'url(/assets/images/console/hong_1.png)' }, - { name: '设备告警', sort: 2, key: 'device', data: workData?.toolSurplus || 0, img: 'url(/assets/images/console/hong_1.png)' }, + { name: '数据告警', sort: 2, key: 'data', data: workData?.dataSurplus || 0, img: 'url(/assets/images/console/lv_1.png)', url: '/problem/dataAlarm/dataLnterrupt' }, + { name: '应用告警', sort: 2, key: 'app', data: workData?.appSurplus || 0, img: 'url(/assets/images/console/hong_1.png)', url: '/problem/useAlarm/useAbnormal' }, + { name: '设备告警', sort: 2, key: 'device', data: workData?.toolSurplus || 0, img: 'url(/assets/images/console/hong_1.png)', url: '/problem/deviceAlarm/deviceAbnormal' }, { name: '项目里程碑', sort: 1, key: 'milestone', }, { name: '相关成员', sort: 2, key: 'personnel', }, @@ -194,11 +259,11 @@ const Control = (props) => { { name: '异常&问题', sort: 5, key: 'problem', }, { name: '数据中断', sort: 1, key: 'dataInterrupt', }, - { name: '数据异常', sort: 2, key: 'dataAnomaly', }, - { name: '策略命中', sort: 3, key: 'strategyHit', }, + { name: '数据异常', sort: 2, key: 'dataAbnormal', }, + { name: '策略命中', sort: 3, key: 'policyHit', }, { name: '视频异常', sort: 4, key: 'videoException', }, { name: '应用异常', sort: 5, key: 'appAbnormal', }, - { name: '设备异常', sort: 6, key: 'unitException', }, + { name: '设备异常', sort: 6, key: 'deviceAbnormal', }, { name: '问题处置效率分析', sort: 7, key: 'problemAnalysis', }, ] @@ -222,7 +287,6 @@ const Control = (props) => { return ( - // 11 ? : <>