巴林闲侠 2 years ago
parent
commit
16129d03ba
  1. 154
      api/app/lib/controllers/operationData/index.js
  2. 297
      api/app/lib/controllers/problemData/index.js
  3. 34
      api/app/lib/models/system_problem.js
  4. 16
      api/app/lib/routes/operationData/index.js
  5. 16
      api/app/lib/routes/problemData/index.js
  6. 15
      script/0.28/schema/01create_sysyem_problem.sql
  7. 5
      web/client/src/sections/analysis/actions/index.js
  8. 53
      web/client/src/sections/analysis/actions/maintenceRecordTotal.js
  9. 50
      web/client/src/sections/analysis/actions/operationData.js
  10. 293
      web/client/src/sections/analysis/containers/operationData.jsx
  11. 372
      web/client/src/sections/analysis/containers/problemData.jsx
  12. 2
      web/client/src/sections/service/components/maintenanceRecordModal.jsx
  13. 5
      web/client/src/sections/service/components/recordModal.jsx
  14. 19
      web/client/src/utils/webapi.js

154
api/app/lib/controllers/operationData/index.js

@ -0,0 +1,154 @@
'use strict';
const moment = require('moment')
//const db = require('../')
async function getFailureTime(ctx) {
const sequelize = ctx.fs.dc.orm
try {
const res = await sequelize.query(
`SELECT substring(to_char(w.wmonth,'yyyy-mm'),1,7) wmonth,COALESCE(e.counts,0) count
from ( SELECT COUNT(1) counts,to_char(maintenance_record.occurrence_time, 'yyyy-mm') months
FROM maintenance_record
GROUP BY months
) e
RIGHT JOIN
(
SELECT to_date(EXTRACT(YEAR FROM (current_date - INTERVAL '1 month' * (t - 1))) ||'-'
|| EXTRACT(MONTH FROM (current_date - INTERVAL '1 month' * (t - 1))),'yyyy-mm')as wmonth
from generate_series(1, 12) as t
) w on e.months = substring(to_char(w.wmonth,'yyyy-mm'),1,7)
GROUP BY w.wmonth,e.counts
ORDER BY w.wmonth desc`
)
console.log('111112232', res)
let resList = []
if (res.length > 0) {
res[0].forEach((item) => {
resList.push(item)
})
}
ctx.status = 200
ctx.body = resList
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询故障发生时间失败'
}
}
}
async function getSystemAvailability(ctx) {
const sequelize = ctx.fs.dc.orm
try {
const res = await sequelize.query(
`SELECT substring(to_char(w.wmonth,'yyyy-mm'),1,7) wmonth,
COALESCE((w.seconds-e.counts)/w.seconds,0) ability
from (
SELECT to_char(maintenance_record.occurrence_time, 'yyyy-MM') months,
SUM(maintenance_record.interrupt_duration) counts
FROM maintenance_record
where maintenance_record.occurrence_time is not null
GROUP BY months
) e
RIGHT JOIN
(
SELECT to_date(EXTRACT(YEAR FROM (current_date - INTERVAL '1 month' * (n - 1))) ||'-'|| EXTRACT(MONTH FROM (current_date - INTERVAL '1 month' * (n - 1))),'yyyy-mm') as wmonth,
(EXTRACT(DAY FROM (DATE_TRUNC('MONTH', current_date - INTERVAL '1 month' * (n - 1) + INTERVAL '1 MONTH') - DATE_TRUNC('MONTH', current_date - INTERVAL '1 month' * (n - 1)) - INTERVAL '1 DAY'))+1)* 24 * 60 * 60 AS seconds
from generate_series(1, 12) AS n
) w
on e.months = substring(to_char(w.wmonth,'yyyy-mm'),1,7)
GROUP BY w.wmonth,w.seconds,e.counts
ORDER BY w.wmonth DESC;`
)
let resList = []
if (res.length > 0) {
res[0].forEach((item) => {
resList.push(item)
})
}
ctx.status = 200
ctx.body = resList
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询系统可用性失败'
}
}
}
async function getProblemType(ctx) {
const sequelize = ctx.fs.dc.orm
try {
const res = await sequelize.query(`
SELECT n.type,COALESCE(m.count,0) count FROM (SELECT t.type,count(1) FROM maintenance_record t GROUP BY t.type) m
RIGHT JOIN (SELECT p.type FROM system_problem p) n
ON m.type=n.type
GROUP BY n.type,m.count
`)
let resList = []
if (res.length > 0) {
res[0].forEach((item) => {
resList.push(item)
})
}
ctx.status = 200
ctx.body = resList
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询故障类型失败'
}
}
}
async function getOperationsPersonnel(ctx) {
const sequelize = ctx.fs.dc.orm
const { clickHouse } = ctx.app.fs
try {
//查询用户id
const res = await sequelize.query(`SELECT t.pep_user_id userId,count(1) FROM maintenance_plan_execute_user t GROUP BY pep_user_id`)
let useList = new Set()
console.log('rss', res)
if (res.length > 0) {
res[0].forEach((item) => {
useList.add(item.userid)
})
}
let users = useList ? await clickHouse.pepEmis.query(`SELECT DISTINCT user.id AS id, "user"."name" AS name FROM user WHERE user.id IN
(${[...useList].join(',')}, -1)`).toPromise() : []
let resRecord = []
if (res.length > 0) {
res[0].forEach((item) => {
resRecord.push(item)
})
}
let mergedArray = []
if (users.length > 0 && resRecord.length > 0) {
mergedArray = users.map(item1 => {
const item2 = resRecord.find(item2 => item2.userid === item1.id);
return {
userid: item1.id,
name: item1.name,
count: parseInt(item2.count)
}
})
}
ctx.status = 200
ctx.body = mergedArray
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询运维人员失败'
}
}
}
module.exports = {
getFailureTime, getSystemAvailability, getProblemType, getOperationsPersonnel
}

