完成看板前端开发
This commit is contained in:
byteblogs168 2023-01-14 20:46:02 +08:00
parent 811bcb0727
commit 08b7db297b
26 changed files with 590 additions and 342 deletions

View File

@ -86,7 +86,9 @@ CREATE TABLE `retry_task_log`
`error_message` text NOT NULL COMMENT '异常信息',
PRIMARY KEY (`id`),
KEY `idx_group_name` (`group_name`),
KEY `idx_scene_name` (`scene_name`)
KEY `idx_scene_name` (`scene_name`),
KEY `idx_retry_status` (`retry_status`),
KEY `idx_biz_id` (`biz_id`),
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT='重试日志表'
;

View File

@ -19,7 +19,7 @@ import java.util.Objects;
/**
* RestTemplate 拦截器
*
* @author: shuguang.zhang
* @author: www.byteblogs.com
* @date : 2022-04-17 15:22
*/
public class ExampleClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

View File

@ -1,10 +1,7 @@
package com.example.demo;
import com.x.retry.client.core.client.request.BeatHttpRequestHandler;
import com.x.retry.client.core.strategy.RetryMethod;
import com.x.retry.common.core.model.Result;
import com.x.retry.common.core.util.JsonUtil;
import com.x.retry.server.model.dto.ConfigDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

View File

@ -38,4 +38,4 @@ logging:
x-retry:
server:
host: 192.168.100.3
host: 127.0.0.1

View File

