wenlele 1 year ago
parent
commit
c56297e987
  1. 48
      api/app/lib/controllers/homepage/index.js
  2. 2
      api/app/lib/index.js
  3. 62
      api/app/lib/models/quality_check_alarm.js
  4. 4
      api/app/lib/routes/homepage/index.js
  5. 9
      scripts/0.0.9/02_add_table_dbStatistics.sql
  6. 32
      web/client/src/sections/homePage/components/alarmList.js
  7. 14
      web/client/src/sections/homePage/components/dataShare.js
  8. 13
      web/client/src/sections/homePage/components/dataTop5.js
  9. 43
      web/client/src/sections/homePage/components/hotspotData.js
  10. 3
      web/client/src/sections/homePage/components/public/noData.js
  11. 3
      web/client/src/sections/homePage/components/public/table-card.js

48
api/app/lib/controllers/homepage/index.js

@ -171,9 +171,36 @@ function getRestfulInfo(opts) {
model: models.RestfulApi,
}]
})
let top3Obj = {}
allApis.rows.map(s => {
if (top3Obj[s.restServiceId]) {
top3Obj[s.restServiceId] = { ...top3Obj[s.restServiceId], count: top3Obj[s.restServiceId].count + 1 }
} else {
top3Obj[s.restServiceId] = { count: 1, name: s.restfulApi.name }
}
})
let top3Arr = []
Object.keys(top3Obj).map(key => {
top3Arr.push({
id: key,
name: top3Obj[key].name,
count: top3Obj[key].count
})
})
let total = allApis.count;
let todayArr = allApis.rows.filter(s => moment().startOf('d') < moment(s.visitTime) && moment().endOf('d') > moment(s.visitTime))
let todayUser = Array.from(new Set(todayArr.map(s => s.token)))
let totalUser = Array.from(new Set(allApis.rows.map(s => s.token)))
ctx.status = 200;
ctx.body = { allApis };
ctx.body = {
total, //接口访问总次数
todayTotal: todayArr.length, //今日访问次数
todayUser: todayUser.length, //今日访问用户数
totalUser: totalUser.length,//接口访问用户总数
top3: top3Arr.sort((a, b) => b.count - a.count).slice(0, 3),//接口访问次数top3
};
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
@ -182,10 +209,27 @@ function getRestfulInfo(opts) {
}
}
function getQualityCheckAlarm(opts) {
return async function (ctx, next) {
const models = ctx.fs.dc.models;
try {
let alarms = await models.QualityCheckAlarm.findAll({})
ctx.status = 200;
ctx.body = alarms;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '获取预警列表失败' }
}
}
}
module.exports = {
getNodeResources,
getDataTotalTop5,
getClusterInfo,
getRestfulInfo
getRestfulInfo,
getQualityCheckAlarm
}

2
api/app/lib/index.js

@ -101,5 +101,5 @@ module.exports.models = function (dc) {
DbStatistics.belongsTo(DataSource, { foreignKey: 'sourceId', targetKey: 'id' });
DataSource.hasMany(DbStatistics, { foreignKey: 'sourceId', sourceKey: 'id' })
RestfulApiRecord.belongsTo(RestfulApi, { foreignKey: 'restServiceId', targetKey: 'id' })
RestfulApiRecord.belongsTo(RestfulApi, { foreignKey: 'restServiceId', targetKey: 'id' });
};

62
api/app/lib/models/quality_check_alarm.js

@ -0,0 +1,62 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const QualityCheckAlarm = sequelize.define("qualityCheckAlarm", {
id: {
type: DataTypes.BIGINT,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true
},
key: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "key",
autoIncrement: false,
unique: "idx_unique_alarm_key"
},
content: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "content",
autoIncrement: false
},
level: {
type: DataTypes.BIGINT,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "level",
autoIncrement: false
},
time: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
}
}, {
tableName: "t_quality_check_alarm",
comment: "",
indexes: []
});
dc.models.QualityCheckAlarm = QualityCheckAlarm;
return QualityCheckAlarm;
};

4
api/app/lib/routes/homepage/index.js