297
api/app/lib/controllers/problemData/index.js

@ -0,0 +1,297 @@
'use strict';
const moment = require('moment')
async function getMaintenceRecordRank(ctx) {
const sequelize = ctx.fs.dc.orm
const Sequelize = ctx.fs.dc.ORM;
const { clickHouse } = ctx.app.fs
const models = ctx.fs.dc.models
const { startTime, endTime } = ctx.query
console.log(startTime, endTime, ctx.query, '1212312')
try {
const res = await sequelize.query(`
SELECT emrp.project_id,count(1)
FROM equipment_maintenance_record
RIGHT JOIN equipment_maintenance_record_project emrp
on equipment_maintenance_record.id = emrp.equipment_maintenance_record_id
where report_time BETWEEN :startTime AND :endTime
GROUP BY emrp.project_id
`
, {
replacements: {
startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss ')
}
//, type: sequelize.QueryTypes.SELECT
}
)
//查询equipment_maintenance_record返回的结果[{project_id: 22, count: 1}]
let projectList = []
//存project的id
let projectIdList = []
// console.log('resssss', res)
if (res.length > 0) {
res[0].forEach((item) => {
projectList.push({ project_id: item.project_id, count: Number(item.count) })
projectIdList.push(item.project_id)
})
}
const projectNameList = await models.ProjectCorrelation.findAll({
attributes:
['id', 'name'],
where: {
id: { $in: projectIdList },
name: {
[Sequelize.Op.not]: null//有name的结果
}
// del: false
}
}) || []
//在ProjectCorrelation中查不到名字,去clickHouse中去查
const projectNameList1 = await models.ProjectCorrelation.findAll({
attributes:
['id', 'name', 'pepProjectId'],
where: {
id: { $in: projectIdList },
name: {
[Sequelize.Op.eq]: null//无name的结果
}
// del: false
}
})
//存放需要去查询clickHouse的id
let idList = new Set()
if (projectNameList1.length) {
projectNameList1.forEach((item) => {
idList.add(item.pepProjectId)
})
}
//pepProject名称
const projectManageName = idList.size ? await clickHouse.projectManage.query(`
SELECT id,project_name FROM t_pim_project
WHERE id IN (${[...idList].join(',')}, -1)
`).toPromise() : []
// if (projectList.length) {
// projectList.forEach((item) => {
// projectManageName
// })
// }
//存的是{id,projectName}
let project = []
if (projectNameList1.length && projectManageName.length) {
projectManageName.forEach((item) => {
const pepObj = projectNameList1.find((item1) => { return item1.pepProjectId === item.id })
project.push({ id: pepObj.id, projectName: item.project_name })
})
}
const resAll = project.concat(projectNameList)
let mergedArray = []
if (resAll.length && projectList) {
mergedArray = projectList.map(obj1 => {
const matchingObj = resAll.find(obj2 => obj2.id === obj1.project_id);
return { id: obj1.project_id, pepProjectId: matchingObj.id, projectName: matchingObj.projectName || matchingObj.dataValues.name, count: obj1.count };
});
}
// console.log('ididididid', resAll)
// console.log('ididididid', project)
// console.log('ididididid', projectManageName)
// console.log('ididididid', projectNameList)
// console.log('ididididid', projectList)
ctx.status = 200
ctx.body = mergedArray
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询维修记录排名失败'
}
}
}
async function getMaintenceTotal(ctx) {
const sequelize = ctx.fs.dc.orm
const Sequelize = ctx.fs.dc.ORM;
const { clickHouse } = ctx.app.fs
const models = ctx.fs.dc.models
const { startTime, endTime } = ctx.query
try {
//所有维修记录
const res = await sequelize.query(`
SELECT emrp.project_id,
count(case when record.status in ('维修中','待维修') then record.id end) incomplete,
count(case when record.status in ('维修完成') then record.id end) completed
FROM equipment_maintenance_record record
RIGHT JOIN equipment_maintenance_record_project emrp
on record.id = emrp.equipment_maintenance_record_id
where report_time BETWEEN :startTime AND :endTime
GROUP BY emrp.project_id
`
, {
replacements: {
startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss '),
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss ')
}
//, type: sequelize.QueryTypes.SELECT
}
)
//查询equipment_maintenance_record返回的结果[{project_id: 22,status:'' count: 1}]
let projectList = []
//存project的id
let projectIdList = new Set()
// console.log('resssss', res)
if (res.length > 0) {
res[0].forEach((item) => {
projectList.push({ project_id: item.project_id, 'incomplete': Number(item.incomplete), completed: Number(item.completed) })
projectIdList.add(item.project_id)
})
}
// const result = projectList.reduce((acc, curr) => {
// if (curr.status === '待维修' || curr.status === '维修中') {
// const existingItem = acc.find(item => item.project_id === curr.project_id && item.status === '异常数');
// if (existingItem) {
// existingItem.count += curr.count;
// } else {
// acc.push({ project_id: curr.project_id, status: '异常数', count: curr.count });
// }
// } else if (curr.status === '维修完成') {
// const existingItem = acc.find(item => item.project_id === curr.project_id && item.status === '维修数');
// if (existingItem) {
// existingItem.count += curr.count;
// } else {
// acc.push({ project_id: curr.project_id, status: '维修数', count: curr.count });
// }
// }
// return acc;
// }, [])
//console.log('resssssresult', result)
const projectNameList = await models.ProjectCorrelation.findAll({
attributes:
['id', 'name'],
where: {
id: { $in: [...projectIdList] },
name: {
[Sequelize.Op.not]: null//有name的结果
}
// del: false
}
}) || []
//在ProjectCorrelation中查不到名字,去clickHouse中去查
const projectNameList1 = await models.ProjectCorrelation.findAll({
attributes:
['id', 'name', 'pepProjectId'],
where: {
id: { $in: [...projectIdList] },
name: {
[Sequelize.Op.eq]: null//无name的结果
}
// del: false
}
})
//存放需要去查询clickHouse的id
let idList = new Set()
if (projectNameList1.length) {
projectNameList1.forEach((item) => {
idList.add(item.pepProjectId)
})
}
//pepProject名称
const projectManageName = idList.size ? await clickHouse.projectManage.query(`
SELECT id,project_name FROM t_pim_project
WHERE id IN (${[...idList].join(',')}, -1)
`).toPromise() : []
let project = []
if (projectNameList1.length && projectManageName.length) {
projectManageName.forEach((item) => {
const pepObj = projectNameList1.find((item1) => { return item1.pepProjectId === item.id })
project.push({ id: pepObj.id, projectName: item.project_name })
})
}
//pg的数据和clcikHouse的数据(名字)合并
const resAll = project.concat(projectNameList)
let mergedArray = []
if (resAll.length && projectList) {
mergedArray = projectList.map(obj1 => {
const matchingObj = resAll.find(obj2 => obj2.id === obj1.project_id)
return {
id: obj1.project_id, incomplete: obj1.incomplete, completed: obj1.completed, pepProjectId: matchingObj.id,
projectName: matchingObj.projectName || matchingObj.dataValues.name
}
});
}
// console.log('ididididid', resAll)
// console.log('ididididid', project)
// console.log('ididididid', projectManageName)
// console.log('ididididid', projectNameList)
// console.log('ididididid', projectList)
ctx.status = 200
ctx.body = mergedArray
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询维修记录统计失败'
}
}
}
async function getEquipmentCategory(ctx) {
const { startTime, endTime } = ctx.query
const Sequelize = ctx.fs.dc.ORM
const models = ctx.fs.dc.models
try {
const res = await models.EquipmentMaintenanceRecord.findAll({
attributes: [
'equipment_category',
[Sequelize.fn('COUNT', Sequelize.col('equipment_category')), 'count']
],
where: { reportTime: { $between: [moment(startTime).format('YYYY-MM-DD HH:mm:ss '), moment(endTime).format('YYYY-MM-DD HH:mm:ss ')] } },
group: ['equipment_category']
})
ctx.status = 200
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询设备类型失败'
}
}
}
async function getStatus(ctx) {
const { startTime, endTime } = ctx.query
const Sequelize = ctx.fs.dc.ORM
const models = ctx.fs.dc.models
try {
const res = await models.EquipmentMaintenanceRecord.findAll({
attributes: [
'status',
[Sequelize.fn('COUNT', Sequelize.col('status')), 'count']
],
where: { reportTime: { $between: [moment(startTime).format('YYYY-MM-DD HH:mm:ss '), moment(endTime).format('YYYY-MM-DD HH:mm:ss ')] } },
group: ['status']
})
ctx.status = 200
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: '查询设备类型失败'
}
}
}
module.exports = {
getMaintenceRecordRank, getMaintenceTotal, getEquipmentCategory, getStatus
}

