diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index 69a9028..f9f2031 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 node35:6667,node36:6667,node37:6667", "--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/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 801caad..950ae75 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() @@ -56,22 +53,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"); @@ -154,25 +189,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', }, @@ -181,11 +246,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', }, ] @@ -209,7 +274,6 @@ const Control = (props) => { return ( - // 11 ? : <>
{/* 头部 */} @@ -290,7 +354,7 @@ const Control = (props) => { {item.name == '关注的项目' ? ' ( 个 )' : ' ( 条 )'}
-
{item.data}
+
dispatch(push(item.url))} style={{ fontSize: 32, color: index == 0 ? '#0F7EFB' : index == 1 ? '#0091A1' : index == 2 ? '#FE9812' : '#FF7575', fontFamily: 'YouSheBiaoTiHei' }}>{item.data}
{item.name == '关注的项目' ? '' :
待处理
}
@@ -387,7 +451,7 @@ const Control = (props) => { 相关成员
- {memberList.map((item, index) => { + {memberList?.map((item, index) => { return (
@@ -395,7 +459,7 @@ const Control = (props) => { 成员
- 刘昊然 + {item.name}
(负责人) @@ -454,17 +518,19 @@ const Control = (props) => { { webList.map((item, index) => { return ( -
+
web应用
- superchangnan.anxiny + + {item.url} +
-
- 第三方 +
+ {item.name}
) @@ -481,14 +547,16 @@ const Control = (props) => {
{ - problemsList.map((item, index) => { + problemsList?.map((v, index) => { return ( -
-
- 【告警源A】数据信息中断,诊断为 服务异常,请前往确认 +
{ + dispatch(push(v.url)) + }}> +
+ 【{v.SourceName}】{v.groupName}{v.groupName == '视频异常' ? "" : ',诊断为 '} {v.typeName},请前往确认
- 2022-05-21 15:23:41 + {moment(v.StartTime).format("YYYY-MM-DD HH:mm:ss")}
) @@ -518,7 +586,37 @@ const Control = (props) => {
{exhibition.current?.analyse?.map((v, index) => { - return
+ let startValue = '' + if (v.key !== 'problemAnalysis') { + switch (v.key) { + case 'videoException': + let videos = videoBI?.filter(u => moment(moment().day(moment().day() - 30).format('YYYY-MM-DD')).isBefore(u.day)) || [] + if (videos.length) { + startValue = videos[0]?.day + } else { + startValue = videoBI?.slice(-1)[0]?.day + } + break; + case 'appAbnormal': + let apps = appBI?.filter(u => moment(moment().day(moment().day() - 30).format('YYYY-MM-DD')).isBefore(u.day)) || [] + if (apps.length) { + startValue = apps[0]?.day + } else { + startValue = appBI?.slice(-1)[0]?.day + } + break; + default: + let datas = dataBI[v.key]?.filter(u => moment(moment().day(moment().day() - 30).format('YYYY-MM-DD')).isBefore(u.day)) || [] + if (datas.length) { + startValue = datas[0]?.day + } else { + startValue = dataBI[v.key]?.slice(-1)[0]?.day + } + break; + } + } + // console.log(startValue); + return v.key == 'problemAnalysis' ?
{ }, dataZoom: [ { - show: true, - type: 'inside', - filterMode: 'none', - xAxisIndex: [0], + type: 'slider', + // startValue: }, { - show: true, type: 'inside', - filterMode: 'none', - yAxisIndex: [0], - } + }, ], tooltip: { trigger: 'axis' }, legend: { - data: ['次数'], + data: [v.name, '已处理(含自动恢复)'], right: '10%', }, xAxis: { @@ -559,21 +652,18 @@ const Control = (props) => { }, yAxis: { type: 'value', - name: "次数", + name: "条数", }, series: [ { - data: [], type: 'line', - name: '次数', + name: v.name, smooth: true, areaStyle: { color: '#0e9cff26', }, - markLine: { - data: [{ type: 'average', name: 'Avg' }] - } - } + data: [] + }, ] }} notMerge={true} @@ -584,6 +674,74 @@ const Control = (props) => { // opts={} />
+ :
+ u.day) : v.key == 'appAbnormal' ? appBI?.map(u => u.day) : dataBI[v.key]?.map(u => u.day) || [] + }, + yAxis: { + type: 'value', + name: "条数", + }, + series: [ + { + type: 'line', + name: v.name, + smooth: true, + areaStyle: { + color: '#0e9cff26', + }, + data: v.key == 'videoException' ? videoBI?.map(u => u.total) : v.key == 'appAbnormal' ? appBI?.map(u => u.total) : dataBI[v.key]?.map(u => u.total) || [] + }, + { + type: 'line', + name: '已处理(含自动恢复)', + smooth: true, + areaStyle: { + color: '#0e9cff26', + }, + data: v.key == 'videoException' ? videoBI?.map(u => u.done) : v.key == 'appAbnormal' ? appBI?.map(u => u.done) : dataBI[v.key]?.map(u => u.done) || [] + }, + + ] + }} + notMerge={true} + lazyUpdate={true} + theme={'ReactEChart' + index} + // onChartReady={this.onChartReadyCallback} + // onEvents={EventsDict} + // opts={} + /> +
})}
diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index 1ba6232..4f51d0f 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -60,8 +60,11 @@ export const ApiTable = { consoleToollink: 'console/toollink', //常用工具 deleteConsoleToollink: 'console/toollink/{linkId}', //删除常用工具 getConsoleCount: 'console/count', //工作台数量查询 + getConsoleUser: 'analysis/userlist', //查询关联人员,web应用 getConsoleAbnormal: 'analysis/problem', //项目概览异常查询 - + getDataAlarmsAggDay: 'data/alarms/agg/day', //查询BI分析数据-数据 + getVideoAlarmsAggDay: 'video/alarms/agg/day', //查询BI分析数据-视频异常 + getAppAlarmsAggDay: 'app/alarms/agg/day', //查询BI分析数据-应用 }; export const RouteTable = { apiRoot: "/api/root",