Merge branch 'feature/1.7.1'

This commit is contained in:
aoshiguchen
2023-03-26 23:38:50 +08:00
181 changed files with 2238 additions and 925 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 311 KiB

+200
View File
@@ -0,0 +1,200 @@
## 1、关于作者
- dromara开源组织成员,[dromara/neutrino-proxy](https://gitee.com/asgc/neutrino-proxy)项目作者
- 名称:傲世孤尘、雨韵诗泽
- 名言:
- 扎根土壤,心向太阳。积蓄能量,绽放微光。
- 拘浊酒邀明月,借赤日暖苍穹。喻万物为星斗,化沧海成诗篇。
- 还生命以坦然,遗沧海以一笑。
- 微信号:yuyunshize
- Gitee: https://gitee.com/asgc
## 2、项目简介
- [中微子代理(neutrino-proxy)](https://gitee.com/asgc/neutrino-proxy) 是一款基于netty的内网穿透神器。该项目采用最为宽松的MIT协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。
- 演示环境:
- 地址:http://103.163.47.16:9527
- 账号:visitor/123456
- 该开源版本针对开发者个人,不提供任何付费服务。上面提供的地址、账号仅用于演示,已提供现成的license供大家测试。
- 市面上基于内网穿透的常见产品有:花生壳、TeamView、cpolar等。
- 该项目的目标是打开内网穿透功能黑盒,让更多感兴趣的朋友能更简单、低成本的掌握内网穿透技术。
- 学习、使用过程中,有任何问题都可以通过微信(通过上面的微信号,或扫描结尾的二维码)与笔者联系。同时,若该项目对您有所帮助,也请大家不吝转发推荐。
## 3、目录&文件说明
```
├── data.db 'sqlite数据库文件。若未配置mysql,默认使用sqlite,项目首次启动会自动初始化sqlite数据库。
├── docs '项目相关的一些文档
├── ├── Aop.MD '框架层Aop机制、使用说明
├── └── Channel.MD '内网穿透实现原理、代理实现流程说明
├── lib '项目开启了将自动生成的类保存到本地后,运行过程中动态生成的类自动保存到此处,方便学习、调试
├── neutrino-core '一套手写的基于netty的框架,相当于简易版的SpringBoot + Mybatis + xxljob,计划后期分离为单独开源项目维护
├── neutrino-proxy-admin '基于vue-element-admin开发的一个管理系统,用于可视化操作端口映射、代理数据实时监控
├── neutrino-proxy-client '基于netty的代理客户端,用于和服务端交互、转发内网数据
├── neutrino-proxy-core '代理相关的公共代码(协议、常量)
├── neutrino-proxy-server '基于netty的代理服务端,用于和客户段交互,将客户端转发的内网数据转发至外网端口
└── todolist.MD '近期的开发计划
```
## 4、项目亮点
- 性能:基于netty,天然拥有netty的高性能优势
- 学习:除netty之外,只依赖了最基本的日志、数据库驱动/连接池、json等jar包,代理层代码量非常精简。极其适合java初学者用来学习和掌握Web框架底层原理、内网穿透实现机制。
- 调试:只需要安装jdk8,甚至连数据库都无需考虑,即可直接在本地运行服务端+客户端,调试代理流程。尽可能减少前期步骤,确保大家能更快上手。
- 测试:针对框架层封装(如:AOP、Web容器、ORM、调度管理等),提供了丰富的测试代码。
## 5、运行环境
以下版本均为笔者开发时实际的使用版本
- `Node`v13.12.0
- `Java`1.8.0_351
- `Mysql`8.0.31 (非必需,使用sqlite则无需考虑数据库)
- `Nginx`:1.21.4 (非必需,可直接使用服务端项目提供的静态资源服务)
## 6、使用方法
### 6.1、打包
可直接前往[Gitee仓库发行版页面](https://gitee.com/dromara/neutrino-proxy/releases)下载所需版本已打好的包。
若需手动打包,则可参照下面的执行命令:
```sh
# 服务端打包
mvn clean install -U -pl neutrino-proxy-server -am -Dmaven.test.skip=true
# 客户端打包
clean install -U -pl neutrino-proxy-client -am -Dmaven.test.skip=true
# 管理后台前端项目打包(本地环境,local改为dev则为dev环境,同时需要修改config目录下面的环境配置)
npm run build:local
```
### 6.2、部署
#### 6.2.1、服务端部署
使用常规的jar包部署方式即可,如:java -jar xxxx
#### 6.2.2、管理后台部署
Nginx方式部署(推荐):
```
server {
listen 9527;
server_name localhost;
#开启gzip
gzip on;
#低于1kb的资源不压缩
gzip_min_length 1k;
#压缩级别1-9,越大压缩率越高,同时消耗cpu资源也越多,建议设置在5左右。
gzip_comp_level 5;
#需要压缩哪些响应类型的资源,多个空格隔开。不建议压缩图片.
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
#配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_disable "MSIE [1-6]\.";
#是否添加“Vary: Accept-Encoding”响应头
gzip_vary on;
location / {
root /work/projects/neutrino-proxy-server/neutrino-proxy-admin/dist;
try_files $uri $uri/ /index.html;
add_header Last-Modified $date_gmt;
}
}
```
在没有Nginx时,为了快速体验代理效果,可直接使用服务端项目提供的静态资源服务。
直接将`neutrino-proxy-admin`打包后的文件解压放在`neutrino-proxy-server.jar`同级别目录下即可。
例如:服务端配置的web端口为8080,则访问http://服务端IP:8080, 则会直接解析渲染`neutrino-proxy-server.jar`同级别目录下`neutrino-proxy-admin/dist/index.html`
需要注意的是,使用服务端自带的静态资源服务时,由于框架目前未支持缓存、gzip压缩,所以访问速度没有使用nginx快,正式使用推荐用nginx。
### 6.3、配置
#### 6.3.1、配置端口池
端口池的作用是将代理服务需要对外暴露的端口进行集中管理,方便安全组设置时统一操作。
本项目为了简化首次配置,默认初始化数据会将9101~9120的所有端口加入端口池。
可以在服务端项目sql配置(port_pool.data.sql文件)中自行修改初始化数据,也可以运行后在端口池管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认端口池,则无需任何配置。
#### 6.3.2、配置用户
用户是登录管理后台的身份主体,同时也是持有license的身份主体,用于支持代理连接的合法性认证。
本项目为了简化首次配置,默认初始化admin、visitor两个用户,默认密码为123456。
可以在服务端项目sql配置(user.data.sql文件)中自行修改初始化数据,也可以运行后在用户管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认用户,则无需任何配置。
#### 6.3.3、配置License
License是客户端连接代理服务端时所需要的唯一合法凭证,一个License同时只能被一个客户端使用。
本项目为了简化首次配置,默认为每个用户初始化了一些license。
可以在服务端项目sql配置(license.data.sql文件)中自行修改初始化数据,也可以运行后在License管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认License,则无需任何配置。
#### 6.3.4、配置端口映射
端口映射是代理的基本单元,一个外网端口在同一时刻被唯一的映射到一个内网IP+端口。所有流出该外网端口的流量都转发自对应的内网端口,同理所有流入该外网端口的流量都会转发到对应的内网端口。
本项目为了简化首次配置,默认为每个用户初始化了一些端口映射。可以在服务端项目sql配置(port_mapping.data.sql文件)中自行修改初始化数据,也可以运行后在端口映射管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认端口映射,则无需任何配置。
### 6.4、开启代理
上述步骤完成后,就可以开始代理自己的局域网设备了。
比如现在有一个licensea123456,该license配置了一个或多个端口映射,其中包含9010到localhost:8080的映射(localhost表示代理客户端所在主机的端口,可以换成客户端所在局域网的任何IP)。此时修改客户端配置的服务端ip、license等启动客户端,就可以开启代理了,端口映射管理对应的记录在线状态为"在线"则证明代理成功建立。
该license配置也可以通过客户端启动参数、启动后引导式输入、外置配置提供,为了简化首次体验门槛,这里直接提供一个外置配置模版:
```json
{
"jksPath":"classpath:/test.jks",
"licenseKey":"b0a907332b474b25897c4dcb31fc7eb6",
"serverIp":"localhost",
"serverPort":9002,
"sslEnable":true
}
```
修改上述json中的licenseKeyserverIp、serverPort、sslEnabled后,保存文件命名为`.neutrino-proxy-client.json`,放在客户端当前目录下(jar包启动时,放在jar包同级别目录下。idea启动时,放在项目根目录下),然后直接启动客户端。
此时通过访问外网ip+端口,可以成功访问内网服务。通过此方式,可以代理任何基于TCP之上的协议,如:socket、websocket、http、ftp、ssh等。
## 7、开发&调试
篇幅所限,此处不便赘述管理后台相关的开发,有vue基础的童鞋基本都可以自行开发。服务端的初始化数据足够调试工作,开发过程无需单独在管理页面操作。
与SpringBoot项目类似,客户端、服务端项目均由一个入口类完成整个项目的启动,分别为`ProxyClient``ProxyServer`
内置配置采用yml风格,主要涉及Http端口、静态资源路径、协议参数、代理服务端端口、jks证书、数据源、license等。
整个项目基于`neutrino-core`,风格类似于SpringBoot。笔者不喜欢因过分炫技而引入过多花式操作,因为项目的定位是个人开发者,且满足使用的同时兼顾学习其原理的需求。基本使用方式与SpringBoot类似,尽可能降低首次学习成本。
`neutrino-core`项目test目录下包含众多核心封装的测试代码,通过调试这些代码,能尽可能减少大家学习的障碍。
## 8、运行截图
License管理:
<img src="./license1.png" width="100%"/>
端口映射管理:
<img src="./port-mapping1.png" width="100%"/>
端口池管理:
<img src="./port-pool1.png" width="100%"/>
用户管理:
<img src="./user-manager1.png" width="100%"/>
## 9、迭代进度&未来计划
`2022-06-16` 第一个完整可用版本发布(无管理后台、无DB,纯配置实现)起,该项目已具备完整的内网穿透功能。经过后续的不断重构,初步完成了以下迭代:
- 使用sqlite取代纯配置,增加管理后台界面。解决修改端口映射需要重新打包的问题。
- 增加mysql支持。初学时,直接默认使用sqlite,可以专注于学习代理相关细节而不必额外安装、配置数据库。有了自己的服务器后,可配置使用mysql,完全兼容。
- 增加端口池管理。方便服务器统一设置安全组,避免暴露的外网端口杂乱无章。
- 集成调度管理。个人版项目自带调度管理,降低学习、使用成本。无需部署、接入其他调度服务。
- 增加简单的游客权限限制。个人版项目无需权限管理,但是为了演示需要,同时避免游客篡改管理员数据。
- 封装一套类似于SpringBoot + Mybatis的简化框架,用于支撑上层代理项目,减少外部依赖,为后期孵化一个开源框架做准备。
由于笔者工作之余才能进行开发,可投入时间不固定。截至目前(2023-02),该项目2.0版本仍未发布,基于此未来的计划如下:
- 完成2.0版本发布(还剩下报表管理、首页图表功能)。
- 底层框架全部重构。
- 分离出一个开源框架项目,并持续维护。
- 思考或着手基于内网穿透的产品开发,包括但不限于内网穿透SaaS服务、堡垒机。
## 10、联系我们
笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。
对项目有什么想法或者建议,可以加我微信拉交流群,或者创建[issues](https://gitee.com/dromara/neutrino-proxy/issues),一起完善项目
<img src="MyWeChatQRCode.jpeg" width="100%"/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

+8
View File
@@ -43,6 +43,14 @@ export function createPortPool(data) {
})
}
export function updatePortPool(data) {
return request({
url: '/port-pool/update',
method: 'post',
data
})
}
export function deletePortPool(id) {
return request({
url: '/port-pool/delete',
+24
View File
@@ -14,3 +14,27 @@ export function fetchLicenseFlowReportList(query) {
params: query
})
}
export function fetchUserFlowMonthReportList(query) {
return request({
url: '/report/user/flow-month-report/page',
method: 'get',
params: query
})
}
export function fetchLicenseFlowMonthReportList(query) {
return request({
url: '/report/license/flow-month-report/page',
method: 'get',
params: query
})
}
export function homeData(query) {
return request({
url: '/report/home/data-view',
method: 'get',
params: query
})
}
+3 -1
View File
@@ -59,7 +59,9 @@ export default {
clientConnectLog: '客户端连接日志',
report: '报表管理',
userFlowReport: '用户流量报表',
licenseFlowReport: 'License流量报表'
licenseFlowReport: 'License流量报表',
userFlowMonthReport: '用户流量月度明细',
licenseFlowMonthReport: 'License流量月度明细'
},
navbar: {
logOut: '退出登录',
+3 -1
View File
@@ -91,7 +91,9 @@ export const asyncRouterMap = [
},
children: [
{ path: 'userFlowReport', component: _import('report/userFlowReport'), name: 'userFlowReport', meta: { title: 'userFlowReport' }},
{ path: 'licenseFlowReport', component: _import('report/licenseFlowReport'), name: 'licenseFlowReport', meta: { title: 'licenseFlowReport' }}
{ path: 'licenseFlowReport', component: _import('report/licenseFlowReport'), name: 'licenseFlowReport', meta: { title: 'licenseFlowReport' }},
{ path: 'userFlowMonthReport', component: _import('report/userFlowMonthReport'), name: 'userFlowMonthReport', meta: { title: 'userFlowMonthReport' }},
{ path: 'licenseFlowMonthReport', component: _import('report/licenseFlowMonthReport'), name: 'licenseFlowMonthReport', meta: { title: 'licenseFlowMonthReport' }}
]
},
{
+29
View File
@@ -0,0 +1,29 @@
const SIZE_UNINTS = ['B', 'KB', 'MB', 'GB', 'TB']
const SIZE_SYSTEM = 1024
/**
* 根据字节数获取大小描述
* 1、小于1024字节的以B为单位
* 2、小于1024KB的以KB为单位
* 3、小于1024M的以MB为单位
* 4、小于1024G的以GB为单位
* 5、其他以TB为单位
*/
export function getSizeDescByByteCount(byteCount) {
if (byteCount <= 0) {
return '0B'
}
let res = byteCount
let index = 0
while (index < SIZE_UNINTS.length && res >= SIZE_SYSTEM) {
res /= SIZE_SYSTEM
index++
}
if (index >= SIZE_UNINTS.length) {
index = SIZE_UNINTS.length - 1
res *= 1024
}
return parseFloat(res.toFixed(2)) + SIZE_UNINTS[index]
}
@@ -5,6 +5,7 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
let that = null
export default {
props: {
@@ -22,14 +23,14 @@ export default {
},
height: {
type: String,
default: '250px'
default: '220px'
},
data: {
type: Object,
default: () => {
return {
upload: 90, // 上行
download: 100, // 下行
upFlowBytes: 0, // 上行
downFlowBytes: 0, // 下行
text: '今日流量'
}
}
@@ -41,6 +42,7 @@ export default {
}
},
mounted() {
that = this
this.initChart()
},
beforeDestroy() {
@@ -57,7 +59,7 @@ export default {
const option = {
title: {
text: this.data.text,
subtext: '上行:' + this.data.upload + '\n\n' + '下行:' + this.data.download,
subtext: '上行:' + this.data.upFlowDesc + '\n\n' + '下行:' + this.data.downFlowDesc + '\n\n' + '汇总:' + this.data.totalFlowDesc,
textStyle: {
fontSize: 18,
fontWeight: 800,
@@ -65,7 +67,10 @@ export default {
}
},
tooltip: {
trigger: 'item'
trigger: 'item',
formatter: function(value) {
return `${value.seriesName} <br/>${value.marker} : ${value.name} ${value.name === '上行' ? that.data.upFlowDesc : that.data.downFlowDesc}`
}
},
legend: {
data: ['上行', '下行'],
@@ -77,7 +82,7 @@ export default {
name: this.data.text,
type: 'pie',
radius: [45, 65],
center: ['50%', '58%'],
center: ['60%', '60%'],
// 隐藏指示线
labelLine: {
normal: {
@@ -93,30 +98,30 @@ export default {
},
data: [
{
value: this.data.upload,
value: this.data.upFlowBytes,
name: '上行',
lineStyle: {
normal: {
color: '#2B81B1'
color: '#63b2ee'
}
},
itemStyle: {
normal: {
color: '#2B81B1'
color: '#63b2ee'
}
}
},
{
value: this.data.download,
value: this.data.downFlowBytes,
name: '下行',
lineStyle: {
normal: {
color: '#dbebf7'
color: '#76da91'
}
},
itemStyle: {
normal: {
color: '#dbebf7'
color: '#76da91'
}
}
}
@@ -20,14 +20,14 @@ export default {
},
height: {
type: String,
default: '250px'
default: '220px'
},
data: {
type: Object,
default: () => {
return {
onLine: 90, // 进度条最大值
total: 100 // 当前进度
onlineCount: 0, // 进度条最大值
totalCount: 0 // 当前进度
}
}
}
@@ -54,7 +54,7 @@ export default {
const option = {
title: {
text: 'License统计',
subtext: '在线数:' + this.data.onLine + '\n\n' + '离线数:' + (this.data.total - this.data.onLine),
subtext: '在线数' + this.data.onlineCount + '\n\n' + '离线数' + (this.data.totalCount - this.data.onlineCount) + '\n\n' + '汇总数量:' + this.data.totalCount,
textStyle: {
fontSize: 18,
fontWeight: 800,
@@ -75,7 +75,7 @@ export default {
name: 'License',
type: 'pie',
radius: [45, 65],
center: ['50%', '58%'],
center: ['60%', '60%'],
avoidLabelOverlap: false,
// 隐藏指示线
labelLine: {
@@ -103,29 +103,29 @@ export default {
// value当前进度 + 颜色
{
name: '在线数',
value: this.data.onLine,
value: this.data.onlineCount,
lineStyle: {
normal: {
color: '#2B81B1'
color: '#63b2ee'
}
},
itemStyle: {
normal: {
color: '#2B81B1'
color: '#63b2ee'
}
}
},
{
name: '离线数',
value: this.data.total - this.data.onLine,
value: this.data.totalCount - this.data.onlineCount,
lineStyle: {
normal: {
color: '#dbebf7'
color: '#76da91'
}
},
itemStyle: {
normal: {
color: '#dbebf7'
color: '#76da91'
}
}
}
@@ -18,14 +18,14 @@ export default {
},
height: {
type: String,
default: '250px'
default: '220px'
},
data: {
type: Object,
default: () => {
return {
onLine: 90, // 进度条最大值
total: 100 // 当前进度
onlineCount: 0, // 进度条最大值
totalCount: 0 // 当前进度
}
}
}
@@ -52,7 +52,7 @@ export default {
const option = {
title: {
text: '端口映射统计',
subtext: '在线数:' + this.data.onLine + '\n\n' + '离线数:' + (this.data.total - this.data.onLine),
subtext: '在线数' + this.data.onlineCount + '\n\n' + '离线数' + (this.data.totalCount - this.data.onlineCount) + '\n\n' + '汇总数量:' + this.data.totalCount,
textStyle: {
fontSize: 18,
fontWeight: 800,
@@ -73,7 +73,7 @@ export default {
name: '端口映射',
type: 'pie',
radius: [45, 65],
center: ['50%', '58%'],
center: ['60%', '60%'],
avoidLabelOverlap: false,
// 隐藏指示线
labelLine: {
@@ -92,29 +92,29 @@ export default {
// value当前进度 + 颜色
{
name: '在线数',
value: this.data.onLine,
value: this.data.onlineCount,
lineStyle: {
normal: {
color: '#2B81B1'
color: '#63b2ee'
}
},
itemStyle: {
normal: {
color: '#2B81B1'
color: '#63b2ee'
}
}
},
{
name: '离线数',
value: this.data.total - this.data.onLine,
value: this.data.totalCount - this.data.onlineCount,
lineStyle: {
normal: {
color: '#dbebf7'
color: '#76da91'
}
},
itemStyle: {
normal: {
color: '#dbebf7'
color: '#76da91'
}
}
}
@@ -5,6 +5,8 @@
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import { getSizeDescByByteCount } from '@/utils/utils'
let that = null
export default {
props: {
@@ -26,16 +28,17 @@ export default {
},
height: {
type: String,
default: '450px'
default: '500px'
}
},
data() {
return {
chartDom: null,
colorList: ['#2B81B1', '#75ddfa', '#53a7e3', '#029fe8', '#015dae']
colorList: ['#63b2ee', '#76da91', '#f8cb7f', '#f89588', '#7cd6cf', '#9192ab', '#7898e1', '#7898e1']
}
},
mounted() {
that = this
this.initChart()
},
beforeDestroy() {
@@ -51,7 +54,7 @@ export default {
this.myChart = echarts.init(this.chartDom)
const seriesList = []
const legendList = []
this.data.list.forEach((item, index) => {
this.data.list && this.data.list.forEach((item, index) => {
seriesList.push({
name: item.name,
type: 'line',
@@ -71,25 +74,34 @@ export default {
normal: {
color: this.colorList[index]
}
}
},
smooth: true
})
legendList.push(item.name)
})
const option = {
title: {
text: this.data.text + '折线图',
text: this.data.text,
subtext: this.data.subtext || ''
},
tooltip: {
trigger: 'axis'
trigger: 'axis',
formatter: function(value) {
let title = that.data.text + '<br/>'
value.forEach(item => {
title = title + item.marker + item.seriesName + ' : ' + that.data.list[item.seriesIndex].label[item.dataIndex] + '<br/>'
})
return title
}
},
legend: {
data: legendList,
left: 'right'
},
grid: {
left: '2%',
right: '2%',
left: '0',
right: '3%',
bottom: '2%',
containLabel: true
},
@@ -99,7 +111,12 @@ export default {
data: this.data.title
},
yAxis: {
type: 'value'
type: 'value',
axisLabel: {
formatter: function(value) {
return getSizeDescByByteCount(value)
}
}
},
series: seriesList
}
@@ -1,40 +1,32 @@
<template>
<div class="dashboard-editor">
<div class="log-div">
</div>
<div class="container">
<el-row class="line-chart" :gutter="16">
<el-col :span="12">
<el-card>
<traffic-sum-chart :data="echartsData.trafficSumChart" :chartId="'traffic-sum-div1'"/>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<traffic-sum-chart :data="echartsData.trafficSumChart2"/>
</el-card>
</el-col>
</el-row>
<el-row class="pie-chart" :gutter="16">
<el-col :span="6">
<el-card class="box-card">
<license-chart :data="echartsData.licenseChart"/>
<license-chart :data="licenseChart" v-if="licenseChart"/>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="box-card">
<port-mapping-chart :data="echartsData.portMappingChart"/>
<port-mapping-chart :data="portMappingChart" v-if="portMappingChart"/>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="box-card">
<daily-traffic-chart :data="echartsData.dailyTrafficChart" :chartId="'daily-traffic-div'"/>
<daily-traffic-chart :data="dailyTrafficChart" :chartId="'daily-traffic-div'" v-if="dailyTrafficChart"/>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="box-card">
<daily-traffic-chart :data="echartsData.historyTrafficChart" :chartId="'history-traffic-div'"/>
<daily-traffic-chart :data="historyTrafficChart" :chartId="'history-traffic-div'" v-if="historyTrafficChart"/>
</el-card>
</el-col>
</el-row>
<el-row class="line-chart" :gutter="16">
<el-col :span="24">
<el-card>
<traffic-sum-chart :data="trafficSumChart" :chartId="'traffic-sum-div1'" v-if="trafficSumChart"/>
</el-card>
</el-col>
</el-row>
@@ -48,69 +40,69 @@ import LicenseChart from './components/LicenseChart'
import PortMappingChart from './components/PortMappingChart'
import DailyTrafficChart from './components/DailyTrafficChart'
import TrafficSumChart from './components/TrafficSumChart'
const echartsData = {
licenseChart: {
total: 100,
onLine: 80
},
portMappingChart: {
total: 100,
onLine: 25
},
dailyTrafficChart: {
upload: 300,
download: 680,
text: '今日流量'
},
historyTrafficChart: {
upload: 4100,
download: 9520,
text: '历史流量'
},
trafficSumChart: {
text: '今日流量',
subtext: '当日0-24时',
title: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00'],
list: [
{
name: '上行',
value: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '下行',
value: [112, 333, 150, 60, 99, 50, 102]
}
]
},
trafficSumChart2: {
text: '历史流量',
title: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
list: [
{
name: '上行',
value: [132, 101, 134, 90, 120, 132, 101, 134, 90, 230, 210, 120]
},
{
name: '下行',
value: [111, 0, 50, 101, 134, 90, 230, 120, 132, 101, 60, 20]
}
]
}
}
import { homeData } from '@/api/report'
export default {
name: 'dashboard-admin',
components: { TrafficSumChart, DailyTrafficChart, PortMappingChart, LicenseChart },
data() {
return {
echartsData: echartsData
licenseChart: undefined,
portMappingChart: undefined,
dailyTrafficChart: undefined,
historyTrafficChart: undefined,
trafficSumChart: undefined
}
},
created() {
this.getHomeData()
},
mounted() {
},
methods: {
getHomeData() {
homeData().then(res => {
this.licenseChart = res.data.data.license
this.portMappingChart = res.data.data.portMapping
this.dailyTrafficChart = Object.assign(res.data.data.todayFlow, { text: '今日流量' })
this.historyTrafficChart = Object.assign(res.data.data.totalFlow, { text: '流量汇总' })
this.trafficSumChart = this.getChartData(res.data.data.last7dFlow)
})
},
getChartData(last7dFlow) {
const title = []
const downFlowDesc = []
const totalFlowDesc = []
const upFlowDesc = []
last7dFlow.dataList.forEach(item => {
title.push(item.dateStr)
totalFlowDesc.push(item.totalFlowDesc)
downFlowDesc.push(item.downFlowDesc)
upFlowDesc.push(item.upFlowDesc)
})
const list = []
last7dFlow.seriesList.forEach(item => {
let label = []
if (item.seriesName.indexOf('上') > -1) {
label = upFlowDesc
} else if (item.seriesName.indexOf('下') > -1) {
label = downFlowDesc
} else if (item.seriesName.indexOf('总') > -1) {
label = totalFlowDesc
}
list.push({
name: item.seriesName,
value: item.seriesData,
label: label
})
})
return {
text: '流量监控',
subtext: `最近${last7dFlow.dataList.length || 0}天流量监控`,
title: title,
list: list
}
}
}
}
</script>
@@ -127,9 +119,9 @@ export default {
flex: 0 0 auto;
}
.pie-chart{
padding-top: 16px;
padding-bottom: 16px;
.el-card{
min-height: 300px;
min-height: 250px;
}
}
}
@@ -1,6 +1,9 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.licenseId" placeholder="请选择license" clearable style="margin-right:10px">
<el-option v-for="item in licenseList" :key="item.key" :label="item.name" :value="item.id" />
</el-select>
<el-button type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
</div>
@@ -66,6 +69,7 @@
<script>
import { fetchList } from '@/api/clientConnectLog'
import { licenseList } from '@/api/license'
import waves from '@/directive/waves' // 水波纹指令
export default {
@@ -85,7 +89,8 @@ export default {
jobId: undefined
},
dialogVisible: false,
selectRow: {}
selectRow: {},
licenseList: []
}
},
filters: {
@@ -112,6 +117,7 @@ export default {
}
},
created() {
this.getLicenseList()
this.getList()
},
methods: {
@@ -123,6 +129,11 @@ export default {
this.listLoading = false
})
},
getLicenseList() {
licenseList().then(response => {
this.licenseList = response.data.data
})
},
handleFilter() {
this.listQuery.current = 1
this.getList()
@@ -1,6 +1,9 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable>
<el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
</div>
@@ -37,6 +40,7 @@
<script>
import { fetchList } from '@/api/loginLog'
import { userList } from '@/api/user'
import waves from '@/directive/waves' // 水波纹指令
export default {
@@ -53,8 +57,9 @@ export default {
listQuery: {
current: 1,
size: 10,
jobId: undefined
}
userId: null
},
userList: []
}
},
filters: {
@@ -98,13 +103,11 @@ export default {
},
created() {
this.getList()
this.getDataList()
},
activated() {
if (this.$route.query.jobId) {
this.listQuery.jobId = this.$route.query.jobId
console.log(this.listQuery.jobId, this.$route.query.jobId)
this.getList()
}
this.getUserList()
this.getList()
},
methods: {
getList() {
@@ -115,6 +118,22 @@ export default {
this.listLoading = false
})
},
getUserList() {
userList().then(response => {
this.userList = response.data.data
})
},
getDataList() {
const loginName = this.$store.state.user.loginName
userList().then(response => {
this.userList = response.data.data
const curUser = this.userList.find((val) => val.loginName === loginName)
if (curUser) {
this.listQuery.userId = curUser.id
}
this.getList()
})
},
handleFilter() {
this.listQuery.current = 1
this.getList()
@@ -0,0 +1,149 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable>
<el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="listQuery.licenseId" placeholder="请选择License" clearable>
<el-option v-for="item in licenseList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row style="width: 100%">
<el-table-column type="index" width="100" :label="$t('table.id')"></el-table-column>
<el-table-column align="center" :label="$t('table.userName')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.userName}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.licenseName')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.licenseName}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.upFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.upFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.downFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.downFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.totalFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.totalFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column width="150px" align="center" :label="$t('table.date')">
<template slot-scope="scope">
<span>{{scope.row.date | parseTime('{y}-{m}')}}</span>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-pageInfo.sync="listQuery.current"
:pageInfo-sizes="[10,20,30, 50]" :pageInfo-size="listQuery.size" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import { fetchLicenseFlowMonthReportList } from '@/api/report'
import { userList } from '@/api/user'
import waves from '@/directive/waves'
import { licenseList } from '@/api/license'
export default {
name: 'licenseFlowMonthReport',
directives: {
waves
},
data() {
return {
tableKey: 0,
list: null,
total: null,
listLoading: false,
listQuery: {
current: 1,
size: 10,
userId: undefined,
licenseId: undefined
},
userList: [],
licenseList: [],
dialogVisible: false,
selectRow: {}
}
},
filters: {
},
created() {
this.getList()
this.getUserList()
this.getLicenseList()
},
activated() {
this.getUserList()
this.getLicenseList()
if (this.$route.query.userId) {
this.listQuery.userId = this.$route.query.userId
}
if (this.$route.query.licenseId) {
this.listQuery.licenseId = this.$route.query.licenseId
}
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchLicenseFlowMonthReportList(this.listQuery).then(response => {
this.list = response.data.data.records
this.total = response.data.data.total
this.listLoading = false
})
},
getUserList() {
userList().then(response => {
this.userList = response.data.data
})
},
getLicenseList() {
licenseList().then(response => {
this.licenseList = response.data.data
})
},
handleFilter() {
this.listQuery.current = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.size = val
this.listQuery.current = 1
this.getList()
},
handleCurrentChange(val) {
this.listQuery.current = val
this.getList()
},
handleShowClick(row) {
console.log(row)
},
handleLookOver(row) {
this.selectRow = row
this.dialogVisible = true
}
}
}
</script>
<style>
.job-msg-div{
max-height: 400px;
overflow-y: auto;
}
</style>
@@ -34,6 +34,11 @@
<span>{{scope.row.totalFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.actions')" min-width="230" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="handleFlowMonthReportClick(scope.row)">流量月度明细</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-pageInfo.sync="listQuery.current"
@@ -49,7 +54,7 @@ import { userList } from '@/api/user'
import waves from '@/directive/waves'
export default {
name: 'jobLog',
name: 'licenseFlowReport',
directives: {
waves
},
@@ -115,6 +120,9 @@ export default {
handleLookOver(row) {
this.selectRow = row
this.dialogVisible = true
},
handleFlowMonthReportClick(row) {
this.$router.push({ path: '/report/licenseFlowMonthReport', query: { userId: row.userId, licenseId: row.licenseId }})
}
}
}
@@ -0,0 +1,128 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable>
<el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row style="width: 100%">
<el-table-column type="index" width="100" :label="$t('table.id')"></el-table-column>
<el-table-column align="center" :label="$t('table.userName')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.userName}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.upFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.upFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.downFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.downFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.totalFlow')" min-width="120">
<template slot-scope="scope">
<span>{{scope.row.totalFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column width="150px" align="center" :label="$t('table.date')">
<template slot-scope="scope">
<span>{{scope.row.date | parseTime('{y}-{m}')}}</span>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-pageInfo.sync="listQuery.current"
:pageInfo-sizes="[10,20,30, 50]" :pageInfo-size="listQuery.size" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import { fetchUserFlowMonthReportList } from '@/api/report'
import { userList } from '@/api/user'
import waves from '@/directive/waves' // 水波纹指令
export default {
name: 'userFlowMonthReport',
directives: {
waves
},
data() {
return {
tableKey: 0,
list: null,
total: null,
listLoading: false,
listQuery: {
current: 1,
size: 10,
userId: undefined
},
userList: [],
dialogVisible: false,
selectRow: {}
}
},
filters: {
},
created() {
this.getList()
this.getUserList()
},
activated() {
this.getUserList()
if (this.$route.query.userId) {
this.listQuery.userId = this.$route.query.userId
this.getList()
}
},
methods: {
getList() {
this.listLoading = true
fetchUserFlowMonthReportList(this.listQuery).then(response => {
this.list = response.data.data.records
this.total = response.data.data.total
this.listLoading = false
})
},
getUserList() {
userList().then(response => {
this.userList = response.data.data
})
},
handleFilter() {
this.listQuery.current = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.size = val
this.listQuery.current = 1
this.getList()
},
handleCurrentChange(val) {
this.listQuery.current = val
this.getList()
},
handleShowClick(row) {
console.log(row)
},
handleLookOver(row) {
this.selectRow = row
this.dialogVisible = true
}
}
}
</script>
<style>
.job-msg-div{
max-height: 400px;
overflow-y: auto;
}
</style>
@@ -29,6 +29,12 @@
<span>{{scope.row.totalFlowDesc}}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.actions')" min-width="230" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="handleFlowMonthReportClick(scope.row)">流量月度明细</el-button>
<el-button size="mini" type="text" @click="handleLicenseFlowMonthReportClick(scope.row)">License流量月度明细</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-pageInfo.sync="listQuery.current"
@@ -44,7 +50,7 @@ import { userList } from '@/api/user'
import waves from '@/directive/waves' // 水波纹指令
export default {
name: 'jobLog',
name: 'userFlowReport',
directives: {
waves
},
@@ -57,7 +63,7 @@ export default {
listQuery: {
current: 1,
size: 10,
jobId: undefined
userId: undefined
},
userList: [],
dialogVisible: false,
@@ -72,8 +78,8 @@ export default {
},
activated() {
this.getUserList()
if (this.$route.query.jobId) {
this.listQuery.jobId = this.$route.query.jobId
if (this.$route.query.userId) {
this.listQuery.userId = this.$route.query.userId
this.getList()
}
},
@@ -110,6 +116,12 @@ export default {
handleLookOver(row) {
this.selectRow = row
this.dialogVisible = true
},
handleFlowMonthReportClick(row) {
this.$router.push({ path: '/report/userFlowMonthReport', query: { userId: row.userId }})
},
handleLicenseFlowMonthReportClick(row) {
this.$router.push({ path: '/report/licenseFlowMonthReport', query: { userId: row.userId }})
}
}
}
@@ -1,6 +1,9 @@
<template>
<div class="app-container calendar-list-container">
<div class="filter-container">
<el-select v-model="listQuery.groupId" placeholder="请选择端口池分组" clearable>
<el-option v-for="item in portGroupList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{$t('table.search')}}</el-button>
<el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary" icon="el-icon-edit">{{$t('table.add')}}</el-button>
</div>
@@ -39,6 +42,7 @@
</el-table-column>
<el-table-column align="center" :label="$t('table.actions')" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">{{$t('table.edit')}}</el-button>
<el-button v-if="scope.row.enable =='1'" size="mini" type="danger" @click="handleModifyStatus(scope.row,2)">{{$t('table.disable')}}</el-button>
<el-button v-if="scope.row.enable =='2'" size="mini" type="success" @click="handleModifyStatus(scope.row,1)">{{$t('table.enable')}}</el-button>
<!-- <el-button v-if="scope.row.status!='deleted'" size="mini" type="danger" @click="handleDelete(scope.row,'deleted')">{{$t('table.delete')}}</el-button>-->
@@ -56,12 +60,11 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="temp" label-position="left" label-width="70px" style='width: 400px; margin-left:50px;'>
<el-form-item :label="$t('table.port')" prop="port">
<el-input v-model="temp.port"></el-input>
<el-input v-model="temp.port" :disabled="dialogStatus=='update'"></el-input>
</el-form-item>
<el-form-item :label="$t('table.group')" prop="group">
<el-select style="width: 330px" class="filter-item" v-model="temp.groupId"
:disabled="dialogStatus=='update'">
<el-select style="width: 330px" class="filter-item" v-model="temp.groupId">
<el-option v-for="item in portGroupList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
@@ -71,6 +74,7 @@
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">{{$t('table.cancel')}}</el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData">{{$t('table.confirm')}}</el-button>
<el-button v-else type="primary" @click="updateData">{{$t('table.confirm')}}</el-button>
</div>
</el-dialog>
@@ -88,7 +92,7 @@
</template>
<script>
import { fetchList, updateEnableStatus, createPortPool, deletePortPool } from '@/api/portPool'
import { fetchList, updateEnableStatus, createPortPool, deletePortPool, updatePortPool } from '@/api/portPool'
import { portGroupList } from '@/api/portGroup'
import waves from '@/directive/waves' // 水波纹指令
import { parseTime } from '@/utils'
@@ -124,9 +128,7 @@
listQuery: {
current: 1,
size: 10,
importance: undefined,
title: undefined,
type: undefined
groupId: undefined
},
importanceOptions: [1, 2, 3],
calendarTypeOptions,
@@ -268,6 +270,25 @@
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const tempData = Object.assign({}, this.temp)
updatePortPool(tempData).then(response => {
if (response.data.code === 0) {
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
this.dialogFormVisible = false
this.getList()
}
})
}
})
},
handleDelete(row) {
deletePortPool(row.id).then(response => {
if (response.data.code === 0) {
+10
View File
@@ -51,6 +51,16 @@
<!-- </plugins>-->
<!-- </build>-->
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>app-*.yml</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
@@ -286,7 +286,7 @@ public class DateUtil {
public static Date getHourBegin(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
setCalender(calendar, calendar.get(Calendar.HOUR), 0, 0, 0);
setCalender(calendar, calendar.get(Calendar.HOUR_OF_DAY), 0, 0, 0);
return calendar.getTime();
}
@@ -298,7 +298,7 @@ public class DateUtil {
public static Date getHourEnd(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
setCalender(calendar, calendar.get(Calendar.HOUR), 59, 59, 999);
setCalender(calendar, calendar.get(Calendar.HOUR_OF_DAY), 59, 59, 999);
return calendar.getTime();
}
+11
View File
@@ -83,10 +83,21 @@
<!-- </build>-->
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>app-*.yml</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
<appendAssemblyId>false</appendAssemblyId>
<finalName>${artifactId}</finalName>
<archive>
@@ -9,4 +9,8 @@ public interface Constants {
* 默认的端口分组ID
*/
int DEFAULT_PORT_GROUP_ID = 1;
/**
* 首页流量监控展示天数
*/
int HOME_FLOW_DAYS = 15;
}
@@ -23,8 +23,8 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.ClientConnectRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.ClientConnectRecordListRes;
import fun.asgc.neutrino.proxy.server.controller.req.log.ClientConnectRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.log.ClientConnectRecordListRes;
import fun.asgc.neutrino.proxy.server.service.ClientConnectRecordService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import lombok.extern.slf4j.Slf4j;
@@ -22,8 +22,8 @@
package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.rest.Authorization;
import fun.asgc.neutrino.proxy.server.controller.req.LoginReq;
import fun.asgc.neutrino.proxy.server.controller.res.LoginRes;
import fun.asgc.neutrino.proxy.server.controller.req.system.LoginReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.LoginRes;
import fun.asgc.neutrino.proxy.server.service.UserService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.noear.solon.annotation.*;
@@ -24,14 +24,14 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.Authorization;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoExecuteReq;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoListReq;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoExecuteRes;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoListRes;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoUpdateEnableStatusRes;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoUpdateRes;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoExecuteReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoListReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoExecuteRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoListRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoUpdateEnableStatusRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoUpdateRes;
import fun.asgc.neutrino.proxy.server.dal.entity.JobInfoDO;
import fun.asgc.neutrino.proxy.server.service.JobInfoService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
@@ -23,8 +23,8 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.JobLogListReq;
import fun.asgc.neutrino.proxy.server.controller.res.JobLogListRes;
import fun.asgc.neutrino.proxy.server.controller.req.log.JobLogListReq;
import fun.asgc.neutrino.proxy.server.controller.res.log.JobLogListRes;
import fun.asgc.neutrino.proxy.server.service.JobLogService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import lombok.extern.slf4j.Slf4j;
@@ -24,8 +24,8 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.Authorization;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.*;
import fun.asgc.neutrino.proxy.server.controller.res.proxy.*;
import fun.asgc.neutrino.proxy.server.service.LicenseService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.noear.solon.annotation.*;
@@ -3,12 +3,15 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.Authorization;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupDeleteReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupListReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupCreateRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupListRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupUpdateEnableStatusRes;
import fun.asgc.neutrino.proxy.server.service.PortGroupService;
import fun.asgc.neutrino.proxy.server.service.PortPoolService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.annotation.*;
import java.util.List;
@@ -2,8 +2,8 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.*;
import fun.asgc.neutrino.proxy.server.controller.res.proxy.*;
import fun.asgc.neutrino.proxy.server.service.PortMappingService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.apache.commons.lang3.StringUtils;
@@ -24,8 +24,8 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.Authorization;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.system.*;
import fun.asgc.neutrino.proxy.server.controller.res.system.*;
import fun.asgc.neutrino.proxy.server.service.PortPoolService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.noear.solon.annotation.*;
@@ -51,6 +51,15 @@ public class PortPoolController {
return portPoolService.page(pageQuery, req);
}
@Post
@Mapping("/update")
public PortPoolUpdateRes update(PortPoolUpdateReq req) {
ParamCheckUtil.checkNotNull(req, "req");
ParamCheckUtil.checkNotNull(req.getId(), "id");
ParamCheckUtil.checkNotNull(req.getGroupId(), "groupId");
return portPoolService.update(req);
}
@Get
@Mapping("/list")
public List<PortPoolListRes> list(PortPoolListReq req) {
@@ -2,11 +2,11 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.res.ReportDataViewRes;
import fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.req.report.LicenseFlowMonthReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.report.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.report.UserFlowMonthReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.report.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.report.*;
import fun.asgc.neutrino.proxy.server.service.ReportService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.noear.solon.annotation.Controller;
@@ -27,27 +27,13 @@ public class ReportController {
private ReportService reportService;
/**
* 数据一览
* 在线用户数 查询token有效的用户ID数
* 在线Client数 查询license表,状态为在线的数量
* 今日流量 上行/下行/总计
* 历史流量 上行/下行/总计
*
* 1、点击在线用户数:下方展示在线的用户列表,带分页
* 2、点击在线client数:下方展示在线的license列表,带分页
* 3、点击今日流量:下方展示今日的流量折线图(小时)。 筛选项:全部/用户列表
* 4、点击历史流量:下方展示历史流量折线图。筛选项:日/月 日期选择:日(展示最近30天,最多跨30日),月(展示最近12个月,最多跨12个月)
* 首页数据一览
* @return
*/
@Get
@Mapping("/data-view")
public ReportDataViewRes dataView() {
return new ReportDataViewRes()
.setUserOnlineNumber(2).setEnableUserNumber(3).setUserNumber(5)
.setLicenseNumber(3).setEnableLicenseNumber(6).setLicenseNumber(6)
.setServerPortOnlineNumber(3).setEnableServerPortNumber(5).setServerPortNumber(6)
.setTotalUpstreamFlow("23K").setTotalDownwardFlow("47M")
;
@Mapping("/home/data-view")
public HomeDataView homeDataView() {
return reportService.homeDataView();
}
/**
@@ -77,4 +63,32 @@ public class ReportController {
return reportService.licenseFlowReportPage(pageQuery, req);
}
/**
* 用户流量报表月度明细
* @param pageQuery
* @param req
* @return
*/
@Get
@Mapping("/user/flow-month-report/page")
public PageInfo<UserFlowMonthReportRes> userFlowMonthReportPage(PageQuery pageQuery, UserFlowMonthReportReq req) {
ParamCheckUtil.checkNotNull(pageQuery, "pageQuery");
return reportService.userFlowMonthReportPage(pageQuery, req);
}
/**
* license流量报表月度明细
* @param pageQuery
* @param req
* @return
*/
@Get
@Mapping("/license/flow-month-report/page")
public PageInfo<LicenseFlowMonthReportRes> licenseFlowMonthReportPage(PageQuery pageQuery, LicenseFlowMonthReportReq req) {
ParamCheckUtil.checkNotNull(pageQuery, "pageQuery");
return reportService.licenseFlowMonthReportPage(pageQuery, req);
}
}
@@ -26,8 +26,8 @@ import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.Authorization;
import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.system.*;
import fun.asgc.neutrino.proxy.server.controller.res.system.*;
import fun.asgc.neutrino.proxy.server.dal.UserMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.UserDO;
import fun.asgc.neutrino.proxy.server.service.UserService;
@@ -23,8 +23,8 @@ package fun.asgc.neutrino.proxy.server.controller;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.UserLoginRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.UserLoginRecordListRes;
import fun.asgc.neutrino.proxy.server.controller.req.log.UserLoginRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.log.UserLoginRecordListRes;
import fun.asgc.neutrino.proxy.server.service.UserLoginRecordService;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import org.noear.solon.annotation.Controller;
@@ -1,34 +0,0 @@
/**
* 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.controller.req;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Data
public class LicenseFlowReportReq {
private Integer userId;
private Integer licenseId;
}
@@ -1,36 +0,0 @@
/**
* 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.controller.req;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Data
public class UserFlowReportReq {
/**
* 用户ID
*/
private Integer userId;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.log;
import lombok.Data;
@@ -29,5 +29,8 @@ import lombok.Data;
*/
@Data
public class ClientConnectRecordListReq {
/**
* licenseId
*/
private Integer licenseId;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.log;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.log;
import lombok.Data;
@@ -30,5 +30,8 @@ import lombok.Data;
*/
@Data
public class UserLoginRecordListReq {
/**
* 用户ID
*/
private Integer userId;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.proxy;
import lombok.Data;
@@ -0,0 +1,13 @@
package fun.asgc.neutrino.proxy.server.controller.req.report;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Data
public class LicenseFlowMonthReportReq {
private Integer userId;
private Integer licenseId;
}
@@ -0,0 +1,13 @@
package fun.asgc.neutrino.proxy.server.controller.req.report;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Data
public class LicenseFlowReportReq {
private Integer userId;
private Integer licenseId;
}
@@ -0,0 +1,15 @@
package fun.asgc.neutrino.proxy.server.controller.req.report;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Data
public class UserFlowMonthReportReq {
/**
* 用户ID
*/
private Integer userId;
}
@@ -0,0 +1,15 @@
package fun.asgc.neutrino.proxy.server.controller.req.report;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Data
public class UserFlowReportReq {
/**
* 用户ID
*/
private Integer userId;
}
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -30,5 +30,8 @@ import lombok.Data;
*/
@Data
public class PortPoolListReq {
/**
* 分组ID
*/
private Integer groupId;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -0,0 +1,14 @@
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2023/3/25
*/
@Data
public class PortPoolUpdateReq {
private Integer id;
private Integer groupId;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
/**
* 用户列表请求
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.req;
package fun.asgc.neutrino.proxy.server.controller.req.system;
import lombok.Data;
@@ -1,76 +0,0 @@
/**
* 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.controller.res;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class LicenseFlowReportRes {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
/**
* licenseId
*/
private Integer licenseId;
/**
* license名称
*/
private String licenseName;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@@ -1,4 +0,0 @@
package fun.asgc.neutrino.proxy.server.controller.res;
public class PortGroupCreateRes {
}
@@ -1,4 +0,0 @@
package fun.asgc.neutrino.proxy.server.controller.res;
public class PortPoolUpdateGroupRes {
}
@@ -1,57 +0,0 @@
package fun.asgc.neutrino.proxy.server.controller.res;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author: aoshiguchen
* @date: 2022/9/12
*/
@Accessors(chain = true)
@Data
public class ReportDataViewRes {
/**
* 在线用户数
*/
private Integer userOnlineNumber;
/**
* 启用用户数
*/
private Integer enableUserNumber;
/**
* 用户总数
*/
private Integer userNumber;
/**
* 在线license数
*/
private Integer licenseOnlineNumber;
/**
* 启用license数
*/
private Integer enableLicenseNumber;
/**
* license总数
*/
private Integer licenseNumber;
/**
* 服务端口在线数
*/
private Integer serverPortOnlineNumber;
/**
* 启用服务端口数
*/
private Integer enableServerPortNumber;
/**
* 总的服务端口数
*/
private Integer serverPortNumber;
/**
* 累计上行流量
*/
private String totalUpstreamFlow;
/**
* 累计下行流量
*/
private String totalDownwardFlow;
}
@@ -1,68 +0,0 @@
/**
* 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.controller.res;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class UserFlowReportRes {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.log;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.log;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.log;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
/**
* 更新启用状态响应
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
/**
*
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import lombok.Data;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.proxy;
import lombok.Data;
@@ -0,0 +1,208 @@
package fun.asgc.neutrino.proxy.server.controller.res.report;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
/**
* @author: aoshiguchen
* @date: 2023/3/26
*/
@Accessors(chain = true)
@Data
public class HomeDataView {
/**
* licenses数据
*/
private License license;
/**
* 端口映射
*/
private PortMapping portMapping;
/**
* 今日流量
*/
private TodayFlow todayFlow;
/**
* 总流量
*/
private TotalFlow totalFlow;
/**
* 最近7日流量
*/
private Last7dFlow last7dFlow;
@Accessors(chain = true)
@Data
public static class License {
/**
* 总数
*/
private Integer totalCount;
/**
* 在线数
*/
private Integer onlineCount;
/**
* 离线数
*/
private Integer offlineCount;
}
@Accessors(chain = true)
@Data
public static class PortMapping {
/**
* 总数
*/
private Integer totalCount;
/**
* 在线数
*/
private Integer onlineCount;
/**
* 离线数
*/
private Integer offlineCount;
}
@Accessors(chain = true)
@Data
public static class TodayFlow {
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@Accessors(chain = true)
@Data
public static class TotalFlow {
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@Accessors(chain = true)
@Data
public static class Last7dFlow {
/**
* 最近7日流量数据
*/
private List<SingleDayFlow> dataList;
/**
* x轴日期
*/
private List<String> xDate;
/**
* 图例(上行流量、下行流量、总流量)
*/
private List<String> legendData;
/**
* 折线列表
*/
private List<Series> seriesList;
}
@Accessors(chain = true)
@Data
public static class SingleDayFlow {
/**
* 日期字符串
*/
private String dateStr;
/**
* 日期
*/
private Date date;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@Accessors(chain = true)
@Data
public static class Series {
/**
* 此处目前固定为:line
*/
private String seriesType;
/**
* 名称:上行流量、下行流量、总流量
*/
private String seriesName;
/**
* y值序列
*/
private List<Long> seriesData;
}
}
@@ -0,0 +1,59 @@
package fun.asgc.neutrino.proxy.server.controller.res.report;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class LicenseFlowMonthReportRes {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
/**
* licenseId
*/
private Integer licenseId;
/**
* license名称
*/
private String licenseName;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
/**
* 日期
*/
private Date date;
}
@@ -0,0 +1,55 @@
package fun.asgc.neutrino.proxy.server.controller.res.report;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class LicenseFlowReportRes {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
/**
* licenseId
*/
private Integer licenseId;
/**
* license名称
*/
private String licenseName;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@@ -0,0 +1,51 @@
package fun.asgc.neutrino.proxy.server.controller.res.report;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class UserFlowMonthReportRes {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
/**
* 日期
*/
private Date date;
}
@@ -0,0 +1,47 @@
package fun.asgc.neutrino.proxy.server.controller.res.report;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* @author: aoshiguchen
* @date: 2022/12/21
*/
@Accessors(chain = true)
@Data
public class UserFlowReportRes {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
/**
* 总流量字节数
*/
private Long totalFlowBytes;
/**
* 上行流量描述
*/
private String upFlowDesc;
/**
* 下行流量描述
*/
private String downFlowDesc;
/**
* 总流量描述
*/
private String totalFlowDesc;
}
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
import java.util.Date;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -0,0 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res.system;
public class PortGroupCreateRes {
}
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -62,5 +62,5 @@ public class PortPoolListRes {
/**
* 分组ID
*/
private String groupId;
private Integer groupId;
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -0,0 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res.system;
public class PortPoolUpdateGroupRes {
}
@@ -0,0 +1,11 @@
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2023/3/25
*/
@Data
public class PortPoolUpdateRes {
}
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
/**
*
@@ -1,4 +1,4 @@
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
import lombok.Data;
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
/**
*
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.proxy.server.controller.res;
package fun.asgc.neutrino.proxy.server.controller.res.system;
/**
*
@@ -33,7 +33,7 @@ import java.util.List;
public interface FlowReportDayMapper extends BaseMapper<FlowReportDayDO> {
default void clean(Date date) {
this.delete(new LambdaQueryWrapper<FlowReportDayDO>()
.lt(FlowReportDayDO::getCreateTime, date)
.lt(FlowReportDayDO::getDate, date)
);
}
@@ -45,8 +45,8 @@ public interface FlowReportDayMapper extends BaseMapper<FlowReportDayDO> {
default List<FlowReportDayDO> findListByDateRange(Date startDate, Date endDate) {
return this.selectList(new LambdaQueryWrapper<FlowReportDayDO>()
.le(FlowReportDayDO::getDate, startDate)
.gt(FlowReportDayDO::getDate, endDate)
.ge(FlowReportDayDO::getDate, startDate)
.le(FlowReportDayDO::getDate, endDate)
);
}
}
@@ -33,7 +33,7 @@ import java.util.List;
public interface FlowReportHourMapper extends BaseMapper<FlowReportHourDO> {
default void clean(Date date) {
this.delete(new LambdaQueryWrapper<FlowReportHourDO>()
.lt(FlowReportHourDO::getCreateTime, date)
.lt(FlowReportHourDO::getDate, date)
);
}
@@ -34,7 +34,7 @@ import java.util.Set;
public interface FlowReportMinuteMapper extends BaseMapper<FlowReportMinuteDO> {
default void clean(Date date) {
this.delete(new LambdaQueryWrapper<FlowReportMinuteDO>()
.lt(FlowReportMinuteDO::getCreateTime, date)
.lt(FlowReportMinuteDO::getDate, date)
);
}
@@ -2,8 +2,8 @@ package fun.asgc.neutrino.proxy.server.dal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import fun.asgc.neutrino.proxy.server.controller.req.PortGroupListReq;
import fun.asgc.neutrino.proxy.server.controller.res.PortGroupListRes;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupListReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupListRes;
import fun.asgc.neutrino.proxy.server.dal.entity.PortGroupDO;
import org.apache.ibatis.annotations.Mapper;
@@ -5,8 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.controller.req.PortMappingListReq;
import fun.asgc.neutrino.proxy.server.controller.res.PortMappingListRes;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.PortMappingListReq;
import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -24,9 +24,8 @@ package fun.asgc.neutrino.proxy.server.dal;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import fun.asgc.neutrino.proxy.server.controller.req.PortPoolListReq;
import fun.asgc.neutrino.proxy.server.controller.res.AvailablePortListRes;
import fun.asgc.neutrino.proxy.server.controller.res.PortPoolListRes;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortPoolListReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortPoolListRes;
import fun.asgc.neutrino.proxy.server.dal.entity.PortPoolDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -66,7 +65,7 @@ public interface PortPoolMapper extends BaseMapper<PortPoolDO> {
);
}
List<PortPoolListRes> selectResList(PortPoolListReq req);
List<PortPoolListRes> selectResList(@Param("req") PortPoolListReq req);
List<PortPoolListRes> getAvailablePortList(@Param("licenseId") Integer licenseId,@Param("userId") Integer userId);
}
@@ -1,9 +1,8 @@
package fun.asgc.neutrino.proxy.server.dal;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.res.report.*;
import fun.asgc.neutrino.proxy.server.service.bo.FlowBO;
import fun.asgc.neutrino.proxy.server.service.bo.SingleDayFlowBO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -23,9 +22,48 @@ public interface ReportMapper {
*/
List<UserFlowReportRes> userFlowReportList(@Param("userId") Integer userId, @Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* 基于用户维度的流量报表
* 基于License维度的流量报表
* @param userId
* @return
*/
List<LicenseFlowReportRes> licenseFLowReportList(@Param("userId") Integer userId, @Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* 用户流量月度明细
* @param userId
* @return
*/
List<UserFlowMonthReportRes> userFlowMonthReportList(@Param("userId") Integer userId, @Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* License流量月度明细
* @param userId
* @return
*/
List<LicenseFlowMonthReportRes> licenseFLowMonthReportList(@Param("userId") Integer userId, @Param("licenseId") Integer licenseId, @Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* 首页 - 今日流量
* @param curDayBeginDate
* @param curDate
* @return
*/
FlowBO homeTodayFlow(@Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* 首页 - 总流量
* @param curMonthBeginDate
* @param curDayBeginDate
* @param curDate
* @return
*/
FlowBO homeTotalFlow(@Param("curMonthBeginDate") Date curMonthBeginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
/**
* 首页最近7日流量
* @param beginDate
* @param curDayBeginDate
* @param curDate
* @return
*/
List<SingleDayFlowBO> homeLast7dFlowList(@Param("beginDate") Date beginDate, @Param("curDayBeginDate") Date curDayBeginDate, @Param("curDate") Date curDate);
}
@@ -33,6 +33,7 @@ import fun.asgc.neutrino.proxy.server.service.FlowReportService;
import fun.asgc.solon.extend.job.IJobHandler;
import fun.asgc.solon.extend.job.annotation.JobHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
@@ -60,7 +61,7 @@ public class FlowReportForDayJob implements IJobHandler {
@Override
public void execute(String param) throws Exception {
Date now = new Date();
String dateStr = DateUtil.format(DateUtil.addDate(now, Calendar.DATE, -1), "yyyy-MM-dd");
String dateStr = getDateStr(now, param); // DateUtil.format(DateUtil.addDate(now, Calendar.DATE, -1), "yyyy-MM-dd");
Date date = DateUtil.parse(dateStr, "yyyy-MM-dd");
Date startHourDate = DateUtil.getDayBegin(date);
Date endHourDate = DateUtil.getDayEnd(date);
@@ -99,4 +100,16 @@ public class FlowReportForDayJob implements IJobHandler {
}
}
private String getDateStr(Date now, String params) {
if (StringUtils.isNotBlank(params)) {
try {
// 参数格式错误则取当前时间
DateUtil.parse(params, "yyyy-MM-dd");
return params;
} catch (Exception e) {
// ignore
}
}
return DateUtil.format(DateUtil.addDate(now, Calendar.DATE, -1), "yyyy-MM-dd");
}
}
@@ -11,6 +11,7 @@ import fun.asgc.neutrino.proxy.server.service.FlowReportService;
import fun.asgc.solon.extend.job.IJobHandler;
import fun.asgc.solon.extend.job.annotation.JobHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
@@ -36,7 +37,7 @@ public class FlowReportForHourJob implements IJobHandler {
@Override
public void execute(String param) throws Exception {
Date now = new Date();
String dateStr = DateUtil.format(DateUtil.addDate(now, Calendar.HOUR, -1), "yyyy-MM-dd HH");
String dateStr = getDateStr(now, param); // DateUtil.format(DateUtil.addDate(now, Calendar.HOUR, -1), "yyyy-MM-dd HH");
Date date = DateUtil.parse(dateStr, "yyyy-MM-dd HH");
Date startHourDate = DateUtil.getHourBegin(date);
Date endHourDate = DateUtil.getHourEnd(date);
@@ -75,4 +76,17 @@ public class FlowReportForHourJob implements IJobHandler {
}
}
private String getDateStr(Date now, String params) {
if (StringUtils.isNotBlank(params)) {
try {
// 参数格式错误则取当前时间
DateUtil.parse(params, "yyyy-MM-dd HH");
return params;
} catch (Exception e) {
// ignore
}
}
return DateUtil.format(DateUtil.addDate(now, Calendar.HOUR, -1), "yyyy-MM-dd HH");
}
}
@@ -9,6 +9,7 @@ import fun.asgc.neutrino.proxy.server.service.FlowReportService;
import fun.asgc.solon.extend.job.IJobHandler;
import fun.asgc.solon.extend.job.annotation.JobHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
@@ -38,7 +39,7 @@ public class FlowReportForMonthJob implements IJobHandler {
@Override
public void execute(String param) throws Exception {
Date now = new Date();
String dateStr = DateUtil.format(DateUtil.addDate(now, Calendar.MONTH, -1), "yyyy-MM");
String dateStr = getDateStr(now, param); // DateUtil.format(DateUtil.addDate(now, Calendar.MONTH, -1), "yyyy-MM");
Date date = DateUtil.parse(dateStr, "yyyy-MM");
Date startDayDate = DateUtil.getMonthBegin(date);
Date endEndDate = DateUtil.getMonthEnd(date);
@@ -77,4 +78,16 @@ public class FlowReportForMonthJob implements IJobHandler {
}
}
private String getDateStr(Date now, String params) {
if (StringUtils.isNotBlank(params)) {
try {
// 参数格式错误则取当前时间
DateUtil.parse(params, "yyyy-MM");
return params;
} catch (Exception e) {
// ignore
}
}
return DateUtil.format(DateUtil.addDate(now, Calendar.MONTH, -1), "yyyy-MM");
}
}
@@ -8,8 +8,8 @@ import com.github.pagehelper.PageHelper;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.controller.req.ClientConnectRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.ClientConnectRecordListRes;
import fun.asgc.neutrino.proxy.server.controller.req.log.ClientConnectRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.log.ClientConnectRecordListRes;
import fun.asgc.neutrino.proxy.server.dal.ClientConnectRecordMapper;
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
import fun.asgc.neutrino.proxy.server.dal.UserMapper;
@@ -51,6 +51,7 @@ public class ClientConnectRecordService {
public PageInfo<ClientConnectRecordListRes> page(PageQuery pageQuery, ClientConnectRecordListReq req) {
Page<ClientConnectRecordListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
List<ClientConnectRecordDO> list = clientConnectRecordMapper.selectList(new LambdaQueryWrapper<ClientConnectRecordDO>()
.eq(null != req.getLicenseId(), ClientConnectRecordDO::getLicenseId, req.getLicenseId())
.orderByDesc(ClientConnectRecordDO::getId)
);
List<ClientConnectRecordListRes> respList = mapperFacade.mapAsList(list, ClientConnectRecordListRes.class);
@@ -9,14 +9,14 @@ import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoExecuteReq;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoListReq;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.JobInfoUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoExecuteRes;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoListRes;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoUpdateEnableStatusRes;
import fun.asgc.neutrino.proxy.server.controller.res.JobInfoUpdateRes;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoExecuteReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoListReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.JobInfoUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoExecuteRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoListRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoUpdateEnableStatusRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.JobInfoUpdateRes;
import fun.asgc.neutrino.proxy.server.dal.JobInfoMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.JobInfoDO;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
@@ -26,8 +26,8 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.JobLogListReq;
import fun.asgc.neutrino.proxy.server.controller.res.JobLogListRes;
import fun.asgc.neutrino.proxy.server.controller.req.log.JobLogListReq;
import fun.asgc.neutrino.proxy.server.controller.res.log.JobLogListRes;
import fun.asgc.neutrino.proxy.server.dal.JobLogMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.JobLogDO;
import fun.asgc.solon.extend.job.IJobCallback;
@@ -12,11 +12,11 @@ import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseListReq;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.LicenseCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.LicenseListReq;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.LicenseUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.LicenseUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.proxy.*;
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
import fun.asgc.neutrino.proxy.server.dal.UserMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO;
@@ -78,7 +78,7 @@ public class LicenseService implements Lifecycle {
public List<LicenseListRes> list(LicenseListReq req) {
List<LicenseDO> list = licenseMapper.selectList(new LambdaQueryWrapper<LicenseDO>()
.eq(LicenseDO::getEnable, EnableStatusEnum.ENABLE.getStatus())
.eq(null != req.getEnable(), LicenseDO::getEnable, req.getEnable())
);
List<LicenseListRes> licenseList = assembleConvertLicenses(list);
return licenseList;
@@ -10,15 +10,15 @@ import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.Constants;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.controller.req.PortGroupCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.PortGroupListReq;
import fun.asgc.neutrino.proxy.server.controller.req.PortGroupUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupListReq;
import fun.asgc.neutrino.proxy.server.controller.req.system.PortGroupUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupCreateRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupListRes;
import fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupUpdateEnableStatusRes;
import fun.asgc.neutrino.proxy.server.dal.PortGroupMapper;
import fun.asgc.neutrino.proxy.server.dal.PortPoolMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO;
import fun.asgc.neutrino.proxy.server.dal.entity.PortGroupDO;
import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO;
import fun.asgc.neutrino.proxy.server.dal.entity.PortPoolDO;
import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil;
import ma.glasnost.orika.MapperFacade;
@@ -1,7 +1,6 @@
package fun.asgc.neutrino.proxy.server.service;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
@@ -12,8 +11,11 @@ import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.PortMappingCreateReq;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.PortMappingListReq;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.PortMappingUpdateEnableStatusReq;
import fun.asgc.neutrino.proxy.server.controller.req.proxy.PortMappingUpdateReq;
import fun.asgc.neutrino.proxy.server.controller.res.proxy.*;
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
import fun.asgc.neutrino.proxy.server.dal.PortMappingMapper;
import fun.asgc.neutrino.proxy.server.dal.PortPoolMapper;
@@ -1,38 +1,17 @@
/**
* Copyright (c) 2022 aoshiguchen
* <p>
* 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:
* <p>
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* <p>
* 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.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.base.rest.ServiceException;
import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.system.*;
import fun.asgc.neutrino.proxy.server.controller.res.system.*;
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
import fun.asgc.neutrino.proxy.server.dal.PortGroupMapper;
import fun.asgc.neutrino.proxy.server.dal.PortMappingMapper;
@@ -44,7 +23,6 @@ import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@@ -94,6 +72,15 @@ public class PortPoolService {
return list.stream().filter(item -> !serverPorts.contains(item.getPort())).collect(Collectors.toList());
}
public PortPoolUpdateRes update(PortPoolUpdateReq req) {
portPoolMapper.update(null, new LambdaUpdateWrapper<PortPoolDO>()
.eq(PortPoolDO::getId, req.getId())
.set(PortPoolDO::getGroupId, req.getGroupId())
.set(PortPoolDO::getUpdateTime, new Date())
);
return new PortPoolUpdateRes();
}
public PortPoolCreateRes create(PortPoolCreateReq req) {
PortPoolDO oldPortPoolDO = portPoolMapper.findByPort(req.getPort());
@@ -1,6 +1,7 @@
package fun.asgc.neutrino.proxy.server.service;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
@@ -8,12 +9,20 @@ import fun.asgc.neutrino.proxy.core.util.DateUtil;
import fun.asgc.neutrino.proxy.server.base.db.DbConfig;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.JobLogListRes;
import fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes;
import fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes;
import fun.asgc.neutrino.proxy.server.constant.Constants;
import fun.asgc.neutrino.proxy.server.constant.OnlineStatusEnum;
import fun.asgc.neutrino.proxy.server.controller.req.report.LicenseFlowMonthReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.report.LicenseFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.report.UserFlowMonthReportReq;
import fun.asgc.neutrino.proxy.server.controller.req.report.UserFlowReportReq;
import fun.asgc.neutrino.proxy.server.controller.res.report.*;
import fun.asgc.neutrino.proxy.server.dal.LicenseMapper;
import fun.asgc.neutrino.proxy.server.dal.PortMappingMapper;
import fun.asgc.neutrino.proxy.server.dal.ReportMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.LicenseDO;
import fun.asgc.neutrino.proxy.server.dal.entity.PortMappingDO;
import fun.asgc.neutrino.proxy.server.service.bo.FlowBO;
import fun.asgc.neutrino.proxy.server.service.bo.SingleDayFlowBO;
import fun.asgc.neutrino.proxy.server.util.FormatUtil;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
@@ -21,8 +30,8 @@ import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import java.util.Date;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author: aoshiguchen
@@ -35,9 +44,56 @@ public class ReportService {
private MapperFacade mapperFacade;
@Db
private ReportMapper reportMapper;
@Db
private LicenseMapper licenseMapper;
@Db
private PortMappingMapper portMappingMapper;
@Inject
private DbConfig dbConfig;
/**
* 首页图表
* @return
*/
public HomeDataView homeDataView() {
HomeDataView homeDataView = new HomeDataView();
homeDataView.setLicense(new HomeDataView.License().setTotalCount(0).setOnlineCount(0));
homeDataView.setPortMapping(new HomeDataView.PortMapping().setTotalCount(0).setOnlineCount(0));
homeDataView.setTodayFlow(new HomeDataView.TodayFlow().setUpFlowBytes(0L).setDownFlowBytes(0L));
homeDataView.setTotalFlow(new HomeDataView.TotalFlow().setUpFlowBytes(0L).setDownFlowBytes(0L));
// 查询license列表
List<LicenseDO> licenseDOList = licenseMapper.listAll();
if (CollectionUtil.isNotEmpty(licenseDOList)) {
homeDataView.getLicense().setTotalCount(licenseDOList.size());
homeDataView.getLicense().setOnlineCount((int)licenseDOList.stream().filter(e -> OnlineStatusEnum.ONLINE.getStatus().equals(e.getIsOnline())).count());
}
// 查询端口映射列表
List<PortMappingDO> portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper<>());
if (CollectionUtil.isNotEmpty(portMappingDOList)) {
homeDataView.getPortMapping().setTotalCount(portMappingDOList.size());
homeDataView.getPortMapping().setOnlineCount((int)portMappingDOList.stream().filter(e -> OnlineStatusEnum.ONLINE.getStatus().equals(e.getIsOnline())).count());
}
// 今日流量
Date now = new Date();
FlowBO todayFlow = reportMapper.homeTodayFlow(DateUtil.getDayBegin(now), now);
homeDataView.setTodayFlow(mapperFacade.map(todayFlow, HomeDataView.TodayFlow.class));
// 总流量
FlowBO totalFlow = reportMapper.homeTotalFlow(DateUtil.getMonthBegin(now), DateUtil.getDayBegin(now), now);
homeDataView.setTotalFlow(mapperFacade.map(totalFlow, HomeDataView.TotalFlow.class));
// 最近n日流量
Integer days = Constants.HOME_FLOW_DAYS;
List<SingleDayFlowBO> last7dFlowList = reportMapper.homeLast7dFlowList(DateUtil.getDayBegin(DateUtil.addDate(now, Calendar.DATE, -(days - 1))), DateUtil.getDayBegin(now), now);
homeDataView.setLast7dFlow(new HomeDataView.Last7dFlow());
homeDataView.getLast7dFlow().setDataList(mapperFacade.mapAsList(last7dFlowList, HomeDataView.SingleDayFlow.class));
// 数据处理
fillHomeDataView(homeDataView, now);
return homeDataView;
}
/**
* 用户流量报表分页
* @param pageQuery
@@ -66,6 +122,34 @@ public class ReportService {
return PageInfo.of(list, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
/**
* 用户流量月度明细
* @param pageQuery
* @param req
* @return
*/
public PageInfo<UserFlowMonthReportRes> userFlowMonthReportPage(PageQuery pageQuery, UserFlowMonthReportReq req) {
Page<UserFlowMonthReportRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
Date now = new Date();
List<UserFlowMonthReportRes> list = reportMapper.userFlowMonthReportList(req.getUserId(), DateUtil.getMonthBegin(now), DateUtil.getDayBegin(now), now);
fillUserFlowMonthReport(list);
return PageInfo.of(list, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
/**
* license流量月度明细
* @param pageQuery
* @param req
* @return
*/
public PageInfo<LicenseFlowMonthReportRes> licenseFlowMonthReportPage(PageQuery pageQuery, LicenseFlowMonthReportReq req) {
Page<LicenseFlowMonthReportRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
Date now = new Date();
List<LicenseFlowMonthReportRes> list = reportMapper.licenseFLowMonthReportList(req.getUserId(), req.getLicenseId(), DateUtil.getMonthBegin(now), DateUtil.getDayBegin(now), now);
fillLicenseFlowMonthReport(list);
return PageInfo.of(list, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
private void fillUserFlowReport(List<UserFlowReportRes> list) {
if (CollectionUtil.isEmpty(list)) {
return;
@@ -99,4 +183,171 @@ public class ReportService {
item.setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(totalFlowBytes));
}
}
private void fillUserFlowMonthReport(List<UserFlowMonthReportRes> list) {
if (CollectionUtil.isEmpty(list)) {
return;
}
for (UserFlowMonthReportRes item : list) {
long upFlowBytes = (null == item.getUpFlowBytes()) ? 0 : item.getUpFlowBytes();
long downFlowBytes = (null == item.getDownFlowBytes()) ? 0 : item.getDownFlowBytes();
long totalFlowBytes = upFlowBytes + downFlowBytes;
item.setUpFlowBytes(upFlowBytes);
item.setDownFlowBytes(downFlowBytes);
item.setTotalFlowBytes(totalFlowBytes);
item.setUpFlowDesc(FormatUtil.getSizeDescByByteCount(upFlowBytes));
item.setDownFlowDesc(FormatUtil.getSizeDescByByteCount(downFlowBytes));
item.setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(totalFlowBytes));
}
}
private void fillLicenseFlowMonthReport(List<LicenseFlowMonthReportRes> list) {
if (CollectionUtil.isEmpty(list)) {
return;
}
for (LicenseFlowMonthReportRes item : list) {
long upFlowBytes = (null == item.getUpFlowBytes()) ? 0 : item.getUpFlowBytes();
long downFlowBytes = (null == item.getDownFlowBytes()) ? 0 : item.getDownFlowBytes();
long totalFlowBytes = upFlowBytes + downFlowBytes;
item.setUpFlowBytes(upFlowBytes);
item.setDownFlowBytes(downFlowBytes);
item.setTotalFlowBytes(totalFlowBytes);
item.setUpFlowDesc(FormatUtil.getSizeDescByByteCount(upFlowBytes));
item.setDownFlowDesc(FormatUtil.getSizeDescByByteCount(downFlowBytes));
item.setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(totalFlowBytes));
}
}
private void fillHomeDataView(HomeDataView homeDataView, Date now) {
if (null == homeDataView) {
return;
}
// License
if (null == homeDataView.getLicense()) {
homeDataView.setLicense(new HomeDataView.License());
}
if (null == homeDataView.getLicense().getTotalCount()) {
homeDataView.getLicense().setTotalCount(0);
}
if (null == homeDataView.getLicense().getOnlineCount()) {
homeDataView.getLicense().setOnlineCount(0);
}
homeDataView.getLicense().setOfflineCount(homeDataView.getLicense().getTotalCount() - homeDataView.getLicense().getOnlineCount());
// PortMapping
if (null == homeDataView.getPortMapping()) {
homeDataView.setPortMapping(new HomeDataView.PortMapping());
}
if (null == homeDataView.getPortMapping().getTotalCount()) {
homeDataView.getPortMapping().setTotalCount(0);
}
if (null == homeDataView.getPortMapping().getOnlineCount()) {
homeDataView.getPortMapping().setOnlineCount(0);
}
homeDataView.getPortMapping().setOfflineCount(homeDataView.getPortMapping().getTotalCount() - homeDataView.getPortMapping().getOnlineCount());
// TodayFlow
if (null == homeDataView.getTodayFlow()) {
homeDataView.setTodayFlow(new HomeDataView.TodayFlow());
}
if (null == homeDataView.getTodayFlow().getUpFlowBytes()) {
homeDataView.getTodayFlow().setUpFlowBytes(0L);
}
if (null == homeDataView.getTodayFlow().getDownFlowBytes()) {
homeDataView.getTodayFlow().setDownFlowBytes(0L);
}
homeDataView.getTodayFlow().setTotalFlowBytes(homeDataView.getTodayFlow().getUpFlowBytes() + homeDataView.getTodayFlow().getDownFlowBytes());
homeDataView.getTodayFlow().setUpFlowDesc(FormatUtil.getSizeDescByByteCount(homeDataView.getTodayFlow().getUpFlowBytes()));
homeDataView.getTodayFlow().setDownFlowDesc(FormatUtil.getSizeDescByByteCount(homeDataView.getTodayFlow().getDownFlowBytes()));
homeDataView.getTodayFlow().setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(homeDataView.getTodayFlow().getTotalFlowBytes()));
// TotalFlow
if (null == homeDataView.getTotalFlow()) {
homeDataView.setTotalFlow(new HomeDataView.TotalFlow());
}
if (null == homeDataView.getTotalFlow().getUpFlowBytes()) {
homeDataView.getTotalFlow().setUpFlowBytes(0L);
}
if (null == homeDataView.getTotalFlow().getDownFlowBytes()) {
homeDataView.getTotalFlow().setDownFlowBytes(0L);
}
homeDataView.getTotalFlow().setTotalFlowBytes(homeDataView.getTotalFlow().getUpFlowBytes() + homeDataView.getTotalFlow().getDownFlowBytes());
homeDataView.getTotalFlow().setUpFlowDesc(FormatUtil.getSizeDescByByteCount(homeDataView.getTotalFlow().getUpFlowBytes()));
homeDataView.getTotalFlow().setDownFlowDesc(FormatUtil.getSizeDescByByteCount(homeDataView.getTotalFlow().getDownFlowBytes()));
homeDataView.getTotalFlow().setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(homeDataView.getTotalFlow().getTotalFlowBytes()));
// 最近N日流量
Integer days = Constants.HOME_FLOW_DAYS;
HomeDataView.Last7dFlow last7dFlow = homeDataView.getLast7dFlow();
if (null == last7dFlow) {
last7dFlow = new HomeDataView.Last7dFlow();
homeDataView.setLast7dFlow(last7dFlow);
}
if (null == last7dFlow.getDataList()) {
last7dFlow.setDataList(Collections.emptyList());
}
// 数据列表
Set<String> existDateStrList = new HashSet<>();
if (CollectionUtil.isNotEmpty(last7dFlow.getDataList())) {
for (HomeDataView.SingleDayFlow singleDayFlow : last7dFlow.getDataList()) {
if (null == singleDayFlow.getUpFlowBytes()) {
singleDayFlow.setUpFlowBytes(0L);
}
if (null == singleDayFlow.getDownFlowBytes()) {
singleDayFlow.setDownFlowBytes(0L);
}
singleDayFlow.setTotalFlowBytes(singleDayFlow.getUpFlowBytes() + singleDayFlow.getDownFlowBytes());
singleDayFlow.setUpFlowDesc(FormatUtil.getSizeDescByByteCount(singleDayFlow.getUpFlowBytes()));
singleDayFlow.setDownFlowDesc(FormatUtil.getSizeDescByByteCount(singleDayFlow.getDownFlowBytes()));
singleDayFlow.setTotalFlowDesc(FormatUtil.getSizeDescByByteCount(singleDayFlow.getTotalFlowBytes()));
singleDayFlow.setDateStr(DateUtil.format(singleDayFlow.getDate(), "yyyy-MM-dd"));
existDateStrList.add(singleDayFlow.getDateStr());
}
}
// 获取最近7天的日期字符串列表。防止因统计数据缺失,造成图表展示错误的问题,缺失的日期数据,自动填充0
List<String> dateList = DateUtil.getBetweenTimes(DateUtil.format(DateUtil.addDate(now, Calendar.DATE, -(days - 1)), "yyyy-MM-dd"), DateUtil.format(now, "yyyy-MM-dd"));
for (String dateStr : dateList) {
if (existDateStrList.contains(dateStr)) {
continue;
}
last7dFlow.getDataList().add(new HomeDataView.SingleDayFlow()
.setDate(DateUtil.parse(dateStr, "yyyy-MM-dd"))
.setDateStr(dateStr)
.setUpFlowBytes(0L)
.setUpFlowDesc("0B")
.setDownFlowBytes(0L)
.setDownFlowDesc("0B")
.setTotalFlowBytes(0L)
.setTotalFlowDesc("0B")
);
}
// 按日期升序
Collections.sort(last7dFlow.getDataList(), Comparator.comparing(HomeDataView.SingleDayFlow::getDate));
// x轴日期
last7dFlow.setXDate(last7dFlow.getDataList().stream().map(HomeDataView.SingleDayFlow::getDateStr).collect(Collectors.toList()));
// 图例
last7dFlow.setLegendData(Lists.newArrayList("上行流量", "下行流量", "总流量"));
// 折线图
List<HomeDataView.Series> seriesList = Lists.newArrayList();
last7dFlow.setSeriesList(seriesList);
// 上行流量折线
seriesList.add(new HomeDataView.Series()
.setSeriesType("line")
.setSeriesName(last7dFlow.getLegendData().get(0))
.setSeriesData(last7dFlow.getDataList().stream().map(HomeDataView.SingleDayFlow::getUpFlowBytes).collect(Collectors.toList()))
);
// 下行流量折线
seriesList.add(new HomeDataView.Series()
.setSeriesType("line")
.setSeriesName(last7dFlow.getLegendData().get(1))
.setSeriesData(last7dFlow.getDataList().stream().map(HomeDataView.SingleDayFlow::getDownFlowBytes).collect(Collectors.toList()))
);
// 总流量折线
seriesList.add(new HomeDataView.Series()
.setSeriesType("line")
.setSeriesName(last7dFlow.getLegendData().get(2))
.setSeriesData(last7dFlow.getDataList().stream().map(HomeDataView.SingleDayFlow::getTotalFlowBytes).collect(Collectors.toList()))
);
}
}
@@ -7,8 +7,8 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import fun.asgc.neutrino.proxy.server.base.page.PageInfo;
import fun.asgc.neutrino.proxy.server.base.page.PageQuery;
import fun.asgc.neutrino.proxy.server.controller.req.UserLoginRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.UserLoginRecordListRes;
import fun.asgc.neutrino.proxy.server.controller.req.log.UserLoginRecordListReq;
import fun.asgc.neutrino.proxy.server.controller.res.log.UserLoginRecordListRes;
import fun.asgc.neutrino.proxy.server.dal.UserLoginRecordMapper;
import fun.asgc.neutrino.proxy.server.dal.UserMapper;
import fun.asgc.neutrino.proxy.server.dal.entity.UserDO;
@@ -41,6 +41,7 @@ public class UserLoginRecordService {
public PageInfo<UserLoginRecordListRes> page(PageQuery pageQuery, UserLoginRecordListReq req) {
Page<UserLoginRecordListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
List<UserLoginRecordDO> list = userLoginRecordMapper.selectList(new LambdaQueryWrapper<UserLoginRecordDO>()
.eq(null != req.getUserId(), UserLoginRecordDO::getUserId, req.getUserId())
.orderByDesc(UserLoginRecordDO::getCreateTime)
);
List<UserLoginRecordListRes> respList = mapperFacade.mapAsList(list, UserLoginRecordListRes.class);
@@ -11,8 +11,8 @@ import fun.asgc.neutrino.proxy.server.base.rest.ServiceException;
import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder;
import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum;
import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant;
import fun.asgc.neutrino.proxy.server.controller.req.*;
import fun.asgc.neutrino.proxy.server.controller.res.*;
import fun.asgc.neutrino.proxy.server.controller.req.system.*;
import fun.asgc.neutrino.proxy.server.controller.res.system.*;
import fun.asgc.neutrino.proxy.server.dal.UserLoginRecordMapper;
import fun.asgc.neutrino.proxy.server.dal.UserMapper;
import fun.asgc.neutrino.proxy.server.dal.UserTokenMapper;
@@ -0,0 +1,19 @@
package fun.asgc.neutrino.proxy.server.service.bo;
import lombok.Data;
/**
* @author: aoshiguchen
* @date: 2023/3/26
*/
@Data
public class FlowBO {
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
}
@@ -0,0 +1,26 @@
package fun.asgc.neutrino.proxy.server.service.bo;
import lombok.Data;
import java.util.Date;
/**
* 单日流量
* @author: aoshiguchen
* @date: 2023/3/26
*/
@Data
public class SingleDayFlowBO {
/**
* 日期
*/
private Date date;
/**
* 上行流量字节数
*/
private Long upFlowBytes;
/**
* 下行流量字节数
*/
private Long downFlowBytes;
}
@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="fun.asgc.neutrino.proxy.server.dal.PortGroupMapper">
<select id="selectPortGroupListResList" resultType="fun.asgc.neutrino.proxy.server.controller.res.PortGroupListRes">
<select id="selectPortGroupListResList" resultType="fun.asgc.neutrino.proxy.server.controller.res.system.PortGroupListRes">
SELECT g.*,
CASE
WHEN g.possessor_type = 1 THEN
@@ -2,16 +2,21 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="fun.asgc.neutrino.proxy.server.dal.PortPoolMapper">
<select id="selectResList" resultType="fun.asgc.neutrino.proxy.server.controller.res.PortPoolListRes">
<select id="selectResList" resultType="fun.asgc.neutrino.proxy.server.controller.res.system.PortPoolListRes">
SELECT p.*,
g.possessor_type,
g.`name` group_name
FROM port_pool p
LEFT JOIN port_group g ON p.group_id = g.id
LEFT JOIN port_group g ON p.group_id = g.id
<where>
<if test="req.groupId != null">
AND p.group_id = #{req.groupId}
</if>
</where>
ORDER BY p.id
</select>
<select id="getAvailablePortList" resultType="fun.asgc.neutrino.proxy.server.controller.res.PortPoolListRes">
<select id="getAvailablePortList" resultType="fun.asgc.neutrino.proxy.server.controller.res.system.PortPoolListRes">
SELECT
*
FROM
@@ -2,18 +2,18 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="fun.asgc.neutrino.proxy.server.dal.ReportMapper">
<select id="userFlowReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes">
<select id="userFlowReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.report.UserFlowReportRes">
SELECT
u.id AS 'userId',
u.name AS 'userName',
SUM(frm.write_bytes + frd.write_bytes + frm2.write_bytes) AS 'upFlowBytes',
SUM(frm.read_bytes + frd.read_bytes + frm2.read_bytes) AS 'downFlowBytes',
SUM(frm.write_bytes) monthWriteBytes,
SUM(frm.read_bytes) monthReadBytes,
SUM(frd.write_bytes) dayWriteBytes,
SUM(frd.read_bytes) dayReadBytes,
SUM(frm2.write_bytes) minuteWriteBytes,
SUM(frm2.read_bytes) minuteReadBytes
SUM(IFNULL(frm.write_bytes,0) + IFNULL(frd.write_bytes,0) + IFNULL(frm2.write_bytes,0)) AS 'upFlowBytes',
SUM(IFNULL(frm.read_bytes,0) + IFNULL(frd.read_bytes,0) + IFNULL(frm2.read_bytes,0)) AS 'downFlowBytes',
IFNULL(SUM(frm.write_bytes),0) monthWriteBytes,
IFNULL(SUM(frm.read_bytes),0) monthReadBytes,
IFNULL(SUM(frd.write_bytes),0) dayWriteBytes,
IFNULL(SUM(frd.read_bytes),0) dayReadBytes,
IFNULL(SUM(frm2.write_bytes),0) minuteWriteBytes,
IFNULL(SUM(frm2.read_bytes),0) minuteReadBytes
FROM `user` u
LEFT JOIN (SELECT user_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_month GROUP BY user_id) frm ON u.id = frm.user_id
LEFT JOIN (SELECT user_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_day
@@ -30,20 +30,20 @@
GROUP BY u.id
</select>
<select id="licenseFLowReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes">
<select id="licenseFLowReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.report.LicenseFlowReportRes">
SELECT
l.id AS 'licenseId',
l.name AS 'licenseName',
u.id AS 'userId',
u.name AS 'userName',
SUM(frm.write_bytes + frd.write_bytes + frm2.write_bytes) AS 'upFlowBytes',
SUM(frm.read_bytes + frd.read_bytes + frm2.read_bytes) AS 'downFlowBytes',
SUM(frm.write_bytes) monthWriteBytes,
SUM(frm.read_bytes) monthReadBytes,
SUM(frd.write_bytes) dayWriteBytes,
SUM(frd.read_bytes) dayReadBytes,
SUM(frm2.write_bytes) minuteWriteBytes,
SUM(frm2.read_bytes) minuteReadBytes
SUM(IFNULL(frm.write_bytes,0) + IFNULL(frd.write_bytes,0) + IFNULL(frm2.write_bytes,0)) AS 'upFlowBytes',
SUM(IFNULL(frm.read_bytes,0) + IFNULL(frd.read_bytes,0) + IFNULL(frm2.read_bytes,0)) AS 'downFlowBytes',
IFNULL(SUM(frm.write_bytes),0) monthWriteBytes,
IFNULL(SUM(frm.read_bytes),0) monthReadBytes,
IFNULL(SUM(frd.write_bytes),0) dayWriteBytes,
IFNULL(SUM(frd.read_bytes),0) dayReadBytes,
IFNULL(SUM(frm2.write_bytes),0) minuteWriteBytes,
IFNULL(SUM(frm2.read_bytes),0) minuteReadBytes
FROM `license` l
LEFT JOIN `user` u ON l.user_id = u.id
LEFT JOIN (SELECT license_id,sum(write_bytes) write_bytes,sum(read_bytes) read_bytes from flow_report_month GROUP BY license_id) frm ON l.id = frm.license_id
@@ -60,4 +60,191 @@
</where>
GROUP BY l.id
</select>
<select id="userFlowMonthReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.report.UserFlowMonthReportRes">
SELECT
T2.user_id,
u.name AS 'userName',
T2.date,
T2.upFlowBytes,
T2.downFlowBytes
FROM (
(
SELECT
frm.user_id,
frm.date,
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_month frm
GROUP BY frm.user_id,frm.date
)
UNION ALL
(
SELECT
T1.user_id,
#{curMonthBeginDate} AS 'date',
IFNULL(SUM(T1.upFlowBytes),0) AS 'upFlowBytes',
IFNULL(SUM(T1.downFlowBytes),0) AS 'downFlowBytes'
FROM (
(
SELECT
frd.user_id,
frd.license_id,
frd.write_bytes AS 'upFlowBytes',
frd.read_bytes AS 'downFlowBytes',
frd.date
FROM flow_report_day frd
WHERE frd.date >= #{curMonthBeginDate} AND frd.date &lt;= #{curDayBeginDate}
)
UNION ALL
(
SELECT
frm.user_id,
frm.license_id,
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes',
#{curDayBeginDate} AS 'date'
FROM flow_report_minute frm
WHERE frm.date >= #{curDayBeginDate} AND frm.date &lt;= #{curDate}
GROUP BY frm.user_id,frm.license_id
)
) T1
GROUP BY T1.user_id
)
) T2
LEFT JOIN `user` u ON u.id = T2.user_id
<where>
<if test="userId != null">
AND u.id = #{userId}
</if>
</where>
ORDER BY T2.user_id ASC,T2.date DESC
</select>
<select id="licenseFLowMonthReportList" resultType="fun.asgc.neutrino.proxy.server.controller.res.report.LicenseFlowMonthReportRes">
SELECT
T2.user_id,
u.name AS 'userName',
T2.license_id,
l.`name` AS 'licenseName',
T2.date,
T2.upFlowBytes,
T2.downFlowBytes
FROM (
(
SELECT
frm.user_id,
frm.license_id,
frm.date,
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_month frm
GROUP BY frm.user_id,frm.license_id,frm.date
)
UNION ALL
(
SELECT
T1.user_id,
T1.license_id,
#{curMonthBeginDate} AS 'date',
IFNULL(SUM(T1.upFlowBytes),0) AS 'upFlowBytes',
IFNULL(SUM(T1.downFlowBytes),0) AS 'downFlowBytes'
FROM (
(
SELECT
frd.user_id,
frd.license_id,
frd.write_bytes AS 'upFlowBytes',
frd.read_bytes AS 'downFlowBytes',
frd.date
FROM flow_report_day frd
WHERE frd.date >= #{curMonthBeginDate} AND frd.date &lt;= #{curDayBeginDate}
)
UNION ALL
(
SELECT
frm.user_id,
frm.license_id,
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes',
#{curDayBeginDate} AS 'date'
FROM flow_report_minute frm
WHERE frm.date >= #{curDayBeginDate} AND frm.date &lt;= #{curDate}
GROUP BY frm.user_id,frm.license_id
)
) T1
GROUP BY T1.user_id,T1.license_id
)
) T2
LEFT JOIN `user` u ON u.id = T2.user_id
LEFT JOIN `license` l ON l.id = T2.license_id
<where>
<if test="userId != null">
AND u.id = #{userId}
</if>
<if test="licenseId != null">
AND l.id = #{licenseId}
</if>
</where>
ORDER BY T2.user_id ASC,T2.license_id,T2.date DESC
</select>
<select id="homeTodayFlow" resultType="fun.asgc.neutrino.proxy.server.service.bo.FlowBO">
SELECT
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_minute frm
WHERE frm.date >= #{curDayBeginDate} AND frm.date &lt;= #{curDate}
</select>
<select id="homeTotalFlow" resultType="fun.asgc.neutrino.proxy.server.service.bo.FlowBO">
SELECT
IFNULL(SUM(T.upFlowBytes),0) AS 'upFlowBytes',
IFNULL(SUM(T.downFlowBytes),0) AS 'downFlowBytes'
FROM (
(
SELECT
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_month frm
) UNION ALL (
SELECT
IFNULL(SUM(frd.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frd.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_day frd
WHERE frd.date >= #{curMonthBeginDate} AND frd.date &lt;= #{curDayBeginDate}
) UNION ALL (
SELECT
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_minute frm
WHERE frm.date >= #{curDayBeginDate} AND frm.date &lt;= #{curDate}
)
) T
</select>
<select id="homeLast7dFlowList" resultType="fun.asgc.neutrino.proxy.server.service.bo.SingleDayFlowBO">
SELECT
T.*
FROM (
(
SELECT
frd.date,
IFNULL(SUM(frd.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frd.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_day frd
WHERE frd.date >= #{beginDate} AND frd.date &lt;= #{curDayBeginDate}
GROUP BY frd.date
) UNION ALL (
SELECT
#{curDayBeginDate} AS 'date',
IFNULL(SUM(frm.write_bytes),0) AS 'upFlowBytes',
IFNULL(SUM(frm.read_bytes),0) AS 'downFlowBytes'
FROM flow_report_minute frm
WHERE frm.date >= #{curDayBeginDate} AND frm.date &lt;= #{curDate}
)
) T
</select>
</mapper>
@@ -9,7 +9,7 @@ module.exports = {
title: "中微子代理",
description: '一个基于 netty 的、开源的 java 内网穿透项目',
// base: '/', // 默认'/'。如果你想将你的网站部署到如 https://foo.github.io/bar/,那么 base 应该被设置成 "/bar/",(否则页面将失去样式等文件)
base: '/neutrino-proxy-vuepress/', // 默认'/'。如果你想将你的网站部署到如 https://foo.github.io/bar/,那么 base 应该被设置成 "/bar/",(否则页面将失去样式等文件)
head: [ // 注入到页面<head> 中的标签,格式[tagName, { attrName: attrValue }, innerHTML?]
['link', {rel: 'icon', href: '/img/favicon.ico'}], //favicons,资源放在public文件夹
['meta', {name: 'keywords', content: 'vuepress,theme,blog,vdoing'}],
@@ -101,8 +101,9 @@ module.exports = {
],
sidebarDepth: 2, // 侧边栏显示深度,默认1,最大2(显示到h3标题)
logo: '/img/logo.png', // 导航栏logo
repo: 'aoshiguchen/neutrino-proxy', // 导航栏右侧生成Github链接
// repo: 'https://gitee.com/dromara/neutrino-proxy', // 导航栏右侧生成Github链接
// repo: 'aoshiguchen/neutrino-proxy', // 导航栏右侧生成Github链接
repo: 'https://gitee.com/dromara/neutrino-proxy', // 导航栏右侧生成Github链接
repoLabel: 'Gitee地址',
searchMaxSuggestions: 10, // 搜索结果显示最大数
lastUpdated: '上次更新', // 更新的时间,及前缀文字 string | boolean (取值为git提交时间)
@@ -133,8 +134,8 @@ module.exports = {
// archive: false, // 是否打开归档功能,默认true。 如打开,会做的事情有:1.自动生成归档页面(在@pages文件夹)。如关闭,则反之。
author: { // 文章默认的作者信息,可在md文件中单独配置此信息 String | {name: String, href: String}
name: 'Evan Xu', // 必需
href: 'https://github.com/xugaoyi' // 可选的
name: '傲世孤尘', // 必需
href: 'https://gitee.com/dromara/neutrino-proxy' // 可选的
},
social: { // 社交图标,显示于博主信息栏和页脚栏
// iconfontCssFile: '//at.alicdn.com/t/font_1678482_u4nrnp8xp6g.css', // 可选,阿里图标库在线css文件地址,对于主题没有的图标可自由添加
@@ -46,7 +46,7 @@ module.exports = {
</style>
`,
// 赞助商广告
sidebarT: `<a href="http://apifox.cn/a103xugaoyi" target="_blank"><img src="https://cdn.staticaly.com/gh/xugaoyi/blog-gitalk-comment@master/img/431669861564_.2470ykdcpbds.jpg" alt="npm" class="no-zoom" style="width: 100%;border-radius: 2px;"></a>`,
// sidebarT: `<a href="http://apifox.cn/a103xugaoyi" target="_blank"><img src="https://cdn.staticaly.com/gh/xugaoyi/blog-gitalk-comment@master/img/431669861564_.2470ykdcpbds.jpg" alt="npm" class="no-zoom" style="width: 100%;border-radius: 2px;"></a>`,
// windowRB: `
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

@@ -7,7 +7,7 @@ article: false
## 1. 打包
可直接前往Gitee仓库发行版页面下载所需版本已打好的包。若需手动打包,则可参照下面的执行命令:
```
```text
# 服务端打包
mvn clean install -U -pl neutrino-proxy-server -am -Dmaven.test.skip=true
@@ -23,7 +23,7 @@ npm run build:local
- 使用常规的jar包部署方式即可,如:java -jar xxxx
### 2.4 管理后台部署
- Nginx方式部署(推荐):
````
```conf
server {
listen 9527;
server_name localhost;
@@ -50,26 +50,77 @@ server {
````
- 无nginx时
在没有Nginx时,为了快速体验代理效果,可直接使用服务端项目提供的静态资源服务。直接将neutrino-proxy-admin打包后的文件解压放在neutrino-proxy-server.jar同级别目录下即可。例如:服务端配置的web端口为8080,则访问http://服务端IP:8080, 则会直接解析渲染neutrino-proxy-server.jar同级别目录下neutrino-proxy-admin/dist/index.html。
-
::: warning
需要注意的是,使用服务端自带的静态资源服务时,由于框架目前未支持缓存、gzip压缩,所以访问速度没有使用nginx快,正式使用推荐用nginx。
:::
## 使用Vdoing主题
## 3.配置
1. 安装最新的Vdoing主题包:
```sh
npm install vuepress-theme-vdoing -D
```
### 3.1 配置端口池
端口池的作用是将代理服务需要对外暴露的端口进行集中管理,方便安全组设置时统一操作。
2. 在`.vuepress/config.js`中配置使用主题:
```js
// config.js
module.exports = {
theme: 'vdoing'
本项目为了简化首次配置,默认初始化数据会将9101~9120的所有端口加入端口池。可以在服务端项目sql配置(port_pool.data.sql文件)中自行修改初始化数据,也可以运行后在端口池管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认端口池,则无需任何配置。
### 3.2 配置License
License是客户端连接代理服务端时所需要的唯一合法凭证,一个License同时只能被一个客户端使用。
本项目为了简化首次配置,默认为每个用户初始化了一些license。可以在服务端项目sql配置(license.data.sql文件)中自行修改初始化数据,也可以运行后在License管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认License,则无需任何配置。
### 3.3 配置端口映射
端口映射是代理的基本单元,一个外网端口在同一时刻被唯一的映射到一个内网IP+端口。所有流出该外网端口的流量都转发自对应的内网端口,同理所有流入该外网端口的流量都会转发到对应的内网端口。
本项目为了简化首次配置,默认为每个用户初始化了一些端口映射。可以在服务端项目sql配置(port_mapping.data.sql文件)中自行修改初始化数据,也可以运行后在端口映射管理页面手动维护。
首次使用如果为了快速体验,建议直接使用默认端口映射,则无需任何配置。
### 3.4 开启代理
上述步骤完成后,就可以开始代理自己的局域网设备了。
比如现在有一个licensea123456,该license配置了一个或多个端口映射,其中包含9010到localhost:8080的映射(localhost表示代理客户端所在主机的端口,可以换成客户端所在局域网的任何IP)。此时修改客户端配置的服务端ip、license等启动客户端,就可以开启代理了,端口映射管理对应的记录在线状态为"在线"则证明代理成功建立。
该license配置也可以通过客户端启动参数、启动后引导式输入、外置配置提供,为了简化首次体验门槛,这里直接提供一个外置配置模版:
```json
{
"jksPath":"classpath:/test.jks",
"licenseKey":"b0a907332b474b25897c4dcb31fc7eb6",
"serverIp":"localhost",
"serverPort":9002,
"sslEnable":true
}
```
修改上述json中的licenseKeyserverIp、serverPort、sslEnabled后,保存文件命名为<font color="#9b6e23">.neutrino-proxy-client.json</font>,放在客户端当前目录下(jar包启动时,放在jar包同级别目录下。idea启动时,放在项目根目录下),然后直接启动客户端。
此时通过访问外网ip+端口,可以成功访问内网服务。通过此方式,可以代理任何基于TCP之上的协议,如:socket、websocket、http、ftp、ssh等。
## 4.开发&调试
篇幅所限,此处不便赘述管理后台相关的开发,有vue基础的童鞋基本都可以自行开发。服务端的初始化数据足够调试工作,开发过程无需单独在管理页面操作。
与SpringBoot项目类似,客户端、服务端项目均由一个入口类完成整个项目的启动,分别为ProxyClient、ProxyServer。
内置配置采用yml风格,主要涉及Http端口、静态资源路径、协议参数、代理服务端端口、jks证书、数据源、license等。整个项目基于neutrino-core,风格类似于SpringBoot。笔者不喜欢因过分炫技而引入过多花式操作,因为项目的定位是个人开发者,且满足使用的同时兼顾学习其原理的需求。基本使用方式与SpringBoot类似,尽可能降低首次学习成本。
neutrino-core项目test目录下包含众多核心封装的测试代码,通过调试这些代码,能尽可能减少大家学习的障碍。
## 5.运行截图
### 用户管理
<img :src="$withBase('/img/run-example/user-manager1.png')"></img>
### 端口池管理
<img :src="$withBase('/img/run-example/port-pool1.png')"></img>
### License管理
<img :src="$withBase('/img/run-example/license1.png')"></img>
### 端口映射管理
<img :src="$withBase('/img/run-example/port-mapping1.png')"></img>
### 客户端启动示例
<img :src="$withBase('/img/run-example/client-run1.png')"></img>
<!--
## 4.开发&调试
::: tip
1. 不建议在原默认vuepress项目上单独安装使用本主题包,而是clone我的整个项目再替换你自己的内容即可。
@@ -96,3 +147,4 @@ module.exports = {
}
```
:::
-->
@@ -0,0 +1,13 @@
---
title: 演示环境
date: 2020-05-12 15:10:15
permalink: /pages/db78e2
article: false
---
### 演示环境
可使用分配好的游客license试用。服务器带宽较低,仅供学习使用!
管理后台地址:<a href="http://103.163.47.16:9527" target="_blank">http://103.163.47.16:9527</a>
游客账号:visitor/123456
@@ -1,208 +0,0 @@
---
title: 资源
date: 2020-05-12 15:10:15
permalink: /pages/db78e2
article: false
---
## 插件推荐
* [vuepress-plugin-fulltext-search](https://github.com/leo-buneev/vuepress-plugin-fulltext-search) 全文搜索
* [vuepress-plugin-thirdparty-search](https://github.com/xugaoyi/vuepress-plugin-thirdparty-search) 可以添加第三方搜索链接的搜索框
* [vuepress-plugin-one-click-copy](https://www.npmjs.com/package/vuepress-plugin-one-click-copy) 代码块一键复制
* [vuepress-plugin-comment](https://github.com/dongyuanxin/vuepress-plugin-comment) 评论区
* [vuepress-plugin-vssue](https://vssue.js.org/) 评论区(单页)
* [vuepress-plugin-vssue-global](https://github.com/u2sb/vuepress-plugin-vssue-global) 评论区(全局)
* [vuepress-plugin-smplayer](https://github.com/u2sb/vuepress-plugin-smplayer) 播放器
* [vuepress-plugin-flowchart](https://www.npmjs.com/package/vuepress-plugin-flowchart) 流程图
* [vuepress-plugin-mathjax](https://www.npmjs.com/package/vuepress-plugin-mathjax) 数学公式
* [vuepress-plugin-tabs](https://www.npmjs.com/package/vuepress-plugin-tabs/) 选项卡
* [vuepress-plugin-element-ui](https://www.npmjs.com/package/vuepress-plugin-element-ui/) Element UI
* [花里胡哨的插件](https://moefyit.github.io/moefy-vuepress/) 鼠标点击特效、背景彩带、音乐播放器等花里胡哨的插件
**更多插件...**
- [Awesome VuePress](https://github.com/vuepressjs/awesome-vuepress)
- [在npm中搜索"vuepressplugin"](https://www.npmjs.com/search?q=vuepress%E2%80%93plugin)
## 社区优秀解决方案
### [1. 站点信息模块](https://notes.youngkbt.cn/about/website/info/)
在首页添加`站点信息模块`,效果:
![](https://fastly.jsdelivr.net/gh/Kele-Bingtang/static/img/%E5%85%B3%E4%BA%8E/%E5%85%B3%E4%BA%8E%E6%9C%AC%E7%AB%99/20220102230720.png)
在文章页添加`文章字数``阅读时间``浏览量`,效果:
![](https://fastly.jsdelivr.net/gh/Kele-Bingtang/static/img/%E5%85%B3%E4%BA%8E/%E5%85%B3%E4%BA%8E%E6%9C%AC%E7%AB%99/20220103180059.png)
### [2. 私密文章功能](https://notes.youngkbt.cn/about/website/private/)
当大家想要「云端备份」文章到博客时,又不希望别人看到,该功能能满足你。
### [3. 首页大图模块](https://notes.youngkbt.cn/about/website/index-big-img/)
喜欢首页大图模式的朋友可以参考此教程。
### [4. 优雅的全文搜索方案](https://wiki.eryajf.net/pages/dfc792/#%E5%89%8D%E8%A8%80)
## 文章管理和发布
使用 [此插件 src-sy-post-publisher](https://github.com/terwer/src-sy-post-publisher) 发布[思源笔记](https://b3log.org/siyuan/)的文章到Vuepress等平台。
## 图标&配图
说明:以下图标**非主题内置**,你可以选择喜欢的图标右键保存或到图标库下载。更多图标:[阿里图标库](https://www.iconfont.cn/home/index)
### 静态图标
<table class="icons-table">
<tbody>
<tr>
<td align="center" valign="middle">
<img src="/img/png/编程.png" width=60>
<p class="name">编程</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/服务器.png" width=60>
<p class="name">服务器</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/机器学习.png" width=60>
<p class="name">机器学习</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/计算机网络.png" width=60>
<p class="name">计算机网络</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/面向对象.png" width=60>
<p class="name">面向对象</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/软件开发.png" width=60>
<p class="name">软件开发</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/数据结构.png" width=60>
<p class="name">数据结构</p>
</td>
</tr><tr></tr>
<tr>
<td align="center" valign="middle">
<img src="/img/png/数据库.png" width=60>
<p class="name">数据库</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/思维导图.png" width=60>
<p class="name">思维导图</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/算法.png" width=60>
<p class="name">算法</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/网络技术.png" width=60>
<p class="name">网络技术</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/系统.png" width=60>
<p class="name">系统</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/系统分析.png" width=60>
<p class="name">系统分析</p>
</td>
<td align="center" valign="middle">
<img src="/img/png/项目管理.png" width=60>
<p class="name">项目管理</p>
</td>
</tr><tr></tr>
</tbody>
</table>
<style>
.icons-table td{
padding: 1em;
}
.icons-table p.name{
font-size: 14px;
margin: 10px 0 0 0;
}
</style>
### 萌系图标
- [猫咪系列](https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.dc64b3430&cid=37776)
![](https://fastly.jsdelivr.net/gh/xugaoyi/image_store@master/maomi.51n7h2qwlv00.webp)</br>
- [数码宝贝](https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.dc64b3430&cid=38124)
![](https://fastly.jsdelivr.net/gh/xugaoyi/image_store@master/smbb.3h83bez4dka0.webp)
- [水果系列](https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.dc64b3430&cid=38124)
![](https://fastly.jsdelivr.net/gh/xugaoyi/image_store@master/QQ20220122-122349@2x.4oqggy71iso0.webp)
### 动态图标
<table class="icons-table">
<tbody>
<tr>
<td align="center" valign="middle">
<img src="/img/gif/猫1.gif" width=60>
<p class="name">猫1</p>
</td>
<td align="center" valign="middle">
<img src="/img/gif/猫2.gif" width=60>
<p class="name">猫2</p>
</td>
<td align="center" valign="middle">
<img src="/img/gif/猫3.gif" width=60>
<p class="name">猫3</p>
</td>
<td align="center" valign="middle">
<img src="/img/gif/猫4.gif" width=60>
<p class="name">猫4</p>
</td>
</tr><tr></tr>
</tbody>
</table>
### 插画
[免费插画](https://undraw.co/illustrations)</br>
[2D/3D/手绘插画](https://storytale.io/)</br>
[阿里插画库](https://www.iconfont.cn/illustrations/index)</br>
### 配图
[可画-文章配图](https://www.canva.cn/)
### Logo
[logo生成1](https://www.designevo.com/logo-maker/)</br>
[logo生成2](https://instantlogodesign.com/)
### Emoji表情
[Emoji百科](https://emojipedia.org/)
::: tip 小技巧
在任意输入框快速打开emoji表情方法:</br>
Windows系统下按<kbd>Win</kbd> + <kbd>.</kbd></br>
Mac系统下按<kbd>Control</kbd> + <kbd>Command</kbd> + <kbd>空格</kbd>
:::
## 共享资源
如果您有不错的资源,欢迎在 [资源分享区](https://github.com/xugaoyi/vuepress-theme-vdoing/discussions/categories/%E8%B5%84%E6%BA%90%E5%88%86%E4%BA%AB) 留言。
<br/>
<br/>
<a href="http://apifox.cn/a103xugaoyi" target="_blank"><img src="/img/Apifox-860x320.png" alt="npm" class="no-zoom" style="width: 100%;border-radius: 2px;"></a>
+6 -4
View File
@@ -128,14 +128,16 @@ postList: none
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<img :src="$withBase('/img/qrcode/wxq.png')" class="no-zoom" style="width:120px;margin: 10px;">
<p>中微子 微信群(添加我微信备注"进群")</p>
</td>
<!--
<td align="center" valign="middle">
<img src="https://cdn.staticaly.com/gh/xugaoyi/blog-gitalk-comment@master/img/0.4pp7r95mdai0.jpeg" class="no-zoom" style="width:120px;margin: 10px;">
<p>vdoing微信群(添加我微信备注"进群")</p>
</td>
<td align="center" valign="middle">
<img :src="$withBase('/img/qrcode/qqq.webp')" alt="群号: 694387113" class="no-zoom" style="width:120px;margin: 10px;">
<p>vdoing QQ群: 694387113</p>
</td>
-->
</tr>
</tbody>
</table>
+25 -44
View File
@@ -1,49 +1,30 @@
# 功能点
- [x] 用户流量报表
- [ ] 兼容sqlite
- [x] License流量报表
- [ ] 兼容sqlite
- 首页图表📈
- 1、License在线数
- 2、端口映射在线数
- 3、今日流量(上行、下行)
- 4、历史流量(上行、下行)
- 点击14 切换列表
- 在线License列表
- 用户名
- License名称
- LicenseKey
- 在线端口映射列表
- 用户名
- License名称
- 服务端口
- 代理客户端
- 今日24小时流量列表(按小时到排序)
- 用户名
- License名称
- 时间
- 流量
- 历史流量列表(取最近12个月按月到排序)
- 用户名
- License名称
- 时间
- 流量
- 今日流量折线图(上行、下行、总流量,按分钟统计0~24小时)
# 1.x剩余规划
- [ ] 参考鹊桥,增加对域名的支持
- [ ] 官网上线
- [ ] 参考鹊桥,增加对域名的支持
# Bug
- windows环境下直接运行发布版的jar包,日志输出乱码
- ~~部份用户windows环境下启动客户端,扫描类个数为0个~~
- 代理mysql时,使用未开启远程访问的账号走代理访问mysql,代理客户端出现断开现象
# 2.x规划
- [x] 全面重构:底层更换为Solon + Mybatis Plus
- 规范协议:代理协议规范化,方便后续更好扩展、支持不同语言客户端接入
- 端口池优化:支持为license设置独占端口。方便后续开发jetbrains插件、solon插件
- 精细化控制:支持针对用户限速、限流
- 参考鹊桥,增加对域名的支持
- MGT增加服务端维护
- 支持调整服务端端口
- 支持重启
- 支持查看服务端日志
- 支持证书上传下载
- 支持客户端版本管理
- 支持原生编译
- 插件开发
- [ ] neutrino-proxy-solon-plugin
- [ ] neutrino-proxy-jetbrains-plugin
- [ ] neutrino-proxy-starter (SpringBoot)
- 基础优化
- [ ] 支持批量端口映射
- [ ] 代理协议规范化,方便后续更好扩展、支持不同语言客户端接入
- 监控运维
- [ ] 监控服务端状态
- [ ] 支持调整服务端端口、证书
- [ ] 服务端版本、协议版本、客户端版本管理
- [ ] 服务端日志
- client+计划启动
- [ ] 安卓客户端
- [ ] 基于electron-egg的多平台客户端
# 3.x规划
- [ ] 支持针对用户限速、限流
- [ ] 支持P2P穿透
- [ ] 支持原生编译