34
api/app/lib/models/system_problem.js

@ -0,0 +1,34 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const SystemProblem = sequelize.define("systemProblem", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true
},
type: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "问题类型:数据库异常、es异常、kafka异常、服务器异常、应用异常、其他",
primaryKey: false,
field: "type",
autoIncrement: false
}
}, {
tableName: "system_problem",
comment: "",
indexes: []
});
dc.models.SystemProblem = SystemProblem;
return SystemProblem;
};

16
api/app/lib/routes/operationData/index.js

@ -0,0 +1,16 @@
'use strict';
const operationData = require('../../controllers/operationData');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/failureTime'] = { content: '获取故障发生时间', visible: true };
router.get('/failureTime', operationData.getFailureTime)
app.fs.api.logAttr['GET/systemAvailability'] = { content: '获取系统可用性', visible: true };
router.get('/systemAvailability', operationData.getSystemAvailability)
app.fs.api.logAttr['GET/problemType'] = { content: '获取故障类型', visible: true };
router.get('/problemType', operationData.getProblemType)
app.fs.api.logAttr['GET/operationsPersonnel'] = { content: '获取运维人员', visible: true };
router.get('/operationsPersonnel', operationData.getOperationsPersonnel)
}

16
api/app/lib/routes/problemData/index.js