@ -89,7 +89,7 @@ public class ExistsTransactionalRetryServiceTest {
.thenReturn(new Result(0, "5"))
;
try {
for (int i = 0; i < 400; i++) {
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(() -> testExistsTransactionalRetryService.testSimpleInsert(UUID.randomUUID().toString()));
}
} catch (Exception e) {

View File

@ -13,6 +13,7 @@
"dependencies": {
"@ant-design-vue/pro-layout": "^1.0.11",
"@antv/data-set": "^0.10.2",
"@antv/g2": "^3.5.1",
"ant-design-vue": "^1.7.6",
"axios": ">=0.21.1",
"core-js": "^3.1.2",

View File

@ -24,11 +24,54 @@ const api = {
notifyConfigList: '/notify-config/list',
userPageList: '/user/page/list',
saveUser: '/user',
systemUserByUserName: '/user/username/user-info'
systemUserByUserName: '/user/username/user-info',
countTask: '/dashboard/task/count',
countDispatch: '/dashboard/dispatch/count',
countActivePod: '/dashboard/active-pod/count',
rankSceneQuantity: '/dashboard/scene/rank',
lineDispatchQuantity: '/dashboard/dispatch/line'
}
export default api
export function getLineDispatchQuantity (parameter) {
return request({
url: api.lineDispatchQuantity,
method: 'get',
params: parameter
})
}
export function rankSceneQuantity (parameter) {
return request({
url: api.rankSceneQuantity,
method: 'get',
params: parameter
})
}
export function countActivePod () {
return request({
url: api.countActivePod,
method: 'get'
})
}
export function countTask () {
return request({
url: api.countTask,
method: 'get'
})
}
export function countDispatch () {
return request({
url: api.countDispatch,
method: 'get'
})
}
export function getGroupConfigForPage (parameter) {
return request({
url: api.groupConfigForPage,

View File

@ -0,0 +1,78 @@
<template>
<div>
<div id="viewData"></div>
</div>
</template>
<script>
import * as G2 from '@antv/g2'
import { getLineDispatchQuantity } from '@/api/manage'
const DataSet = require('@antv/data-set')
export default {
name: 'G2Line',
data () {
return {
viewRecords: [],
chart: null
}
},
mounted () {
this.getLineDispatchQuantity()
this.createView()
},
methods: {
getLineDispatchQuantity () {
getLineDispatchQuantity().then(res => {
this.viewCharts(res.data)
})
},
viewCharts (viewRecords, type = 'day') {
var ds = new DataSet()
console.log(this.lineDispatchQuantity)
var dv = ds.createView().source(viewRecords)
dv.transform({
type: 'fold',
fields: ['success', 'fail'], //
key: 'name',
value: 'viewTotal',
retains: [ 'total', 'createDt' ]
})
console.log(dv.rows)
this.chart.source(dv, {
date: {
type: 'cat'
}
})
this.chart.axis('viewTotal', {
label: {
textStyle: {
fill: '#aaaaaa'
}
}
})
this.chart.tooltip({
crosshairs: {
type: 'line'
}
})
this.chart.line().position('createDt*viewTotal').color('name', ['#1890ff', '#c28c62']).shape('smooth')
this.chart.point().position('createDt*viewTotal').color('name', ['#1890ff', '#c28c62']).size(4).shape('circle').style({
stroke: '#fff',
lineWidth: 1
})
this.chart.render()
},
createView () {
this.chart = new G2.Chart({
container: 'viewData',
forceFit: true,
height: 410,
padding: [20, 90, 60, 50]
})
}
}
}
</script>

View File

@ -10,6 +10,7 @@ import Radar from '@/components/Charts/Radar'
import RankList from '@/components/Charts/RankList'
import TransferBar from '@/components/Charts/TransferBar'
import TagCloud from '@/components/Charts/TagCloud'
import G2Line from '@/components/Charts/Line'
// pro components
import AvatarList from '@/components/AvatarList'
@ -51,6 +52,7 @@ export {
TagSelect,
StandardFormRow,
ArticleListContent,
G2Line,
Dialog
}

View File

@ -15,8 +15,8 @@
export default {
navTheme: 'dark', // theme for nav menu
primaryColor: '#1890ff', // '#F5222D', // primary color of ant design
layout: 'sidemenu', // nav menu position: `sidemenu` or `topmenu`
contentWidth: 'Fluid', // layout of content: `Fluid` or `Fixed`, only works when layout is topmenu
layout: 'topmenu', // nav menu position: `sidemenu` or `topmenu`
contentWidth: 'Fixed', // layout of content: `Fluid` or `Fixed`, only works when layout is topmenu
fixedHeader: false, // sticky header
fixSiderbar: false, // sticky siderbar
colorWeak: false,

View File

@ -14,34 +14,22 @@ export const asyncRouterMap = [
component: BasicLayout,
meta: { title: 'menu.home' },
redirect: '/basic-config-list',
redirect: '/dashboard/workplace',
children: [
// dashboard
{
path: '/dashboard',
name: 'dashboard',
hidden: true,
redirect: '/dashboard/workplace',
redirect: '/dashboard/analysis',
hideChildrenInMenu: true,
component: RouteView,
meta: { title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: ['dashboard'] },
children: [
{
path: '/dashboard/analysis/:pageNo([1-9]\\d*)?',
path: '/dashboard/analysis',
name: 'Analysis',
component: () => import('@/views/dashboard/Analysis'),
meta: { title: 'menu.dashboard.analysis', keepAlive: false, permission: ['dashboard'] }
},
// 外部链接
{
path: 'https://www.baidu.com/',
name: 'Monitor',
meta: { title: 'menu.dashboard.monitor', target: '_blank' }
},
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/Workplace'),
meta: { title: 'menu.dashboard.workplace', keepAlive: true, permission: ['dashboard'] }
meta: { title: 'menu.dashboard.analysis', keepAlive: true, permission: ['dashboard'] }
}
]
},

View File

@ -1,8 +1,8 @@
export default {
'dashboard.analysis.test': '工专路 {no} 号店',
'dashboard.analysis.introduce': '指标说明',
'dashboard.analysis.total-sales': '总销售额',
'dashboard.analysis.day-sales': '日均销售额¥',
'dashboard.analysis.total-sales': '总任务量',
'dashboard.analysis.day-sales': '日均任务',
'dashboard.analysis.visits': '访问量',
'dashboard.analysis.visits-trend': '访问量趋势',
'dashboard.analysis.visits-ranking': '门店访问量排名',
@ -13,7 +13,7 @@ export default {
'dashboard.analysis.conversion-rate': '转化率',
'dashboard.analysis.operational-effect': '运营活动效果',
'dashboard.analysis.sales-trend': '销售趋势',
'dashboard.analysis.sales-ranking': '门店销售额排名',
'dashboard.analysis.sales-ranking': '异常重试量排名',
'dashboard.analysis.all-year': '全年',
'dashboard.analysis.all-month': '本月',
'dashboard.analysis.all-week': '本周',
@ -27,7 +27,7 @@ export default {
'dashboard.analysis.channel.all': '全部渠道',
'dashboard.analysis.channel.online': '线上',
'dashboard.analysis.channel.stores': '门店',
'dashboard.analysis.sales': '销售额',
'dashboard.analysis.sales': '调度',
'dashboard.analysis.traffic': '客流量',
'dashboard.analysis.table.rank': '排名',
'dashboard.analysis.table.search-keyword': '搜索关键词',

View File

@ -1,64 +1,47 @@
<template>
<div>
<a-row :gutter="24">
<a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" :title="$t('dashboard.analysis.total-sales')" total="¥126,560">
<a-col :sm="24" :md="12" :xl="8" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" :title="$t('dashboard.analysis.total-sales')" :total="taskQuantity.total">
<a-tooltip :title="$t('dashboard.analysis.introduce')" slot="action">
<a-icon type="info-circle-o" />
</a-tooltip>
<div>
<trend flag="up" style="margin-right: 16px;">
<span slot="term">{{ $t('dashboard.analysis.week') }}</span>
12%
</trend>
<trend flag="down">
<span slot="term">{{ $t('dashboard.analysis.day') }}</span>
11%
</trend>
<span slot="term">完成</span>
{{ taskQuantity.finish }}
<a-divider type="vertical" />
<span slot="term">运行中</span>
{{ taskQuantity.running }}
<a-divider type="vertical" />
<span slot="term">最大次数</span>
{{ taskQuantity.maxRetryCount }}
</div>
<template slot="footer">{{ $t('dashboard.analysis.day-sales') }}<span> 234.56</span></template>
</chart-card>
</a-col>
<a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" :title="$t('dashboard.analysis.visits')" :total="8846 | NumberFormat">
<a-col :sm="24" :md="12" :xl="8" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" title="总调度量" :total="dispatchQuantity.total">
<a-tooltip :title="$t('dashboard.analysis.introduce')" slot="action">
<a-icon type="info-circle-o" />
</a-tooltip>
<div>
<mini-area />
<a-tooltip title="成功率">
<a-progress stroke-linecap="square" :percent="dispatchQuantity.successPercent" />
</a-tooltip>
</div>
<template slot="footer">{{ $t('dashboard.analysis.day-visits') }}<span> {{ '1234' | NumberFormat }}</span></template>
</chart-card>
</a-col>
<a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" :title="$t('dashboard.analysis.payments')" :total="6560 | NumberFormat">
<a-col :sm="24" :md="12" :xl="8" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" title="总在线机器" :total="countActivePodQuantity.total">
<a-tooltip :title="$t('dashboard.analysis.introduce')" slot="action">
<a-icon type="info-circle-o" />
</a-tooltip>
<div>
<mini-bar />
<span slot="term">客户端</span>
{{ countActivePodQuantity.clientTotal }}
<a-divider type="vertical" />
<span slot="term">服务端</span>
{{ countActivePodQuantity.serverTotal }}
</div>
<template slot="footer">{{ $t('dashboard.analysis.conversion-rate') }} <span>60%</span></template>
</chart-card>
</a-col>
<a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
<chart-card :loading="loading" :title="$t('dashboard.analysis.operational-effect')" total="78%">
<a-tooltip :title="$t('dashboard.analysis.introduce')" slot="action">
<a-icon type="info-circle-o" />
</a-tooltip>
<div>
<mini-progress color="rgb(19, 194, 194)" :target="80" :percentage="78" height="8px" />
</div>
<template slot="footer">
<trend flag="down" style="margin-right: 16px;">
<span slot="term">{{ $t('dashboard.analysis.week') }}</span>
12%
</trend>
<trend flag="up">
<span slot="term">{{ $t('dashboard.analysis.day') }}</span>
80%
</trend>
</template>
</chart-card>
</a-col>
</a-row>
@ -73,146 +56,31 @@
<a>{{ $t('dashboard.analysis.all-month') }}</a>
<a>{{ $t('dashboard.analysis.all-year') }}</a>
</div>
<a-range-picker :style="{width: '256px'}" />
<div class="extra-item">
<a-range-picker :style="{width: '256px'}" />
</div>
<a-select placeholder="请输入组名称" @change="value => handleChange(value)" :style="{width: '256px'}">
<a-select-option v-for="item in groupNameList" :value="item" :key="item">{{ item }}</a-select-option>
</a-select>
</div>
<a-tab-pane loading="true" :tab="$t('dashboard.analysis.sales')" key="1">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
<bar :data="barData" :title="$t('dashboard.analysis.sales-trend')" />
<g2-line/>
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<rank-list :title="$t('dashboard.analysis.sales-ranking')" :list="rankList"/>
</a-col>
</a-row>
</a-tab-pane>
<a-tab-pane :tab="$t('dashboard.analysis.visits')" key="2">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
<bar :data="barData2" :title="$t('dashboard.analysis.visits-trend')" />
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<rank-list :title="$t('dashboard.analysis.visits-ranking')" :list="rankList"/>
</a-col>
</a-row>
</a-tab-pane>
</a-tabs>
</div>
</a-card>
<div class="antd-pro-pages-dashboard-analysis-twoColLayout" :class="!isMobile && 'desktop'">
<a-row :gutter="24" type="flex" :style="{ marginTop: '24px' }">
<a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
<a-card :loading="loading" :bordered="false" :title="$t('dashboard.analysis.online-top-search')" :style="{ height: '100%' }">
<a-dropdown :trigger="['click']" placement="bottomLeft" slot="extra">
<a class="ant-dropdown-link" href="#">
<a-icon type="ellipsis" />
</a>
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:;">{{ $t('dashboard.analysis.dropdown-option-one') }}</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">{{ $t('dashboard.analysis.dropdown-option-two') }}</a>
</a-menu-item>
</a-menu>
</a-dropdown>
<a-row :gutter="68">
<a-col :xs="24" :sm="12" :style="{ marginBottom: ' 24px'}">
<number-info :total="12321" :sub-total="17.1">
<span slot="subtitle">
<span>{{ $t('dashboard.analysis.search-users') }}</span>
<a-tooltip :title="$t('dashboard.analysis.introduce')" slot="action">
<a-icon type="info-circle-o" :style="{ marginLeft: '8px' }" />
</a-tooltip>
</span>
</number-info>
<!-- miniChart -->
<div>
<mini-smooth-area :style="{ height: '45px' }" :dataSource="searchUserData" :scale="searchUserScale" />
</div>
</a-col>
<a-col :xs="24" :sm="12" :style="{ marginBottom: ' 24px'}">
<number-info :total="2.7" :sub-total="26.2" status="down">
<span slot="subtitle">
<span>{{ $t('dashboard.analysis.per-capita-search') }}</span>
<a-tooltip :title="$t('dashboard.analysis.introduce')" slot="action">
<a-icon type="info-circle-o" :style="{ marginLeft: '8px' }" />
</a-tooltip>
</span>
</number-info>
<!-- miniChart -->
<div>
<mini-smooth-area :style="{ height: '45px' }" :dataSource="searchUserData" :scale="searchUserScale" />
</div>
</a-col>
</a-row>
<div class="ant-table-wrapper">
<a-table
row-key="index"
size="small"
:columns="searchTableColumns"
:dataSource="searchData"
:pagination="{ pageSize: 5 }"
>
<span slot="range" slot-scope="text, record">
<trend :flag="record.status === 0 ? 'up' : 'down'">
{{ text }}%
</trend>
</span>
</a-table>
</div>
</a-card>
</a-col>
<a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
<a-card class="antd-pro-pages-dashboard-analysis-salesCard" :loading="loading" :bordered="false" :title="$t('dashboard.analysis.the-proportion-of-sales')" :style="{ height: '100%' }">
<div slot="extra" style="height: inherit;">
<!-- style="bottom: 12px;display: inline-block;" -->
<span class="dashboard-analysis-iconGroup">
<a-dropdown :trigger="['click']" placement="bottomLeft">
<a-icon type="ellipsis" class="ant-dropdown-link" />
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:;">{{ $t('dashboard.analysis.dropdown-option-one') }}</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">{{ $t('dashboard.analysis.dropdown-option-two') }}</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
<div class="analysis-salesTypeRadio">
<a-radio-group defaultValue="a">
<a-radio-button value="a">{{ $t('dashboard.analysis.channel.all') }}</a-radio-button>
<a-radio-button value="b">{{ $t('dashboard.analysis.channel.online') }}</a-radio-button>
<a-radio-button value="c">{{ $t('dashboard.analysis.channel.stores') }}</a-radio-button>
</a-radio-group>
</div>
</div>
<h4>{{ $t('dashboard.analysis.sales') }}</h4>
<div>
<!-- style="width: calc(100% - 240px);" -->
<div>
<v-chart :force-fit="true" :height="405" :data="pieData" :scale="pieScale">
<v-tooltip :showTitle="false" dataKey="item*percent" />
<v-axis />
<!-- position="right" :offsetX="-140" -->
<v-legend dataKey="item"/>
<v-pie position="percent" color="item" :vStyle="pieStyle" />
<v-coord type="theta" :radius="0.75" :innerRadius="0.6" />
</v-chart>
</div>
</div>
</a-card>
</a-col>
</a-row>
</div>
</div>
</template>
<script>
import moment from 'moment'
// import moment from 'moment'
import {
ChartCard,
MiniArea,
@ -220,88 +88,12 @@ import {
MiniProgress,
RankList,
Bar,
Trend,
NumberInfo,
MiniSmoothArea
MiniSmoothArea,
G2Line
} from '@/components'
import { baseMixin } from '@/store/app-mixin'
const barData = []
const barData2 = []
for (let i = 0; i < 12; i += 1) {
barData.push({
x: `${i + 1}`,
y: Math.floor(Math.random() * 1000) + 200
})
barData2.push({
x: `${i + 1}`,
y: Math.floor(Math.random() * 1000) + 200
})
}
const rankList = []
for (let i = 0; i < 7; i++) {
rankList.push({
name: '白鹭岛 ' + (i + 1) + ' 号店',
total: 1234.56 - i * 100
})
}
const searchUserData = []
for (let i = 0; i < 7; i++) {
searchUserData.push({
x: moment().add(i, 'days').format('YYYY-MM-DD'),
y: Math.ceil(Math.random() * 10)
})
}
const searchUserScale = [
{
dataKey: 'x',
alias: '时间'
},
{
dataKey: 'y',
alias: '用户数',
min: 0,
max: 10
}]
const searchData = []
for (let i = 0; i < 50; i += 1) {
searchData.push({
index: i + 1,
keyword: `搜索关键词-${i}`,
count: Math.floor(Math.random() * 1000),
range: Math.floor(Math.random() * 100),
status: Math.floor((Math.random() * 10) % 2)
})
}
const DataSet = require('@antv/data-set')
const sourceData = [
{ item: '家用电器', count: 32.2 },
{ item: '食用酒水', count: 21 },
{ item: '个护健康', count: 17 },
{ item: '服饰箱包', count: 13 },
{ item: '母婴产品', count: 9 },
{ item: '其他', count: 7.8 }
]
const pieScale = [{
dataKey: 'percent',
min: 0,
formatter: '.0%'
}]
const dv = new DataSet.View().source(sourceData)
dv.transform({
type: 'percent',
field: 'count',
dimension: 'item',
as: 'percent'
})
const pieData = dv.rows
import { getAllGroupNameList, countTask, countDispatch, countActivePod, rankSceneQuantity } from '@/api/manage'
export default {
name: 'Analysis',
@ -313,27 +105,30 @@ export default {
MiniProgress,
RankList,
Bar,
Trend,
NumberInfo,
MiniSmoothArea
MiniSmoothArea,
G2Line
},
data () {
return {
loading: true,
rankList,
//
searchUserData,
searchUserScale,
searchData,
barData,
barData2,
//
pieScale,
pieData,
sourceData,
rankList: [],
groupNameList: [],
taskQuantity: {
total: 0,
running: 0,
finish: 0,
maxRetryCount: 0
},
dispatchQuantity: {
successPercent: '0',
total: 0
},
countActivePodQuantity: {
clientTotal: 0,
serverTotal: 0,
total: 0
},
pieStyle: {
stroke: '#fff',
lineWidth: 1
@ -341,32 +136,35 @@ export default {
}
},
computed: {
searchTableColumns () {
return [
{
dataIndex: 'index',
title: this.$t('dashboard.analysis.table.rank'),
width: 90
},
{
dataIndex: 'keyword',
title: this.$t('dashboard.analysis.table.search-keyword')
},
{
dataIndex: 'count',
title: this.$t('dashboard.analysis.table.users')
},
{
dataIndex: 'range',
title: this.$t('dashboard.analysis.table.weekly-range'),
align: 'right',
sorter: (a, b) => a.range - b.range,
scopedSlots: { customRender: 'range' }
}
]
}
},
methods: {
},
created () {
getAllGroupNameList().then(res => {
this.groupNameList = res.data
})
countTask().then(res => {
this.taskQuantity = res.data
})
countDispatch().then(res => {
this.dispatchQuantity = res.data
})
countActivePod().then(res => {
this.countActivePodQuantity = res.data
})
rankSceneQuantity().then(res => {
res.data.forEach(res => {
this.rankList.push({
name: res.groupName + '/' + res.sceneName,
total: res.total
})
})
})
setTimeout(() => {
this.loading = !this.loading
}, 1000)

View File

@ -21,7 +21,7 @@ import java.util.Objects;
/**
* 请求头和响应头传递
*
* @author: shuguang.zhang
* @author: www.byteblogs.com
* @date : 2022-04-18 09:19
*/
@Aspect

View File

@ -5,7 +5,7 @@ import lombok.Data;
/**
* x-retry 请求头信息
*
* @author: shuguang.zhang
* @author: www.byteblogs.com
* @date : 2022-04-16 22:20
*/
@Data

View File

@ -2,6 +2,12 @@ package com.x.retry.server.persistence.mybatis.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.x.retry.server.persistence.mybatis.po.RetryTaskLog;
import com.x.retry.server.web.model.response.ActivePodQuantityResponseVO;
import com.x.retry.server.web.model.response.DispatchQuantityResponseVO;
import com.x.retry.server.web.model.response.SceneQuantityRankResponseVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface RetryTaskLogMapper extends BaseMapper<RetryTaskLog> {
@ -9,4 +15,13 @@ public interface RetryTaskLogMapper extends BaseMapper<RetryTaskLog> {
RetryTaskLog selectByPrimaryKey(Long id);
}
long countTaskTotal();
long countTaskByRetryStatus(@Param("retryStatus") Integer retryStatus);
List<SceneQuantityRankResponseVO> rankSceneQuantity(@Param("groupName") String groupName);
List<DispatchQuantityResponseVO> lineDispatchQuantity(@Param("groupName") String groupName,
@Param("retryStatus") Integer retryStatus);
}

View File

@ -0,0 +1,26 @@
package com.x.retry.server.service;
import com.x.retry.server.web.model.response.ActivePodQuantityResponseVO;
import com.x.retry.server.web.model.response.DispatchQuantityResponseVO;
import com.x.retry.server.web.model.response.SceneQuantityRankResponseVO;
import com.x.retry.server.web.model.response.TaskQuantityResponseVO;
import java.util.List;
/**
* @author: www.byteblogs.com
* @date : 2022-04-22 20:19
*/
public interface DashBoardService {
TaskQuantityResponseVO countTask();
DispatchQuantityResponseVO countDispatch();
ActivePodQuantityResponseVO countActivePod();
List<SceneQuantityRankResponseVO> rankSceneQuantity(String groupName);
List<DispatchQuantityResponseVO> lineDispatchQuantity(String groupName);
}

View File

@ -0,0 +1,108 @@
package com.x.retry.server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.x.retry.common.core.enums.NodeTypeEnum;
import com.x.retry.common.core.enums.RetryStatusEnum;
import com.x.retry.server.persistence.mybatis.mapper.RetryTaskLogMapper;
import com.x.retry.server.persistence.mybatis.mapper.ServerNodeMapper;
import com.x.retry.server.persistence.mybatis.po.RetryTaskLog;
import com.x.retry.server.persistence.mybatis.po.ServerNode;
import com.x.retry.server.service.DashBoardService;
import com.x.retry.server.web.model.response.ActivePodQuantityResponseVO;
import com.x.retry.server.web.model.response.DispatchQuantityResponseVO;
import com.x.retry.server.web.model.response.SceneQuantityRankResponseVO;
import com.x.retry.server.web.model.response.TaskQuantityResponseVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author: www.byteblogs.com
* @date : 2022-04-22 20:19
*/
@Service
public class DashBoardServiceImpl implements DashBoardService {
@Autowired
private RetryTaskLogMapper retryTaskLogMapper;
@Autowired
private ServerNodeMapper serverNodeMapper;
@Override
public TaskQuantityResponseVO countTask() {
TaskQuantityResponseVO taskQuantityResponseVO = new TaskQuantityResponseVO();
taskQuantityResponseVO.setTotal(retryTaskLogMapper.countTaskTotal());
taskQuantityResponseVO.setFinish(retryTaskLogMapper.countTaskByRetryStatus(RetryStatusEnum.FINISH.getLevel()));
taskQuantityResponseVO.setMaxRetryCount(retryTaskLogMapper.countTaskByRetryStatus(RetryStatusEnum.MAX_RETRY_COUNT.getLevel()));
taskQuantityResponseVO.setRunning(taskQuantityResponseVO.getTotal() - taskQuantityResponseVO.getFinish() - taskQuantityResponseVO.getMaxRetryCount());
return taskQuantityResponseVO;
}
@Override
public DispatchQuantityResponseVO countDispatch() {
DispatchQuantityResponseVO dispatchQuantityResponseVO = new DispatchQuantityResponseVO();
Long total = retryTaskLogMapper.selectCount(null);
dispatchQuantityResponseVO.setTotal(total);
if (total == 0) {
return dispatchQuantityResponseVO;
}
Long success = retryTaskLogMapper.selectCount(new LambdaQueryWrapper<RetryTaskLog>()
.in(RetryTaskLog::getRetryStatus, RetryStatusEnum.MAX_RETRY_COUNT.getLevel(),
RetryStatusEnum.FINISH.getLevel()));
dispatchQuantityResponseVO.setSuccessPercent(BigDecimal.valueOf(success).divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP));
return dispatchQuantityResponseVO;
}
@Override
public ActivePodQuantityResponseVO countActivePod() {
ActivePodQuantityResponseVO activePodQuantityResponseVO = new ActivePodQuantityResponseVO();
activePodQuantityResponseVO.setTotal(serverNodeMapper.selectCount(null));
activePodQuantityResponseVO.setServerTotal(serverNodeMapper.selectCount(new LambdaQueryWrapper<ServerNode>().eq(ServerNode::getNodeType, NodeTypeEnum.SERVER.getType())));
activePodQuantityResponseVO.setClientTotal(serverNodeMapper.selectCount(new LambdaQueryWrapper<ServerNode>().eq(ServerNode::getNodeType, NodeTypeEnum.CLIENT.getType())));
return activePodQuantityResponseVO;
}
@Override
public List<SceneQuantityRankResponseVO> rankSceneQuantity(String groupName) {
return retryTaskLogMapper.rankSceneQuantity(groupName);
}
@Override
public List<DispatchQuantityResponseVO> lineDispatchQuantity(String groupName) {
List<DispatchQuantityResponseVO> totalDispatchQuantityResponseList = retryTaskLogMapper.lineDispatchQuantity(groupName, null);
List<DispatchQuantityResponseVO> successDispatchQuantityResponseList = retryTaskLogMapper.lineDispatchQuantity(groupName, RetryStatusEnum.FINISH.getLevel());
Map<String, DispatchQuantityResponseVO> dispatchQuantityResponseVOMap = successDispatchQuantityResponseList.stream().collect(Collectors.toMap(DispatchQuantityResponseVO::getCreateDt, i -> i));
for (DispatchQuantityResponseVO dispatchQuantityResponseVO : totalDispatchQuantityResponseList) {
DispatchQuantityResponseVO quantityResponseVO = dispatchQuantityResponseVOMap.get(dispatchQuantityResponseVO.getCreateDt());
if (Objects.isNull(dispatchQuantityResponseVO)) {
dispatchQuantityResponseVO.setSuccess(0L);
} else {
dispatchQuantityResponseVO.setSuccess(quantityResponseVO.getTotal());
}
dispatchQuantityResponseVO.setFail(dispatchQuantityResponseVO.getTotal() - dispatchQuantityResponseVO.getSuccess());
}
return totalDispatchQuantityResponseList;
}
}

View File

@ -74,6 +74,8 @@ public class RetryDeadLetterServiceImpl implements RetryDeadLetterService {
RetryTask retryTask = new RetryTask();
BeanUtils.copyProperties(retryDeadLetter, retryTask);
retryTask.setNextTriggerAt(WaitStrategies.randomWait(1, TimeUnit.SECONDS, 60, TimeUnit.SECONDS).computeRetryTime(null));
retryTask.setCreateDt(LocalDateTime.now());
retryTask.setUpdateDt(LocalDateTime.now());
RequestDataHelper.setPartition(groupName);
Assert.isTrue(1 == retryTaskMapper.insert(retryTask), new XRetryServerException("新增重试任务失败"));

View File

@ -18,7 +18,7 @@ import org.springframework.stereotype.Component;
/**
* 不重试,只更新下次触发时间
*
* @author: shuguang.zhang
* @author: www.byteblogs.com
* @date : 2022-04-14 16:11
*/
@Component("NoRetryActor")

View File

@ -0,0 +1,59 @@
package com.x.retry.server.web.controller;
import com.x.retry.server.service.DashBoardService;
import com.x.retry.server.web.annotation.LoginRequired;
import com.x.retry.server.web.model.response.ActivePodQuantityResponseVO;
import com.x.retry.server.web.model.response.DispatchQuantityResponseVO;
import com.x.retry.server.web.model.response.SceneQuantityRankResponseVO;
import com.x.retry.server.web.model.response.TaskQuantityResponseVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author: www.byteblogs.com
* @date : 2022-04-22 20:17
*/
@RestController
@RequestMapping("/dashboard")
public class DashBoardController {
@Autowired
private DashBoardService dashBoardService;
@LoginRequired
@GetMapping("/task/count")
public TaskQuantityResponseVO countTask() {
return dashBoardService.countTask();
}
@LoginRequired
@GetMapping("/dispatch/count")
public DispatchQuantityResponseVO countDispatch() {
return dashBoardService.countDispatch();
}
@LoginRequired
@GetMapping("/active-pod/count")
public ActivePodQuantityResponseVO countActivePod() {
return dashBoardService.countActivePod();
}
@LoginRequired
@GetMapping("/scene/rank")
public List<SceneQuantityRankResponseVO> rankSceneQuantity(@RequestParam(value = "groupName", required = false)
String groupName) {
return dashBoardService.rankSceneQuantity(groupName);
}
@LoginRequired
@GetMapping("/dispatch/line")
public List<DispatchQuantityResponseVO> lineDispatchQuantity(@RequestParam(value = "groupName", required = false)
String groupName) {
return dashBoardService.lineDispatchQuantity(groupName);
}
}

View File

@ -0,0 +1,17 @@
package com.x.retry.server.web.model.response;
import lombok.Data;
/**
* @author: www.byteblogs.com
* @date : 2022-04-23 10:39
*/
@Data
public class ActivePodQuantityResponseVO {
private Long total;
private Long clientTotal;
private Long serverTotal;
}

View File

@ -0,0 +1,24 @@
package com.x.retry.server.web.model.response;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author: www.byteblogs.com
* @date : 2022-04-22 20:27
*/
@Data
public class DispatchQuantityResponseVO {
private Long total;
private BigDecimal successPercent = BigDecimal.ZERO;
private Long success;
private Long fail;
private String createDt;
}

View File

@ -0,0 +1,17 @@
package com.x.retry.server.web.model.response;
import lombok.Data;
/**
* @author: www.byteblogs.com
* @date : 2022-04-23 10:56
*/
@Data
public class SceneQuantityRankResponseVO {
private String groupName;
private String sceneName;
private String total;
}

View File

@ -0,0 +1,22 @@
package com.x.retry.server.web.model.response;
import lombok.Data;
/**
* @author: www.byteblogs.com
* @date : 2022-04-22 20:27
*/
@Data
public class TaskQuantityResponseVO {
private Long total;
private Long running;
private Long finish;
private Long maxRetryCount;
}

View File

@ -1,31 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.x.retry.server.persistence.mybatis.mapper.RetryTaskLogMapper">
<resultMap id="BaseResultMap" type="com.x.retry.server.persistence.mybatis.po.RetryTaskLog">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
<result column="biz_id" jdbcType="VARCHAR" property="bizId" />
<result column="biz_no" jdbcType="VARCHAR" property="bizNo" />
<result column="executor_name" jdbcType="VARCHAR" property="executorName" />
<result column="args_str" jdbcType="VARCHAR" property="argsStr" />
<result column="ext_attrs" jdbcType="VARCHAR" property="extAttrs" />
<result column="retry_status" jdbcType="TINYINT" property="retryStatus" />
<result column="error_message" jdbcType="VARCHAR" property="errorMessage" />
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
</resultMap>
<sql id="Base_Column_List">
id, group_name, scene_name, biz_id, biz_no, executor_name, args_str, ext_attrs, retry_status, error_message,
<resultMap id="BaseResultMap" type="com.x.retry.server.persistence.mybatis.po.RetryTaskLog">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName"/>
<result column="scene_name" jdbcType="VARCHAR" property="sceneName"/>
<result column="biz_id" jdbcType="VARCHAR" property="bizId"/>
<result column="biz_no" jdbcType="VARCHAR" property="bizNo"/>
<result column="executor_name" jdbcType="VARCHAR" property="executorName"/>
<result column="args_str" jdbcType="VARCHAR" property="argsStr"/>
<result column="ext_attrs" jdbcType="VARCHAR" property="extAttrs"/>
<result column="retry_status" jdbcType="TINYINT" property="retryStatus"/>
<result column="error_message" jdbcType="VARCHAR" property="errorMessage"/>
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt"/>
</resultMap>
<sql id="Base_Column_List">
id
, group_name, scene_name, biz_id, biz_no, executor_name, args_str, ext_attrs, retry_status, error_message,
create_dt
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from retry_task_log
where id = #{id,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from retry_task_log
where id = #{id,jdbcType=BIGINT}
</delete>
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from retry_task_log
where id = #{id,jdbcType=BIGINT}
</select>
<select id="countTaskTotal" resultType="java.lang.Long">
select count(*)
from (
select group_name, scene_name, biz_id from retry_task_log group by group_name, scene_name, biz_id) a
</select>
<select id="countTaskByRetryStatus" resultType="java.lang.Long">
select count(*)
from (
select group_name, scene_name, biz_id
from retry_task_log
where retry_status = #{retryStatus}
group by group_name, scene_name, biz_id) a
</select>
<select id="rankSceneQuantity"
resultType="com.x.retry.server.web.model.response.SceneQuantityRankResponseVO">
select group_name, scene_name, count(*) total
from (
select group_name, scene_name, biz_id, count(*)
from retry_task_log
group by group_name, scene_name, biz_id) a
<where>
<if test="groupName != '' and groupName != null">
group_name = #{groupName}
</if>
</where>
group by group_name, scene_name
order by total desc;
</select>
<select id="lineDispatchQuantity"
resultType="com.x.retry.server.web.model.response.DispatchQuantityResponseVO">
select distinct(create_dt), count(*) total
from (
select group_name, scene_name, biz_id, DATE_FORMAT(create_dt, '%Y-%m-%d') as create_dt, count(*)
from retry_task_log
<where>
<if test="groupName != '' and groupName != null">
group_name = #{groupName}
</if>
<if test="retryStatus!=null ">
retry_status = #{retryStatus}
</if>
</where>
group by group_name, scene_name, biz_id, create_dt) a
group by create_dt
order by total desc;
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete
from retry_task_log
where id = #{id,jdbcType=BIGINT}
</delete>
</mapper>