@ -15,4 +15,8 @@ module.exports = function (app, router, opts, AuthCode) {
app.fs.api.logAttr['GET/homepage/restful/info'] = { content: '获取restful统计信息', visible: true };
router.get('/homepage/restful/info', backups.getRestfulInfo(opts))
app.fs.api.logAttr['GET/homepage/alarms'] = { content: '获取预警列表', visible: true };
router.get('/homepage/alarms', backups.getQualityCheckAlarm(opts))
};

9
scripts/0.0.9/02_add_table_dbStatistics.sql

@ -0,0 +1,9 @@
DROP TABLE IF EXISTS "public"."dbStatistics";
CREATE TABLE "public"."dbStatistics" (
id serial NOT NULL PRIMARY KEY,
"dbName" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
"sourceId" int4 NOT NULL,
"dbRecordCount" int8 NOT NULL,
"time" timestamptz NOT NULL,
"description" varchar(255) NULL
);

32
web/client/src/sections/homePage/components/alarmList.js

@ -2,28 +2,34 @@ import React, { useEffect, useState } from 'react'
import Box from './public/table-card';
import CarouselList from './public/carousel-list';
import { Tooltip } from 'antd';
import moment from 'moment';
import NoData from './public/noData';
import { useFsRequest } from '$utils';
function AlarmList(props) {
const { cardContentHeight } = props;
const { data: alarms = [] } = useFsRequest({
url: 'homepage/alarms',
pollingInterval: 1000 * 60,
cacheKey: 'alarms',
});
const data = alarms.map(s => {
return [
s.content,
s.level == 1 ? '一级' : s.level == 2 ? '二级' : s.level == 3 ? '三级' : '四级',
moment(s.time).format('YYYY-MM-DD HH:mm:ss')
]
})
const data = [
['预警内容预警内容预警内容预警内容预警内容', '一级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '二级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '三级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '四级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '一级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '三级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '四级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '二级', '2022-12-08 12:22:11'],
['预警内容预警内容预警内容预警内容预警内容', '一级', '2022-12-08 12:22:11'],
]
const renderBody = () => {
return <CarouselList
header={['预警内容', '预警等级', '预警时间']}
data={data?.map(s => {
return [
<Tooltip placement="top" title={s[0]}>
{s[0].length > 20 ? s[0]?.taskNamesubstring(0, 20) + '...' : s[0]}
{s[0].length > 20 ? s[0]?.substring(0, 20) + '...' : s[0]}
</Tooltip>,
<div style={{ color: s[1] == '一级' ? 'rgba(245, 27, 27, 1)' : s[1] == '二级' ? '#FF7900' : s[1] == '三级' ? '#FFCD00' : '#00DA9F' }}>{s[1]}</div>,
s[2]
@ -37,7 +43,7 @@ function AlarmList(props) {
}
return <Box title={"预警列表"}>
{renderBody()}
{alarms?.length > 0 ? renderBody() : <NoData />}
</Box>
}

14
web/client/src/sections/homePage/components/dataShare.js

@ -10,6 +10,12 @@ function DataShare(props) {
cacheKey: 'datatotal',
});
const { data: restfulInfo = {} } = useFsRequest({
url: 'homepage/restful/info',
pollingInterval: 1000 * 60,
cacheKey: 'restfulInfo',
});
const renderItem = (s) => {
return <div className='_item_content'>
<div className={'_item_icon' + s.key} />
@ -24,11 +30,11 @@ function DataShare(props) {
const leftData = [
{ key: '1', data: mathRound(dataTotal?.total), unit: '万条', title: '共享库数据总量' },
{ key: '2', data: 2000, unit: '次', title: '访问接口总次数' },
{ key: '3', data: 2000, unit: '个', title: '访问接口用户总数' }]
{ key: '2', data: restfulInfo?.total, unit: '次', title: '访问接口总次数' },
{ key: '3', data: restfulInfo?.totalUser, unit: '个', title: '访问接口用户总数' }]
const rightData = [
{ key: '2', data: 2000, unit: '次', title: '接口访问次数' },
{ key: '3', data: 2000, unit: '个', title: '访问接口用户总数' }]
{ key: '2', data: restfulInfo?.todayTotal, unit: '次', title: '接口访问次数' },
{ key: '3', data: restfulInfo?.todayUser, unit: '个', title: '访问接口用户总数' }]
return <Box title={"数据共享"} >
<div className='data_share'>

13
web/client/src/sections/homePage/components/dataTop5.js

@ -4,7 +4,7 @@ import ReactEcharts from 'echarts-for-react';
import './style.less';
import { useFsRequest } from '$utils';
import { mathRound } from './util';
import NoData from './public/noData';
function DataTop5(props) {
const { cardContentHeight } = props;
const { data: dataTotal = {} } = useFsRequest({
@ -245,8 +245,15 @@ function DataTop5(props) {
}
return <Box title={"数据量TOP5单位"} bodyPaddingTop={1} >
<div className='data_top5_unit'>数据量万条</div>
{renderBody()}
{
dataTotal?.top5?.length > 0 ?
<>
<div className='data_top5_unit'>数据量万条</div>
{renderBody()}
</>
: <NoData />
}
</Box>
}

43
web/client/src/sections/homePage/components/hotspotData.js

@ -1,22 +1,39 @@
import React, { useEffect, useState } from 'react'
import Box from './public/table-card';
import NoData from './public/noData';
import './style.less';
import { ApiTable, useFsRequest } from '$utils';
function HotspotData(props) {
const { data: restfulInfo = {} } = useFsRequest({
url: 'homepage/restful/info',
pollingInterval: 1000 * 60,
cacheKey: 'restfulInfo',
});
const top3 = restfulInfo?.top3
return <Box title={"热点数据"} bodyPaddingTop={25} >
<div className='hotspot_data_container'>
<div className='_img'></div>
<div className='_top1'>
<span className='hotspot_title'>热点数据TOP1</span><div className='hotspot_data_number'>256</div>
</div>
<div className='_top2'>
<span className='hotspot_title'>热点数据TOP2</span><div className='hotspot_data_number'>256</div>
</div>
<div className='_top3'>
<span className='hotspot_title'>热点数据TOP3</span><div className='hotspot_data_number'>256</div>
</div>
</div>
{top3?.length > 0 ?
<div className='hotspot_data_container'>
<div className='_img'></div>
<div className='_top1'>
<span className='hotspot_title' title={top3[0].name}>{top3[0].name?.length > 6 ? top3[0].name.substring(0, 6) + '...' : top3[0].name}</span>
<div className='hotspot_data_number'>{top3[0].count}</div>
</div>
<div className='_top2'>
{top3?.length > 2 && <>
<span className='hotspot_title' title={top3[2].name}>{top3[2].name?.length > 6 ? top3[2].name.substring(0, 6) + '...' : top3[2].name}</span>
<div className='hotspot_data_number'>{top3[2].count}</div>
</>}
</div>
<div className='_top3'>
{top3?.length > 1 && <>
<span className='hotspot_title' title={top3[1].name}>{top3[1].name?.length > 6 ? top3[1].name.substring(0, 6) + '...' : top3[1].name}</span>
<div className='hotspot_data_number'>{top3[1].count}</div>
</>}
</div>
</div> : <NoData />
}
</Box>
}

3
web/client/src/sections/homePage/components/public/noData.js

@ -5,14 +5,13 @@ import { Empty } from 'antd';
function NoData({ height = 180, marginTop = 0 }) {
return (
<Empty
image="/assets/images/homepage/bigscreen/empty.png"
image="/assets/images/homePage/bigscreen/empty.png"
imageStyle={{
height,
marginTop
}}
description={false}
/>
);
}

3
web/client/src/sections/homePage/components/public/table-card.js

@ -7,9 +7,8 @@ class Box extends React.Component {
const { title, height = '100%', children, bodyPaddingTop = 1, titlePaddingTop, margin, overflow } = this.props
const headerbg = {
background: 'url(/assets/images/homepage/bigscreen/headertitlebg.png) no-repeat',
background: 'url(/assets/images/homePage/bigscreen/headertitlebg.png) no-repeat',
backgroundSize: '100% 100%',
}
return (
<div style={{ height, width: '100%', margin: margin || "0px 0px 28px" }}>

Loading…
Cancel
Save