@ -0,0 +1,16 @@
'use strict';
const problemData = require('../../controllers/problemData');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/maintenceRecordRank'] = { content: '获取维修记录排名', visible: true };
router.get('/maintenceRecordRank', problemData.getMaintenceRecordRank)
app.fs.api.logAttr['GET/maintenceTotal'] = { content: '获取维修次数统计', visible: true };
router.get('/maintenceTotal', problemData.getMaintenceTotal)
app.fs.api.logAttr['GET/equipmentCategory'] = { content: '获取设备类型', visible: true };
router.get('/equipmentCategory', problemData.getEquipmentCategory)
app.fs.api.logAttr['GET/maintenanceStatus'] = { content: '获取状态', visible: true };
router.get('/maintenanceStatus', problemData.getStatus)
}

15
script/0.28/schema/01create_sysyem_problem.sql

@ -0,0 +1,15 @@
create table public.system_problem(
id serial primary key,
type varchar(20)
);
comment on column public.system_problem.type is '问题类型:数据库异常、es异常、kafka异常、服务器异常、应用异常、其他';
insert into system_problem (id, type)values ('es异常');
insert into system_problem (id, type)values ('数据库异常');
insert into system_problem (id, type)values ('应用异常');
insert into system_problem (id, type)values ('kafka异常');
insert into system_problem (id, type)values ('服务器异常');
insert into system_problem (id, type)values ('DAC进程异常');
insert into system_problem (id, type)values ('K8S集群异常');
insert into system_problem (id, type)values ('redis服务异常');
insert into system_problem (id, type)values ('其他');

5
web/client/src/sections/analysis/actions/index.js

@ -1,7 +1,8 @@
'use strict';
import * as console from './console'
import * as operation from './operationData'
import * as maintenceRecord from './maintenceRecordTotal'
export default {
...console
...console, ...operation, ...maintenceRecord
}

53
web/client/src/sections/analysis/actions/maintenceRecordTotal.js

@ -0,0 +1,53 @@
'use strict';
import { ApiTable, basicAction } from '$utils'
export function getMaintenceRecordRank(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query,
actionType: 'GET_MAINTENCE_RECORD_RANK',
url: ApiTable.getMaintenceRecordRank,
msg: { option: '获取维修记录排名' },
reducer: { name: 'maintenceRecordRank' }
})
}
export function getMaintenceTotal(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query,
actionType: 'GET_MAINTENCE_TOTAL',
url: ApiTable.getMaintenceTotal,
msg: { option: '获取维修次数统计' },
reducer: { name: 'maintenceTotal' }
})
}
export function getEquipmentCategory(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query,
actionType: 'GET_EQUIPMENT_CATEGORY',
url: ApiTable.getEquipmentCategory,
msg: { option: '获取设备类型' },
reducer: { name: 'equipmentCategory' }
})
}
export function getStatus(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query,
actionType: 'GET_STATUS',
url: ApiTable.getMaintenanceStatus,
msg: { option: '获取维修状态数据' },
reducer: { name: 'maintenanceStatus' }
})
}

50
web/client/src/sections/analysis/actions/operationData.js

