Merge remote-tracking branch 'origin/feature/1.6.2'
This commit is contained in:
@@ -21,15 +21,15 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.db.template;
|
||||
|
||||
import fun.asgc.neutrino.core.db.annotation.Id;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import fun.asgc.neutrino.core.util.ReflectUtil;
|
||||
import fun.asgc.neutrino.core.util.TypeUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -38,6 +38,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class JdbcOperations {
|
||||
private static final JdbcOperations instance = new JdbcOperations();
|
||||
|
||||
private static final Map<Class<?>, Field> generateIdFieldMap = new ConcurrentHashMap<>();
|
||||
|
||||
private JdbcOperations() {
|
||||
|
||||
@@ -85,6 +87,45 @@ public class JdbcOperations {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行更新操作
|
||||
* 临时兼容返设主键问题
|
||||
* @param conn
|
||||
* @param sql
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public int executeUpdateByModel(final Connection conn , final String sql, final Object model, final Object[] params) throws SQLException {
|
||||
return this.execute(new PreparedStatementJdbcCallback<Integer>(){
|
||||
@Override
|
||||
public Integer execute(PreparedStatement ps) throws SQLException {
|
||||
Integer res = ps.executeUpdate();
|
||||
if (null != model) {
|
||||
ResultSet resultSet = ps.getGeneratedKeys();
|
||||
if (resultSet.next()) {
|
||||
Field field = getGenerateIdField(model.getClass());
|
||||
if (null != field) {
|
||||
ReflectUtil.setFieldValue(field, model, resultSet.getInt(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@Override
|
||||
public Object[] getParams() {
|
||||
return params;
|
||||
}
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
@Override
|
||||
public Connection getConnection(){
|
||||
return conn;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行单条查询操作
|
||||
* @param conn
|
||||
@@ -225,4 +266,28 @@ public class JdbcOperations {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自动生成ID字段
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
private static Field getGenerateIdField(Class<?> clazz) {
|
||||
if (null == clazz) {
|
||||
return null;
|
||||
}
|
||||
if (generateIdFieldMap.containsKey(clazz)) {
|
||||
return generateIdFieldMap.get(clazz);
|
||||
}
|
||||
Set<Field> fields = ReflectUtil.getDeclaredFields(clazz);
|
||||
if (CollectionUtil.isEmpty(fields)) {
|
||||
return null;
|
||||
}
|
||||
Field field = fields.stream().filter(f -> f.isAnnotationPresent(Id.class)).findFirst().orElse(null);
|
||||
if (null != field) {
|
||||
return field;
|
||||
}
|
||||
field = fields.stream().filter(f -> f.getName().equals("id")).findFirst().orElse(null);
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,12 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.db.template;
|
||||
|
||||
import fun.asgc.neutrino.core.util.ArrayUtil;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -74,6 +70,31 @@ public class JdbcTemplate {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 临时用来兼容insert之后需要返设主键的问题
|
||||
* @param sql
|
||||
* @param params
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public int updateByModel(String sql, Object model, Object ...params) throws SQLException {
|
||||
int res = -1;
|
||||
Connection conn = null;
|
||||
|
||||
try {
|
||||
conn = dataSourceHolder.getConnection();
|
||||
res = jdbcOperations.executeUpdateByModel(conn,sql, model, params);
|
||||
} finally {
|
||||
try {
|
||||
dataSourceHolder.tryClose(conn);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public int update(SqlAndParams sqlAndParams) throws SQLException {
|
||||
return update(sqlAndParams.getSql(), sqlAndParams.getParamArray());
|
||||
}
|
||||
@@ -83,7 +104,8 @@ public class JdbcTemplate {
|
||||
}
|
||||
|
||||
public int updateByModel(String sql, Object model) throws SQLException {
|
||||
return update(new SqlAndParams(sql, model));
|
||||
SqlAndParams sqlAndParams = new SqlAndParams(sql, model);
|
||||
return updateByModel(sqlAndParams.getSql(), model, sqlAndParams.getParamArray());
|
||||
}
|
||||
|
||||
public <T> T query(Class<T> clazz, String sql, Object ...params) throws SQLException {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div :class="className" :id="dailyTrafficChart.isHistory ? 'history-traffic-div' : 'daily-traffic-div'" :style="{height:height,width:width}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '160px'
|
||||
},
|
||||
dailyTrafficChart: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
upload: 90, // 上行
|
||||
download: 100, // 下行
|
||||
isHistory: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartDom: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chartDom.dispose()
|
||||
this.chartDom = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
const isHistory = this.dailyTrafficChart.isHistory
|
||||
this.chartDom = document.getElementById(isHistory ? 'history-traffic-div' : 'daily-traffic-div')
|
||||
this.myChart = echarts.init(this.chartDom)
|
||||
const option = {
|
||||
title: {
|
||||
text: isHistory ? '历史流量' : '今日流量',
|
||||
left: 'center',
|
||||
bottom: '0',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
fontWeight: 800,
|
||||
color: '#6c7a89'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: isHistory ? '历史流量' : '今日流量',
|
||||
type: 'pie',
|
||||
radius: '68%',
|
||||
// 隐藏指示线
|
||||
labelLine: {
|
||||
normal: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'inner',
|
||||
fontSize: 10,
|
||||
color: '#fff',
|
||||
formatter: (data) => {
|
||||
return `${data.name}${data.value > 1000 ? ':\n\n' : ':'}${data.value}`
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: this.dailyTrafficChart.upload,
|
||||
name: '上行'
|
||||
},
|
||||
{
|
||||
value: this.dailyTrafficChart.download,
|
||||
name: '下行'
|
||||
}
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
option && this.myChart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div :class="className" id="license-div" :style="{height:height,width:width}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '160px'
|
||||
},
|
||||
licenseChart: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
onLine: 90, // 进度条最大值
|
||||
total: 100 // 当前进度
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartDom: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chartDom.dispose()
|
||||
this.chartDom = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chartDom = document.getElementById('license-div')
|
||||
this.myChart = echarts.init(this.chartDom)
|
||||
const option = {
|
||||
title: {
|
||||
text: this.licenseChart.onLine,
|
||||
subtext: 'License在线数',
|
||||
left: 'center',
|
||||
top: '32%',
|
||||
textStyle: {
|
||||
fontSize: 22,
|
||||
fontWeight: 800,
|
||||
color: '#c23531',
|
||||
align: 'center'
|
||||
},
|
||||
subtextStyle: {
|
||||
fontSize: 10,
|
||||
fontWeight: 800,
|
||||
color: '#6c7a89'
|
||||
}
|
||||
// bottom:'0'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
// 第一张圆环
|
||||
name: 'License',
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
// 隐藏指示线
|
||||
labelLine: {
|
||||
normal: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
// 隐藏圆环上文字
|
||||
label: {
|
||||
normal: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
data: [
|
||||
// value当前进度 + 颜色
|
||||
{
|
||||
name: '在线数',
|
||||
value: this.licenseChart.onLine
|
||||
},
|
||||
{
|
||||
name: '离线数',
|
||||
value: this.licenseChart.total - this.licenseChart.onLine
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
option && this.myChart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div :class="className" id="port-mapping-div" :style="{height:height,width:width}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '160px'
|
||||
},
|
||||
portMappingChart: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
onLine: 90, // 进度条最大值
|
||||
total: 100 // 当前进度
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartDom: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chartDom.dispose()
|
||||
this.chartDom = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chartDom = document.getElementById('port-mapping-div')
|
||||
this.myChart = echarts.init(this.chartDom)
|
||||
const option = {
|
||||
title: {
|
||||
text: this.portMappingChart.onLine,
|
||||
subtext: '端口映射在线数',
|
||||
left: 'center',
|
||||
top: '32%',
|
||||
textStyle: {
|
||||
fontSize: 22,
|
||||
fontWeight: 800,
|
||||
color: '#c23531',
|
||||
align: 'center'
|
||||
},
|
||||
subtextStyle: {
|
||||
fontSize: 10,
|
||||
fontWeight: 800,
|
||||
color: '#6c7a89'
|
||||
}
|
||||
// bottom:'0'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
// 第一张圆环
|
||||
name: '端口映射',
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
// 隐藏指示线
|
||||
labelLine: {
|
||||
normal: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
// 隐藏圆环上文字
|
||||
label: {
|
||||
normal: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
data: [
|
||||
// value当前进度 + 颜色
|
||||
{
|
||||
name: '在线数',
|
||||
value: this.portMappingChart.onLine
|
||||
},
|
||||
{
|
||||
name: '离线数',
|
||||
value: this.portMappingChart.total - this.portMappingChart.onLine
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
option && this.myChart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div id="port-mapping-div" :style="{height:height+'px',width:width}">
|
||||
<el-table
|
||||
:data="tableChart"
|
||||
style="width: 100%"
|
||||
:default-sort = "{prop: 'date', order: 'descending'}"
|
||||
:height="height"
|
||||
>
|
||||
<el-table-column prop="date" label="日期" sortable />
|
||||
<el-table-column prop="name" label="姓名" sortable />
|
||||
<el-table-column prop="address" label="地址" sortable show-overflow-tooltip/>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '340'
|
||||
},
|
||||
tableChart: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div :class="className" id="traffic-sum-div" :style="{height:height,width:width}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '380px'
|
||||
},
|
||||
licenseChart: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
onLine: 90, // 进度条最大值
|
||||
total: 100 // 当前进度
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartDom: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chartDom.dispose()
|
||||
this.chartDom = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chartDom = document.getElementById('traffic-sum-div')
|
||||
this.myChart = echarts.init(this.chartDom)
|
||||
const option = {
|
||||
title: {
|
||||
text: '今日流量折线图',
|
||||
subtext: '当日0-24时'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['上行', '下行'],
|
||||
left: 'right'
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '2%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '上行',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
data: [120, 132, 101, 134, 90, 230, 210]
|
||||
},
|
||||
{
|
||||
name: '下行',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
data: [220, 182, 191, 234, 290, 330, 310]
|
||||
}
|
||||
]
|
||||
}
|
||||
option && this.myChart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="dashboard-editor-container">
|
||||
<github-corner></github-corner>
|
||||
|
||||
<panel-group @handleSetLineChartData="handleSetLineChartData"></panel-group>
|
||||
|
||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||
<line-chart :chart-data="lineChartData"></line-chart>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="32">
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<raddar-chart></raddar-chart>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<pie-chart></pie-chart>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<bar-chart></bar-chart>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="8">
|
||||
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
||||
<transaction-table></transaction-table>
|
||||
</el-col>
|
||||
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}">
|
||||
<todo-list></todo-list>
|
||||
</el-col>
|
||||
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}">
|
||||
<box-card></box-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GithubCorner from '@/components/GithubCorner'
|
||||
import PanelGroup from './components/PanelGroup'
|
||||
import LineChart from './components/LineChart'
|
||||
import RaddarChart from './components/RaddarChart'
|
||||
import PieChart from './components/PieChart'
|
||||
import BarChart from './components/BarChart'
|
||||
import TransactionTable from './components/TransactionTable'
|
||||
import TodoList from './components/TodoList'
|
||||
import BoxCard from './components/BoxCard'
|
||||
|
||||
const lineChartData = {
|
||||
newVisitis: {
|
||||
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
||||
actualData: [120, 82, 91, 154, 162, 140, 145]
|
||||
},
|
||||
messages: {
|
||||
expectedData: [200, 192, 120, 144, 160, 130, 140],
|
||||
actualData: [180, 160, 151, 106, 145, 150, 130]
|
||||
},
|
||||
purchases: {
|
||||
expectedData: [80, 100, 121, 104, 105, 90, 100],
|
||||
actualData: [120, 90, 100, 138, 142, 130, 130]
|
||||
},
|
||||
shoppings: {
|
||||
expectedData: [130, 140, 141, 142, 145, 150, 160],
|
||||
actualData: [120, 82, 91, 154, 162, 140, 130]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'dashboard-admin',
|
||||
components: {
|
||||
GithubCorner,
|
||||
PanelGroup,
|
||||
LineChart,
|
||||
RaddarChart,
|
||||
PieChart,
|
||||
BarChart,
|
||||
TransactionTable,
|
||||
TodoList,
|
||||
BoxCard
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lineChartData: lineChartData.newVisitis
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSetLineChartData(type) {
|
||||
this.lineChartData = lineChartData[type]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.dashboard-editor-container {
|
||||
padding: 32px;
|
||||
background-color: rgb(240, 242, 245);
|
||||
.chart-wrapper {
|
||||
background: #fff;
|
||||
padding: 16px 16px 0;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,110 +1,107 @@
|
||||
<template>
|
||||
<div class="dashboard-editor-container">
|
||||
<github-corner></github-corner>
|
||||
|
||||
<panel-group @handleSetLineChartData="handleSetLineChartData"></panel-group>
|
||||
|
||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||
<line-chart :chart-data="lineChartData"></line-chart>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="32">
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<raddar-chart></raddar-chart>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<pie-chart></pie-chart>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<bar-chart></bar-chart>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="8">
|
||||
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
||||
<transaction-table></transaction-table>
|
||||
<el-col :span="12">
|
||||
<el-card class="box-card">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<license-chart :licenseChart="echartsData.licenseChart"/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<port-mapping-chart :portMappingChart="echartsData.portMappingChart"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<daily-traffic-chart :dailyTrafficChart="echartsData.dailyTrafficChart"/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<daily-traffic-chart :dailyTrafficChart="echartsData.historyTrafficChart"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}">
|
||||
<todo-list></todo-list>
|
||||
<el-col :span="12">
|
||||
<el-card class="box-card">
|
||||
<el-col :span="24">
|
||||
<table-chart :tableChart="echartsData.tableChart"/>
|
||||
</el-col>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}">
|
||||
<box-card></box-card>
|
||||
</el-row>
|
||||
<el-row class="line-chart">
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<traffic-sum-chart/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GithubCorner from '@/components/GithubCorner'
|
||||
import PanelGroup from './components/PanelGroup'
|
||||
import LineChart from './components/LineChart'
|
||||
import RaddarChart from './components/RaddarChart'
|
||||
import PieChart from './components/PieChart'
|
||||
import BarChart from './components/BarChart'
|
||||
import TransactionTable from './components/TransactionTable'
|
||||
import TodoList from './components/TodoList'
|
||||
import BoxCard from './components/BoxCard'
|
||||
|
||||
const lineChartData = {
|
||||
newVisitis: {
|
||||
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
||||
actualData: [120, 82, 91, 154, 162, 140, 145]
|
||||
import LicenseChart from './components/LicenseChart'
|
||||
import PortMappingChart from './components/PortMappingChart'
|
||||
import DailyTrafficChart from './components/DailyTrafficChart'
|
||||
import TableChart from './components/TableChart'
|
||||
import TrafficSumChart from './components/TrafficSumChart'
|
||||
|
||||
const echartsData = {
|
||||
licenseChart: {
|
||||
total: 100,
|
||||
onLine: 80
|
||||
},
|
||||
messages: {
|
||||
expectedData: [200, 192, 120, 144, 160, 130, 140],
|
||||
actualData: [180, 160, 151, 106, 145, 150, 130]
|
||||
portMappingChart: {
|
||||
total: 100,
|
||||
onLine: 25
|
||||
},
|
||||
purchases: {
|
||||
expectedData: [80, 100, 121, 104, 105, 90, 100],
|
||||
actualData: [120, 90, 100, 138, 142, 130, 130]
|
||||
dailyTrafficChart: {
|
||||
upload: 300,
|
||||
download: 680,
|
||||
isHistory: false
|
||||
},
|
||||
shoppings: {
|
||||
expectedData: [130, 140, 141, 142, 145, 150, 160],
|
||||
actualData: [120, 82, 91, 154, 162, 140, 130]
|
||||
}
|
||||
historyTrafficChart: {
|
||||
upload: 4100,
|
||||
download: 9520,
|
||||
isHistory: true
|
||||
},
|
||||
tableChart: [
|
||||
{ date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' },
|
||||
{ date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路 1517 弄' },
|
||||
{ date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路 1519 弄' },
|
||||
{ date: '2016-05-03', name: '王小虎', address: '上海市普陀区金沙江路 1516 弄' },
|
||||
{ date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路 1517 弄' }
|
||||
]
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'dashboard-admin',
|
||||
components: {
|
||||
GithubCorner,
|
||||
PanelGroup,
|
||||
LineChart,
|
||||
RaddarChart,
|
||||
PieChart,
|
||||
BarChart,
|
||||
TransactionTable,
|
||||
TodoList,
|
||||
BoxCard
|
||||
},
|
||||
components: { TrafficSumChart, TableChart, DailyTrafficChart, PortMappingChart, LicenseChart },
|
||||
data() {
|
||||
return {
|
||||
lineChartData: lineChartData.newVisitis
|
||||
echartsData: echartsData
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
handleSetLineChartData(type) {
|
||||
this.lineChartData = lineChartData[type]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.dashboard-editor-container {
|
||||
padding: 32px;
|
||||
min-height: calc(100vh - 85px);
|
||||
padding: 16px;
|
||||
background-color: rgb(240, 242, 245);
|
||||
.chart-wrapper {
|
||||
background: #fff;
|
||||
padding: 16px 16px 0;
|
||||
margin-bottom: 32px;
|
||||
.line-chart {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+9
@@ -24,6 +24,11 @@ package fun.asgc.neutrino.proxy.server.constant;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 启用状态枚举
|
||||
* @author: aoshiguchen
|
||||
@@ -34,7 +39,11 @@ import lombok.Getter;
|
||||
public enum EnableStatusEnum {
|
||||
ENABLE(1, "启用"),
|
||||
DISABLE(2, "禁用");
|
||||
private static Map<Integer, EnableStatusEnum> CACHE = Stream.of(EnableStatusEnum.values()).collect(Collectors.toMap(EnableStatusEnum::getStatus, Function.identity()));
|
||||
|
||||
private Integer status;
|
||||
private String desc;
|
||||
public static EnableStatusEnum of(Integer status) {
|
||||
return CACHE.get(status);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ public enum ExceptionConstant {
|
||||
LOGIN_PASSWORD_NO_CHANGE_MODIFY_FAIL(12004, "密码没有变化,修改失败"),
|
||||
// 端口池管理(13000)
|
||||
PORT_CANNOT_REPEAT(13000,"端口不能重复"),
|
||||
PORT_NOT_EXIST(13001, "该端口在端口池中不存在,不允许映射"),
|
||||
PORT_NOT_EXIST(13001, "该端口在端口池中不存在"),
|
||||
// 端口映射管理(14000)
|
||||
PORT_MAPPING_NOT_EXIST(14000, "端口映射记录不存在"),
|
||||
PORT_CANNOT_REPEAT_MAPPING(14001, "服务端口[{}]不能重复映射"),
|
||||
|
||||
+4
@@ -62,6 +62,10 @@ public interface LicenseMapper extends SqlMapper {
|
||||
@Select("select * from license")
|
||||
List<LicenseDO> listAll();
|
||||
|
||||
@ResultType(LicenseDO.class)
|
||||
@Select("select * from `license` where user_id = :userId")
|
||||
List<LicenseDO> listByUserId(@Param("userId") Integer userId);
|
||||
|
||||
/**
|
||||
* 新增license
|
||||
* @param license
|
||||
|
||||
+8
@@ -74,6 +74,14 @@ public interface PortMappingMapper extends SqlMapper {
|
||||
@Select("select * from port_mapping where license_id = ? and enable = 1")
|
||||
List<PortMappingDO> findEnableListByLicenseId(Integer licenseId);
|
||||
|
||||
@ResultType(PortMappingDO.class)
|
||||
@Select("select * from port_mapping where server_port = :serverPort")
|
||||
List<PortMappingDO> findListByServerPort(@Param("serverPort") Integer serverPort);
|
||||
|
||||
@ResultType(PortMappingDO.class)
|
||||
@Select("select * from port_mapping where license_id = ?")
|
||||
List<PortMappingDO> findListByLicenseId(Integer licenseId);
|
||||
|
||||
@Update("update `port_mapping` set is_online = :isOnline,update_time = :updateTime where license_id = :licenseId and server_port = :serverPort")
|
||||
void updateOnlineStatus(@Param("licenseId") Integer licenseId, @Param("serverPort") Integer serverPort, @Param("isOnline") Integer isOnline, @Param("updateTime") Date updateTime);
|
||||
|
||||
|
||||
+3
@@ -62,4 +62,7 @@ public interface PortPoolMapper extends SqlMapper {
|
||||
|
||||
@Select("select * from port_pool where port = ? limit 0,1")
|
||||
PortPoolDO findByPort(Integer port);
|
||||
|
||||
@Select("select * from port_pool where id = ?")
|
||||
PortPoolDO findById(Integer id);
|
||||
}
|
||||
|
||||
+14
-8
@@ -23,6 +23,7 @@
|
||||
package fun.asgc.neutrino.proxy.server.proxy.core;
|
||||
|
||||
import fun.asgc.neutrino.core.util.BeanManager;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
import fun.asgc.neutrino.proxy.core.Constants;
|
||||
import fun.asgc.neutrino.proxy.core.ProxyMessage;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.domain.VisitorChannelAttachInfo;
|
||||
@@ -87,10 +88,15 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf>
|
||||
} else {
|
||||
String visitorId = newVisitorId();
|
||||
String lanInfo = ProxyUtil.getClientLanInfoByServerPort(sa.getPort());
|
||||
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
|
||||
visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, visitorChannel);
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
||||
if (StringUtil.isEmpty(lanInfo)) {
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
// 用户连接到代理服务器时,设置用户连接不可读,等待代理后端服务器连接成功后再改变为可读状态
|
||||
visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);
|
||||
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, visitorChannel, sa.getPort());
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
super.channelActive(ctx);
|
||||
@@ -133,17 +139,17 @@ public class VisitorChannelHandler extends SimpleChannelInboundHandler<ByteBuf>
|
||||
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
||||
|
||||
// 通知代理客户端
|
||||
Channel userChannel = ctx.channel();
|
||||
InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
|
||||
Channel visitorChannel = ctx.channel();
|
||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
|
||||
|
||||
if (cmdChannel == null) {
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
} else {
|
||||
Channel proxyChannel = userChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (proxyChannel != null) {
|
||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, userChannel.isWritable());
|
||||
proxyChannel.config().setOption(ChannelOption.AUTO_READ, visitorChannel.isWritable());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -52,11 +52,15 @@ public class ProxyMapping {
|
||||
return list;
|
||||
}
|
||||
for (PortMappingDO portMapping : portMappingList) {
|
||||
list.add(new ProxyMapping()
|
||||
.setServerPort(portMapping.getServerPort())
|
||||
.setLanInfo(String.format("%s:%s", portMapping.getClientIp(), portMapping.getClientPort())));
|
||||
list.add(build(portMapping));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static ProxyMapping build(PortMappingDO portMappingDO) {
|
||||
return new ProxyMapping()
|
||||
.setServerPort(portMappingDO.getServerPort())
|
||||
.setLanInfo(String.format("%s:%s", portMappingDO.getClientIp(), portMappingDO.getClientPort()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ import lombok.experimental.Accessors;
|
||||
public class VisitorChannelAttachInfo {
|
||||
private String visitorId;
|
||||
private String lanInfo;
|
||||
private Integer serverPort;
|
||||
/**
|
||||
* licenseId
|
||||
*/
|
||||
|
||||
+10
-52
@@ -27,36 +27,24 @@ import fun.asgc.neutrino.core.annotation.Component;
|
||||
import fun.asgc.neutrino.core.annotation.Match;
|
||||
import fun.asgc.neutrino.core.annotation.NonIntercept;
|
||||
import fun.asgc.neutrino.core.util.ChannelUtil;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
import fun.asgc.neutrino.proxy.core.*;
|
||||
import fun.asgc.neutrino.proxy.server.base.proxy.ProxyConfig;
|
||||
import fun.asgc.neutrino.proxy.server.constant.ClientConnectTypeEnum;
|
||||
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
|
||||
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
|
||||
import fun.asgc.neutrino.proxy.server.constant.SuccessCodeEnum;
|
||||
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.ClientConnectRecordDO;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.UserDO;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.core.BytesMetricsHandler;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.core.VisitorChannelHandler;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.domain.CmdChannelAttachInfo;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.domain.ProxyMapping;
|
||||
import fun.asgc.neutrino.proxy.server.service.*;
|
||||
import fun.asgc.neutrino.proxy.server.util.ProxyUtil;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.BindException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -68,10 +56,6 @@ import java.util.stream.Collectors;
|
||||
@Match(type = Constants.ProxyDataTypeName.AUTH)
|
||||
@Component
|
||||
public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
@Autowired("serverBossGroup")
|
||||
private NioEventLoopGroup serverBossGroup;
|
||||
@Autowired("serverWorkerGroup")
|
||||
private NioEventLoopGroup serverWorkerGroup;
|
||||
@Autowired
|
||||
private ProxyConfig proxyConfig;
|
||||
@Autowired
|
||||
@@ -86,6 +70,10 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
private FlowReportService flowReportService;
|
||||
@Autowired
|
||||
private ClientConnectRecordService clientConnectRecordService;
|
||||
@Autowired
|
||||
private LicenseMapper licenseMapper;
|
||||
@Autowired
|
||||
private VisitorChannelService visitorChannelService;
|
||||
|
||||
@Override
|
||||
public void handle(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
|
||||
@@ -167,44 +155,14 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
.setCode(SuccessCodeEnum.SUCCESS.getCode())
|
||||
.setCreateTime(now));
|
||||
|
||||
List<PortMappingDO> portMappingList = portMappingService.findEnableListByLicenseId(licenseDO.getId());
|
||||
// 没有端口映射仍然保持连接
|
||||
if (!CollectionUtil.isEmpty(portMappingList)) {
|
||||
ProxyUtil.initProxyInfo(licenseDO.getId(), ProxyMapping.buildList(portMappingList));
|
||||
|
||||
ProxyUtil.addCmdChannel(licenseDO.getId(), ctx.channel(), portMappingList.stream().map(PortMappingDO::getServerPort).collect(Collectors.toSet()));
|
||||
|
||||
startUserPortServer(ProxyUtil.getAttachInfo(ctx.channel()), portMappingList);
|
||||
}
|
||||
// 更新license在线状态
|
||||
licenseMapper.updateOnlineStatus(licenseDO.getId(), OnlineStatusEnum.ONLINE.getStatus(), now);
|
||||
// 初始化VisitorChannel
|
||||
visitorChannelService.initVisitorChannel(licenseDO.getId(), ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ProxyDataTypeEnum.AUTH.getDesc();
|
||||
}
|
||||
|
||||
private void startUserPortServer(CmdChannelAttachInfo cmdChannelAttachInfo, List<PortMappingDO> portMappingList) {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(serverBossGroup, serverWorkerGroup)
|
||||
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addFirst(new BytesMetricsHandler());
|
||||
ch.pipeline().addLast(new VisitorChannelHandler());
|
||||
}
|
||||
});
|
||||
|
||||
for (PortMappingDO portMapping : portMappingList) {
|
||||
try {
|
||||
proxyMutualService.bindServerPort(cmdChannelAttachInfo, portMapping.getServerPort());
|
||||
bootstrap.bind(portMapping.getServerPort()).get();
|
||||
log.info("绑定用户端口: {}", portMapping.getServerPort());
|
||||
} catch (Exception ex) {
|
||||
// BindException表示该端口已经绑定过
|
||||
if (!(ex.getCause() instanceof BindException)) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -58,6 +58,8 @@ public class LicenseService {
|
||||
private LicenseMapper licenseMapper;
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
@Autowired
|
||||
private VisitorChannelService visitorChannelService;
|
||||
|
||||
public Page<LicenseListRes> page(PageQuery pageQuery, LicenseListReq req) {
|
||||
Page<LicenseListRes> page = Page.create(pageQuery);
|
||||
@@ -159,7 +161,8 @@ public class LicenseService {
|
||||
*/
|
||||
public LicenseUpdateEnableStatusRes updateEnableStatus(LicenseUpdateEnableStatusReq req) {
|
||||
licenseMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
||||
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByLicenseId(req.getId(), req.getEnable());
|
||||
return new LicenseUpdateEnableStatusRes();
|
||||
}
|
||||
|
||||
@@ -169,6 +172,8 @@ public class LicenseService {
|
||||
*/
|
||||
public void delete(Integer id) {
|
||||
licenseMapper.delete(id);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByLicenseId(id, EnableStatusEnum.DISABLE.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+22
@@ -68,6 +68,8 @@ public class PortMappingService {
|
||||
private UserMapper userMapper;
|
||||
@Autowired
|
||||
private PortPoolMapper portPoolMapper;
|
||||
@Autowired
|
||||
private VisitorChannelService visitorChannelService;
|
||||
|
||||
public Page<PortMappingListRes> page(PageQuery pageQuery, PortMappingListReq req) {
|
||||
Page<PortMappingListRes> page = Page.create(pageQuery);
|
||||
@@ -126,6 +128,8 @@ public class PortMappingService {
|
||||
portMappingDO.setCreateTime(now);
|
||||
portMappingDO.setUpdateTime(now);
|
||||
portMappingMapper.add(portMappingDO);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
|
||||
return new PortMappingCreateRes();
|
||||
}
|
||||
|
||||
@@ -140,6 +144,10 @@ public class PortMappingService {
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort());
|
||||
|
||||
// 查询原端口映射
|
||||
PortMappingDO oldPortMappingDO = portMappingMapper.findById(req.getId());
|
||||
ParamCheckUtil.checkNotNull(oldPortMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
|
||||
|
||||
PortMappingDO portMappingDO = new PortMappingDO();
|
||||
portMappingDO.setId(req.getId());
|
||||
portMappingDO.setLicenseId(req.getLicenseId());
|
||||
@@ -147,7 +155,10 @@ public class PortMappingService {
|
||||
portMappingDO.setClientIp(req.getClientIp());
|
||||
portMappingDO.setClientPort(req.getClientPort());
|
||||
portMappingDO.setUpdateTime(new Date());
|
||||
portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
|
||||
portMappingMapper.update(portMappingDO);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO);
|
||||
return new PortMappingUpdateRes();
|
||||
}
|
||||
|
||||
@@ -193,6 +204,14 @@ public class PortMappingService {
|
||||
|
||||
portMappingMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
||||
|
||||
// 更新VisitorChannel
|
||||
portMappingDO.setEnable(req.getEnable());
|
||||
if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(req.getEnable())) {
|
||||
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
|
||||
} else {
|
||||
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
|
||||
}
|
||||
|
||||
return new PortMappingUpdateEnableStatusRes();
|
||||
}
|
||||
|
||||
@@ -207,6 +226,9 @@ public class PortMappingService {
|
||||
}
|
||||
|
||||
portMappingMapper.delete(id);
|
||||
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-2
@@ -52,6 +52,8 @@ public class PortPoolService {
|
||||
|
||||
@Autowired
|
||||
private PortPoolMapper portPoolMapper;
|
||||
@Autowired
|
||||
private VisitorChannelService visitorChannelService;
|
||||
|
||||
public Page<PortPoolListRes> page(PageQuery pageQuery, PortPoolListReq req) {
|
||||
Page<PortPoolListRes> page = Page.create(pageQuery);
|
||||
@@ -65,7 +67,7 @@ public class PortPoolService {
|
||||
|
||||
public PortPoolCreateRes create(PortPoolCreateReq req) {
|
||||
PortPoolDO oldPortPoolDO = portPoolMapper.findByPort(req.getPort());
|
||||
ParamCheckUtil.checkNotNull(oldPortPoolDO, ExceptionConstant.PORT_CANNOT_REPEAT);
|
||||
ParamCheckUtil.checkMustNull(oldPortPoolDO, ExceptionConstant.PORT_CANNOT_REPEAT);
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
@@ -75,19 +77,31 @@ public class PortPoolService {
|
||||
.setCreateTime(now)
|
||||
.setUpdateTime(now)
|
||||
);
|
||||
// 更新visitorChannel
|
||||
visitorChannelService.updateVisitorChannelByPortPool(req.getPort(), EnableStatusEnum.ENABLE.getStatus());
|
||||
|
||||
return new PortPoolCreateRes();
|
||||
}
|
||||
|
||||
public PortPoolUpdateEnableStatusRes updateEnableStatus(PortPoolUpdateEnableStatusReq req) {
|
||||
|
||||
PortPoolDO portPoolDO = portPoolMapper.findById(req.getId());
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
portPoolMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
||||
|
||||
// 更新visitorChannel
|
||||
visitorChannelService.updateVisitorChannelByPortPool(portPoolDO.getPort(), req.getEnable());
|
||||
|
||||
return new PortPoolUpdateEnableStatusRes();
|
||||
}
|
||||
|
||||
public void delete(Integer id) {
|
||||
PortPoolDO portPoolDO = portPoolMapper.findById(id);
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
|
||||
portPoolMapper.delete(id);
|
||||
|
||||
// 更新visitorChannel
|
||||
visitorChannelService.updateVisitorChannelByPortPool(portPoolDO.getPort(), EnableStatusEnum.DISABLE.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -62,6 +62,8 @@ public class UserService {
|
||||
private UserTokenMapper userTokenMapper;
|
||||
@Autowired
|
||||
private UserLoginRecordMapper userLoginRecordMapper;
|
||||
@Autowired
|
||||
private VisitorChannelService visitorChannelService;
|
||||
|
||||
public LoginRes login(LoginReq req) {
|
||||
UserDO userDO = userMapper.findByLoginName(req.getLoginName());
|
||||
@@ -162,7 +164,8 @@ public class UserService {
|
||||
|
||||
public UserUpdateEnableStatusRes updateEnableStatus(UserUpdateEnableStatusReq req) {
|
||||
userMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
||||
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByUserId(req.getId(), req.getEnable());
|
||||
return new UserUpdateEnableStatusRes();
|
||||
}
|
||||
|
||||
@@ -208,5 +211,7 @@ public class UserService {
|
||||
|
||||
public void delete(Integer id) {
|
||||
userMapper.delete(id);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByUserId(id, EnableStatusEnum.DISABLE.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.proxy.server.service;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import fun.asgc.neutrino.core.annotation.Autowired;
|
||||
import fun.asgc.neutrino.core.annotation.Component;
|
||||
import fun.asgc.neutrino.core.annotation.NonIntercept;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import fun.asgc.neutrino.proxy.core.Constants;
|
||||
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
|
||||
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
|
||||
import fun.asgc.neutrino.proxy.server.dal.PortMappingMapper;
|
||||
import fun.asgc.neutrino.proxy.server.dal.PortPoolMapper;
|
||||
import fun.asgc.neutrino.proxy.server.dal.UserMapper;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.PortPoolDO;
|
||||
import fun.asgc.neutrino.proxy.server.dal.entity.UserDO;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.core.BytesMetricsHandler;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.core.VisitorChannelHandler;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.domain.CmdChannelAttachInfo;
|
||||
import fun.asgc.neutrino.proxy.server.proxy.domain.ProxyMapping;
|
||||
import fun.asgc.neutrino.proxy.server.util.ProxyUtil;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.BindException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 访问者通道服务
|
||||
* @author: aoshiguchen
|
||||
* @date: 2023/2/5
|
||||
*/
|
||||
@Slf4j
|
||||
@NonIntercept
|
||||
@Component
|
||||
public class VisitorChannelService {
|
||||
@Autowired("serverBossGroup")
|
||||
private NioEventLoopGroup serverBossGroup;
|
||||
@Autowired("serverWorkerGroup")
|
||||
private NioEventLoopGroup serverWorkerGroup;
|
||||
@Autowired
|
||||
private ProxyMutualService proxyMutualService;
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
@Autowired
|
||||
private LicenseMapper licenseMapper;
|
||||
@Autowired
|
||||
private PortMappingMapper portMappingMapper;
|
||||
@Autowired
|
||||
private PortPoolMapper portPoolMapper;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param licenseId
|
||||
*/
|
||||
public void initVisitorChannel(Integer licenseId, Channel cmdChannel) {
|
||||
List<PortMappingDO> portMappingList = portMappingMapper.findEnableListByLicenseId(licenseId);
|
||||
// 没有端口映射仍然保持连接
|
||||
ProxyUtil.initProxyInfo(licenseId, ProxyMapping.buildList(portMappingList));
|
||||
|
||||
ProxyUtil.addCmdChannel(licenseId, cmdChannel, portMappingList.stream().map(PortMappingDO::getServerPort).collect(Collectors.toSet()));
|
||||
|
||||
startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), portMappingList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* 触发时机:删除端口池、禁用端口池、启用端口池
|
||||
* @param serverPort
|
||||
* @param enable
|
||||
*/
|
||||
public void updateVisitorChannelByPortPool(Integer serverPort, Integer enable) {
|
||||
if (null == serverPort) {
|
||||
return;
|
||||
}
|
||||
List<PortMappingDO> portMappingDOList = portMappingMapper.findListByServerPort(serverPort);
|
||||
if (CollectionUtil.isEmpty(portMappingDOList)) {
|
||||
return;
|
||||
}
|
||||
EnableStatusEnum enableStatusEnum = EnableStatusEnum.of(enable);
|
||||
for (PortMappingDO portMappingDO : portMappingDOList) {
|
||||
if (EnableStatusEnum.DISABLE == enableStatusEnum) {
|
||||
removeVisitorChannelByPortMapping(portMappingDO);
|
||||
} else if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(portMappingDO.getEnable())) {
|
||||
addVisitorChannelByPortMapping(portMappingDO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* 触发时机:删除用户、禁用用户、启用用户 (新增、修改用户不涉及VisitorChannel的变更)
|
||||
* @param userId
|
||||
*/
|
||||
public void updateVisitorChannelByUserId(Integer userId, Integer enable) {
|
||||
if (null == userId) {
|
||||
return;
|
||||
}
|
||||
List<LicenseDO> licenseDOList = licenseMapper.listByUserId(userId);
|
||||
if (CollectionUtil.isEmpty(licenseDOList)) {
|
||||
return;
|
||||
}
|
||||
for (LicenseDO licenseDO : licenseDOList) {
|
||||
updateVisitorChannelByLicenseId(licenseDO.getId(), enable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* 触发时机:删除license、禁用license、启用license (新增、修改license不涉及VisitorChannel的变更)
|
||||
* 重置licenseKey,不会立即影响已经连接成功的license,如果想要立即影响,请先进行禁用
|
||||
* @param licenseId
|
||||
*/
|
||||
public void updateVisitorChannelByLicenseId(Integer licenseId, Integer enable) {
|
||||
if (null == licenseId) {
|
||||
return;
|
||||
}
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseId);
|
||||
if (null == cmdChannel) {
|
||||
// 如果不存在有效的cmdChannel,则无需更新VisitorChannel
|
||||
return;
|
||||
}
|
||||
EnableStatusEnum enableStatusEnum = EnableStatusEnum.of(enable);
|
||||
List<PortMappingDO> portMappingDOList = portMappingMapper.findListByLicenseId(licenseId);
|
||||
if (!CollectionUtil.isEmpty(portMappingDOList)) {
|
||||
for (PortMappingDO portMappingDO : portMappingDOList) {
|
||||
if (EnableStatusEnum.DISABLE == enableStatusEnum) {
|
||||
removeVisitorChannelByPortMapping(portMappingDO);
|
||||
} else if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(portMappingDO.getEnable())) {
|
||||
addVisitorChannelByPortMapping(portMappingDO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* 触发时机:修改端口映射
|
||||
* @param oldPortMappingDO
|
||||
* @param newPortMappingDO
|
||||
*/
|
||||
public void updateVisitorChannelByPortMapping(PortMappingDO oldPortMappingDO, PortMappingDO newPortMappingDO) {
|
||||
if (null == oldPortMappingDO || null == newPortMappingDO) {
|
||||
return;
|
||||
}
|
||||
removeVisitorChannelByPortMapping(oldPortMappingDO);
|
||||
addVisitorChannelByPortMapping(newPortMappingDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增VisitorChannel
|
||||
* 触发时机:新增端口映射、启用端口映射
|
||||
* @param portMappingDO
|
||||
*/
|
||||
public void addVisitorChannelByPortMapping(PortMappingDO portMappingDO) {
|
||||
if (null == portMappingDO) {
|
||||
return;
|
||||
}
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(portMappingDO.getLicenseId());
|
||||
if (null == cmdChannel) {
|
||||
// 如果不存在有效的cmdChannel,则无需更新VisitorChannel
|
||||
return;
|
||||
}
|
||||
// 判断端口映射是否启用
|
||||
if (EnableStatusEnum.DISABLE != EnableStatusEnum.of(portMappingDO.getEnable())) {
|
||||
LicenseDO licenseDO = licenseMapper.findById(portMappingDO.getLicenseId());
|
||||
// 判断license是否启用
|
||||
if (null != licenseDO && EnableStatusEnum.ENABLE == EnableStatusEnum.of(licenseDO.getEnable())) {
|
||||
UserDO userDO = userMapper.findById(licenseDO.getUserId());
|
||||
// 判断用户是否启用
|
||||
if (null != userDO && EnableStatusEnum.ENABLE == EnableStatusEnum.of(userDO.getEnable())) {
|
||||
PortPoolDO portPoolDO = portPoolMapper.findByPort(portMappingDO.getServerPort());
|
||||
// 判断端口池是否启用
|
||||
if (null != portPoolDO && EnableStatusEnum.ENABLE == EnableStatusEnum.of(portPoolDO.getEnable())) {
|
||||
// 未删除且未禁用,则开启代理
|
||||
ProxyUtil.addProxyInfo(portMappingDO.getLicenseId(), ProxyMapping.build(portMappingDO));
|
||||
ProxyUtil.addCmdChannel(portMappingDO.getLicenseId(), cmdChannel, Sets.newHashSet(portMappingDO.getServerPort()));
|
||||
startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), Lists.newArrayList(portMappingDO));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除VisitorChannel
|
||||
* 触发时机:删除端口映射、禁用端口映射
|
||||
* @param portMappingDO
|
||||
*/
|
||||
public void removeVisitorChannelByPortMapping(PortMappingDO portMappingDO) {
|
||||
if (null == portMappingDO) {
|
||||
return;
|
||||
}
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(portMappingDO.getLicenseId());
|
||||
if (null == cmdChannel) {
|
||||
// 如果不存在有效的cmdChannel,则无需更新VisitorChannel
|
||||
return;
|
||||
}
|
||||
Channel visitorChannel = ProxyUtil.getVisitorChannelByServerPort(portMappingDO.getServerPort());
|
||||
if (null != visitorChannel) {
|
||||
Channel proxyChannel = visitorChannel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (null != proxyChannel) {
|
||||
proxyChannel.close();
|
||||
}
|
||||
visitorChannel.close();
|
||||
}
|
||||
ProxyUtil.removeProxyInfo(portMappingDO.getServerPort());
|
||||
}
|
||||
|
||||
private void startUserPortServer(CmdChannelAttachInfo cmdChannelAttachInfo, List<PortMappingDO> portMappingList) {
|
||||
if (CollectionUtil.isEmpty(portMappingList)) {
|
||||
return;
|
||||
}
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(serverBossGroup, serverWorkerGroup)
|
||||
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addFirst(new BytesMetricsHandler());
|
||||
ch.pipeline().addLast(new VisitorChannelHandler());
|
||||
}
|
||||
});
|
||||
|
||||
for (PortMappingDO portMapping : portMappingList) {
|
||||
try {
|
||||
proxyMutualService.bindServerPort(cmdChannelAttachInfo, portMapping.getServerPort());
|
||||
bootstrap.bind(portMapping.getServerPort()).get();
|
||||
log.info("绑定用户端口: {}", portMapping.getServerPort());
|
||||
} catch (Exception ex) {
|
||||
// BindException表示该端口已经绑定过
|
||||
if (!(ex.getCause() instanceof BindException)) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
-14
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.proxy.server.util;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import fun.asgc.neutrino.core.util.ChannelUtil;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import fun.asgc.neutrino.proxy.core.ChannelAttribute;
|
||||
@@ -50,7 +51,7 @@ public class ProxyUtil {
|
||||
/**
|
||||
* 代理信息映射
|
||||
*/
|
||||
private static final Map<Integer, String> proxyInfoMap = new HashMap<>();
|
||||
private static final Map<Integer, String> proxyInfoMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 服务端口 -> 指令通道映射
|
||||
*/
|
||||
@@ -59,6 +60,10 @@ public class ProxyUtil {
|
||||
* license -> 指令通道映射
|
||||
*/
|
||||
private static Map<Integer, Channel> licenseToCmdChannelMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 服务端口 -> 访问通道映射
|
||||
*/
|
||||
private static Map<Integer, Channel> serverPortToVisitorChannel = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* cmdChannelAttachInfo.getUserChannelMap() 读写锁
|
||||
@@ -72,12 +77,30 @@ public class ProxyUtil {
|
||||
*/
|
||||
public static void initProxyInfo(Integer licenseId, List<ProxyMapping> proxyMappingList) {
|
||||
licenseToServerPortMap.put(licenseId, new HashSet<>());
|
||||
for (ProxyMapping proxyMapping : proxyMappingList) {
|
||||
licenseToServerPortMap.get(licenseId).add(proxyMapping.getServerPort());
|
||||
proxyInfoMap.put(proxyMapping.getServerPort(), proxyMapping.getLanInfo());
|
||||
addProxyInfo(licenseId, proxyMappingList);
|
||||
}
|
||||
|
||||
public static void addProxyInfo(Integer licenseId, List<ProxyMapping> proxyMappingList) {
|
||||
if (!CollectionUtil.isEmpty(proxyMappingList)) {
|
||||
for (ProxyMapping proxyMapping : proxyMappingList) {
|
||||
licenseToServerPortMap.get(licenseId).add(proxyMapping.getServerPort());
|
||||
proxyInfoMap.put(proxyMapping.getServerPort(), proxyMapping.getLanInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void addProxyInfo(Integer licenseId, ProxyMapping proxyMapping) {
|
||||
if (null == licenseId || null == proxyMapping) {
|
||||
return;
|
||||
}
|
||||
licenseToServerPortMap.get(licenseId).add(proxyMapping.getServerPort());
|
||||
proxyInfoMap.put(proxyMapping.getServerPort(), proxyMapping.getLanInfo());
|
||||
}
|
||||
|
||||
public static void removeProxyInfo(Integer serverPort) {
|
||||
proxyInfoMap.remove(serverPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据licenseId获取服务端端口集合
|
||||
* @param licenseId licenseId
|
||||
@@ -103,19 +126,25 @@ public class ProxyUtil {
|
||||
* @param serverPorts 服务端端口集合
|
||||
*/
|
||||
public static void addCmdChannel(Integer licenseId, Channel cmdChannel, Set<Integer> serverPorts) {
|
||||
if (CollectionUtil.isEmpty(serverPorts)) {
|
||||
return;
|
||||
if (!CollectionUtil.isEmpty(serverPorts)) {
|
||||
for (int port : serverPorts) {
|
||||
serverPortToCmdChannelMap.put(port, cmdChannel);
|
||||
}
|
||||
}
|
||||
CmdChannelAttachInfo cmdChannelAttachInfo = getAttachInfo(cmdChannel);
|
||||
if (null == cmdChannelAttachInfo) {
|
||||
cmdChannelAttachInfo = new CmdChannelAttachInfo()
|
||||
.setIp(ChannelUtil.getIP(cmdChannel))
|
||||
.setLicenseId(licenseId)
|
||||
.setVisitorChannelMap(new HashMap<>(16))
|
||||
.setServerPorts(Sets.newHashSet());
|
||||
setAttachInfo(cmdChannel, cmdChannelAttachInfo);
|
||||
}
|
||||
|
||||
for (int port : serverPorts) {
|
||||
serverPortToCmdChannelMap.put(port, cmdChannel);
|
||||
if (!CollectionUtil.isEmpty(serverPorts)) {
|
||||
cmdChannelAttachInfo.getServerPorts().addAll(serverPorts);
|
||||
}
|
||||
|
||||
setAttachInfo(cmdChannel, new CmdChannelAttachInfo()
|
||||
.setIp(ChannelUtil.getIP(cmdChannel))
|
||||
.setServerPorts(serverPorts)
|
||||
.setLicenseId(licenseId)
|
||||
.setVisitorChannelMap(new HashMap<>(16)));
|
||||
licenseToCmdChannelMap.put(licenseId, cmdChannel);
|
||||
}
|
||||
|
||||
@@ -174,7 +203,7 @@ public class ProxyUtil {
|
||||
* @param visitorId
|
||||
* @param visitorChannel
|
||||
*/
|
||||
public static void addVisitorChannelToCmdChannel(Channel cmdChannel, String visitorId, Channel visitorChannel) {
|
||||
public static void addVisitorChannelToCmdChannel(Channel cmdChannel, String visitorId, Channel visitorChannel, Integer serverPort) {
|
||||
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
|
||||
String lanInfo = getClientLanInfoByServerPort(sa.getPort());
|
||||
CmdChannelAttachInfo cmdChannelAttachInfo = getAttachInfo(cmdChannel);
|
||||
@@ -182,6 +211,7 @@ public class ProxyUtil {
|
||||
setAttachInfo(visitorChannel, new VisitorChannelAttachInfo()
|
||||
.setVisitorId(visitorId)
|
||||
.setLanInfo(lanInfo)
|
||||
.setServerPort(serverPort)
|
||||
.setLicenseId(cmdChannelAttachInfo.getLicenseId())
|
||||
.setIp(ChannelUtil.getIP(visitorChannel))
|
||||
);
|
||||
@@ -191,6 +221,7 @@ public class ProxyUtil {
|
||||
} finally {
|
||||
userChannelMapLock.writeLock().unlock();
|
||||
}
|
||||
serverPortToVisitorChannel.put(serverPort, visitorChannel);
|
||||
}
|
||||
|
||||
public static Channel removeVisitorChannelFromCmdChannel(Channel cmdChannel, String visitorId) {
|
||||
@@ -219,6 +250,15 @@ public class ProxyUtil {
|
||||
return ((CmdChannelAttachInfo)getAttachInfo(cmdChannel)).getVisitorChannelMap().get(visitorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务端口获取访问通道
|
||||
* @param serverPort
|
||||
* @return
|
||||
*/
|
||||
public static Channel getVisitorChannelByServerPort(Integer serverPort) {
|
||||
return serverPortToVisitorChannel.get(serverPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问者ID
|
||||
*
|
||||
|
||||
+25
-1
@@ -6,7 +6,31 @@
|
||||
- 弹框展示月度明细
|
||||
- 弹框展示今日流量明细
|
||||
- 首页图表📈
|
||||
|
||||
- 1、License在线数
|
||||
- 2、端口映射在线数
|
||||
- 3、今日流量(上行、下行)
|
||||
- 4、历史流量(上行、下行)
|
||||
- 点击1~4 切换列表
|
||||
- 在线License列表
|
||||
- 用户名
|
||||
- License名称
|
||||
- LicenseKey
|
||||
- 在线端口映射列表
|
||||
- 用户名
|
||||
- License名称
|
||||
- 服务端口
|
||||
- 代理客户端
|
||||
- 今日24小时流量列表(按小时到排序)
|
||||
- 用户名
|
||||
- License名称
|
||||
- 时间
|
||||
- 流量
|
||||
- 历史流量列表(取最近12个月按月到排序)
|
||||
- 用户名
|
||||
- License名称
|
||||
- 时间
|
||||
- 流量
|
||||
- 今日流量折线图(上行、下行、总流量,按分钟统计0~24小时)
|
||||
# Bug
|
||||
- windows环境下直接运行发布版的jar包,日志输出乱码
|
||||
- 部份用户windows环境下启动客户端,扫描类个数为0个
|
||||
|
||||
Reference in New Issue
Block a user