@ -0,0 +1,50 @@
'use strict';
import { ApiTable, basicAction } from '$utils'
export function getFailureTime() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_FAILURE_TIME',
url: ApiTable.getFailureTime,
msg: { error: '获取故障发生时间分析' },
reducer: { name: 'failureTime' }
})
}
export function getSystemAvailability() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_SYSTEM_AVAILABILITY',
url: ApiTable.getSystemAvailability,
msg: { error: '获取可用性分析' },
reducer: { name: 'systemAvailability' }
})
}
export function getProblemType() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_PROBLEM_TYPE',
url: ApiTable.getProblemType,
msg: { error: '获取故障类型' },
reducer: { name: 'probleType' }
})
}
export function getOperationsPersonnel() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_OPERATIONS_PERSONNEL',
url: ApiTable.getOperationsPersonnel,
msg: { error: '获取故障类型' },
reducer: { name: 'operationsPersonnel' }
})
}

293
web/client/src/sections/analysis/containers/operationData.jsx

@ -1,48 +1,267 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card } from '@douyinfe/semi-ui';
import { Spin, Card, Divider, CardGroup } from '@douyinfe/semi-ui';
import '../style.less'
import ReactECharts from 'echarts-for-react';
const { Meta } = Card;
const Console = (props) => {
const { dispatch, actions, user, loading, socket } = props
useEffect(() => {
// ACTION
// dispatch(actions.example.getMembers(user.orgId))
}, [])
// websocket 使
// useEffect(() => {
// if (socket) {
// socket.on('TEST', function (msg) {
// console.info(msg);
// });
// return () => {
// socket.off("TEST");
// }
// }
// }, [socket])
return (
<>
<div>
<img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative",top: -12, left: -8, }} />
const { dispatch, actions, user, loading, socket, clientHeight } = props
const [left1List, setLeft1List] = useState([])//
const [right1List, setRight1List] = useState([])//
const [left2List, setLeft2List] = useState([])//
const [right2List, setRight2List] = useState([])//
const { analysis } = actions
console.log('actions', actions)
useEffect(() => {
// ACTION
// dispatch(actions.example.getMembers(user.orgId))
}, [])
useEffect(() => {
//
dispatch(analysis.getFailureTime()).then((res) => {
if (res.success) setLeft1List(res.payload.data)
})
//
dispatch(analysis.getSystemAvailability()).then((res) => {
if (res.success) setRight1List(res.payload.data)
})
//
dispatch(analysis.getProblemType()).then((res) => {
if (res.success) setLeft2List(res.payload.data)
})
//
dispatch(analysis.getOperationsPersonnel()).then((res) => {
if (res.success) setRight2List(res.payload.data)
})
}, [])
// websocket 使
// useEffect(() => {
// if (socket) {
// socket.on('TEST', function (msg) {
// console.info(msg);
// });
// return () => {
// socket.off("TEST");
// }
// }
// }, [socket])
return (
<>
{/* <img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative",top: -12, left: -8, }} /> */}
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px', minHeight: clientHeight - 32, display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div>
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>维修记录统计</div>
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>MAINTAIN RECORDS STATISTICAL</div>
</div>
</div>
</>
)
<div style={{ margin: '20px 0', flex: 1, display: 'flex', }}>
<CardGroup type='grid' style={{ width: '100%' }}>
{
[{
t: '故障发生时间分析',
echartOption: {
xAxis: {
type: 'category',
data: left1List.map((item) => {
return item.wmonth
}),
axisLabel: {
interval: 0,//x
rotate: 40
},
},
yAxis: {
type: 'value',
name: "单位:次数",
// nameLocation: "end",
// nameGap: 20
},
legend: {
orient: 'vertical',
right: '5%',
top: '0%',
data: ['异常数']
},
tooltip: {},
grid: {
top: '10%',
right: '5%',
left: '5%',
bottom: '10%',
containLabel: true
},
series: [
{
data: left1List.map((item) => {
return Number(item.count)
}),
type: 'bar',
name: '异常数',
}
]
},
}, {
t: '系统可用性分析',
echartOption: {
xAxis: {
type: 'category',
data: right1List.map((item) => {
return item.wmonth
}),
axisLabel: {
interval: 0,//x
rotate: 40
},
},
yAxis: {
type: 'value',
name: "单位:%",
// nameLocation: "end",
// nameGap: 20
},
legend: {},
tooltip: {},
grid: {
//left: '3%',
//right: '3%',
bottom: '1%',
// top: '24%',
containLabel: true
},
series: [
{
data: right1List.map((item) => {
return Math.round(item.ability * 10000) / 100
}),
type: 'line',
}
]
},
}, {
t: '故障类型分析',
echartOption: {
xAxis: {
type: 'category',
data: left2List.map((item) => {
return item.type
}),
axisLabel: {
interval: 0,//x
rotate: 40
},
},
yAxis: {
type: 'value',
name: "单位:个",
nameLocation: "end",
nameGap: 20
},
legend: {},
tooltip: {},
grid: {
left: '3%',
right: '4%',
bottom: '1%',
// top: '24%',
containLabel: true
},
series: [
{
data: left2List.map((item) => {
return Number(item.count)
}),
type: 'bar',
//name: ''
}
]
},
}, {
t: '运维人员分析',
echartOption: {
tooltip: {},
series: [
{
// name: right2List?.map((item)=>{
// return item.name
// }),
type: 'pie',
radius: '80%',//
data: right2List?.map((item) => {
return { value: item.count, name: item.name }
}),
label: {
}
}
],
media: [
{
query: { minAspectRatio: 1 },
option: {
series: [
{ center: ['50%', '50%'] },
{ center: ['50%', '50%'] }
]
}
},
]
},
},].map((c, idx) => (
<Card
key={idx}
shadows='hover'
title={c.t}
bordered={false}
headerLine={false}
headerStyle={{
padding: '12px',
}}
bodyStyle={{
padding: '12px',
}}
style={{ width: '50%', height: '50%' }}
>
<ReactECharts
notMerge={true}
lazyUpdate={true}
option={c.echartOption}
/>
</Card>
))
}
</CardGroup>
</div>
</div>
</>
)
}
function mapStateToProps (state) {
const { auth, global, members, webSocket } = state;
return {
// loading: members.isRequesting,
// user: auth.user,
// actions: global.actions,
// members: members.data,
// socket: webSocket.socket
};
function mapStateToProps(state) {
const { auth, global, members, webSocket } = state;
return {
// loading: members.isRequesting,
// user: auth.user,
actions: global.actions,
// members: members.data,
// socket: webSocket.socket
clientHeight: global.clientHeight
};
}
export default connect(mapStateToProps)(Console);

372
web/client/src/sections/analysis/containers/problemData.jsx

@ -1,17 +1,83 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card } from '@douyinfe/semi-ui';
import { Spin, Card, CardGroup, DatePicker, Space, RadioGroup, Radio, Divider } from '@douyinfe/semi-ui';
import '../style.less'
import ReactECharts from 'echarts-for-react';
import moment from 'moment'
const { Meta } = Card;
const Console = (props) => {
const { dispatch, actions, user, loading, socket } = props
const { dispatch, actions, user, loading, socket, clientHeight } = props
const [startTime, setStartTime] = useState('1970-1-1')//
const [endTime, setEndTime] = useState('2099-12-31')//
const [mode, setMode] = useState('')//
const [dateValue, setDateValue] = useState([])//
const [left1List, setLeft1List] = useState([])//
const [right1List, setRight1List] = useState([])//
const [left2List, setLeft2List] = useState([])//
const [right2List, setRight2List] = useState([])//
const { analysis } = actions
const getData = (query = { startTime, endTime }) => {
//
dispatch(analysis.getMaintenceRecordRank(query)).then((res) => {
if (res.success) setLeft1List(res.payload.data)
})
//
dispatch(analysis.getMaintenceTotal(query)).then((res) => {
if (res.success) setRight1List(res.payload.data)
})
//
dispatch(analysis.getEquipmentCategory(query)).then((res) => {
if (res.success) setLeft2List(res.payload.data)
})
//
dispatch(analysis.getStatus(query)).then((res) => {
if (res.success) setRight2List(res.payload.data)
})
}
useEffect(() => {
// ACTION
// dispatch(actions.example.getMembers(user.orgId))
}, [])
getData()
}, [startTime,endTime])
//datepicker
const onChangeDate = (e) => {
// alert(111)
setStartTime(moment(e[0]).format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment(e[1]).format('YYYY-MM-DD')+' '+'23:59:59')
setDateValue(e)
// const query={ startTime:e[0]+'', endTime:e[1]+'' }
// getData(query)
setMode('')
}
const clearHandler=()=>{
setStartTime(moment('1970-1-1').startOf('day').format('YYYY-MM-DD'))
setEndTime(moment('2099-12-31').startOf('day').format('YYYY-MM-DD'))
// const query={ startTime:'1970-1-1', endTime:'2099-12-31' }
// getData(query)
}
//
const onSelect = (e) => {
// console.log('21',e)
// console.log('111',moment().startOf('day').format('YYYY-MM-DD '))
setMode(e.target.value)
if (e.target.value === 'day') {
setStartTime(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'))
} else if (e.target.value === 'week') {
setStartTime(moment().startOf('week').format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment().endOf('week').format('YYYY-MM-DD HH:mm:ss'))
} else if (e.target.value === 'month') {
setStartTime(moment().startOf('month').format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment().endOf('month').format('YYYY-MM-DD HH:mm:ss'))
} else if (e.target.value === 'year') {
setStartTime(moment().startOf('year').format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment().endOf('year').format('YYYY-MM-DD HH:mm:ss'))
}
setDateValue([])
}
const data = [3, 2, 1, 0, 1, 2, 3];
// websocket 使
// useEffect(() => {
// console.log(socket)
@ -28,21 +94,303 @@ const Console = (props) => {
return (
<>
<div>
<img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative",top: -12, left: -8,}} />
{/* <img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative",top: -12, left: -8, }} /> */}
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px', minHeight: clientHeight - 32, display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div>
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>维修记录统计</div>
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>MAINTAIN RECORDS STATISTICAL</div>
</div>
</div>
<Divider></Divider>
<div style={{ display: 'flex', alignItems: 'center', marginTop: 10 }}>
<span style={{ margin: '0px 30px 0px 20px' }}>时间选择</span>
<DatePicker type="dateRange"
onChange={e => onChangeDate(e)}
density="compact"
style={{ width: 400 }}
value={dateValue}
onClear={clearHandler}
/>
<RadioGroup onChange={e => onSelect(e)}
value={mode}
type="button">
<Radio value={'day'} >今日</Radio>
<Radio value={'week'}>本周</Radio>
<Radio value={'month'} >本月</Radio>
<Radio value={'year'}>全年</Radio>
</RadioGroup>
</div>
<div style={{ margin: '20px 0', flex: 1, display: 'flex', }}>
<CardGroup type='grid' style={{ width: '100%' }}>
{
[{
t: '维修记录排名',
echartOption: {
xAxis: {
type: 'value',
// axisLabel: {
// interval: 0,//x
// rotate: 40
// },
},
yAxis: {
type: 'category',
data: left1List.map((item) => {
return item.projectName
}),
axisLabel:{formatter: function (arg) {
return arg.length>8?arg.substring(0,8)+'...':arg
}},
// nameLocation: "end",
// nameGap: 20
},
legend: {
orient: 'vertical',
right: '5%',
top: '0%',
icon: 'circle'
},
tooltip: {},
grid: {
top: '10%',
right: '5%',
left: '5%',
bottom: '10%',
containLabel: true
},
series: [
{
data: left1List.map((item) => {
return item.count
}),
type: 'bar',
name: '异常数',
itemStyle: {
color: 'green',
}
}
]
},
}, {
t: '维修次数统计',
echartOption: {
xAxis: {
type: 'category',
data: right1List.map((item=>{
return item.projectName
})),
axisLabel: {
interval: 0,//x
rotate: 40,
formatter: function (arg) {
return arg.length>8?arg.substring(0,8)+'...':arg
}
},
},
yAxis: [{
type: 'value',
name: '单位:次数',
axisLabel: {
formatter: (value) => {
//
if (value < 0) return -value
else return value
}
},
// min: function (value) {
// //-value.max * 1.05 1.05 使
// return (Math.abs(value.min) < value.max ? -value.max * 1.05 : value.min * 1.05).toFixed(2);
// },
// max: function (value) {
// //-value.min * 1.05 1.05 使
// return (Math.abs(value.min) < value.max ? value.max * 1.05 : -value.min * 1.05).toFixed(2);
// }
},
],
legend: {
//orient: 'vertical',//
right: '0%',
// top: '-5%',
icon: 'circle'
},
grid: {
top: '10%',
right: '5%',
left: '5%',
bottom: '10%',
containLabel: true
},
tooltip: {
show:true,
formatter: function (params) {
//console.log('params',params)
return '<div style="white-space: pre-wrap;">'+params.name+'\n'+params.seriesName+':'+Math.abs(params.value)+'</div>'
}
},
itemStyle: {
borderRadius: 5 //
},
series: [
{
// barWidth: 20, //
name: '异常数',
type: 'bar',
stack: '总量', //
data: right1List.map((item=>{
return item.incomplete
})),
label: {
normal: {
show: false,
formatter: (params) => {
const value = params.value;
return '异常数'+Math.abs(value)
}//
}
}
},
{
// barWidth: 20, //
name: '维修数',
type: 'bar',
stack: '总量', //
label: {
normal: {
show: false,
//data: [1, 2, 3],
formatter: (params) => {
const value = params.value;
return '维修数'+Math.abs(value)
}//
}
},
data:right1List.map((item=>{
return ~item.completed+1
}))
}
],
},
}, {
t: '设备类型分析',
echartOption: {
xAxis: {
type: 'category',
data:left2List.map((item)=>{
return item.equipment_category
}),
axisLabel: {
interval: 0,//x
rotate: 40
},
},
yAxis: {
type: 'value',
// name: "",
// nameLocation: "end",
nameGap: 20
},
legend: {},
tooltip: {},
grid: {
left: '3%',
right: '4%',
bottom: '1%',
// top: '24%',
containLabel: true
},
series: [
{
data: left2List.map((item)=>{
return Number(item.count)
}),
type: 'bar',
//name: ''
}
]
},
}, {
t: '维修状态分析',
echartOption: {
tooltip: {},
series: [
{
name:'维修状态分析',
type: 'pie',
radius: '80%',//
data:right2List.map((item=>{
return {value: Number(item.count), name: item.status}
})),
label: {
// name: right2List.map((item=>{
// return{}
// }))
}
}
],
media: [
{
query: { minAspectRatio: 1 },
option: {
series: [
{ center: ['50%', '50%'] },
{ center: ['50%', '50%'] }
]
}
},
]
},
},].map((c, idx) => (
<Card
key={idx}
shadows='hover'
title={c.t}
bordered={false}
headerLine={false}
headerStyle={{
padding: '12px',
}}
bodyStyle={{
padding: '12px',
}}
style={{ width: '50%', height: '50%' }}
>
<ReactECharts
notMerge={true}
lazyUpdate={true}
option={c.echartOption}
/>
</Card>
))
}
</CardGroup>
</div>
</div>
</>
)
}
function mapStateToProps (state) {
function mapStateToProps(state) {
const { auth, global, members, webSocket } = state;
return {
// loading: members.isRequesting,
// user: auth.user,
// actions: global.actions,
actions: global.actions,
// members: members.data,
// socket: webSocket.socket
// socket: webSocket.socket,
clientHeight: global.clientHeight
};
}

2
web/client/src/sections/service/components/maintenanceRecordModal.jsx

@ -104,7 +104,7 @@ const MaintenanceRecordModal = (props) => {
rules={[{ required: true, message: '请选择上报时间' }]}
></Form.DatePicker>
<Form.DatePicker type='dateTime' label='维修完成时间:' field='completedTime'></Form.DatePicker>
<Form.Select label='状态' style={{ width: 200 }} field='status'>
<Form.Select label='状态' style={{ width: 200 }} field='status' rules={[{ required: true, message: '请输入维修状态' }]}>
<Form.Select.Option value='待维修'>待维修</Form.Select.Option>
<Form.Select.Option value='维修中'>维修中</Form.Select.Option>
<Form.Select.Option value='维修完成'>维修完成</Form.Select.Option>

5
web/client/src/sections/service/components/recordModal.jsx

@ -110,12 +110,15 @@ const okHandler=()=>{
})}
</Form.Select.OptGroup> )})}
</Form.Select>
<Form.Select field="type" label={{ text: '故障类型'}} style={{ width: 200 }}>
<Form.Select field="type" label={{ text: '故障类型'}} style={{ width: 200 }} rules={[{ required: true, message:'请输入故障类型' }]}>
<Form.Select.Option value="es异常">es异常</Form.Select.Option>
<Form.Select.Option value="数据库异常">数据库异常</Form.Select.Option>
<Form.Select.Option value="应用异常">应用异常</Form.Select.Option>
<Form.Select.Option value="kafka异常">kafka异常</Form.Select.Option>
<Form.Select.Option value="服务器异常">服务器异常</Form.Select.Option>
<Form.Select.Option value="DAC进程异常">DAC进程异常</Form.Select.Option>
<Form.Select.Option value="K8S集群异常">K8S集群异常</Form.Select.Option>
<Form.Select.Option value="redis服务异常">redis服务异常</Form.Select.Option>
<Form.Select.Option value="其他">其他</Form.Select.Option>
</Form.Select>
<Form.TextArea field="record" label={{text:'故障记录'}} rules={[{ required: true, message:'请输入故障记录' }]}>

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

@ -112,7 +112,24 @@ export const ApiTable = {
//编辑服务器信息维护记录(编辑和新增)
editServerInfoMaintenanceRecord: 'serverInfoMaintenanceRecord',
//删除服务器信息维护记录
delServerInfoMaintenanceRecord: 'serverInfoMaintenanceRecord/{id}'
delServerInfoMaintenanceRecord: 'serverInfoMaintenanceRecord/{id}',
//运维分析-运维数据
//故障发生时间分析
getFailureTime: 'failureTime',
//系统可用性分析
getSystemAvailability: 'systemAvailability',
//故障类型
getProblemType: 'problemType',
//y运维人员
getOperationsPersonnel: 'operationsPersonnel',
//维修记录排名
getMaintenceRecordRank: 'maintenceRecordRank',
//维修数统计
getMaintenceTotal: 'maintenceTotal',
//获取设备类型
getEquipmentCategory: 'equipmentCategory',
//获取状态数据
getMaintenanceStatus: 'maintenanceStatus'
};
// 项企的接口

Loading…
Cancel
Save