diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9c7dd6bb3afe96dc471795c46acd39f0e01d59b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Default ignored files +.idea +.DS_Store +**/.DS_Store +.DS_Store? +**/.flattened-pom.xml +.gradle/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..91de47fe7a7a01e1f9b41052b113b52779f2c21f --- /dev/null +++ b/LICENSE @@ -0,0 +1,84 @@ +木兰宽松许可证, 第2版 + +2020年1月 http://license.coscl.org.cn/MulanPSL2 + +您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: + +0. 定义 + +“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + +“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + +“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + +“法人实体” 是指提交贡献的机构及其“关联实体”。 + +“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + +1. 授予版权许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 + +2. 授予专利许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 + +3. 无商标许可 + +“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 + +4. 分发限制 + +您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + +5. 免责声明与责任限制 + +“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + +6. 语言 + +“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 + + +Mulan Permissive Software License,Version 2 (Mulan PSL v2) + +January 2020 http://license.coscl.org.cn/MulanPSL2 + +Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: + +0. Definition + +Software means the program and related documents which are licensed under this License and comprise all Contribution(s). + +Contribution means the copyrightable work licensed by a particular Contributor under this License. + +Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. + +Legal Entity means the entity making a Contribution and all its Affiliates. + +Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. + +1. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. + +2. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. + +3. No Trademark License + +No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4. + +4. Distribution Restriction + +You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. + +5. Disclaimer of Warranty and Limitation of Liability + +THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +6. Language + +THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 7a7e6be1bb80297a64c52b91c7b8e8a33dfd0162..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# yunyi - -#### Description -Quickly cloud serve traditional data caching middleware and add unified proxy capabilities and service monitoring and management capabilities - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 13f2dfcdd5a032380e427e97632f9005c60fd8a1..f7da379acc8bd1c5017b8fe45c0ee6bf0e66d4b6 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,31 @@ # yunyi #### 介绍 -Quickly cloud serve traditional data caching middleware and add unified proxy capabilities and service monitoring and management capabilities +云翼数据缓存中间件云原生管理平台通过将数据缓存中间件服务纳入管理,增加数据缓存产品的云服务能力,适应云平台需要具备的动态扩展、自动部署、故障自动恢复、统一接口服务能力,对原数据缓存节点进行云化管理,包括对缓存服务节点的监控、数据操作等功能。 #### 软件架构 -软件架构说明 +包含以下目录: + +1. rds-console + + 管理控制台应用,基于springboot架构的java web应用,对数据缓存进行监控、数据管理。详见[README.md](rds-console/README.md) + +2. rds-proxy + + 通过统一的代理服务解决云环境下的节点漂移问题,提供统一的数据访问接口。同时包含自制容器镜像相关dockerfile,k8s相关资源参考定义。详见[README.md](rds-proxy/README.md) + +3. images + + TongRDS企业版容器镜像,为本项目依赖项。 #### 安装教程 -1. xxxx -2. xxxx -3. xxxx +详见子目录下的README.md文件 #### 使用说明 -1. xxxx -2. xxxx -3. xxxx +详见子目录下的README.md文件 #### 参与贡献 diff --git a/images/TongRDS-2.2.1.4.MC.docker_x86.tar.gz b/images/TongRDS-2.2.1.4.MC.docker_x86.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..54225c787ba2a6da703b0f58ed0771dac5657773 Binary files /dev/null and b/images/TongRDS-2.2.1.4.MC.docker_x86.tar.gz differ diff --git a/images/TongRDS-2.2.1.4.Node.docker_x86.tar.gz b/images/TongRDS-2.2.1.4.Node.docker_x86.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..43d392f124fa8eded2dc472fb7d64fd503360c99 Binary files /dev/null and b/images/TongRDS-2.2.1.4.Node.docker_x86.tar.gz differ diff --git a/rds-console/.gitignore b/rds-console/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8cc521ebc3b6260b1abb1ed077a52858e82996ef --- /dev/null +++ b/rds-console/.gitignore @@ -0,0 +1,75 @@ +# Compiled class file +*.class + +# Eclipse +.project +.classpath +.settings/ + +# Intellij +*.ipr +*.iml +*.iws +.idea + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +# Maven +target/ +!.mvn/wrapper/maven-wrapper.jar + +# Gradle +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +# Log file +*.log +log/ + +# out +**/out/ + +# Mac +.DS_Store + +# others +*.jar +*.war +*.zip +*.pid +*.orig +temp/ + +!*/build/*.java +!*/build/*.html +!*/build/*.xml + +*.xml.versionsBackup +*.swp + +#H2数据库跟踪文件# +consoledb.trace.db + +/console-admin/src/main/resources/public/ +/console-release/resources/tongtech/apphome/data/package/ +/console-release/resources/inforsuite/apphome/data/package/ +/apphome/ + + + + diff --git a/rds-console/README.md b/rds-console/README.md new file mode 100644 index 0000000000000000000000000000000000000000..33d341b33da88f56a8442bcd8042f2407a0ff53f --- /dev/null +++ b/rds-console/README.md @@ -0,0 +1,287 @@ +# 1RDS管控台功能介绍 + +RDS管控台是基于Java开发的应用程序,它为TongRDS-CN提供了节点状态跟踪/节点监控/数据维护等功能,支持单点模式/哨兵模式/集群模式/可伸缩集群模式 四种部署模式。 + +# 2名词定义 + +由于后续对于此平台的功能描述中涉及诸多基本概念,本节将介绍涉及的术语和先关名词定义。 + +**管控台:** 指的是TongRDS-CN管理控制台,支持通过WEB的方式进行TongRDS-CN服务以及节点的管理/TongRDS-CN服务以及节点的监控/TongRDS-CN服务的数据操作等功能. + +**节点:** 即TongRDS-CN的节点,分为工作节点、哨兵节和代理节点两种类型. + +**服务:** 即TongRDS-CN的服务,服务支持单点模式/哨兵模式/集群模式/可伸缩集群模式 四种部署模式;单点部署模式下,仅可添加一个工作节点;哨兵部署模式下,最少需要添加一个工作节点和一个哨兵节点;集群部署模式下,最少需要添加一个分片,每个分片最少需要添加一个工作节点。 + +# 3开发和编译 +## 3.1前台工程开发和编译 +前端工程采用NodeJS+VUE技术开发,所在目录 console-ui +### 系统需求 +Node >= 12 +### 开发 +``` +# 进入项目目录, 安装依赖 +npm install + +# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 +npm install --registry=https://registry.npmmirror.com --legacy-peer-deps + +# 启动服务 +npm run dev +``` +浏览器访问 http://localhost:1024 +### 发布 +```bash +# 构建测试环境 +npm run build:stage + +# 构建生产环境 +npm run build:prod +``` + +## 3.2后台工程开发和编译 +后端工程采用Java+SpringBoot技术开发。 +### 系统需求 +JDK >= 1.8 +Maven >= 3.0 + +### 正常编译并打包 ### +在项目根目录下执行下面命令, 会编译除console-release外的所有其他子工程。 +针对所有的子工程,逐一清除原编译信息并生成新的编译后的jar包。 + +`mvn clean package` +### 系统发布打包(console-release) ### +该过程由console-release子工程来完成,会生成最终的部署包到 +console-release/target/console.tar.gz。 +``` +cd console-release +mvn clean package +``` + +### 开发环境配置 ### +首先保证整个工程的编译: +mvn clean package +进入到 console-release目录中,运行compile命令会拷贝必要的运行资源到根目录下的apphome中。 +``` +cd console-release +mvn clean compile +``` + +#### 在IDEA中运行工程 #### +- 用IDEA导入工程. +- 在console-release中运行compile命令用于准备运行环境(参看上一节)。 +- 系统启动运行类, 指定console-admin子工程下的 com.tongtech.ConsoleApplication类为运行类; + **注意** 在运行时要指定工作路径(Working directory)为 apphome +- 运行 ConsoleApplication 即可开启web运行环境。 + ![IDEA运行配置](docs/images/image011.png) + ![IDEA运行](docs/images/image012.png) + +#### 通过命令行运行工程 #### +在根目录中 +``` +# 在根目录下编译相关工程 +mvn clean install -pl console-admin -am + +# 进入 console-admin目录 +cd console-admin + +# 通过spring boot插件运行后台工程。 +mvn spring-boot:run +``` + + + +# 4部署过程说明 +## 4.1控制台支持的操作系统 +Microsoft Windows 系列 + +Linux 平台 + +RedHat 系列 + +RedFlag 系列 + +Suse Linux 系列 + +## 4.2系统软件环境要求 + +Java环境:JDK1.8 及以上版本 + +内存:至少需要 1024MB 的内存 + +硬盘空间:至少需要 300MB 磁盘空间 + +浏览器:MicrosoftIE10、 Firefox3.0 、Chrome80及以上版本兼容ECMAScript 5的浏览器。 + +## 4.3安装操作过程 + +### 4.3.1环境配置 + +控制台安装需设置使用的 JDK 路径: + +设置本机的 JAVA\_HOME 值,系统默认使用本机 JAVA\_HOME 所设置的 JDK 路径。 + +注:在启动控制台前,需先指定使用的 JDK 的路径。 + +### 4.3.2部署文件安装 + +控制台的安装程序是以tar.gz格式的文件提供。 + +将安装程序上传至服务器,然后使用解压缩工具进行解压,解压完成在当前目录会创建console目录,控制台运行所需文件均在此目录下。 + +### 4.3.3控制台系统配置说明 + +配置文件放置在config/目录下,其中的配置内容是在启动时加载生效(如要生效新变更的配置需重启管理控制台)。应用系统相关配置,对应配置文件"config/application.yml", 主要配置项说明: + +- 应用的访问路径(环境路径):配置项 server.servlet.context-path 默认值:/ +- 服务端口:配置项 server.port 默认值:8083 +- 中心节点管理端口进行通信时的授权码:配置项console.centerAuthKey 取值:提供的默认授权码。 +注意:如果要修改请保证和中心节点中相应的授权码保持一致。中心节点如果是通过控制台自动安装的,会自动配置为何此配置项相同的值,无需人工修改。如果是另外部署的中心节点,就需要人工修改保持该授权码和中心节点的一致。 +- 和节点管理器进行通信时的授权码: 配置项:console.probeAuthKey 取值:提供的默认授权码 +注意:如果要修改请保证和节点管理器中相应的授权码保持一致。节点管理器如果是通过控制台自动安装的,会自动配置为何此配置项相同的值,无需人工修改。如果是另外部署手工部署节点管理器,就需要人工修改保持该授权码和节点管理器中的配置一致。 + +### 4.3.4控制台目录说明 + +系统运行的工作路径这里假设是apphome,控制运行时会以该路径为工作路径(或说应用主目录) + +- apphome/apps 内置节点管理器部署节点的位置 +- apphome/bin 控制台运行的脚本。 +- apphome/config 控制台运行的配置文件。 +- apphome/config/application.yml 控制台系统配置 +- apphome/config/probe.config 内置节点管理器配置等 +- apphome/data/db 内置数据文件存路径 +- apphome/data/dbbak 内置数据备份文件 +- apphome/lib 控制台运行程序库 +- apphome/log 系统日志输出 + +### 4.3.5启动或停止控制台 + +将TongRDS-\*.Console.tar.gz的安装包解压后,把解压后的目录放置到您所需的安装路径(apphome 代表控制台的根目录),使用 apphome/bin 目录下的 Windows系统console.bat 启动文件,Linux系统console.sh 脚本即可启动 RDS控制台。 + +**启动控制台** + +在命令行终端中,首先进入到apphome路径;之后执行: + +console.sh start + +如果运行正常会提示 Start console-admin.jar success.. + +**停止控制台** + +在命令行终端中,首先进入到apphome路径;之后执行: + +console.sh stop + +如果运行正常会提示 console-admin.jar exited. + +**查看控制台运行状态** + +在命令行终端中,首先进入到apphome路径;之后执行: + +console.sh status + +如果系统正在运行,会显示: + +APP\_HOME: /Path/installed/console + +console-admin.jar is running... + +如果系统没在运行,会显示: + +APP\_HOME: /Path/installed/console + +console-admin.jar is not running... + +### 4.3.6验证安装 + +在浏览器中输入容器所在的宿主机IP地址和8083端口(如:[http://192.168.0.19:8083/](http://192.168.0.19:8083/)),可以正常访问到系统的登录页面。 + +# 5功能介绍 + +RDS管理控制台允许系统管理员以 Web 方式登录,并管理RDS服务、RDS节点,以及监控 RDS服务的运行状态。 + +## 5.1容器或容器云环境下的初始配置 + +### 5.1.1首次登录 + +RDS首次启动后,管理控制台通过http://IP地址:8083/ 在浏览器上进行访问。如果控制台启动正常会显示登录页面,如下图: + +![登录界面](docs/images/image001.png) + +初次启动后,请使用默认的管理员用户进行登录,用户名:admin 密码:admin123。 + +为了提高系统安全性建议尽快对admin用户的密码进行更改,设置有一定安全强度的密码。更改密码可以通过,个人中心进行更改。 + +### 5.1.2中心节点的配置 + +容器云环境下是通过中心节点服务完整对容器云中RDS节点的注册发现、节点服务运行状态监控,节点弹性扩缩容等操作。 + +因此首先要配置中心服务的管理地址和端口,通过连接中心节点服务后来拉取相关的配置信息。 + +配置方式如下: + +![中心节点配置](docs/images/image003.jpg) + +在中心服务菜单功能中点击"修改"按钮。 + +![中心节点修改](docs/images/image004.png) + +再修改界面中,部署环境要选为"k8s容器云","服务地址"请填写中心服务的访问地址,"管理关口"是中心服务的管理端口。 +![中心节点修改内容](docs/images/image006.png) + +当填写完成并确定后,系统会去中心节点拉取配置信息,在20秒~60秒左右的时间后,界面中会展示出中心节点的列表。 + +## 5.2系统基础 + +用户管理提供用户添加/修改/删除/密码修改的功能。 + +角色管理提供角色添加/修改/删除;角色对应的功能权限设置。 + +## 5.3RDS管理 + +### 5.3.1中心服务 + +当控制台安装部署完成后会在本机自动安装一个中心节点,在系统初始化配置完成后自动启动。在管理控制台中的中心服务中可以查看中心节点状态,并可通过操作界面来启动和停止中心节点. + +### 5.3.2RDS服务 + +服务管理提供服务节点信息的显示和状态显示,服务支持单节点/哨兵/集群三种部署模式。 + +### 5.3.3数据维护 + +RDS数据维护提供对运行状态的服务,进行五种基本数据类型(String/Hash/List/Set/ZSet)的查看/添加/修改/删除操作。 + +### 5.3.4命令行维护 + +RDS命令行维护提供对运行状态的服务,使用redis命令五种基本数据类型(String/Hash/List/Set/ZSet)的查看/添加/修改/删除操作。 + +## 5.4系统监控 + +### 5.4.1RDS服务监控 + +查看服务监控数据的步骤如下: + +1. 依次展开管控台左侧导航树中的"系统监控-RDS服务监控"节点; + +2. 所有服务的监控数据以列表的形式展现,列表支持根据服务名称/服务状态进行搜索,如下图所示。 + +![服务监控列表](docs/images/image008.png) + +点击列表中的服务名字,会跳转至此服务的监控详情页面,在这个页面可以查看服务及节点的运行状态/内存/连接数/每秒请求数,监控数据以图表的形式展示,如下图所示 + +![服务监控详情](docs/images/image010.png) + +### 5.4.2在线用户 + +在线用户提供查看当前登陆管控台的用户以及对强制退出登陆用户等 + +功能。 + +## 5.5系统审计 + +### 5.5.1操作日志 + +系统中所有的新建、修改、变更、删除等操作的历史都会被操作日志记录下来,且可以通过 系统审计》操作日志 界面进行查询、查看和导出操作。 + +### 5.5.2登录日志 + +登陆日志提供用户登陆日志的查询/导出/清空等功能。 diff --git a/rds-console/console-admin/pom.xml b/rds-console/console-admin/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..57ab5e53e45a9bb7186ab1747390f81cde7ffe9c --- /dev/null +++ b/rds-console/console-admin/pom.xml @@ -0,0 +1,111 @@ + + + + console + com.tongtech + 2.2.C.1 + + 4.0.0 + jar + console-admin + + + 控制台web服务入口 + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + com.tongtech + console-framework + + + + com.github.whvcse + easy-captcha + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + true + ${basedir}/../apphome + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + id.clean + clean + + run + + + + + + + + + id.generate-resources + generate-resources + + run + + + + + + + + + + + + + + ${project.artifactId} + + + diff --git a/rds-console/console-admin/src/main/java/com/tongtech/ConsoleAppInitializer.java b/rds-console/console-admin/src/main/java/com/tongtech/ConsoleAppInitializer.java new file mode 100644 index 0000000000000000000000000000000000000000..28e9e99c77f6f024a413cbf3ab81ea9609e4dfe3 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/ConsoleAppInitializer.java @@ -0,0 +1,28 @@ +package com.tongtech; + +import com.tongtech.system.service.ISysConfigService; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; + +import static com.tongtech.common.constant.ConsoleConstants.CONFIG_SYS_DEVELOPMENT_MODE_KEY; +import static com.tongtech.common.constant.ConsoleConstants.CONFIG_SYS_INITIALIZED_KEY; + +//@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class ConsoleAppInitializer { + public static void main(String[] args) + { + ConfigurableApplicationContext context = SpringApplication.run(ConsoleApplication.class, args); + + ISysConfigService configService = context.getBean("sysConfigServiceImpl", ISysConfigService.class); + + + System.out.println("System is start with arguments '--initialize or -i'. System is initializing ......"); + configService.setConfigValueByKey(CONFIG_SYS_INITIALIZED_KEY, "false"); + configService.setConfigValueByKey(CONFIG_SYS_DEVELOPMENT_MODE_KEY, "false"); + configService.initSysMenu(); + System.out.println("Console Started!"); + + SpringApplication.exit(context); + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/ConsoleApplication.java b/rds-console/console-admin/src/main/java/com/tongtech/ConsoleApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..26be65d6723b7f229670b90eec547c116942a08e --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/ConsoleApplication.java @@ -0,0 +1,76 @@ +package com.tongtech; + +import com.tongtech.common.config.AppHomeConfig; +import com.tongtech.system.service.IDatabaseBackupService; +import com.tongtech.system.service.ISysConfigService; +import com.tongtech.system.service.impl.DatabaseBackupServiceImpl; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; + +import java.io.File; +import java.util.Scanner; + +import static com.tongtech.common.constant.ConsoleConstants.*; + +/** + * 启动程序 + * + * @author XiaoZhangTongZhi + */ +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class ConsoleApplication +{ + public static void main(String[] args) + { + if(args.length == 2 && MAIN_ARG_RESTORE.equals(args[0])) { + //备份恢复测试,开始从备份文件中恢复数据库文件 + String fileName = args[1]; + File restoreFile = AppHomeConfig.getAbsoluteFile(AppHomeConfig.DATABASE_BACKUP_PATH, args[1]); + + System.out.println("Now begin to restore database data from:" + restoreFile);// 提示用户输入字符串 + System.out.println("Current data in database will be replaced! Please be confirm(Y/n):"); + Scanner scanner = new Scanner(System.in);// 获得控制台输入流 + + String text = scanner.nextLine();// 获得用户输入 + if(text != null && text.trim().equals("Y")) { + System.out.println("Database restoring ....."); + + //这里没有启动Spring容器,直接 + IDatabaseBackupService backupService = new DatabaseBackupServiceImpl(); + backupService.restoreDatabase(fileName); + System.out.println("Database restoring completed."); + } + else { + System.out.println("Quit database restoring."); + } + } + else if(args.length == 1) { + ConfigurableApplicationContext context = SpringApplication.run(ConsoleApplication.class, args); + ISysConfigService configService = context.getBean("sysConfigServiceImpl", ISysConfigService.class); + + if(MAIN_ARG_INITIALIZE.equals(args[0]) || MAIN_ARG_INITIALIZE_SHORT.equals(args[0])) { + System.out.println("System is start with arguments '--initialize or -i'. System is initializing ......"); + configService.setConfigValueByKey(CONFIG_SYS_INITIALIZED_KEY, "false"); + configService.setConfigValueByKey(CONFIG_SYS_DEVELOPMENT_MODE_KEY, "false"); + configService.initSysMenu(); + System.out.println("Console Started!"); + } + else if(MAIN_ARG_DEVELOPMENT.equals(args[0]) || MAIN_ARG_DEVELOPMENT_SHORT.equals(args[0])) { + + System.out.println("System is start with arguments '--development or -d'. System is initializing as development mode!"); + + configService.setConfigValueByKey(CONFIG_SYS_INITIALIZED_KEY, "false"); + configService.setConfigValueByKey(CONFIG_SYS_DEVELOPMENT_MODE_KEY, "true"); + configService.initSysMenu(); + System.out.println("Console Started!"); + } + + } + else { + SpringApplication.run(ConsoleApplication.class, args); + System.out.println("Console Started!"); + } + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/ConsoleServletInitializer.java b/rds-console/console-admin/src/main/java/com/tongtech/ConsoleServletInitializer.java new file mode 100644 index 0000000000000000000000000000000000000000..e965573e7f94160c9ba1a815ce3d03e6580a0745 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/ConsoleServletInitializer.java @@ -0,0 +1,18 @@ +package com.tongtech; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author XiaoZhangTongZhi + */ +public class ConsoleServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(ConsoleApplication.class); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/common/CaptchaController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/common/CaptchaController.java new file mode 100644 index 0000000000000000000000000000000000000000..86fb28bbed1817901c8a3cb7f6f3dbdd02bf9fbc --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/common/CaptchaController.java @@ -0,0 +1,119 @@ +package com.tongtech.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.exception.ServiceException; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.system.service.SysObjectCacheService; +import com.wf.captcha.ArithmeticCaptcha; +import com.wf.captcha.ChineseCaptcha; +import com.wf.captcha.SpecCaptcha; +import com.wf.captcha.base.Captcha; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.tongtech.common.config.UhConsoleConfig; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.utils.sign.Base64; +import com.tongtech.common.utils.uuid.IdUtils; +import com.tongtech.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +public class CaptchaController { + + private static final Integer captchaWidth = 115; + + private static final Integer captchaHeight = 42; + + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private SysObjectCacheService redisCache; + + @Autowired + private ISysConfigService configService; + + + /** + * 生成验证码 + */ + @GetMapping("/web-api/captchaImage") + public AjaxResult getCode(HttpServletResponse response) { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + Captcha captcha = null; + // 生成验证码 + String captchaType = UhConsoleConfig.getCaptchaType(); + if ("rds-math".equals(captchaType)) { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } else if ("rds-char".equals(captchaType)) { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } else if ("easy-math".equals(captchaType)) { + captcha = new ArithmeticCaptcha(captchaWidth, captchaHeight); + } else if ("easy-chinese".equals(captchaType)) { + captcha = new ChineseCaptcha(captchaWidth, captchaHeight); + } else if ("easy-char".equals(captchaType)) { + captcha = new SpecCaptcha(captchaWidth, captchaHeight); + } else { + throw new ServiceException("验证码配置有误,请检查配置字段!"); + } + + ajax.put("uuid", uuid); + + // ry-math ry-char + if ("rds-math".equals(captchaType) || "rds-char".equals(captchaType)) { + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try { + ImageIO.write(image, "jpg", os); + } catch (IOException e) { + return AjaxResult.error(e.getMessage()); + } + ajax.put("img", Base64.encode(os.toByteArray())); + } else { + + code = captcha.text(); + + if (StringUtils.isEmpty(code)) + throw new ServiceException("验证码获取失败,请稍后重试!"); + ajax.put("img", captcha.toBase64().split(",")[1]); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + return ajax; + + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/common/CommonController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/common/CommonController.java new file mode 100644 index 0000000000000000000000000000000000000000..e7408028521995942ad06614e8250b61bf05f521 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/common/CommonController.java @@ -0,0 +1,185 @@ +package com.tongtech.web.controller.common; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.config.AppHomeConfig; +import org.apache.commons.io.FilenameUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.file.FileUploadUtils; +import com.tongtech.common.utils.file.FileUtils; +import com.tongtech.framework.config.ServerConfig; + +/** + * 通用请求处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + private static final String[] IMAGE_EXTENSIONS = {"gif","jpg","jpeg","bmp","png" }; + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = AppHomeConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = AppHomeConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + if(isImageExt(fileName) == false) { + throw new Exception("文件类型非法!只允许图片类文件的后缀(\"gif\",\"jpg\",\"jpeg\",\"bmp\",\"png\" )"); + } + + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List files) throws Exception + { + try + { + // 上传文件路径 + String filePath = AppHomeConfig.getUploadPath(); + List urls = new ArrayList(); + List fileNames = new ArrayList(); + List newFileNames = new ArrayList(); + List originalFilenames = new ArrayList(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = AppHomeConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 是否是上传图片所需的图片后缀 + * @param fileName + * @return + */ + private boolean isImageExt(String fileName) { + String ext = FilenameUtils.getExtension(fileName); + for(String imgExt : IMAGE_EXTENSIONS) { + if(imgExt.equals(ext)) return true; + } + return false; + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/CenterServiceController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/CenterServiceController.java new file mode 100644 index 0000000000000000000000000000000000000000..e56af740cd29f004f1cd152efa04a342e0499f59 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/CenterServiceController.java @@ -0,0 +1,179 @@ +package com.tongtech.web.controller.console; + +import com.tongtech.common.annotation.Log; +import com.tongtech.common.config.UhConsoleConfig; +import com.tongtech.common.constant.ConsoleConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.console.domain.RdsService; +import com.tongtech.console.domain.vo.RdsServiceQueryVo; +import com.tongtech.console.service.RdsNodeService; +import com.tongtech.console.service.RdsServiceService; +import com.tongtech.probe.RestCenterClient; +import com.tongtech.probe.RestCenterResult; +import com.tongtech.probe.stat.StatCenterNode; +import com.tongtech.console.task.RdsCenterDataTask; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static com.tongtech.common.enums.DeployEnvEnum.K8S; + + +/** + * RDS服务Controller + * + * @author Zhang ChenLong + * @date 2023-01-26 + */ +@RestController +@RequestMapping("/web-api/console/centerservice") +public class CenterServiceController extends BaseController +{ + @Autowired + private RdsServiceService serviceService; + + @Autowired + private RdsNodeService nodeService; + + + @Autowired + private RdsCenterDataTask centerDataTask; + + /** + * 获取中心服务详细信息 + */ + @PreAuthorize("@ss.hasPermi('console:centerservice:query')") + @GetMapping + public AjaxResult getService() + { + return AjaxResult.success(serviceService.selectRdsServiceByServiceId(ConsoleConstants.CENTER_SERVICE_ID)); + } + + /** + * 测试中心节点的管理端口是否可以正常连接 + */ + @PreAuthorize("@ss.hasPermi('console:centerservice:edit')") + @GetMapping("/testAdminConnection") + public AjaxResult testAdminConnection() { + + RestCenterClient client = serviceService.getCenterClient(); + if(client != null) { + try { + RestCenterResult res = client.getCenters(); + if(res.getListData() != null && res.getListData().size() > 0) { + return AjaxResult.success("连接成功"); + } + else { + return AjaxResult.error("接口数据异常,无法找到中心节点!"); + } + + } catch (IOException e) { + logger.error("CenterServiceController.testAdminConnection() Error!", e); + return AjaxResult.error("测试连接失败! Error:" + e.getMessage()); + } + } + else { + return AjaxResult.error("测试连接失败! 无法获取客户端连接。"); + } + } + + + /** + * 测试中心节点的管理端口是否可以正常连接 + * + */ + @PreAuthorize("@ss.hasPermi('console:centerservice:edit')") + @PostMapping("/testAdminConnectionNew") + public AjaxResult testAdminConnectionNew(@RequestBody RdsService serivce) { + + RestCenterClient client = serviceService.getCenterClient(serivce); + if(client != null) { + try { + RestCenterResult res = client.getCenters(); + if(res.getListData() != null && res.getListData().size() > 0) { + return AjaxResult.success("连接成功"); + } + else { + return AjaxResult.error("接口数据异常,无法找到中心节点!"); + } + + } catch (IOException e) { + logger.error("CenterServiceController.testAdminConnection() Error!", e); + return AjaxResult.error("测试连接失败! Error:" + e.getMessage()); + } + } + else { + return AjaxResult.error("测试连接失败! 无法获取客户端连接。"); + } + } + + + /** + * 清除所有中心节点,以及管理连接配置。 + * 只有在K8S模式下,才提供此操作。 + * 1.清空所有CenterNode, 2. 清空所有非手工维护的服务及下设节点。 + */ + @PreAuthorize("@ss.hasPermi('console:centerservice:edit')") + @GetMapping("/clearCenterConfig") + public AjaxResult clearCenterConfig() { + RdsService centerServ = serviceService.selectRdsServiceByServiceId(ConsoleConstants.CENTER_SERVICE_ID); + + //重置center service数据 + serviceService.resetRdsService(ConsoleConstants.CENTER_SERVICE_ID); + + //删除中心节点 + nodeService.deleteNodeByServiceId(ConsoleConstants.CENTER_SERVICE_ID); + + //删除非手工维护的节点 + List noneManualAdminService = serviceService.selectListInDeployModes(new RdsServiceQueryVo(new String[] + {"single", "sentinel", "sentinel_worker", "cluster", "scalable"})); //查询出除 center 之外的其他所有服务 + + for(RdsService serv : noneManualAdminService) { + if(serv.isManualAdmin() == false) { //非手工维护的节点 + serviceService.deleteRdsServiceByServiceId(serv.getServiceId()); + } + } + + return AjaxResult.success("中心服务配置已重置!"); + } + + + + /** + * 修改RDS服务 + */ + @PreAuthorize("@ss.hasPermi('console:centerservice:edit')") + @Log(title = "RDS服务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult update(@RequestBody RdsService serivce) + { + if(serivce.getServiceId() == ConsoleConstants.CENTER_SERVICE_ID) { + if(UhConsoleConfig.getDeployEnvEnum() == K8S) { + serivce.setManualAdmin(false); //中心服务的K8S模式下是自动维护的 + } + else { + serivce.setManualAdmin(true); + } + int res = serviceService.updateRdsService(serivce); + + //对center data进行处理分析(获取center节点,RDS服务列表) + if(UhConsoleConfig.getDeployEnvEnum() == K8S) { + centerDataTask.process(); + } + + return toAjax(res); + } + else { + return AjaxResult.error("CenterServiceController.update() 函数仅支持中心服务的更新,不能更新非中心服务!!serviceId=" + + serivce.getServiceId() ); + } + } + + + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/LicenseController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/LicenseController.java new file mode 100644 index 0000000000000000000000000000000000000000..9d99bbc3010b4cb8aaaa54dc7a2ec2a4272478a3 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/LicenseController.java @@ -0,0 +1,218 @@ +package com.tongtech.web.controller.console; + + +import com.tongtech.common.annotation.Log; +import com.tongtech.common.config.AppHomeConfig; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.exception.base.BaseException; +import com.tongtech.common.utils.file.FileUploadUtils; +import com.tongtech.console.domain.CenterLicenseInfo; +import com.tongtech.console.domain.ServiceConfig; +import com.tongtech.console.service.ServiceConfigService; +import com.tongtech.console.utils.CenterLicenseReader; + +import com.tongtech.system.service.ISysConfigService; +import com.tongtech.system.service.SysObjectCacheService; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import static com.tongtech.common.config.AppHomeConfig.CENTER_LICENSE_FILE; +import static com.tongtech.common.config.AppHomeConfig.CENTER_PATH; +import static com.tongtech.common.constant.CacheConstants.TEMP_CENTER_LICENSE_KEY; +import static com.tongtech.common.constant.ConsoleConstants.*; + +@RestController +@RequestMapping("/web-api/console/license") +public class LicenseController { + + + @Autowired + private ServiceConfigService configService; + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private SysObjectCacheService redisCache; + + /** + * 上传RDS授权信息文件,注意只是把安装信息放入到临时文件夹 + */ + @Log(title = "授权信息", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('console:license:edit')") + @PostMapping("/import") + public AjaxResult importLicense(MultipartFile file) throws Exception { + return importToTemp(file); + } + + /** + * 上传RDS授权信息文件,在安装引导功能中。 + */ + @Log(title = "授权信息", businessType = BusinessType.IMPORT) + @PostMapping("/importOnInit") + public AjaxResult importLicenseOnInit(MultipartFile file) throws Exception { + + boolean sysInitialzed = Boolean.parseBoolean(sysConfigService.selectConfigByKey(CONFIG_SYS_INITIALIZED_KEY)); + + if(sysInitialzed) { //如果已经初始化,就抛出异常 + throw new BaseException("初始化完成,不能再调用此接口"); + } + + return importToTemp(file); + } + + private AjaxResult importToTemp(MultipartFile file) throws Exception { + //判断文件是否为空 + if (!file.isEmpty()) { + //文件名长度校验 + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + return AjaxResult.error("文件名过长,请重新选择,长度不能超过100个字符!"); + } + //文件名后缀校验 + String filename = file.getOriginalFilename(); + String EXTS = "lic"; + if (!filename.endsWith("." + EXTS)) { + return AjaxResult.error("文件名后缀有问题,请上传以.lic结尾的文件!"); + } + + //文件大小校验 + long size = file.getSize(); + if (FileUploadUtils.DEFAULT_MAX_SIZE != -1 && size > FileUploadUtils.DEFAULT_MAX_TEXT_SIZE) { + return AjaxResult.error("文件过大,超过了1MB,请重新选择!"); + } + + + String uploadLicenseData = IOUtils.toString(file.getInputStream(), "UTF-8"); + redisCache.setCacheObject(TEMP_CENTER_LICENSE_KEY, uploadLicenseData, 20, TimeUnit.MINUTES); //放入上传的临时缓存 + + return AjaxResult.success(loadCenterLic(uploadLicenseData)); + } else { + return AjaxResult.error("请选择上传文件!"); + } + } + + /** + * 把临时Licesne文件移动到正式文件夹(中心节点目录中)让授权信息正式生效 + * @return + */ + @GetMapping("/add") + @Log(title = "授权信息", businessType = BusinessType.INSERT) + public AjaxResult add() { + String tempLicenseData = redisCache.getCacheObject(TEMP_CENTER_LICENSE_KEY); + + if(tempLicenseData != null) { + CenterLicenseInfo licInfo = loadCenterLic(tempLicenseData); + if(licInfo != null && licInfo.getUserName() != null) { + + ServiceConfig conf = configService.selectServiceConfigBy(CENTER_SERVICE_ID, CONFIG_TYPE_LICENSE); + if(conf == null) { + conf = new ServiceConfig(); + conf.setServiceId(CENTER_SERVICE_ID); + conf.setConfContent(tempLicenseData); + conf.setConfType(CONFIG_TYPE_LICENSE); + conf.setUpdateTime(new Date()); + configService.insertServiceConfig(conf); + } + else { + conf.setConfContent(tempLicenseData); + conf.setUpdateTime(new Date()); + configService.updateServiceConfig(conf); + } + + return AjaxResult.success(); + } + } + + return AjaxResult.error("未找已上传的授权信息,或授权信息错误!"); + } + + /** + * 获取用户授权信息 RDS版本管理打包 + * @return + */ + @GetMapping("/get") + public AjaxResult getLicense() { + ServiceConfig conf = configService.selectServiceConfigBy(CENTER_SERVICE_ID, CONFIG_TYPE_LICENSE); + if(conf != null) { + CenterLicenseInfo licInfo = loadCenterLic(conf.getConfContent()); + return AjaxResult.success(licInfo); + } + else { + return AjaxResult.error("系统还没有License, 请上传License!"); + } + } + + /** + * 获取用户授权信息, 临时上传位置 + * @return + */ + @GetMapping("/getTemp") + public AjaxResult getTempLicense() { + + String tempLicenseData = redisCache.getCacheObject(TEMP_CENTER_LICENSE_KEY); + + if(tempLicenseData != null) { + return AjaxResult.success(loadCenterLic(tempLicenseData)); + } + else { + return AjaxResult.error("为找已上传的授权信息"); + } + + + } + + + + private AjaxResult importCenLic(MultipartFile file) throws Exception { + //判断文件是否为空 + if (!file.isEmpty()) { + //文件名长度校验 + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + return AjaxResult.error("文件名过长,请重新选择,长度不能超过100个字符!"); + } + //文件名后缀校验 + String filename = file.getOriginalFilename(); + + if (!filename.endsWith(".lic")) { + return AjaxResult.error("文件名后缀有问题,请上传以.lic结尾的文件!"); + } + //文件大小校验 + long size = file.getSize(); + if (FileUploadUtils.DEFAULT_MAX_SIZE != -1 && size > FileUploadUtils.DEFAULT_MAX_SIZE) { + return AjaxResult.error("文件过大,超过了50M,请重新选择!"); + } + + //上传文件路径 + File licFile = AppHomeConfig.getAbsoluteFile(CENTER_PATH, CENTER_LICENSE_FILE); + if (licFile.isFile() && licFile.exists()) //删除存在的原文件 + { + licFile.delete(); + } + + file.transferTo(licFile); + return AjaxResult.success("上传成功!"); + } else { + return AjaxResult.error("请选择上传文件!"); + } + } + + + private CenterLicenseInfo loadCenterLic(String licenseData) { + CenterLicenseReader licReader = new CenterLicenseReader(); + return licReader.loadLicenseInfo(licenseData); + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsClientController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsClientController.java new file mode 100644 index 0000000000000000000000000000000000000000..534d63a6f00b548555e2dd9ce14726dcf3c7c2fa --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsClientController.java @@ -0,0 +1,523 @@ +package com.tongtech.web.controller.console; + + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; + +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.AssertUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.console.enums.DeployModeEnum; +import com.tongtech.console.service.RdsClientService; +import com.tongtech.console.utils.JedisDataClient; +import com.tongtech.web.controller.console.vo.RdsVo; +import com.tongtech.web.controller.console.vo.ScanVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.ScanParams; +import redis.clients.jedis.ScanResult; + +import java.io.UnsupportedEncodingException; +import java.util.*; + +@RestController +@RequestMapping("/web-api/console/rdsClient") +public class RdsClientController extends BaseController { + + private static int LIST_FETCH_SIZE = 16; //获得key list时,每次取得的条数 + + private static String NONE_CURSOR = "0"; //开始的cursor,scan到结尾的cursor都是这个 + + + @Autowired + private RdsClientService rdsService; + + @PostMapping("/getDB") + public AjaxResult getDB(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + int size; + if (jedisClient.getDeployMode() == DeployModeEnum.CLUSTER) { + size = 1; + } else { + List list = jedisClient.configGet("databases"); + if(list.size() > 0) { + size = Integer.valueOf(list.get(1)); + } + else { + size = 1; + } + } + + JSONObject result = new JSONObject(); + JSONArray array = new JSONArray(); + for (int i = 0; i < size; i++) { + JSONObject obj = new JSONObject(); + obj.put("key", "DB" + i); + obj.put("value", i); + array.add(obj); + } + + result.put("dbList", array); + result.put("serviceId", rdsVo.getServiceId()); + return AjaxResult.success(result); + } + } + + @GetMapping("/disconnectAll") + public AjaxResult disconnectAll() { + rdsService.deleteConnections(); + return AjaxResult.success(); + } + + + @Log(title = "RDS数据维护-updateTtl", businessType = BusinessType.UPDATE) + @PostMapping("updateTtl") + public AjaxResult updateTtl(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + if (rdsVo.getExpireTime() < 0) { + jedisClient.persist(rdsVo.getKey()); + } else { + jedisClient.expire(rdsVo.getKey(), (long) rdsVo.getExpireTime()); + } + return AjaxResult.success(); + } + } + + @Log(title = "RDS数据维护-setKey", businessType = BusinessType.INSERT) + @PostMapping("setKey") + public AjaxResult setKey(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + if (rdsVo.getExpireTime() <= 0) { + jedisClient.set(rdsVo.getKey(), rdsVo.getValue()); + } else { + jedisClient.setex(rdsVo.getKey(), rdsVo.getExpireTime(), rdsVo.getValue()); + } + return AjaxResult.success("设置成功"); + } + } + + @PostMapping("getType") + public AjaxResult getType(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + String result = jedisClient.type(rdsVo.getKey()); + Long ttl = jedisClient.ttl(rdsVo.getKey()); + + Map map = new HashMap<>(); + map.put("result", StringUtils.defaultString(result)); + map.put("ttl", ttl); + return AjaxResult.success(map); + } + } + + @PostMapping("getKey") + public AjaxResult getKey(@RequestBody RdsVo rdsVo) throws UnsupportedEncodingException { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + JSONObject obj = new JSONObject(); + String value = jedisClient.get(rdsVo.getKey()); + Object type = jedisClient.type(rdsVo.getKey()); + Long ttl = jedisClient.ttl(rdsVo.getKey()); + + obj.put("value", value); + obj.put("type", type); + obj.put("size", value == null ? 0 : value.getBytes("UTF-8").length + "B"); + obj.put("ttl", ttl); + return AjaxResult.success(obj); + } + } + + @PostMapping("keyList") + public AjaxResult keyList(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + ScanParams params = new ScanParams(); + if (rdsVo.getKey().equals("")) { + params.match("*"); + } else { + if (rdsVo.getKeyPatternPrecise()) { + params.match(rdsVo.getKey()); + } else { + params.match("*" + rdsVo.getKey() + "*"); + } + } + + //scan函数返回下次开始的 cursor, 如果是"0"表示没有数据或已经到了结尾。前端页面会不断传回上次scan的cursor + //endpoint是上次查询的节点key, 格式如:"192.168.0.100:6379" + ScanVO resultVO = new ScanVO(); + if (jedisClient.getDeployMode() == DeployModeEnum.CLUSTER) { + //设置是否是第一页, endpoint为空 并且 cursor 为 "0" + boolean firstPage = StringUtils.isEmpty(rdsVo.getEndpoint()) && NONE_CURSOR.equals(rdsVo.getCursor()); + resultVO.setFirstPage(firstPage); + + + SortedSet sortedEndpoints = new TreeSet(jedisClient.getMasterNodes().keySet()); + + String nextEndpoint = (firstPage) ? sortedEndpoints.first() : rdsVo.getEndpoint(); //下一步要scan的 endpoint + String nextCursor = (firstPage) ? NONE_CURSOR : rdsVo.getCursor(); //下一步要scan的 cursor + int fetchedSize = 0; + for (String endpoint : sortedEndpoints) { + + if (nextEndpoint == null) { //循环的上一个 endpoint 中的数据已经取完 + nextEndpoint = endpoint; + } + + int fetchSize = LIST_FETCH_SIZE - fetchedSize; //剩余有多少数据需要取出 + if (fetchSize > 0 && endpoint.equals(nextEndpoint)) { + JedisPool pool = jedisClient.getMasterNodes().get(endpoint); + try (Jedis jedis = pool.getResource()) { + params.count(fetchSize); + ScanResult scanRes = jedis.scan(nextCursor, params); + fetchedSize = resultVO.addScanResult(scanRes); + resultVO.setEndpoint(endpoint); + + nextCursor = scanRes.getCursor(); + if (NONE_CURSOR.equals(nextCursor)) { + //已经取光当前的 endpoint 中的数据,标识需要取下一个 + nextEndpoint = null; + } + + if (fetchedSize >= LIST_FETCH_SIZE) { // + break; + } + } + } + } + + return AjaxResult.success(resultVO); + } else { + params.count(LIST_FETCH_SIZE); + resultVO.setFirstPage(rdsVo.getCursor().equals("0")); + resultVO.addScanResult(jedisClient.scan(rdsVo.getCursor(), params)); + return AjaxResult.success(resultVO); + } + + } + + } + + @Log(title = "RDS数据维护-delKey", businessType = BusinessType.DELETE) + @PostMapping("delKey") + public AjaxResult delKey(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.del(rdsVo.getKey()); + return AjaxResult.success(result); + } + } + + @Log(title = "RDS数据维护-delKeys", businessType = BusinessType.DELETE) + @PostMapping("delKeys") + public AjaxResult delKeys(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.del(rdsVo.getKeys()); + return AjaxResult.success(result); + } + } + + + @PostMapping("hget") + public AjaxResult hget(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + String result = jedisClient.hget(rdsVo.getKey(), rdsVo.getField()); + return AjaxResult.success(StringUtils.defaultString(result)); + } + } + + @Log(title = "RDS数据维护-hdel", businessType = BusinessType.DELETE) + @PostMapping("hdel") + public AjaxResult hdel(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.hdel(rdsVo.getKey(), rdsVo.getField()); + return AjaxResult.success(result); + } + } + + @PostMapping("hgetAll") + public AjaxResult hgetAll(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Map map = jedisClient.hgetAll(rdsVo.getKey()); + JSONArray array = new JSONArray(); + for (Object key : map.keySet()) { + JSONObject obj = new JSONObject(); + obj.put("key", key); + obj.put("value", map.get(key)); + array.add(obj); + } + return AjaxResult.success(array); + } + } + + @Log(title = "RDS数据维护-hset", businessType = BusinessType.INSERT) + @PostMapping("hset") + public AjaxResult hset(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.hset(rdsVo.getKey(), rdsVo.getField(), rdsVo.getValue()); + return AjaxResult.success(result); + } + } + + @Log(title = "RDS数据维护-lpush", businessType = BusinessType.INSERT) + @PostMapping("lpush") + public AjaxResult lpush(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.lpush(rdsVo.getKey(), rdsVo.getValue()); + return AjaxResult.success(result); + } + } + + @Log(title = "RDS数据维护-lset", businessType = BusinessType.INSERT) + @PostMapping("lset") + public AjaxResult lset(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + String result = jedisClient.lset(rdsVo.getKey(), rdsVo.getIndex(), rdsVo.getValue()); + + return AjaxResult.success(result); + } + } + + @PostMapping("lrange") + public AjaxResult lrange(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + List list = jedisClient.lrange(rdsVo.getKey(), rdsVo.getStart(), rdsVo.getEnd()); + + JSONArray array = new JSONArray(); + for (Object e : list) { + JSONObject obj = new JSONObject(); + obj.put("value", e); + array.add(obj); + } + return AjaxResult.success(array); + } + + } + + @Log(title = "RDS数据维护-lrem", businessType = BusinessType.DELETE) + @PostMapping("lrem") + public AjaxResult lrem(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.lrem(rdsVo.getKey(), rdsVo.getCount(), rdsVo.getValue()); + return AjaxResult.success(result); + } + } + + //添加、修改 + @Log(title = "RDS数据维护-sadd", businessType = BusinessType.INSERT) + @PostMapping("sadd") + public AjaxResult sadd(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.sadd(rdsVo.getKey(), rdsVo.getMember()); + return AjaxResult.success(result); + } + } + + //修改 + @Log(title = "RDS数据维护-sedit", businessType = BusinessType.UPDATE) + @PostMapping("sedit") + public AjaxResult sedit(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.sadd(rdsVo.getKey(), rdsVo.getNewMember()); + return AjaxResult.success(result); + } + } + + //查询 + @PostMapping("smembers") + public AjaxResult smembers(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Set setList = jedisClient.smembers(rdsVo.getKey()); + + JSONArray array = new JSONArray(); + for (Object e : setList) { + JSONObject obj = new JSONObject(); + obj.put("value", e); + array.add(obj); + } + + return AjaxResult.success(array); + } + } + + //删除 + @Log(title = "RDS数据维护-srem", businessType = BusinessType.DELETE) + @PostMapping("srem") + public AjaxResult srem(@RequestBody RdsVo rdsVo) { + + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.srem(rdsVo.getKey(), rdsVo.getMember()); + return AjaxResult.success(result); + } + + } + + //zset-添加 + @Log(title = "RDS数据维护-zadd", businessType = BusinessType.INSERT) + @PostMapping("zadd") + public AjaxResult zadd(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.zadd(rdsVo.getKey(), rdsVo.getScore(), rdsVo.getMember()); + return AjaxResult.success(result); + } + } + + //zset-删除 + @Log(title = "RDS数据维护-zrem", businessType = BusinessType.DELETE) + @PostMapping("zrem") + public AjaxResult zrem(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Long result = jedisClient.zrem(rdsVo.getKey(), rdsVo.getMember()); + return AjaxResult.success(result); + } + } + + //zset-查询 + @PostMapping("zsetList") + public AjaxResult zsetList(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + String key = rdsVo.getKey(); + Set sets = jedisClient.zrange(key, 0, jedisClient.zcard(key)); + + JSONArray array = new JSONArray(); + for (String member : sets) { + JSONObject obj = new JSONObject(); + obj.put("member", member); + obj.put("score", jedisClient.zscore(key, member)); + array.add(obj); + } + return AjaxResult.success(array); + } + + } + + //zset 修改 + @Log(title = "RDS数据维护-zsetEdit", businessType = BusinessType.UPDATE) + @PostMapping("zsetEdit") + public AjaxResult zsetEdit(@RequestBody RdsVo rdsVo) { + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + this.zrem(rdsVo); + Long result = jedisClient.zadd(rdsVo.getKey(), rdsVo.getScore(), rdsVo.getNewMember()); + return AjaxResult.success(result); + } + } + + + @Log(title = "命令行交互", businessType = BusinessType.OTHER) + @PostMapping("execCommand") + public AjaxResult rdsExecCommand(@RequestBody RdsVo rdsVo) { + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + AssertUtils.ObjectIsNull(rdsVo.getServiceId(), "参数错误,参数[serviceId]为空!"); + + String res; + try { + JedisDataClient jedisClient = selectClientDB(rdsVo); + res = jedisClient.execCommand(rdsVo.getCommand()); + } catch (Exception e) { + return AjaxResult.error(e.getMessage()); + } + return AjaxResult.success("交互成功", res); + } + + + /** + * stream 类型 操作 + **/ + + /** + * 新增 + * + * @param rdsVo key ,value + * @return + */ + @Log(title = "RDS数据维护-stream-xadd", businessType = BusinessType.INSERT) + @PostMapping("xadd") + public AjaxResult xadd(@RequestBody RdsVo rdsVo) { + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + String xadd = jedisClient.xadd(rdsVo.getKey(), rdsVo.getValue()); + HashMap result = new HashMap<>(); + result.put("id", xadd); + return AjaxResult.success(result); + } + } + + @Log(title = "RDS数据维护-stream-xdel", businessType = BusinessType.DELETE) + @PostMapping("xdel") + public AjaxResult xdel(@RequestBody RdsVo rdsVo) { + + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + AssertUtils.StringIsNull(rdsVo.getKey(), "参数 key 为空!"); + AssertUtils.StringIsNull(rdsVo.getId(), "参数 id 为空!"); + + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + Boolean xdel = jedisClient.xdel(rdsVo.getKey(), rdsVo.getId()); + return AjaxResult.success(xdel); + } + } + + @Log(title = "RDS数据维护-stream-xrevrange", businessType = BusinessType.OTHER) + @PostMapping("xrevrange") + public AjaxResult xrevrange(@RequestBody RdsVo rdsVo) { + + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + AssertUtils.StringIsNull(rdsVo.getKey(), "参数 key 为空!"); + + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + ArrayList> xrevrange = jedisClient.xrevrange(rdsVo.getKey()); + return AjaxResult.success(xrevrange); + } + } + + @Log(title = "RDS数据维护-stream-loadMore", businessType = BusinessType.OTHER) + @PostMapping("xrevrangeLoadMore") + public AjaxResult xrevrangeLoadMore(@RequestBody RdsVo rdsVo) { + + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + AssertUtils.StringIsNull(rdsVo.getKey(), "参数 key 为空!"); + AssertUtils.StringIsNull(rdsVo.getId(), "参数 id 为空!"); + + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + ArrayList> xrevrange = jedisClient.xrevrange(rdsVo.getKey(), rdsVo.getId()); + return AjaxResult.success(xrevrange); + } + } + + @Log(title = "RDS数据维护-stream-xrange", businessType = BusinessType.OTHER) + @PostMapping("xrange") + public AjaxResult xrange(@RequestBody RdsVo rdsVo) { + + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + AssertUtils.StringIsNull(rdsVo.getKey(), "参数 key 为空!"); + + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + ArrayList> xrange = jedisClient.xrange(rdsVo.getKey()); + return AjaxResult.success(xrange); + } + } + + @Log(title = "RDS数据维护-stream-loadMore", businessType = BusinessType.OTHER) + @PostMapping("xrangeLoadMore") + public AjaxResult xrangeLoadMore(@RequestBody RdsVo rdsVo) { + + AssertUtils.ObjectIsNull(rdsVo, "参数错误!"); + AssertUtils.StringIsNull(rdsVo.getKey(), "参数 key 为空!"); + AssertUtils.StringIsNull(rdsVo.getId(), "参数 id 为空!"); + + try (JedisDataClient jedisClient = selectClientDB(rdsVo)) { + ArrayList> xrange = jedisClient.xrange(rdsVo.getKey(), rdsVo.getId()); + return AjaxResult.success(xrange); + } + } + + + private JedisDataClient selectClientDB(RdsVo rdsVo) { + JedisDataClient jedisClient = rdsService.getConnectionClient(rdsVo.getServiceId()); + + if (jedisClient.getDeployMode() != DeployModeEnum.CLUSTER) { + jedisClient.select(rdsVo.getDb()); + } + + return jedisClient; + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsMonitorController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsMonitorController.java new file mode 100644 index 0000000000000000000000000000000000000000..48af6b5ac1c3c2a6b6f8962121a22a04e4891834 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsMonitorController.java @@ -0,0 +1,88 @@ +package com.tongtech.web.controller.console; + +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.console.domain.NodeStat; +import com.tongtech.console.domain.ServiceStat; +import com.tongtech.console.domain.vo.ServiceStatVo; +import com.tongtech.console.service.CenterStatSrcService; +import com.tongtech.console.service.NodeStatService; +import com.tongtech.console.service.ServiceStatService; +import com.tongtech.console.domain.vo.RdsMonitorQueryVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + + +/** + * RDS服务Controller + * + * @author Zhang ChenLong + * @date 2023-01-26 + */ +@RestController +@RequestMapping("/web-api/console/rdsmonitor") +public class RdsMonitorController extends BaseController +{ + @Autowired + private ServiceStatService servStatService; + + @Autowired + private NodeStatService nodeStatService; + + @Autowired + private CenterStatSrcService srcService; + + /** + * 查询最新的ServiceStat列表,其中children包含子节点 + * + * @param param 其中只有 name(ServiceName) 可作为模糊匹配的查询条件 + * @return + */ + @GetMapping("/listServiceStat") + public AjaxResult listServiceStat(ServiceStatVo param) + { + Long srcId = srcService.selectLastSrcId(); + param.setSrcId(srcId); + + List serviceStats = servStatService.selectServiceStatList(param); + List result = new ArrayList<>(serviceStats.size()); + + for(ServiceStat servStat : serviceStats) { + List nodes = nodeStatService.selectNodeStatList(new NodeStat(srcId, servStat.getServiceId())); + //System.out.println("~~~~nodes:" + nodes); + ServiceStatVo vo = new ServiceStatVo(servStat); + vo.setChildren(nodes); + // 进行状态条件的筛选 + if( param.getStatus() != null ) { + if(param.getStatus().equals(vo.getStatus()) ) { + result.add(vo); + } + } + else { + result.add(vo); + } + } + + return AjaxResult.success(result); + } + + + /** + * + */ + @GetMapping("/serviceNodes") + public AjaxResult getServiceNodes(RdsMonitorQueryVo queryVo) + { + //过去的秒数计算查询参数 + if(queryVo.getBeginCreateSecond() == null && queryVo.getPastSecond() != null) { + long beginSecond = (System.currentTimeMillis() / 1000) - queryVo.getPastSecond(); + queryVo.setBeginCreateSecond(beginSecond); + } + + return AjaxResult.success(servStatService.selectSummaryServiceStat(queryVo)); + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsNodeController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsNodeController.java new file mode 100644 index 0000000000000000000000000000000000000000..4c00a69b9979a15471c078cab4ca0aeba8874945 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsNodeController.java @@ -0,0 +1,112 @@ +package com.tongtech.web.controller.console; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.console.domain.RdsNode; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.console.service.RdsNodeService; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.common.core.page.TableDataInfo; + +/** + * 节点信息Controller + * + * @author Zhang ChenLong + * @date 2023-01-24 + */ +@RestController +@RequestMapping("/web-api/console/rdsnode") +public class RdsNodeController extends BaseController +{ + @Autowired + private RdsNodeService nodeService; + + /** + * 查询节点信息列表,返回所有数据(不分页) + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping("/list") + public AjaxResult list(RdsNode rdsNode) + { + return AjaxResult.success(nodeService.selectRdsNodeList(rdsNode)); + } + + + /** + * 查询是否有相同名称的节点在同一个服务或同一个节点管理器中 + * + * @param node 节点信息 只有三个参数可用:managerId(必须), nodeName(必须) + * @return 节点信息集合 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping("/listSameName") + public AjaxResult listSameName(RdsNode node) + { + return AjaxResult.success(nodeService.selectSameNameNodeList(node)); + } + + + /** + * 查询是否有相同端口的节点,在相同主机地址的情况下。 + * 可选传入 serviceId 属性,用来在搜索范围中排除 serviceId + * + * @param node hostAddress(必须), serviceId(可选,排除),servicePort(必须, 同时去匹配 servicePort 和 redisPort) + * @return + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping("/listSamePort") + public AjaxResult listSamePort(RdsNode node) + { + return AjaxResult.success(nodeService.selectSamePortNodeList(node)); + } + + + /** + * 查询节点信息列表,返回分页数据 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping("/listPaging") + public TableDataInfo listPaging(RdsNode rdsNode) + { + startPage(); + List list = nodeService.selectRdsNodeList(rdsNode); + return getDataTable(list); + } + + + /** + * 导出节点信息列表 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:query')") + @Log(title = "节点信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, RdsNode rdsNode) + { + List list = nodeService.selectRdsNodeList(rdsNode); + ExcelUtil util = new ExcelUtil(RdsNode.class); + util.exportExcel(response, list, "节点信息数据"); + } + + /** + * 获取节点信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:query')") + @GetMapping(value = "/{nodeId}") + public AjaxResult getInfo(@PathVariable("nodeId") Long nodeId) + { + return AjaxResult.success(nodeService.selectRdsNodeByNodeId(nodeId)); + } + + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsServiceController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsServiceController.java new file mode 100644 index 0000000000000000000000000000000000000000..7b6b0d60f81159845507c1b1b9753d64b17128ef --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsServiceController.java @@ -0,0 +1,198 @@ +package com.tongtech.web.controller.console; + +import java.util.List; + +import com.tongtech.common.utils.AESUtils; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.utils.StringUtils; +import com.tongtech.console.domain.RdsNode; +import com.tongtech.console.domain.vo.RdsServiceNodesVo; +import com.tongtech.console.domain.vo.RdsServiceQueryVo; +import com.tongtech.console.enums.DeployModeEnum; +import com.tongtech.console.service.RdsNodeService; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.console.domain.RdsService; +import com.tongtech.console.service.RdsServiceService; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.common.core.page.TableDataInfo; + + +/** + * RDS服务Controller + * + * @author Zhang ChenLong + * @date 2023-01-26 + */ +@RestController +@RequestMapping("/web-api/console/rdsservice") +public class RdsServiceController extends BaseController +{ + @Autowired + private RdsServiceService serviceService; + + @Autowired + private RdsNodeService nodeService; + + /** + * 查询RDS服务列表, 非分页方式 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping("/list") + public AjaxResult list(RdsServiceQueryVo rdsService) + { + return AjaxResult.success(serviceService.selectRdsServiceList(rdsService)); + } + + /** + * 查询RDS服务列表,并附带子属性nodes列表,如果是哨兵服务则查询出使用该哨兵的"sentinel_worker"服务的数量(workerServices) + * 默认去除了中心服务和哨兵服务, 但当指定deployMode条件后,则只查询deployMode指定的部署模式 + * + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping("/listWithNodes") + public TableDataInfo listWithNodes(RdsServiceQueryVo queryVo) + { + startPage(); + if(StringUtils.isEmpty(queryVo.getDeployMode())) { + queryVo.addDeployModes(new String[] {"single", "sentinel_worker", "cluster", "scalable"}); + } + else { + queryVo.addDeployMode(queryVo.getDeployMode()); + } + + List list = serviceService.selectListInDeployModes(queryVo); + + //查询并附加节点列表 + for(RdsServiceQueryVo serv : list) { + serv.setNodes(nodeService.selectRdsNodesByServiceId(serv.getServiceId())); + if(serv.getDeployModeEnum() == DeployModeEnum.SENTINEL) { + RdsServiceQueryVo countParam = new RdsServiceQueryVo(); + countParam.setSentinelServiceId(serv.getServiceId()); + serv.setWorkerServices(serviceService.selectRdsServiceCount(countParam)); + } + } + + return getDataTable(list); + } + + /** + * 导出RDS服务列表 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:export')") + @Log(title = "RDS服务", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, RdsServiceQueryVo rdsService) + { + List list = serviceService.selectRdsServiceList(rdsService); + ExcelUtil util = new ExcelUtil(RdsServiceQueryVo.class); + util.exportExcel(response, list, "RDS服务数据"); + } + + /** + * 获取RDS服务详细信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:list')") + @GetMapping(value = "/{serviceId}") + public AjaxResult getInfo(@PathVariable("serviceId") Long serviceId) + { + RdsService serv = serviceService.selectRdsServiceByServiceId(serviceId); + if(StringUtils.isNotEmpty(serv.getPassword())) { + String password = AESUtils.encryptAES(serv.getPassword()); + serv.setPassword(password); + } + + return AjaxResult.success(serv); + } + + /** + * 新增RDS服务 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:add')") + @Log(title = "RDS服务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody RdsService service) + { + if(StringUtils.isNotEmpty(service.getPassword())) { + String password = AESUtils.decryptAES(service.getPassword()); + service.setPassword(password); + } + + + return toAjax(serviceService.insertRdsService(service)); + } + + + + /** + * 修改RDS服务 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:edit')") + @Log(title = "RDS服务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult update(@RequestBody RdsService service) + { + if(StringUtils.isNotEmpty(service.getPassword())) { + String password = AESUtils.decryptAES(service.getPassword()); + service.setPassword(password); + } + + return toAjax(serviceService.updateRdsService(service)); + } + + /** + * 修改RDS服务 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:edit')") + @Log(title = "RDS服务-密码", businessType = BusinessType.UPDATE) + @PutMapping("/servicePassword") + public AjaxResult updatePassword(@RequestBody RdsService service) + { + if(StringUtils.isNotEmpty(service.getPassword())) { + String password = AESUtils.decryptAES(service.getPassword()); + service.setPassword(password); + } + return toAjax(serviceService.updateServicePassword(service)); + } + + + + + + /** + * 获取RDS服务详细信息 和 其所属的node列表 + */ + @PreAuthorize("@ss.hasPermi('console:rdsservice:query')") + @GetMapping(value = "/serviceNodes/{serviceId}") + public AjaxResult getServiceWithNodes(@PathVariable("serviceId") Long serviceId) + { + RdsService serv = serviceService.selectRdsServiceByServiceId(serviceId); + + if(StringUtils.isNotEmpty(serv.getPassword())) { + String password = AESUtils.encryptAES(serv.getPassword()); + serv.setPassword(password); + } + + List nodes = nodeService.selectRdsNodesByServiceId(serviceId); + return AjaxResult.success(new RdsServiceNodesVo(serv, nodes)); + } + + @GetMapping(value = "/existName/{serviceName}") + public AjaxResult existsServiceName(@PathVariable("serviceName") String serviceName) + { + return AjaxResult.success(serviceService.existsServiceName(serviceName)); + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsVersionController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsVersionController.java new file mode 100644 index 0000000000000000000000000000000000000000..d6bf9799e4eb3a0a3c94fa4c01a48ea2dab5472d --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsVersionController.java @@ -0,0 +1,135 @@ +package com.tongtech.web.controller.console; + +import java.util.List; + +import com.tongtech.console.domain.vo.RdsVersionVo; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.console.domain.RdsVersion; +import com.tongtech.console.service.RdsVersionService; +import com.tongtech.common.core.page.TableDataInfo; + +/** + * 版本信息Controller + * + * @author Zhang ChenLong + * @date 2023-01-12 + */ +@RestController +@RequestMapping("/web-api/console/rdsversion") +public class RdsVersionController extends BaseController +{ + @Autowired + private RdsVersionService rdsVersionService; + + /** + * 查询版本信息列表 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:list')") + @GetMapping("/list") + public TableDataInfo list(RdsVersion rdsVersion) + { + startPage(); + List list = rdsVersionService.selectRdsVersionList(rdsVersion); + return getDataTable(list); + } + + /** + * 查询所有版本,by status + * @param status "1" 启用状态,"0" 停用状态, null 查询所有版本 + * @return + */ + @GetMapping("/listAll") + public AjaxResult listAll(String status) + { + List list = rdsVersionService.selectListByStatus(status); + return AjaxResult.success(list); + } + + + + /** + * 获取版本信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:query')") + @GetMapping(value = "/{versionId}") + public AjaxResult getInfo(@PathVariable("versionId") Long versionId) + { + RdsVersion version = rdsVersionService.selectRdsVersionByVersionId(versionId); + RdsVersionVo vo = new RdsVersionVo(version, null); + + return AjaxResult.success(vo); + } + + /** + * 新增版本信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:add')") + @Log(title = "版本信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody RdsVersion rdsVersion) + { + rdsVersion.setCreateBy(getUsername()); + return toAjax(rdsVersionService.insertRdsVersion(rdsVersion)); + } + + /** + * 修改版本信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:edit')") + @Log(title = "版本信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody RdsVersion rdsVersion) + { + rdsVersion.setUpdateBy(getUsername()); + return toAjax(rdsVersionService.updateRdsVersion(rdsVersion)); + } + + /** + * 删除版本信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:remove')") + @Log(title = "版本信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{versionIds}") + public AjaxResult remove(@PathVariable Long[] versionIds) + { + return toAjax(rdsVersionService.deleteRdsVersionByVersionIds(versionIds)); + } + + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:edit')") + @Log(title = "版本信息", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody RdsVersion paramVer) + { + paramVer.setUpdateBy(getUsername()); + return toAjax(rdsVersionService.updateStatus(paramVer)); + } + + + /** + * 改为默认版本 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversion:edit')") + @Log(title = "版本信息", businessType = BusinessType.UPDATE) + @PutMapping("/changeDefault/{versionId}") + public AjaxResult changeDefault(@PathVariable("versionId") Long versionId) + { + return toAjax(rdsVersionService.updateDefaultVersion(versionId, getUsername())); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsVersionPkgController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsVersionPkgController.java new file mode 100644 index 0000000000000000000000000000000000000000..10cdff3aa8813efd83a9be4ca9675fc820cda31d --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/RdsVersionPkgController.java @@ -0,0 +1,189 @@ +package com.tongtech.web.controller.console; + +import java.io.File; +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.config.AppHomeConfig; +import com.tongtech.common.exception.base.BaseException; +import com.tongtech.common.utils.file.FileUploadUtils; +import com.tongtech.common.utils.file.FileUtils; +import com.tongtech.common.utils.file.MimeTypeUtils; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.console.domain.RdsVersionPkg; +import com.tongtech.console.service.RdsVersionPkgService; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.common.core.page.TableDataInfo; +import org.springframework.web.multipart.MultipartFile; + +import static com.tongtech.common.config.AppHomeConfig.PACKAGE_VERSION_PATH; + +/** + * 安装包信息Controller + * + * @author Zhang ChenLong + * @date 2023-01-12 + */ +@RestController +@RequestMapping("/web-api/console/rdsversionpkg") +public class RdsVersionPkgController extends BaseController +{ + @Autowired + private RdsVersionPkgService rdsVersionPkgService; + + /** + * 查询安装包信息列表 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:list')") + @GetMapping("/list") + public TableDataInfo list(RdsVersionPkg rdsVersionPkg) + { + startPage(); + List list = rdsVersionPkgService.selectRdsVersionPkgList(rdsVersionPkg); + return getDataTable(list); + } + + + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:add')") + @Log(title = "安装包信息", businessType = BusinessType.INSERT) + @PostMapping("/upload") + public AjaxResult upload(MultipartFile file, Long versionId, String pkgType) throws Exception + { + if(versionId == null || pkgType == null) { + throw new BaseException("Parameter Error for RdsVersionPkg upload!"); + } + + RdsVersionPkg paramPkg = new RdsVersionPkg(); + paramPkg.setVersionId(versionId); + paramPkg.setPkgType(pkgType); + List resList = rdsVersionPkgService.selectRdsVersionPkgList(paramPkg); + if(resList.size() >= 1) { + return AjaxResult.error("每一种包类型只能上载一安装包,请勿重复上载!"); + } + + String fileName = FileUploadUtils.uploadToDir(AppHomeConfig.getAbsoluteFile(PACKAGE_VERSION_PATH, "v" + versionId), file, MimeTypeUtils.GZIP_EXTENSION); + + RdsVersionPkg pkg = new RdsVersionPkg(); + pkg.setVersionId(versionId); + pkg.setPkgName(getName(fileName)); + pkg.setPkgType(pkgType); + pkg.setCreateBy(getUsername()); + pkg.setFileName(fileName); + pkg.setFileSize(file.getSize()); + + System.out.println("insertRdsVersionPkg: " + pkg); + rdsVersionPkgService.insertRdsVersionPkg(pkg); + + return AjaxResult.success("文件上传成功"); + } + + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:query')") + @PostMapping(value = "/download/{packageId}") + public void download(HttpServletResponse response, @PathVariable("packageId") Long packageId) + { + try + { + + RdsVersionPkg pkg = rdsVersionPkgService.selectRdsVersionPkgByPackageId(packageId); + File downloadFile = rdsVersionPkgService.getPkgFile(pkg); + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, pkg.getFileName()); + FileUtils.writeBytes(downloadFile, response.getOutputStream()); + } + catch (Exception e) + { + logger.error("下载文件失败", e); + } + } + + + + /** + * 导出安装包信息列表 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:export')") + @Log(title = "安装包信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, RdsVersionPkg rdsVersionPkg) + { + List list = rdsVersionPkgService.selectRdsVersionPkgList(rdsVersionPkg); + ExcelUtil util = new ExcelUtil(RdsVersionPkg.class); + util.exportExcel(response, list, "安装包信息数据"); + } + + /** + * 获取安装包信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:query')") + @GetMapping(value = "/{packageId}") + public AjaxResult getInfo(@PathVariable("packageId") Long packageId) + { + return AjaxResult.success(rdsVersionPkgService.selectRdsVersionPkgByPackageId(packageId)); + } + + /** + * 新增安装包信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:add')") + @Log(title = "安装包信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody RdsVersionPkg rdsVersionPkg) + { + return toAjax(rdsVersionPkgService.insertRdsVersionPkg(rdsVersionPkg)); + } + + /** + * 修改安装包信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:edit')") + @Log(title = "安装包信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody RdsVersionPkg rdsVersionPkg) + { + return toAjax(rdsVersionPkgService.updateRdsVersionPkg(rdsVersionPkg)); + } + + /** + * 删除安装包信息 + */ + @PreAuthorize("@ss.hasPermi('console:rdsversionpkg:remove')") + @Log(title = "安装包信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{packageIds}") + public AjaxResult remove(@PathVariable Long[] packageIds) + { + return toAjax(rdsVersionPkgService.deleteRdsVersionPkgByPackageIds(packageIds)); + } + + private String getName(String fileName) { + String fname = fileName.trim(); + int end = fname.indexOf(".tar.gz"); + if(end <= 0) { + end = fname.indexOf(".zip"); + } + if(end <= 0) { + end = fname.indexOf(".tgz"); + } + + if(end > 0) { + return fname.substring(0, end); + } + else { + return fname; + } + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/vo/RdsVo.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/vo/RdsVo.java new file mode 100644 index 0000000000000000000000000000000000000000..f548e7ca3c4b73f25b704580c8a7c5d72a8d1977 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/vo/RdsVo.java @@ -0,0 +1,208 @@ +package com.tongtech.web.controller.console.vo; + + +public class RdsVo { + //private String ip; + //private int port; + + private Long serviceId; + private int db; + private String key; + private long index; + private String field; + private String value; + private String member; + private String newMember; + private String[] keys; + private long start; + private long end; + private long count; + private double score; + private String cursor; + private String endpoint; + private Boolean keyPatternPrecise; + + /** + * 过期时间 + */ + private int expireTime; + + /** + * 控制台命令行交互 命令 + */ + private String command; + + /** + * stream类型xdel所需id + */ + private String id; + +// public String getIp() { +// return ip; +// } +// +// public void setIp(String ip) { +// this.ip = ip; +// } +// +// public int getPort() { +// return port; +// } +// +// public void setPort(int port) { +// this.port = port; +// } + + + public Long getServiceId() { + return serviceId; + } + + public void setServiceId(Long serviceId) { + this.serviceId = serviceId; + } + + public int getDb() { + return db; + } + + public void setDb(int db) { + this.db = db; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getMember() { + return member; + } + + public void setMember(String member) { + this.member = member; + } + + public String getNewMember() { + return newMember; + } + + public void setNewMember(String newMember) { + this.newMember = newMember; + } + + public String[] getKeys() { + return keys; + } + + public void setKeys(String[] keys) { + this.keys = keys; + } + + public long getStart() { + return start; + } + + public void setStart(long start) { + this.start = start; + } + + public long getEnd() { + return end; + } + + public void setEnd(long end) { + this.end = end; + } + + public long getCount() { + return count; + } + + public void setCount(long count) { + this.count = count; + } + + public double getScore() { + return score; + } + + public void setScore(double score) { + this.score = score; + } + + public String getCursor() { + return cursor; + } + + public void setCursor(String cursor) { + this.cursor = cursor; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public Boolean getKeyPatternPrecise() { + return keyPatternPrecise; + } + + public void setKeyPatternPrecise(Boolean keyPatternPrecise) { + this.keyPatternPrecise = keyPatternPrecise; + } + + public int getExpireTime() { + return expireTime; + } + + public void setExpireTime(int expireTime) { + this.expireTime = expireTime; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public long getIndex() { + return index; + } + + public void setIndex(long index) { + this.index = index; + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/vo/ScanVO.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/vo/ScanVO.java new file mode 100644 index 0000000000000000000000000000000000000000..fb9e0456f7406b4367db47656c48c17e85b530f6 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/console/vo/ScanVO.java @@ -0,0 +1,69 @@ +package com.tongtech.web.controller.console.vo; + +import redis.clients.jedis.ScanResult; + +import java.util.List; + + +public class ScanVO { + + public ScanVO() { + } + + public ScanVO(Boolean isFirstPage, String endpoint, ScanResult scanResult) { + this.isFirstPage = isFirstPage; + this.endpoint = endpoint; + this.scanResult = scanResult; + } + + private Boolean isFirstPage; + + private String endpoint; + + private ScanResult scanResult; + + + + public Boolean getFirstPage() { + return isFirstPage; + } + + public void setFirstPage(Boolean firstPage) { + isFirstPage = firstPage; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public ScanResult getScanResult() { + return scanResult; + } + +// public void setScanResult(ScanResult scanResult) { +// this.scanResult = scanResult; +// } +// + + /** + * 在原来Result基础上在加入新的结果,返回Result中数据的总数量。 + * @param newResult + * @return 返回Result中数据的总数量。 + */ + public int addScanResult(ScanResult newResult) { + if(this.scanResult == null) { + this.scanResult = newResult; + } + else { + List lastResult = this.scanResult.getResult(); + lastResult.addAll(newResult.getResult()); + this.scanResult = new ScanResult(newResult.getCursorAsBytes(), lastResult); + } + + return this.scanResult.getResult().size(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/ServerController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/ServerController.java new file mode 100644 index 0000000000000000000000000000000000000000..7bb4435625cc952b1fac27cfda7bc2223d9b2566 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/ServerController.java @@ -0,0 +1,27 @@ +package com.tongtech.web.controller.monitor; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/monitor/server") +public class ServerController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysLogininforController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000000000000000000000000000000000000..d28aed21eb7819d887ce2228ac7434e6c8ef9fb7 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,74 @@ +package com.tongtech.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.framework.web.service.SysPasswordService; +import com.tongtech.system.domain.SysLogininfor; +import com.tongtech.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysOperlogController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000000000000000000000000000000000000..d174188e7273fecc5140a9c638c13d18b130ba49 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,69 @@ +package com.tongtech.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.domain.SysOperLog; +import com.tongtech.system.service.ISysOperLogService; + +/** + * 操作日志记录 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) + { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return AjaxResult.success(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysUserOnlineController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000000000000000000000000000000000000..188217bb92b30a3ab0f97bca728423736700487c --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,93 @@ +package com.tongtech.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.tongtech.system.service.SysObjectCacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.system.domain.SysUserOnline; +import com.tongtech.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private SysObjectCacheService redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + if (StringUtils.equals(userName, user.getUsername())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return AjaxResult.success(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/quartz/SysJobController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/quartz/SysJobController.java new file mode 100644 index 0000000000000000000000000000000000000000..bad467a04a4ada27bdf1604ef7722c90748b70b5 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/quartz/SysJobController.java @@ -0,0 +1,185 @@ +package com.tongtech.web.controller.quartz; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.exception.job.TaskException; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.domain.SysJob; +import com.tongtech.system.service.ISysJobService; +import com.tongtech.system.utils.CronUtils; +import com.tongtech.system.utils.ScheduleUtils; + +/** + * 调度任务信息操作处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/monitor/job") +public class SysJobController extends BaseController +{ + @Autowired + private ISysJobService jobService; + + /** + * 查询定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJob sysJob) + { + startPage(); + List list = jobService.selectJobList(sysJob); + return getDataTable(list); + } + + /** + * 导出定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "定时任务", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJob sysJob) + { + List list = jobService.selectJobList(sysJob); + ExcelUtil util = new ExcelUtil(SysJob.class); + util.exportExcel(response, list, "定时任务"); + } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) + { + return AjaxResult.success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setCreateBy(getUsername()); + return toAjax(jobService.insertJob(job)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setUpdateBy(getUsername()); + return toAjax(jobService.updateJob(job)); + } + + /** + * 定时任务状态修改 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException + { + SysJob newJob = jobService.selectJobById(job.getJobId()); + newJob.setStatus(job.getStatus()); + return toAjax(jobService.changeStatus(newJob)); + } + + /** + * 定时任务立即执行一次 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/run") + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException + { + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); + } + + /** + * 删除定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException + { + jobService.deleteJobByIds(jobIds); + return success(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/quartz/SysJobLogController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/quartz/SysJobLogController.java new file mode 100644 index 0000000000000000000000000000000000000000..f512ca71006577441c93921235aca265ef31a1ad --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/quartz/SysJobLogController.java @@ -0,0 +1,92 @@ +package com.tongtech.web.controller.quartz; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.domain.SysJobLog; +import com.tongtech.system.service.ISysJobLogService; + +/** + * 调度日志操作处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/monitor/jobLog") +public class SysJobLogController extends BaseController +{ + @Autowired + private ISysJobLogService jobLogService; + + /** + * 查询定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJobLog sysJobLog) + { + startPage(); + List list = jobLogService.selectJobLogList(sysJobLog); + return getDataTable(list); + } + + /** + * 导出定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJobLog sysJobLog) + { + List list = jobLogService.selectJobLogList(sysJobLog); + ExcelUtil util = new ExcelUtil(SysJobLog.class); + util.exportExcel(response, list, "调度日志"); + } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) + { + return AjaxResult.success(jobLogService.selectJobLogById(jobLogId)); + } + + + /** + * 删除定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobLogIds}") + public AjaxResult remove(@PathVariable Long[] jobLogIds) + { + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); + } + + /** + * 清空定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "调度日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + jobLogService.cleanJobLog(); + return AjaxResult.success(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysConfigController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysConfigController.java new file mode 100644 index 0000000000000000000000000000000000000000..e008097ad1825b60767a20b36d45662b010ca9ca --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysConfigController.java @@ -0,0 +1,164 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.config.UhConsoleConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.domain.SysConfig; +import com.tongtech.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return AjaxResult.success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return AjaxResult.success(configService.selectConfigByKey(configKey)); + } + + + /** + * 返回系统对外显示的关键配置信息(application.yml中定义) + */ + @GetMapping(value = "/appConfigKey/{key}") + public AjaxResult getAppConfigKey(@PathVariable String key) + { + if("console.name".equals(key)) { + return AjaxResult.success("", UhConsoleConfig.getName()); + } + else if("console.version".equals(key)) { + return AjaxResult.success("", UhConsoleConfig.getVersion()); + } + else if("console.copyrightYear".equals(key)) { + return AjaxResult.success("", UhConsoleConfig.getCopyrightYear()); + } + else if("console.deployEnv".equals(key)) { + return AjaxResult.success("", UhConsoleConfig.getDeployEnv()); + } + else if("console.embedding".equals(key)) { + return AjaxResult.success("", UhConsoleConfig.isEmbedding()); + } + else { + return AjaxResult.error("Failed to find application config key:" + key); + } + } + + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return AjaxResult.success(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysDictDataController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000000000000000000000000000000000000..79b2d15befc71d93e9d91e8248301f24c0e15033 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysDictDataController.java @@ -0,0 +1,121 @@ +package com.tongtech.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysDictData; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.service.ISysDictDataService; +import com.tongtech.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return AjaxResult.success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList(); + } + return AjaxResult.success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysDictTypeController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000000000000000000000000000000000000..1ffbc06f843f0a07cacf5f90f0dca815fbb7f161 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysDictTypeController.java @@ -0,0 +1,132 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysDictType; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictType dictType) + { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return AjaxResult.success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return AjaxResult.success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List dictTypes = dictTypeService.selectDictTypeAll(); + return AjaxResult.success(dictTypes); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysIndexController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysIndexController.java new file mode 100644 index 0000000000000000000000000000000000000000..7182f9e88bbc9c91c75e3c4b1b5f50a0b0d85174 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysIndexController.java @@ -0,0 +1,73 @@ +package com.tongtech.web.controller.system; + +import com.tongtech.common.exception.base.BaseException; +import com.tongtech.framework.interceptor.ContextPathInterceptor; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.config.UhConsoleConfig; +import com.tongtech.common.utils.StringUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 因为是单页面应用,把各个不同的页面路由路径,都定向到/public/index.html资源上。 + * + * @author Zhang Chenlong + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private UhConsoleConfig consoleConfig; + + @Autowired + private ContextPathInterceptor contextPathInterceptor; + + + /** + * 访问首页 + * 如果 classes下"/public/index.html"资源存在,就定位到该资源输出。 + * 如果不存在,就提示"....请通过前端地址访问。" + */ + @RequestMapping({"/", + "/index", + "/login", + "/system/**", + "/user/**", + "/monitor/**", + "/tool/**", + "/rds/**", + "/log/**" + }) + public String index() + { + try { + ClassPathResource indexRes = new ClassPathResource("/public/index.html"); + if(indexRes.exists() ) { + String content = StreamUtils.copyToString(indexRes.getInputStream(), StandardCharsets.UTF_8); + return content; +// if(contextPathInterceptor.contains(content)) { +// //System.out.println("~~~~~~~~~~ /public/index.html replaced!"); +// return contextPathInterceptor.replace(content); +// } +// else { +// return content; +// } + } + else { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", consoleConfig.getName(), consoleConfig.getVersion()); + } + } catch ( IOException e) { + LoggerFactory.getLogger(SysIndexController.class).error("err", e); + throw new BaseException(e.getMessage()); + } + } + + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysLoginController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysLoginController.java new file mode 100644 index 0000000000000000000000000000000000000000..9689956799a9462823f890588044850d4a59414e --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysLoginController.java @@ -0,0 +1,119 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import java.util.Set; + +import com.tongtech.common.utils.AESUtils; +import com.tongtech.common.utils.MessageUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysMenu; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.domain.model.LoginBody; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.framework.web.service.SysLoginService; +import com.tongtech.framework.web.service.SysPermissionService; +import com.tongtech.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author XiaoZhangTongZhi + */ +@RestController +public class SysLoginController +{ + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/web-api/login") + public AjaxResult login(@RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + + String password = AESUtils.decryptAES(loginBody.getPassword()); + + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), password, loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 返回密码是否过期 + * @return + */ + @GetMapping("/web-api/passwordSuggestedChanges") + public AjaxResult passwordSuggestedChanges() { + if (loginService.isUserExpired(SecurityUtils.getUserId())) { + return AjaxResult.success(MessageUtils.message("user.password.expired"), true); + } + else { + return AjaxResult.success(false); + } + } + + /** + * 返回用户密码修改建议 + * @return + * 密码过期: msg="密码已过期,请设置新的密码!",data="expired"; + * 首次登录: msg="首次登录,为保证您的账号安全,请更改密码!",data="initial" + * 无需修改: msg="none", data="none" + */ + @GetMapping("/web-api/passwordSuggest") + public AjaxResult passwordSuggest() { + return loginService.getPasswordSuggestion(SecurityUtils.getUserId()); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("/web-api/getInfo") + public AjaxResult getInfo() + { + SysUser user = SecurityUtils.getLoginUser().getUser(); + user.setPassword(""); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("/web-api/getRouters") + public AjaxResult getRouters() + { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return AjaxResult.success(menuService.buildMenus(menus)); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysMenuController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysMenuController.java new file mode 100644 index 0000000000000000000000000000000000000000..c7b87a8935ccb8f792432ba87b209fbd985edf36 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysMenuController.java @@ -0,0 +1,144 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysMenu; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + //TODO SYS_MENU + List menus = menuService.selectMenuList(menu, getUserId()); + return AjaxResult.success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + //TODO SYS_MENU + List menus = menuService.selectMenuList(menu, getUserId()); + return AjaxResult.success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return AjaxResult.error("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return AjaxResult.error("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysNoticeController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000000000000000000000000000000000000..ac01665eef238e72d605322ca0fb42b0706ef52a --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysNoticeController.java @@ -0,0 +1,91 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.system.domain.SysNotice; +import com.tongtech.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return AjaxResult.success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysProfileController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysProfileController.java new file mode 100644 index 0000000000000000000000000000000000000000..16e7a12c60ec9906b359826d0ad7238b488e26e7 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysProfileController.java @@ -0,0 +1,158 @@ +package com.tongtech.web.controller.system; + +import com.tongtech.common.config.AppHomeConfig; +import com.tongtech.common.utils.AESUtils; +import com.tongtech.framework.web.service.SysPasswordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.file.FileUploadUtils; +import com.tongtech.common.utils.file.MimeTypeUtils; +import com.tongtech.framework.web.service.TokenService; +import com.tongtech.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPasswordService passwordService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + user.setPassword("");//不返回密码信息 + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser sysUser = loginUser.getUser(); + user.setUserName(sysUser.getUserName()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUserId(sysUser.getUserId()); + user.setPassword(null); + user.setAvatar(null); + if (userService.updateUserProfile(user) > 0) + { + // 更新缓存用户信息 + sysUser.setNickName(user.getNickName()); + sysUser.setPhonenumber(user.getPhonenumber()); + sysUser.setEmail(user.getEmail()); + sysUser.setSex(user.getSex()); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String deNewPassword = AESUtils.decryptAES(newPassword); + String deOldPassword = AESUtils.decryptAES(oldPassword); + + if (!SecurityUtils.matchesPassword(deOldPassword, loginUser.getPassword())) + { + return AjaxResult.error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(deNewPassword, loginUser.getPassword())) + { + return AjaxResult.error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(deNewPassword)) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(deNewPassword)); + tokenService.setLoginUser(loginUser); + + // 已当前时间为准,更新到下一个密码过期时间 + SysUser resUser = passwordService.renewPasswordExpiredTime(loginUser.getUserId()); + + // 刷新缓存中的当前登录信息 + loginUser.getUser().setPasswordExpired(resUser.getPasswordExpired()); + tokenService.refreshToken(loginUser); + + return AjaxResult.success(); + } + return AjaxResult.error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(AppHomeConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return AjaxResult.error("上传图片异常,请联系管理员"); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysRegisterController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysRegisterController.java new file mode 100644 index 0000000000000000000000000000000000000000..8981f6bfbecacb2eeea5d18e85f0ebd17773dc49 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.tongtech.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.model.RegisterBody; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.framework.web.service.SysRegisterService; +import com.tongtech.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author XiaoZhangTongZhi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/web-api/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysRoleController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysRoleController.java new file mode 100644 index 0000000000000000000000000000000000000000..e4ac5c4af23c47e3bd8896c760bbf2d86e5c2db0 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysRoleController.java @@ -0,0 +1,234 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysRole; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.framework.web.service.SysPermissionService; +import com.tongtech.framework.web.service.TokenService; +import com.tongtech.system.domain.SysUserRole; +import com.tongtech.system.service.ISysRoleService; +import com.tongtech.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return AjaxResult.success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) + { + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getUsername()); + + if (roleService.updateRole(role) > 0) + { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) + { + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + tokenService.setLoginUser(loginUser); + } + return AjaxResult.success(); + } + return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) + { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return AjaxResult.success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + +} diff --git a/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysUserController.java b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysUserController.java new file mode 100644 index 0000000000000000000000000000000000000000..d6aa2ff983e9d7e483b74c9c76b9dba80ea4ce91 --- /dev/null +++ b/rds-console/console-admin/src/main/java/com/tongtech/web/controller/system/SysUserController.java @@ -0,0 +1,254 @@ +package com.tongtech.web.controller.system; + +import java.util.List; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.framework.web.service.SysPasswordService; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.controller.BaseController; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.entity.SysRole; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.poi.ExcelUtil; +import com.tongtech.system.service.ISysRoleService; +import com.tongtech.system.service.ISysUserService; +import com.tongtech.common.utils.AESUtils; + +/** + * 用户信息 + * + * @author XiaoZhangTongZhi + */ +@RestController +@RequestMapping("/web-api/system/user") +public class SysUserController extends BaseController { + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private SysPasswordService passwordService; + + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysUser user) { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return AjaxResult.success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = {"/", "/{userId}"}) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) { + userService.checkUserDataScope(userId); + AjaxResult ajax = AjaxResult.success(); + List roles = roleService.selectRoleAll(); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + if (StringUtils.isNotNull(userId)) { + SysUser sysUser = userService.selectUserById(userId); + ajax.put(AjaxResult.DATA_TAG, sysUser); + ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); + } + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) { + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(getUsername()); + + //对加密的username password进行解密 + String password = AESUtils.decryptAES(user.getPassword()); + + + user.setPassword(SecurityUtils.encryptPassword(password)); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(getUsername()); + + //如果密码不为空,进行传输解密, + if(StringUtils.isNotEmpty(user.getPassword())) { + String password = AESUtils.decryptAES(user.getPassword());//进行传输解密 + user.setPassword(SecurityUtils.encryptPassword(password));//保存是进行不可逆加密 + } + + return toAjax(userService.updateUserWithRole(user)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + //userService.checkUserDataScope(user.getUserId()); + //对加密的username password进行解密 + String password = AESUtils.decryptAES(user.getPassword()); + + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setUpdateBy(getUsername()); + + int i = userService.resetPwd(user); + + if (i > 0) { + // 已当前时间为准,更新到下一个密码过期时间 + passwordService.renewPasswordExpiredTime(user.getUserId()); + + } + + return toAjax(i); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) { + if (ArrayUtils.contains(userIds, getUserId())) { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理-登录解锁", businessType = BusinessType.CLEAN) + @GetMapping("/unlock/{userId}") + public AjaxResult unlock(@PathVariable("userId") Long userId) + { + passwordService.clearLoginLocked(userId); + return success(); + } + +} diff --git a/rds-console/console-admin/src/main/resources/META-INF/spring-devtools.properties b/rds-console/console-admin/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000000000000000000000000000000000000..2b23f85a3d4877a1d3e7aee610ce1c9cffdd159b --- /dev/null +++ b/rds-console/console-admin/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/rds-console/console-admin/src/main/resources/application-druid.yml b/rds-console/console-admin/src/main/resources/application-druid.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b46d80c0a677000e7909d9bb83b01b9c378cbd8 --- /dev/null +++ b/rds-console/console-admin/src/main/resources/application-druid.yml @@ -0,0 +1,58 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: org.h2.Driver + druid: + # 主库数据源 + master: + url: jdbc:h2:file:./data/db/consoledb;MODE=MySQL + username: root + password: 123456 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: false + statViewServlet: + enabled: false + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: + login-password: + filter: + stat: + enabled: false + # 慢SQL记录 + log-slow-sql: false + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true diff --git a/rds-console/console-admin/src/main/resources/application.yml b/rds-console/console-admin/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..141d005e507b053055e493c76a3416e3cf763779 --- /dev/null +++ b/rds-console/console-admin/src/main/resources/application.yml @@ -0,0 +1,98 @@ +# 项目相关配置 +console: + # 名称 + name: TongRDS-CN管理控制台 + # 版本 + version: 2.2.C.1 + # 版权年份 + copyrightYear: 2023 + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数组计算 char 字符验证 + captchaType: rds-math + #SSH 连接时的超时时间, 单位毫秒 + sshConnectTimeout: 10000 + #SSH 命令执行通道超时时间, 单位毫秒 + sshChannelTimeout: 20000 + # 部署环境,host 主机,k8s 容器云', + deployEnv: k8s + # 集成嵌入 + embedding: false + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8083 + port: 8083 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.tongtech: info + org.springframework: warn + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: druid + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 30 + +# MyBatis配置 +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.tongtech.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* diff --git a/rds-console/console-admin/src/main/resources/banner.txt b/rds-console/console-admin/src/main/resources/banner.txt new file mode 100644 index 0000000000000000000000000000000000000000..85d7ff6739c4bbea4556c3f497c0e4ef8eb080e0 --- /dev/null +++ b/rds-console/console-admin/src/main/resources/banner.txt @@ -0,0 +1,6 @@ + _____ _____ _____ _____ _ +| __ \| __ \ / ____| / ____| | | +| |__) | | | | (___ ______ | | ___ _ __ ___ ___ | | ___ +| _ /| | | |\___ \ |______| | | / _ \| '_ \/ __|/ _ \| |/ _ \ +| | \ \| |__| |____) | | |___| (_) | | | \__ \ (_) | | __/ +|_| \_\_____/|_____/ \_____\___/|_| |_|___/\___/|_|\___| diff --git a/rds-console/console-admin/src/main/resources/i18n/messages.properties b/rds-console/console-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000000000000000000000000000000000000..68ebc2fcdc54f365f664c530c728886807454f95 --- /dev/null +++ b/rds-console/console-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.login.retries.exceed=尝试登录次数达到上限{0}次,账户已被锁定!请联系管理员解锁。 +user.password.initial=首次登录,为保证您的账号安全,请更改密码! +user.password.expired=密码已过期,请设置新的密码! +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/rds-console/console-admin/src/main/resources/logback.xml b/rds-console/console-admin/src/main/resources/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..c3f2ecc315c548377c292c2508067e126d9a19c1 --- /dev/null +++ b/rds-console/console-admin/src/main/resources/logback.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/system.log + + + + ${log.path}/system.%d{yyyy-MM-dd}.log + + 30 + + + ${log.pattern} + + + + + + ${log.path}/user-access.log + + + ${log.path}/user-access.%d{yyyy-MM-dd}.log + + 30 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + diff --git a/rds-console/console-admin/src/main/resources/mybatis/mybatis-config.xml b/rds-console/console-admin/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac47c038e041f2435bdd4975c0ce7225c6b21b28 --- /dev/null +++ b/rds-console/console-admin/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/rds-console/console-client/README.txt b/rds-console/console-client/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..e4ed2e2278e217f451522bc025db6f0a1a0f7de7 --- /dev/null +++ b/rds-console/console-client/README.txt @@ -0,0 +1,7 @@ +Center的rest接口 + +查询服务 : http://localhost:8806/service +查询license和使用情况: http://localhost:8806/license +查询独立哨兵节点(没测过): http://localhost:8806/sentinel +这个是查多个center的信息的: http://localhost:8806/center + diff --git a/rds-console/console-client/pom.xml b/rds-console/console-client/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..64633faa467609deaab63f46b27cdef0d7f21159 --- /dev/null +++ b/rds-console/console-client/pom.xml @@ -0,0 +1,85 @@ + + + console + com.tongtech + 2.2.C.1 + ../pom.xml + + 4.0.0 + jar + + console-client + ${console.version} + + Probe client Library + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + slow + + + + + + + + + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpcore + 4.4.15 + + + org.apache.httpcomponents + httpmime + 4.5.13 + + + commons-io + commons-io + 2.11.0 + + + com.alibaba.fastjson2 + fastjson2 + + + + + org.junit.jupiter + junit-jupiter + test + + + + org.junit.platform + junit-platform-suite + test + + + + + diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/HttpUtil.java b/rds-console/console-client/src/main/java/com/tongtech/probe/HttpUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..e4da882a932984efa0ba6a1e79dc99cbf092cae1 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/HttpUtil.java @@ -0,0 +1,289 @@ +package com.tongtech.probe; + + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; + +import java.io.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + + +public class HttpUtil { + + private final static int STATUS_ERROR = 400; + + //实例化一个HttpClient + private final static HttpClient httpClient = HttpClientBuilder.create().build(); + + private final static RequestConfig requestConfig; + + static { + RequestConfig.Builder builder = RequestConfig.custom(); + builder.setConnectionRequestTimeout(90000); + builder.setConnectTimeout(5000); // + builder.setMaxRedirects(2); + requestConfig = builder.build(); + } + + public static String postMessage(String url, String entityData, Header... headers) throws IOException { + return postMessageResult(url, entityData, headers).getEntity(); + } + + public static ResponseResult postMessageResult(String url, String entityData, Header... headers) throws IOException { + HttpPost post = new HttpPost(url); + + if(headers != null) { + for(Header h : headers) { + post.addHeader(h); + } + } + + ResponseResult result = null; + try { + post.setConfig(requestConfig); + Header header = new BasicHeader("Accept-Encoding", null); + post.setHeader(header); + + post.setEntity(new StringEntity(entityData, ContentType.APPLICATION_JSON)); + + HttpResponse response = httpClient.execute(post); + if (response.getStatusLine().getStatusCode() < STATUS_ERROR) {//请求成功 + //取得请求内容 + HttpEntity entity = response.getEntity(); + + result = new ResponseResult(response.getStatusLine().getStatusCode(), EntityUtils.toString(entity)); + //目前仅保存request中传入同名的Header值 + for(Header h : headers) { + String hName = h.getName(); + Header[] resHeaders = response.getHeaders(hName); + if(resHeaders != null) { + String[] values = new String[resHeaders.length]; + for(int i=0 ; i 0) { + url = url.substring(0, para); + } + + int start = url.lastIndexOf('/'); + if (start < 0) { + return null; + } else if (start + 1 == url.length()) { + return ""; + } + return url.substring(start + 1); + } + + /** + * HttpResponse中的信息对象 + */ + public static class ResponseResult { + private int statusCode; + private String entity; + private Map headers; + private String[] values; + + public ResponseResult() { + this.headers = new HashMap(); + } + + public ResponseResult(int statusCode, String entity) { + this.statusCode = statusCode; + this.headers = new HashMap(); + this.entity = entity; + } + + public void addHeader(String name, String[] value) { + headers.put(name, value); + } + + public String[] getHeaderValues(String name) { + return headers.get(name); + } + + public String getHeaderValue(String name) { + String[] values = headers.get(name); + if(values != null && values.length > 0) { + return values[0]; + } + else { + return null; + } + } + + public int getStatusCode() { + return statusCode; + } + + public String getEntity() { + return entity; + } + + + @Override + public String toString() { + return "ResponseResult{" + + "statusCode=" + statusCode + + ", entity='" + entity + '\'' + + ", headers=" + toStringHeaders() + + '}'; + } + + private String toStringHeaders() { + StringBuilder buf = new StringBuilder(); + buf.append('{'); + for(String name : headers.keySet()) { + String[] values = headers.get(name); + buf.append(name).append('=').append(Arrays.toString(values)).append(','); + } + buf.append('}'); + return buf.toString(); + } + } + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/RestCenterClient.java b/rds-console/console-client/src/main/java/com/tongtech/probe/RestCenterClient.java new file mode 100644 index 0000000000000000000000000000000000000000..50fe81006cfdaf1389c6ae640b4618987c564e60 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/RestCenterClient.java @@ -0,0 +1,169 @@ +package com.tongtech.probe; + +import com.alibaba.fastjson2.JSON; +import com.tongtech.probe.config.ProbeConstants; +import com.tongtech.probe.stat.StatCenterNode; +import com.tongtech.probe.stat.StatLicense; +import com.tongtech.probe.stat.StatSentinelNode; +import com.tongtech.probe.stat.StatService; +import org.apache.commons.io.FileUtils; +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; + +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.List; + +public class RestCenterClient { + + private HttpUtil util; + + private String loginUrl; + + private String servicesUrl; + + private String licenseUrl; + + private String sentinelsUrl; + + private String centersUrl; + + private String commandUrl; + + private File fileBase; //测试时,临时保存报文路径 + + private boolean testing; + + private String authKey; + + private String nextAuthKey; + + public RestCenterClient(String baseUrl) { + this(baseUrl, null, false); + } + + public RestCenterClient(String baseUrl, String authKey, boolean testing) { + this.util = new HttpUtil(); + String bUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/"; + + this.loginUrl = bUrl + "login"; + this.servicesUrl = bUrl + "service"; + this.licenseUrl = bUrl + "license"; + this.sentinelsUrl = bUrl + "sentinel"; + this.centersUrl = bUrl + "center"; + + this.commandUrl = bUrl + "command"; + this.testing = testing; + this.authKey = authKey; + this.nextAuthKey = null; + if(testing == true) { + this.fileBase = new File("temp").getAbsoluteFile(); + } + } + +// public RestCenterResult getServices(String authKey) throws IOException { +// +// +// long beginTime = System.currentTimeMillis(); +// HttpUtil.ResponseResult response = HttpUtil.getMessageResult(this.servicesUrl, new BasicHeader(AUTH_KEY_NAME, authKey)); +// if(testing == true) { printInfo("services", response.getEntity());} +// List res = JSON.parseArray(response.getEntity(), StatService.class); +// for(StatService s : res) { s.reprocessNodes(); } //重新处理一下节点的附加属性 +// long endTime = System.currentTimeMillis(); +// return new RestCenterResult(response.getEntity(), res, new Date(beginTime), endTime - beginTime, response.getHeaderValue("U-Auth-Key")); +// } + + public RestCenterResult getServices() throws IOException { + long beginTime = System.currentTimeMillis(); + String json = getMessage("services", this.servicesUrl); + List res = JSON.parseArray(json, StatService.class); + for(StatService s : res) { s.reprocessNodes(); } //重新处理一下节点的附加属性 + long endTime = System.currentTimeMillis(); + return new RestCenterResult(json, res, new Date(beginTime), endTime - beginTime); + } + + + public RestCenterResult getLicenseUsing() throws IOException { + long beginTime = System.currentTimeMillis(); + String json = getMessage("licenseUsing", this.licenseUrl); + StatLicense res = JSON.parseObject(json, StatLicense.class); + long endTime = System.currentTimeMillis(); + return new RestCenterResult(json, res, new Date(beginTime), endTime - beginTime); + } + + public RestCenterResult getSentinels() throws IOException { + long beginTime = System.currentTimeMillis(); + String json = getMessage("sentinels", this.sentinelsUrl); + List res = JSON.parseArray(json, StatSentinelNode.class); + long endTime = System.currentTimeMillis(); + return new RestCenterResult(json, res, new Date(beginTime), endTime - beginTime); + } + + + public RestCenterResult getCenters() throws IOException { + long beginTime = System.currentTimeMillis(); + String json = getMessage("centers", this.centersUrl); + List res = JSON.parseArray(json, StatCenterNode.class); + long endTime = System.currentTimeMillis(); + return new RestCenterResult(json, res, new Date(beginTime), endTime - beginTime); + } + + + private synchronized String getMessage(String printType, String url) throws IOException { + String json; + if(this.authKey == null) { + //非认证方式来获取信息 + json = HttpUtil.getMessage(url); + } + else { + //以认证方式来获取信息 + try { + Header authHeader = new BasicHeader(ProbeConstants.AUTH_KEY_NAME, getNextAuthKey()); + HttpUtil.ResponseResult res = HttpUtil.getMessageResult(url, authHeader); + this.nextAuthKey = res.getHeaderValue(ProbeConstants.AUTH_KEY_NAME); + json = res.getEntity(); + } + catch (IOException ioe) {//如果发生异常,可能是nextAuthKey已经失效,尝试重新登录并获取 + this.nextAuthKey = null; + Header authHeader = new BasicHeader(ProbeConstants.AUTH_KEY_NAME, getNextAuthKey()); + HttpUtil.ResponseResult res = HttpUtil.getMessageResult(url, authHeader); + this.nextAuthKey = res.getHeaderValue(ProbeConstants.AUTH_KEY_NAME); + json = res.getEntity(); + } + } + + if(testing == true) { printInfo(printType, json); } + return json; + } + + private String getNextAuthKey() throws IOException { + //如果 this.nextAuthKey 是空,进行重新登录,并获取this.nextAuthKey + if(this.nextAuthKey == null) { + Header authHeader = new BasicHeader(ProbeConstants.AUTH_KEY_NAME, this.authKey); + HttpUtil.ResponseResult res = HttpUtil.getMessageResult(this.loginUrl, authHeader); + this.nextAuthKey = res.getHeaderValue(ProbeConstants.AUTH_KEY_NAME); + } + + return this.nextAuthKey; + } + + /** + * 用于测试时把报文信息打印出来并保存到控制台。 + * @param name + * @param data + */ + private void printInfo(String name, String data) { + System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + name + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + System.out.println(data); + System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~ END \n"); + + File file = new File(fileBase, name + ".json"); + try { + FileUtils.writeStringToFile(file, data,"UTF-8"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/RestCenterResult.java b/rds-console/console-client/src/main/java/com/tongtech/probe/RestCenterResult.java new file mode 100644 index 0000000000000000000000000000000000000000..a2083b813fd732c0e303054c17d8d75d93f19233 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/RestCenterResult.java @@ -0,0 +1,66 @@ +package com.tongtech.probe; + +import java.util.Date; +import java.util.List; + +public class RestCenterResult { + + private List listData; + + private T data; + + private String src; + + private Long duration; + + private Date createTime; + + private String authKey; + + public RestCenterResult(String src, List listData, Date createTime, Long durationy) { + this(src, listData, createTime, durationy, null); + } + public RestCenterResult(String src, List listData, Date createTime, Long duration, String authKey) { + this.src = src; + this.listData = listData; + this.createTime = createTime; + this.duration = duration; + this.authKey = authKey; + } + + public RestCenterResult(String src, T data, Date createTime, Long duration) { + this(src, data, createTime, duration, null); + } + public RestCenterResult(String src, T data, Date createTime, Long duration, String authKey) { + this.src = src; + this.data = data; + this.createTime = createTime; + this.duration = duration; + this.authKey = authKey; + } + + + public List getListData() { + return listData; + } + + public T getData() { + return data; + } + + public String getSrc() { + return src; + } + + public Long getDuration() { + return duration; + } + + public Date getCreateTime() { + return createTime; + } + + public String getAuthKey() { + return authKey; + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/config/ProbeConstants.java b/rds-console/console-client/src/main/java/com/tongtech/probe/config/ProbeConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..7f9a9ffb6634e441a47804cf988a0afa3acbfa99 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/config/ProbeConstants.java @@ -0,0 +1,16 @@ +package com.tongtech.probe.config; + +public class ProbeConstants { + + public static final String AUTH_KEY_NAME = "U-Auth-Key"; + + public static final String OUT_OK = "[OK]"; + + public static final String ERR_ERROR = "[ERROR]"; + + public static final String ERR_PORT_OCCUPIED = "service_port occupied!"; + + public static final String ERR_CHECKING_EXCEPTION = "checking exception!"; + + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/CmdsResult.java b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/CmdsResult.java new file mode 100644 index 0000000000000000000000000000000000000000..d5a56644c09cce606c3e69915b57572af00ded16 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/CmdsResult.java @@ -0,0 +1,154 @@ +package com.tongtech.probe.pojo; + +import java.io.Serializable; +import java.util.Hashtable; +import java.util.Map; + +/** + * 命令执行结果 + */ +public class CmdsResult implements Serializable +{ + private static final long serialVersionUID = 1L; + + private String host; + + private Map results; + + public CmdsResult() { + this.results = new Hashtable(); + } + + public CmdsResult(String host) { + this.results = new Hashtable(); + this.host = host; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public synchronized Map getResults() { + return results; + } + + public void setResults(Map results) { + this.results = results; + } + + public synchronized void addResult(String name, Result result) { + this.results.put(name, result); + } + + public synchronized int dataSize() { + int size = 0; + if (results != null) { + return results.size(); + } + else { + return 0; + } + } + + @Override + public String toString() { + return "CmdsResult{" + + "host='" + host + '\'' + + ", results=" + results + + '}'; + } + + public static class Result { + private String type; + private String name; + private String cmd; + private boolean success; + private String msg; + + private long startTime; //开始时间,毫秒 + + private long duration; //持续时间,毫秒 + + public Result() { } + + public Result(String type, String name, String cmd) { + this.type = type; + this.name = name; + this.cmd = cmd; + this.startTime = System.currentTimeMillis(); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCmd() { + return cmd; + } + + public void setCmd(String cmd) { + this.cmd = cmd; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + @Override + public String toString() { + return "Result{" + + "type='" + type + '\'' + + ", name='" + name + '\'' + + ", cmd='" + cmd + '\'' + + ", success=" + success + + ", msg='" + msg + '\'' + + ", startTime='" + startTime + '\'' + + ", duration='" + duration + '\'' + + '}'; + } + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/Command.java b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/Command.java new file mode 100644 index 0000000000000000000000000000000000000000..82effd263d27c79289d06919f3bef2ee045cfbbe --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/Command.java @@ -0,0 +1,79 @@ +package com.tongtech.probe.pojo; + +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; + +/** + * 命令信息 + */ +public class Command implements Serializable +{ + private static final long serialVersionUID = 1L; + + private String cmd; + private String type; + + //节点名称 + private String name; + private String msg; + + /** + * 文件名,可以是部署时指定的文件名 + */ + private String file; + private Map config; + + public String getCmd() { + return cmd; + } + + public void setCmd(String cmd) { + this.cmd = cmd; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public Map getConfig() { + return config; + } + + public void setConfig(Map config) { + this.config = config; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Command command = (Command) o; + return cmd.equals(command.cmd); + } + + @Override + public int hashCode() { + return Objects.hash(cmd); + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/NodeStatisticses.java b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/NodeStatisticses.java new file mode 100644 index 0000000000000000000000000000000000000000..c25ea91f589e97a2951af876e936722d318a4394 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/NodeStatisticses.java @@ -0,0 +1,78 @@ +package com.tongtech.probe.pojo; + +import java.io.Serializable; +import java.util.List; + +/** + * 安装点(probe)整体状态监控信息。包括每一个安装节点的状态监控信息(Statisticses) + */ +public class NodeStatisticses implements Serializable +{ + private static final long serialVersionUID = 1L; + + private String host; + + private String os; + //private final String os = OSUtil.getOsName(); + + private String pid; //进程id + + private List nodes; + + public NodeStatisticses() { + + } + + public NodeStatisticses(String host, String os, String pid) { + this.host = host; + this.os = os; + this.pid = pid; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getOs() { + return os; + } + + public void setOs(String os) { + this.os = os; + } + + public String getPid() { + return pid; + } + + public void setPid(String pid) { + this.pid = pid; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + @Override + public String toString() { +// StringBuffer buf = new StringBuffer("["); +// nodes.forEach(stat -> { +// buf.append(stat.toString()).append(','); +// }); +// buf.append("]"); + return "NodeStatisticses{" + + "host='" + host + '\'' + + ", os='" + os + '\'' + + ", pid='" + pid + '\'' + + ", nodes=" + nodes + + '}'; + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/ReportResponse.java b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/ReportResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..e09324791f9b255225dc4bc6b1b570523daff7b7 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/ReportResponse.java @@ -0,0 +1,44 @@ +package com.tongtech.probe.pojo; + +import com.alibaba.fastjson2.JSON; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +public class ReportResponse implements Serializable +{ + private static final long serialVersionUID = 1L; + private int code; + private String msg; + private Map> data; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Map> getData() { + return data; + } + + public void setData(Map> data) { + this.data = data; + } + + @Override + public String toString(){ + return JSON.toJSONString(this); + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/Statistics.java b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/Statistics.java new file mode 100644 index 0000000000000000000000000000000000000000..4390e70309d724fc50edf5dd4affc25e27ec9a3a --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/Statistics.java @@ -0,0 +1,94 @@ +package com.tongtech.probe.pojo; + + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +/** + * 节点监控信息(node) + */ +public abstract class Statistics implements Serializable +{ + private static final long serialVersionUID = 1L; + + //protected static final Log LOGGER = StaticContext.SERVER_LOGGER; + + private String type; + private String name; + private boolean alive; + + public final synchronized String getType() { + return this.type; + } + + public final synchronized void setType(String type) { + this.type = type; + } + + public final synchronized String getName() { + return this.name; + } + + public final synchronized void setName(String name) { + this.name = name; + } + + public final synchronized boolean isAlive() { + return this.alive; + } + + public final synchronized void setAlive(boolean alive) { + this.alive = alive; + if (!alive) { + cleanFields(); + } + } + + private void cleanFields() { + try { + Class clazz = this.getClass(); + Field[] fields = clazz.getDeclaredFields(); + if (fields != null) { + for (Field field : fields) { + try { + String name = field.getName(); + if ("name".equals(name) || "type".equals(name) || "alive".equals(name)) { + continue; + } + field.setAccessible(true); + Type type = field.getType(); + if (Long.class.equals(type) + || Integer.class.equals(type) + || String.class.equals(type) + || Float.class.equals(type) + || Double.class.equals(type) + || Boolean.class.equals(type) + || Map.class.equals(type) + || List.class.equals(type)) { + field.set(this, null); + //LOGGER.debugLog("Statistics::cleanFields() set {} to null", field); + } else { + //LOGGER.debugLog("Statistics::cleanFields() find filed '{}'", field); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } // for (Field field : fields) { + } // if (fields != null) { + } catch (Throwable t2) { + t2.printStackTrace(); + } + } + + @Override + public String toString() { + return "Statistics{" + + "type='" + type + '\'' + + ", name='" + name + '\'' + + ", alive=" + alive + + '}'; + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/StatisticsPojo.java b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/StatisticsPojo.java new file mode 100644 index 0000000000000000000000000000000000000000..716534820648e29497309a866daf9ee26e7c0128 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/pojo/StatisticsPojo.java @@ -0,0 +1,300 @@ +package com.tongtech.probe.pojo; + +import com.alibaba.fastjson2.JSON; + +import java.util.List; +import java.util.Map; + +public class StatisticsPojo extends Statistics { + // 实例标识名 + private String id; + + // 版本号 + private String version; + + // 节点是否活着 + private boolean alive; + + // jvm已经分配的内存 + private Long jvmAllocated; + + // jvm已经分配但空闲的内存 + private Long jvmFree; + + // jvm可分配的最大内存 + private Long jvmMax; + + // jvm占用的总内存 + private Long memoryTotal; + + // 储存数据占用的内存 + private Long usedMemory; + + // cfg配置文件中配置的内存大小 + private Long memoryStatic; + + // 动态分配的超长数据的总内存大小 + private Long memoryDynamic; + + // 动态分配的超长数据的总个数 + private Long sizeDynamic; + + // 系统可用内存 + private Long totalPhysicalMemory; + + // 内存使用率 + private Double memoryUsedRatio; + + // 当前接入的client连接数量 + private Long currentConnections; + + // 可接入的client的最大连接数量 + private Long maxConnections; + + // 当前接入的client数量和最大接入数量的比值 + private Double connectedRatio; + + // 当前接入的client数量于启动以来接入的总数量(包括已经关闭的连接)的比值 + private Double ConnectionRatio; + + // 自启动以来创建过的客户端连接总数 + private Long totalConnections; + + // 客户端连接使用率 + private Double connectionsRatio; + + // 当前每秒钟处理请求量 + private Long processSecond; + + // 最近一分钟处理请求量 + private Long processMinute; + + // 集群状态,list中的每1项(String数组)代表一个cluster的分片 + // String数组为定长,各项内容分别为:分片主节点名称、主节点地址、主节点状态、分片状态(success 或 fail)、分配槽位号列表 + private List clusters; + + // 各表中数据量 + private Map keys; + + // 授权的总内存使用量,只有Center节点有此配置 + private Long totalLicense; + + // 已经占用的license授权内存量,只有Center节点有此 + private Long usedLicense; + + // license过期时间 + private Long licenseExpired; + + public synchronized String getId() { + return id; + } + + public synchronized void setId(String id) { + this.id = id; + } + + public synchronized String getVersion() { + return version; + } + + public synchronized void setVersion(String version) { + this.version = version; + } + + public synchronized Long getMemoryTotal() { + return memoryTotal; + } + + public synchronized void setMemoryTotal(Long memoryTotal) { + this.memoryTotal = memoryTotal; + } + + public synchronized Long getUsedMemory() { + return usedMemory; + } + + public synchronized void setUsedMemory(Long usedMemory) { + this.usedMemory = usedMemory; + } + + public synchronized Long getMemoryStatic() { + return memoryStatic; + } + + public synchronized void setMemoryStatic(Long memoryStatic) { + this.memoryStatic = memoryStatic; + } + + public synchronized Long getMemoryDynamic() { + return memoryDynamic; + } + + public synchronized void setMemoryDynamic(Long memoryDynamic) { + this.memoryDynamic = memoryDynamic; + } + + public synchronized Long getSizeDynamic() { + return sizeDynamic; + } + + public synchronized void setSizeDynamic(Long sizeDynamic) { + this.sizeDynamic = sizeDynamic; + } + + public synchronized Long getTotalPhysicalMemory() { + return totalPhysicalMemory; + } + + public synchronized void setTotalPhysicalMemory(Long totalPhysicalMemory) { + this.totalPhysicalMemory = totalPhysicalMemory; + } + + public synchronized Double getMemoryUsedRatio() { + return memoryUsedRatio; + } + + public synchronized void setMemoryUsedRatio(Double memoryUsedRatio) { + this.memoryUsedRatio = memoryUsedRatio; + } + + public synchronized Long getJvmAllocated() { + return jvmAllocated; + } + + public synchronized void setJvmAllocated(Long jvmAllocated) { + this.jvmAllocated = jvmAllocated; + } + + public synchronized Long getJvmFree() { + return jvmFree; + } + + public synchronized void setJvmFree(Long jvmFree) { + this.jvmFree = jvmFree; + } + + public synchronized Long getJvmMax() { + return jvmMax; + } + + public synchronized void setJvmMax(Long jvmMax) { + this.jvmMax = jvmMax; + } + + public synchronized Long getCurrentConnections() { + return currentConnections; + } + + public synchronized void setCurrentConnections(Long currentConnections) { + this.currentConnections = currentConnections; + } + + public synchronized Long getMaxConnections() { + return maxConnections; + } + + public synchronized void setMaxConnections(Long maxConnections) { + this.maxConnections = maxConnections; + } + + public synchronized Double getConnectedRatio() { + return connectedRatio; + } + + public synchronized void setConnectedRatio(Double connectedRatio) { + this.connectedRatio = connectedRatio; + } + + public Double getConnectionRatio() { + return ConnectionRatio; + } + + public void setConnectionRatio(Double connectionRatio) { + ConnectionRatio = connectionRatio; + } + + public synchronized Long getTotalConnections() { + return totalConnections; + } + + public synchronized void setTotalConnections(Long totalConnections) { + this.totalConnections = totalConnections; + } + + public synchronized Double getConnectionsRatio() { + return connectionsRatio; + } + + public synchronized void setConnectionsRatio(Double connectionsRatio) { + this.connectionsRatio = connectionsRatio; + } + + public synchronized Long getProcessSecond() { + return processSecond; + } + + public synchronized void setProcessSecond(Long processSecond) { + this.processSecond = processSecond; + } + + public synchronized Long getProcessMinute() { + return processMinute; + } + + public synchronized void setProcessMinute(Long processMinute) { + this.processMinute = processMinute; + } + + public synchronized List getClusters() { + return clusters; + } + + public synchronized void setClusters(List clusters) { + this.clusters = clusters; + } + + public synchronized Map getKeys() { + return keys; + } + + public synchronized void setKeys(Map keys) { + this.keys = keys; + } + + public synchronized Long getTotalLicense() { + return totalLicense; + } + + public synchronized void setTotalLicense(Long totalLicense) { + this.totalLicense = totalLicense; + } + + public synchronized Long getUsedLicense() { + return usedLicense; + } + + public synchronized void setUsedLicense(Long usedLicense) { + this.usedLicense = usedLicense; + } + + public Long getLicenseExpired() { + return licenseExpired; + } + + public void setLicenseExpired(Long licenseExpired) { + this.licenseExpired = licenseExpired; + } + + @Override + public String toString() { +// if (isAlive()) { + return JSON.toJSONString(this); +// } else { +// StringBuilder buf = new StringBuilder(); +// buf.append("{\"nodeName\":\"").append(getName()) +// .append("\",\"nodeType\":\"").append(getType()) +// .append("\",\"alive\":").append("false}"); +// return buf.toString(); +// } + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatBaseNode.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatBaseNode.java new file mode 100644 index 0000000000000000000000000000000000000000..a8874f6958fec4ffd8a060bed557d96798aaa102 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatBaseNode.java @@ -0,0 +1,80 @@ +package com.tongtech.probe.stat; + +import java.io.Serializable; + +public class StatBaseNode implements Serializable { + private static final long serialVersionUID = 1L; + + /* 节点实例名称,全局唯一 */ + protected String instance; + + /* 数据是否过期, 大于3秒钟没有收到心跳就会变成true */ + protected Boolean expired; + /* 外部连接的主机地址 */ + protected String remote; + /* 服务端口 */ + protected Integer port; + /* 性能统计 */ + protected StatRuntime runtime; + + protected String endpoint = null; + + public String getInstance() { + return instance; + } + + public String getEndpoint() { + if(endpoint == null) { + endpoint = remote + ":" + port; + } + + return endpoint; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public Boolean getExpired() { + return expired; + } + + public void setExpired(Boolean expired) { + this.expired = expired; + } + + public String getRemote() { + return remote; + } + + public void setRemote(String remote) { + this.remote = remote; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public StatRuntime getRuntime() { + return runtime; + } + + public void setRuntime(StatRuntime runtime) { + this.runtime = runtime; + } + + @Override + public String toString() { + return "StatSentinel{" + + "expired=" + expired + + ", remote='" + remote + '\'' + + ", port=" + port + + ", runtime=" + runtime + + '}'; + } + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatCenterNode.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatCenterNode.java new file mode 100644 index 0000000000000000000000000000000000000000..cf79f8b5e510ffac70118746ca8e1b5e6a6eedb5 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatCenterNode.java @@ -0,0 +1,27 @@ +package com.tongtech.probe.stat; + +public class StatCenterNode extends StatBaseNode { + + /* 哨兵端口 */ + private Integer sentinelPort; + + public Integer getSentinelPort() { + return sentinelPort; + } + + public void setSentinelPort(Integer sentinelPort) { + this.sentinelPort = sentinelPort; + } + + @Override + public String toString() { + return "StatCenter{" + + "expired=" + expired + + ", remote='" + remote + '\'' + + ", port=" + port + + ", sentinelPort=" + sentinelPort + + ", runtime=" + runtime + + '}'; + } +} + diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatLicense.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatLicense.java new file mode 100644 index 0000000000000000000000000000000000000000..86b2b529de098e0002afde11b028b3f2bc357e2b --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatLicense.java @@ -0,0 +1,70 @@ +package com.tongtech.probe.stat; + +import java.io.Serializable; + +public class StatLicense implements Serializable { + private static final long serialVersionUID = 1L; + + /* 仿HTTP中的status地返回码, 200为正常,400以上是有问题 */ + private Integer code; + /* 正常返回"ok", 错误返回错误信息 */ + private String msg; + /* 总共可以用内存 */ + private Long total; + /* 已用内存 */ + private Long used; + /* 已用内存百分比(字符串格式,如:39%) */ + private String percent; + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Long getUsed() { + return used; + } + + public void setUsed(Long used) { + this.used = used; + } + + public String getPercent() { + return percent; + } + + public void setPercent(String percent) { + this.percent = percent; + } + + @Override + public String toString() { + return "StatLicense{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", total=" + total + + ", used=" + used + + ", percent='" + percent + '\'' + + '}'; + } + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatProxyNode.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatProxyNode.java new file mode 100644 index 0000000000000000000000000000000000000000..0373a517ffc80b54068a24edb584f138f483065c --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatProxyNode.java @@ -0,0 +1,70 @@ +package com.tongtech.probe.stat; + +public class StatProxyNode extends StatBaseNode { + + private Boolean valid; + /* 哨兵端口 */ + private Integer redisPort; + /* 安全级别, 0 Telnet none password, 1 SSL none password, 2 Telnet and password, 3 SSL and password */ + private Integer secureLevel; + /* 启动以来运行了多少秒 */ + private Long running; + + private StatThroughput throughput; + + public Boolean getValid() { + return valid; + } + + public void setValid(Boolean valid) { + this.valid = valid; + } + + public Integer getRedisPort() { + return redisPort; + } + + public void setRedisPort(Integer redisPort) { + this.redisPort = redisPort; + } + + public Long getRunning() { + return running; + } + + public void setRunning(Long running) { + this.running = running; + } + + public Integer getSecureLevel() { + return secureLevel; + } + + public void setSecureLevel(Integer secureLevel) { + this.secureLevel = secureLevel; + } + + public StatThroughput getThroughput() { + return throughput; + } + + public void setThroughput(StatThroughput throughput) { + this.throughput = throughput; + } + + @Override + public String toString() { + return "StatNode{" + + "valid=" + valid + + ", expired=" + expired + + ", remote='" + remote + '\'' + + ", port=" + port + + ", redisPort=" + redisPort + + ", running=" + running + + ", throughput=" + throughput + + ", runtime=" + runtime + + '}'; + } + + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatRuntime.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatRuntime.java new file mode 100644 index 0000000000000000000000000000000000000000..824b20bffbf534a7d6e75c2e0bcba65e877837fe --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatRuntime.java @@ -0,0 +1,187 @@ +package com.tongtech.probe.stat; + +import com.alibaba.fastjson2.annotation.JSONField; + +import java.io.Serializable; + +/** + * 监控的各指标信息 + */ +public class StatRuntime implements Serializable { + private static final long serialVersionUID = 1L; + /* 启动以来运行了多少秒 */ + private Long running; + /* 当前连接数 */ + private Long currentConnections; + /* 启动以来一共连接次数 */ + private Long totalConnections; + /* 当前key总数 */ + private Long keys; + /* 当前内存使用量 */ + private Long memoryUsed; + /* 当前内存剩余量 */ + private Long memoryFree; + /* 实际占用内存总量,JVM当前使用的堆内存总量 */ + private Long memoryTotal; + /* JVM MAX 的值 */ + private Long memoryAvailable; + /* 启动以来返回结果的命令数累计 */ + private Long commandResult; + /* */ + private Long networkInputBytes; + /* */ + @JSONField(name="inputPerSecond(KB/s)") + private Double inputPerSecond; + /* */ + private Long networkOutputBytes; + /* */ + @JSONField(name="outputPerSecond(KB/s)") + private Double outputPerSecond; + /* 当前程序CPU使用率 */ + @JSONField(name="cpuProcessLoad(%)") + private Double cpuProcessLoad; + /* 当前系统CPU使用率 */ + @JSONField(name="cpuSystemLoad(%)") + private Double cpuSystemLoad; + + public Long getRunning() { + return running; + } + + public void setRunning(Long running) { + this.running = running; + } + + public Long getCurrentConnections() { + return currentConnections; + } + + public void setCurrentConnections(Long currentConnections) { + this.currentConnections = currentConnections; + } + + public Long getTotalConnections() { + return totalConnections; + } + + public void setTotalConnections(Long totalConnections) { + this.totalConnections = totalConnections; + } + + public Long getKeys() { + return keys; + } + + public void setKeys(Long keys) { + this.keys = keys; + } + + public Long getMemoryUsed() { + return memoryUsed; + } + + public void setMemoryUsed(Long memoryUsed) { + this.memoryUsed = memoryUsed; + } + + public Long getMemoryFree() { + return memoryFree; + } + + public void setMemoryFree(Long memoryFree) { + this.memoryFree = memoryFree; + } + + public Long getMemoryTotal() { + return memoryTotal; + } + + public void setMemoryTotal(Long memoryTotal) { + this.memoryTotal = memoryTotal; + } + + public Long getMemoryAvailable() { + return memoryAvailable; + } + + public void setMemoryAvailable(Long memoryAvailable) { + this.memoryAvailable = memoryAvailable; + } + + public Long getCommandResult() { + return commandResult; + } + + public void setCommandResult(Long commandResult) { + this.commandResult = commandResult; + } + + public Long getNetworkInputBytes() { + return networkInputBytes; + } + + public void setNetworkInputBytes(Long networkInputBytes) { + this.networkInputBytes = networkInputBytes; + } + + public Long getNetworkOutputBytes() { + return networkOutputBytes; + } + + public void setNetworkOutputBytes(Long networkOutputBytes) { + this.networkOutputBytes = networkOutputBytes; + } + + public Double getInputPerSecond() { + return inputPerSecond; + } + + public void setInputPerSecond(Double inputPerSecond) { + this.inputPerSecond = inputPerSecond; + } + + public Double getOutputPerSecond() { + return outputPerSecond; + } + + public void setOutputPerSecond(Double outputPerSecond) { + this.outputPerSecond = outputPerSecond; + } + + public Double getCpuProcessLoad() { + return cpuProcessLoad; + } + + public void setCpuProcessLoad(Double cpuProcessLoad) { + this.cpuProcessLoad = cpuProcessLoad; + } + + public Double getCpuSystemLoad() { + return cpuSystemLoad; + } + + public void setCpuSystemLoad(Double cpuSystemLoad) { + this.cpuSystemLoad = cpuSystemLoad; + } + + @Override + public String toString() { + return "StatRuntime{" + + "running=" + running + + ", currentConnections=" + currentConnections + + ", totalConnections=" + totalConnections + + ", keys=" + keys + + ", memoryUsed=" + memoryUsed + + ", memoryFree=" + memoryFree + + ", memoryTotal=" + memoryTotal + + ", memoryAvailable=" + memoryAvailable + + ", commandResult=" + commandResult + + ", networkInputBytes=" + networkInputBytes + + ", inputPerSecond=" + inputPerSecond + + ", networkOutputBytes=" + networkOutputBytes + + ", outputPerSecond=" + outputPerSecond + + ", cpuProcessLoad=" + cpuProcessLoad + + ", cpuSystemLoad=" + cpuSystemLoad + + '}'; + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatSentinelNode.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatSentinelNode.java new file mode 100644 index 0000000000000000000000000000000000000000..349c4f8b00fdb428303d51de7aa140e18a250816 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatSentinelNode.java @@ -0,0 +1,117 @@ +package com.tongtech.probe.stat; + +import com.tongtech.probe.util.StringUtil; + +import java.util.TreeMap; + +public class StatSentinelNode extends StatBaseNode { + + private String group; + + private boolean myself; + + /** + * 安全级别, 0 Telnet none password, 1 SSL none password, 2 Telnet and password, 3 SSL and password + * 为之后扩展预留,目前setinels接口返回JSon 中暂无此属性。 + * */ + private Integer secureLevel; + + private TreeMap services; + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public boolean isMyself() { + return myself; + } + + public void setMyself(boolean myself) { + this.myself = myself; + } + + public Integer getSecureLevel() { + return secureLevel; + } + + public void setSecureLevel(Integer secureLevel) { + this.secureLevel = secureLevel; + } + + public TreeMap getServices() { + return services; + } + + public void setServices(TreeMap services) { + this.services = services; + } + + public static class StatEndPoint { + private String endPoint; + + private Boolean alive; + + private Boolean master; + + private Integer port; + + private String host; + + public StatEndPoint() { } + + public StatEndPoint(String endPoint, Boolean alive, Boolean master) { + setEndPoint(endPoint); + this.alive = alive; + this.master = master; + } + + public Integer getPort() { + return port; + } + + public String getHost() { + return host; + } + + public String getEndPoint() { + return endPoint; + } + + public void setEndPoint(String endPoint) { + this.endPoint = endPoint; + if(StringUtil.isNotEmpty(endPoint)) { + String[] s = endPoint.split(":"); + if(s != null && s.length == 2) { + host = s[0].trim(); + try { + port = Integer.parseInt(s[1].trim()); + } + catch (NumberFormatException e) { + port = null; + } + } + } + } + + public Boolean getAlive() { + return alive; + } + + public void setAlive(Boolean alive) { + this.alive = alive; + } + + public Boolean getMaster() { + return master; + } + + public void setMaster(Boolean master) { + this.master = master; + } + } + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatService.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatService.java new file mode 100644 index 0000000000000000000000000000000000000000..90ffb5cfa3124ce727b90fb4034393196e1f2993 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatService.java @@ -0,0 +1,290 @@ +package com.tongtech.probe.stat; + +import java.io.Serializable; +import java.util.*; + +public class StatService implements Serializable { + private static final long serialVersionUID = 1L; + + private String name; + + private String type; //"DEFAULT", "SENTINEL", "CLUSTER", "SCALABLE" + + private String message; + + /* 活着的节点endpoint列表,如:[ "192.168.3.4:6223", "192.168.3.4:6224" ] */ + private String[] alive; + + private StatRuntime statistics; + + /* 服务中包含的工作节点列表 */ + private List nodes; + + /* Sentinel服务时master节点的endpoint, 如:"192.168.3.4:6223"*/ + private String master; + /* Sentinel需要同步节点的endpoint列表, ,如:[ "192.168.3.4:6223", "192.168.3.4:6224" ] */ + private String[] syncList; + /* 每个shard中节点的数量,最小是1 */ + private Integer replicas; + /* 热备节点的数量 */ + private Integer hotspares; + /* 服务状态目前只有:"RUNNING" 和 "CHANGING" */ + private String status; + /* 热备节点endpoint列表, 如:["192.168.0.90:9202", "192.168.0.90:9201"] */ + private String[] hotspare; + /* shard最多数量 */ + private Integer maxShards; + + private List shard; + /* 服务中包含的代理节点列表 */ + private List proxies; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String[] getAlive() { + return alive; + } + + public void setAlive(String[] alive) { + this.alive = alive; + } + + public StatRuntime getStatistics() { + return statistics; + } + + public void setStatistics(StatRuntime statistics) { + this.statistics = statistics; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public String getMaster() { + return master; + } + + public void setMaster(String master) { + this.master = master; + } + + public String[] getSyncList() { + return syncList; + } + + public void setSyncList(String[] syncList) { + this.syncList = syncList; + } + + public List getShard() { + return shard; + } + + public void setShard(List shard) { + this.shard = shard; + } + + public Integer getReplicas() { + return replicas; + } + + public void setReplicas(Integer replicas) { + this.replicas = replicas; + } + + public Integer getHotspares() { + return hotspares; + } + + public void setHotspares(Integer hotspares) { + this.hotspares = hotspares; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String[] getHotspare() { + return hotspare; + } + + public void setHotspare(String[] hotspare) { + this.hotspare = hotspare; + } + + public Integer getMaxShards() { + return maxShards; + } + + public void setMaxShards(Integer maxShards) { + this.maxShards = maxShards; + } + + public List getProxies() { + return proxies; + } + + public void setProxies(List proxies) { + this.proxies = proxies; + } + + /** + * 对Nodes再加工,保证node中的属性和RdsNode一致 + * 需要赋值的属性:shard, slot, hotSpares + */ + public void reprocessNodes() { + String trimType = this.type.toUpperCase().trim(); + boolean isScalable = "SCALABLE".equals(trimType); + boolean isCluster = "CLUSTER".equals(trimType); + if(isScalable || isCluster) { + Set hotSparesEndpoint = null; + if(hotspare != null) { + hotSparesEndpoint = new HashSet<>(Arrays.asList(hotspare)); + } + + for (StatWorkerNode node : this.nodes) { + if (hotSparesEndpoint != null && hotSparesEndpoint.contains(node.getEndpoint())) { + node.setHotSpares(true); + } + + if(this.shard != null) { + if(node.getShardId() != null) { + //System.out.println("~~~~~~~~~~~~~~~this.shard.size():" + this.shard.size()); + //System.out.println("~~~~~~~~~~~~~~~node.getShardId():" + node.getShardId()); + if(node.getShardId() > 0 && node.getShardId() < this.shard.size()) { + //CLUSTER 模式下需要通过 shardId 来找到对应的shard + Shard s = this.shard.get(node.getShardId()); + node.setShard(node.getShardId()); + node.setSlot(s.getSlotsStr()); + } + } + else { + int shardLen = this.shard.size(); + for (int i = 0; i < shardLen; i++) { + Shard s = this.shard.get(i); + if (s.containsEndpoint(node.getEndpoint())) { + node.setShard(i); + node.setSlot(s.getSlotsStr()); + break; + } + } + } + } + } + } + } + + @Override + public String toString() { + return "StatService{" + + "name='" + name + '\'' + + ", type='" + type + '\'' + + ", message='" + message + '\'' + + ", alive=" + Arrays.toString(alive) + + ", statistics=" + statistics + + ", nodes=" + nodes + + ", master='" + master + '\'' + + ", syncList=" + Arrays.toString(syncList) + + ", replicas=" + replicas + + ", hotspares=" + hotspares + + ", status='" + status + '\'' + + ", hotspare=" + Arrays.toString(hotspare) + + ", maxShards=" + maxShards + + ", shard=" + shard + + ", proxies=" + proxies + + '}'; + } + + public static class Shard { + /* 节点Endpoint列表,如:[ "localhost:6231", "localhost:6232" ] */ + private String[] endpoints; + /* slots 区段, 如: ["8192-16383"] */ + private String[] slots; + + private Set endpointsSet; + + public String[] getEndpoints() { + return endpoints; + } + + public boolean containsEndpoint(String endpoint) { + if(endpointsSet == null) { + endpointsSet = new HashSet<>(Arrays.asList(endpoints)); + } + return endpointsSet.contains(endpoint); + } + + public void setEndpoints(String[] endpoints) { + this.endpoints = endpoints; + } + + public String[] getSlots() { + return slots; + } + + /** + * 把多个或一个slot平成,逗号间隔的字符串。如:"1-2334,3000-5555,8192-16383", + * @return + */ + public String getSlotsStr() { + if(slots == null || slots.length == 0) { + return null; + } + else { + if(slots.length == 1) { return slots[0]; } + else { + StringBuilder buf = new StringBuilder(); + buf.append(slots[0]); + for(int i = 1 ; i < slots.length ; i ++) { + buf.append(',').append(slots[i]); + } + return buf.toString(); + } + } + } + + + public void setSlots(String[] slots) { + this.slots = slots; + } + + @Override + public String toString() { + return "Shard{" + + "endpoints=" + Arrays.toString(endpoints) + + ", slots=" + Arrays.toString(slots) + + '}'; + } + } +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatThroughput.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatThroughput.java new file mode 100644 index 0000000000000000000000000000000000000000..1d4043b90347b8f3eb3e7f486bb0ebaaeec62041 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatThroughput.java @@ -0,0 +1,55 @@ +package com.tongtech.probe.stat; + +public class StatThroughput { + /* 当前秒的命令执行数 */ + private Integer current; + /* 前10秒平局每秒的命令执行数量 */ + private Double average10; + /* 前60秒平局每秒的命令执行数量 */ + private Double average60; + /* 上一个小时(整点)的命令执行总数 */ + private Double lasthour; + + public Integer getCurrent() { + return current; + } + + public void setCurrent(Integer current) { + this.current = current; + } + + public Double getAverage10() { + return average10; + } + + public void setAverage10(Double average10) { + this.average10 = average10; + } + + public Double getAverage60() { + return average60; + } + + public void setAverage60(Double average60) { + this.average60 = average60; + } + + public Double getLasthour() { + return lasthour; + } + + public void setLasthour(Double lasthour) { + this.lasthour = lasthour; + } + + @Override + public String toString() { + return "Throughput{" + + "current=" + current + + ", average10=" + average10 + + ", average60=" + average60 + + ", lasthour=" + lasthour + + '}'; + } + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatWorkerNode.java b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatWorkerNode.java new file mode 100644 index 0000000000000000000000000000000000000000..4ad2bef77a0c8b92bc999d72e89b7191664c8890 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/stat/StatWorkerNode.java @@ -0,0 +1,227 @@ +package com.tongtech.probe.stat; + +import java.util.List; + +public class StatWorkerNode extends StatBaseNode { + + private Boolean valid; + + private Boolean current; + /* 哨兵端口 */ + private Integer redisPort; + /* 安全级别, 0 Telnet none password, 1 SSL none password, 2 Telnet and password, 3 SSL and password */ + private Integer secureLevel; + /* 启动以来运行了多少秒 */ + private Long running; + /* 是否为主节点 */ + private Boolean master; + + /** 是否为热备节点, 需要额外运算赋值 */ + private boolean hotSpares; + + /** 分片的插槽范围, 需要额外运算赋值 */ + private String slot; + + /** 分片的编号, 需要额外运算赋值 */ + private Integer shard; + + /** + * 在CLUSTER模式下(不包括SCALABLE模式),标识所属分片的索引号(0 based) + * 目前只有在 在CLUSTER模式下的工作节点,才会有该属性。 + * 用来对应 StatService.shard[] 中对应的数组index, + * 因为该模式下 StatService.shard.endpoints 记录的地址,是配置中地址可能和node.remote中记录的地址不一致。 + * 比如:StatService.shard.endpoints[0] = "localhost:6311" 而与之对应 node.remote = '192.168.0.90::6311" + */ + private Integer shardId; + + private List used; + + private StatThroughput throughput; + + + public boolean isHotSpares() { + return hotSpares; + } + + public void setHotSpares(boolean hotSpares) { + this.hotSpares = hotSpares; + } + + public String getSlot() { + return slot; + } + + public void setSlot(String slot) { + this.slot = slot; + } + + public Integer getShard() { + return shard; + } + + public void setShard(Integer shard) { + this.shard = shard; + } + + public Integer getShardId() { + return shardId; + } + + public void setShardId(Integer shardId) { + this.shardId = shardId; + } + + public Boolean getValid() { + return valid; + } + + public void setValid(Boolean valid) { + this.valid = valid; + } + + public Boolean getCurrent() { + return current; + } + + public void setCurrent(Boolean current) { + this.current = current; + } + + public Integer getRedisPort() { + return redisPort; + } + + public void setRedisPort(Integer redisPort) { + this.redisPort = redisPort; + } + + public Long getRunning() { + return running; + } + + public void setRunning(Long running) { + this.running = running; + } + + public List getUsed() { + return used; + } + + public Integer getSecureLevel() { + return secureLevel; + } + + public void setSecureLevel(Integer secureLevel) { + this.secureLevel = secureLevel; + } + + public void setUsed(List used) { + this.used = used; + } + + public StatThroughput getThroughput() { + return throughput; + } + + public void setThroughput(StatThroughput throughput) { + this.throughput = throughput; + } + + public Boolean getMaster() { + return master; + } + + public void setMaster(Boolean master) { + this.master = master; + } + + @Override + public String toString() { + return "StatWorkerNode{" + + "valid=" + valid + + ", current=" + current + + ", redisPort=" + redisPort + + ", secureLevel=" + secureLevel + + ", running=" + running + + ", master=" + master + + ", hotSpares=" + hotSpares + + ", slot='" + slot + '\'' + + ", shard=" + shard + + ", shardId=" + shardId + + ", used=" + used + + ", throughput=" + throughput + + ", instance='" + instance + '\'' + + ", expired=" + expired + + ", remote='" + remote + '\'' + + ", port=" + port + + ", runtime=" + runtime + + ", endpoint='" + endpoint + '\'' + + '}'; + } + + public static class TableUsing { + /* table 名字 如:"table-1" */ + private String name; + /* Key 的最大数 */ + private Long capacity; + /* 当前使用的Key数量 */ + private Long used; + + private Long meanSquare; + /* 使用比例, 如:"0.0%"*/ + private String usedRate; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getCapacity() { + return capacity; + } + + public void setCapacity(Long capacity) { + this.capacity = capacity; + } + + public Long getUsed() { + return used; + } + + public void setUsed(Long used) { + this.used = used; + } + + public Long getMeanSquare() { + return meanSquare; + } + + public void setMeanSquare(Long meanSquare) { + this.meanSquare = meanSquare; + } + + public String getUsedRate() { + return usedRate; + } + + public void setUsedRate(String usedRate) { + this.usedRate = usedRate; + } + + @Override + public String toString() { + return "TableUsing{" + + "name='" + name + '\'' + + ", capacity=" + capacity + + ", used=" + used + + ", meanSquare=" + meanSquare + + ", usedRate='" + usedRate + '\'' + + '}'; + } + } + + +} diff --git a/rds-console/console-client/src/main/java/com/tongtech/probe/util/StringUtil.java b/rds-console/console-client/src/main/java/com/tongtech/probe/util/StringUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..1a7c88c1250703fdbc9ddd337acc1afc529fa637 --- /dev/null +++ b/rds-console/console-client/src/main/java/com/tongtech/probe/util/StringUtil.java @@ -0,0 +1,92 @@ +package com.tongtech.probe.util; + +import java.util.function.Consumer; + +public final class StringUtil { + public static boolean isEmpty(String str) { + return str == null || str.length() == 0; + } + + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + public static boolean isBlank(String str) { + return str == null || isEmpty(str.trim()); + } + + public static boolean isNotBlank(String str) { + return !isBlank(str); + } + + public static void setIfPresent(String value, Consumer setter) { + if (isNotEmpty(value)) { + setter.accept(value); + } + } + + public static String join(final char delimiter, final String... strings) { + if (strings.length == 0) { + return null; + } + if (strings.length == 1) { + return strings[0]; + } + int length = strings.length - 1; + for (final String s : strings) { + if (s == null) { + continue; + } + length += s.length(); + } + final StringBuilder sb = new StringBuilder(length); + if (strings[0] != null) { + sb.append(strings[0]); + } + for (int i = 1; i < strings.length; ++i) { + if (!isEmpty(strings[i])) { + sb.append(delimiter).append(strings[i]); + } else { + sb.append(delimiter); + } + } + return sb.toString(); + } + + public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { + if (index + substring.length() > str.length()) { + return false; + } + for (int i = 0; i < substring.length(); i++) { + if (str.charAt(index + i) != substring.charAt(i)) { + return false; + } + } + return true; + } + + public static String cut(String str, int threshold) { + if (isEmpty(str) || str.length() <= threshold) { + return str; + } + return str.substring(0, threshold); + } + + public static String trim(final String str, final char ch) { + if (isEmpty(str)) { + return null; + } + + final char[] chars = str.toCharArray(); + + int i = 0, j = chars.length - 1; + // noinspection StatementWithEmptyBody + for (; i < chars.length && chars[i] == ch; i++) { + } + // noinspection StatementWithEmptyBody + for (; j > 0 && chars[j] == ch; j--) { + } + + return new String(chars, i, j - i + 1); + } +} diff --git a/rds-console/console-client/src/test/java/com/tongtech/probe/NetConnectionTests.java b/rds-console/console-client/src/test/java/com/tongtech/probe/NetConnectionTests.java new file mode 100644 index 0000000000000000000000000000000000000000..7e1ac7f5d78c2668a572cd451979f16e66f994b1 --- /dev/null +++ b/rds-console/console-client/src/test/java/com/tongtech/probe/NetConnectionTests.java @@ -0,0 +1,25 @@ +package com.tongtech.probe; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class NetConnectionTests { + + @Test + void testSocket() throws IOException { + Socket socket = new Socket(); + try { + socket.setTcpNoDelay(true); + socket.setSoTimeout(2000);// 1 second + socket.connect(new InetSocketAddress(InetAddress.getByName("localhost"), 6300), 2000); + } catch (IOException e) { + throw e; + }finally{ + socket.close(); + } + } +} diff --git a/rds-console/console-client/src/test/java/com/tongtech/probe/ProbeClientTestUtils.java b/rds-console/console-client/src/test/java/com/tongtech/probe/ProbeClientTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..1118366817c11c877cdc53a1d262f4997c8cee97 --- /dev/null +++ b/rds-console/console-client/src/test/java/com/tongtech/probe/ProbeClientTestUtils.java @@ -0,0 +1,59 @@ +package com.tongtech.probe; + + +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** + * ProbeClient测试工具类。 + * 提供功能:加载classpath中资源文件,启动和停止节点通用方法。 + */ +public class ProbeClientTestUtils { + + /** + * 获得classpath中的资源文件的路径,返回资源文件(java.io.File) + * @param resourceName + * @return + */ + public static File getResourceFile(String resourceName) { + File resFile = null; + try { + Enumeration urls = ProbeClientTestUtils.class.getClassLoader().getResources(resourceName); + while(urls.hasMoreElements()) { + URL url = urls.nextElement(); + resFile = new File(url.toURI()); + break; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return resFile; + } + + /** + * 读取classpath中的资源文件的内容,返回为String类型。 + * @param resourceName + * @return + */ + public static String loadResource(String resourceName, String charsetName) { + String content = null; + try(InputStream licenseIn = ProbeClientTestUtils.class.getClassLoader().getResourceAsStream(resourceName)) { + content = IOUtils.toString(licenseIn, charsetName); + } + catch (Exception e) { + throw new RuntimeException(e); + } + return content; + } + + public static String loadResource(String resourceName) { + return loadResource(resourceName, "UTF-8"); + } + + + +} diff --git a/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientResultTests.java b/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientResultTests.java new file mode 100644 index 0000000000000000000000000000000000000000..c014a5891d0d523788637199d605a389cae6cea3 --- /dev/null +++ b/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientResultTests.java @@ -0,0 +1,125 @@ +package com.tongtech.probe; + +import com.alibaba.fastjson2.JSON; +import com.tongtech.probe.stat.*; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.util.List; + +import static com.tongtech.probe.ProbeClientTestUtils.loadResource; + + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class RestCenterClientResultTests { + + @Test + @Order(1) + void testServices() throws IOException { + String json = ProbeClientTestUtils.loadResource("json/services.json"); + List res = JSON.parseArray(json, StatService.class); + for(StatService s : res) { s.reprocessNodes(); } //重新处理一下节点的附加属性 + + System.out.println(res); + Assertions.assertTrue(res.size() >= 2); + Assertions.assertEquals(res.get(0).getType(), "SCALABLE"); + Assertions.assertTrue(res.get(0).getStatistics().getCpuSystemLoad() > 0); + Assertions.assertTrue(res.get(0).getNodes().size() >= 2); + Assertions.assertTrue(res.get(0).getNodes().get(0).getRunning() >= 10L); + + StatWorkerNode node0 = res.get(0).getNodes().get(0); + System.out.println("node0:" + node0); + Assertions.assertEquals(0.9, node0.getThroughput().getAverage10()); + Assertions.assertEquals(node0.getMaster(), true); + Assertions.assertEquals(node0.getShard(), 0); + Assertions.assertEquals(node0.getSlot(), "0-614"); + Assertions.assertEquals(node0.isHotSpares(), false); + Assertions.assertEquals(node0.getSecureLevel(), 0); + + StatProxyNode proxy0 = res.get(0).getProxies().get(0); + System.out.println("proxy0:" + proxy0); + Assertions.assertEquals(proxy0.getSecureLevel(), 2); + + + + Assertions.assertEquals(res.get(0).getNodes().get(4).isHotSpares(), true); + Assertions.assertEquals(res.get(0).getNodes().get(5).isHotSpares(), true); + + + Assertions.assertTrue(res.get(0).getNodes().get(0).getUsed().get(0).getUsed() >= 0L); + + StatService clusterServ = res.get(2); + System.out.println("clusterServ.getShard():" + clusterServ.getShard()); + Assertions.assertEquals(clusterServ.getType(), "CLUSTER"); + Assertions.assertEquals(clusterServ.getShard().size(), 2); + + StatWorkerNode node_c_0 = clusterServ.getNodes().get(0); + System.out.println("node_c_0:" + node_c_0); + Assertions.assertEquals(0, node_c_0.getShard()); + Assertions.assertEquals("0-8499", node_c_0.getSlot()); + + StatWorkerNode node_c_3 = clusterServ.getNodes().get(3); + System.out.println("node_c_3:" + node_c_3); + Assertions.assertEquals(1, node_c_3.getShard()); + Assertions.assertEquals("8500-16383", node_c_3.getSlot()); + + + } + + + @Test + @Order(2) + void testSentinels() throws IOException { + String json = ProbeClientTestUtils.loadResource("json/sentinels.json"); + List res = JSON.parseArray(json, StatSentinelNode.class); + System.out.println(res); + + Assertions.assertEquals(res.size(), 2); + Assertions.assertEquals(res.get(0).getRemote(), "192.168.0.90"); + Assertions.assertTrue(res.get(0).getRuntime().getCpuSystemLoad() > 0D); + + StatSentinelNode sentinel0 = res.get(0); + Assertions.assertEquals(sentinel0.getServices().size(), 1); + Assertions.assertEquals(sentinel0.getGroup(), "62C2AB3B4C19236B7686"); + StatSentinelNode.StatEndPoint[] endPoints = sentinel0.getServices().get("WebSession"); + Assertions.assertNotNull(endPoints); + Assertions.assertEquals(endPoints.length, 3); + Assertions.assertEquals(endPoints[0].getEndPoint(), "192.168.0.90:7379"); + Assertions.assertEquals(endPoints[0].getHost(), "192.168.0.90"); + Assertions.assertEquals(endPoints[0].getPort(), 7379); + Assertions.assertEquals(endPoints[0].getMaster(), true); + Assertions.assertEquals(endPoints[0].getAlive(), true); + + System.out.println("sentinels 0 getCpuSystemLoad:" + res.get(0).getRuntime().getCpuSystemLoad()); + + } + + @Test + @Order(3) + void testLicenseUsing() throws IOException { + String json = ProbeClientTestUtils.loadResource("json/licenseUsing.json"); + StatLicense res = JSON.parseObject(json, StatLicense.class); + System.out.println(res); + + Assertions.assertEquals(res.getCode(), 200); + Assertions.assertEquals(res.getMsg(), "ok"); + Assertions.assertEquals(res.getPercent(), "0.0%"); + } + + + @Test + @Order(4) + void testCenters() throws IOException { + String centersJson = ProbeClientTestUtils.loadResource("json/centers.json"); + StatCenterNode[] centers; + List res = JSON.parseArray(centersJson, StatCenterNode.class); + System.out.println(res); + + Assertions.assertEquals(res.size(), 3); + Assertions.assertEquals(res.get(0).getRemote(), "192.168.0.90"); + Assertions.assertTrue(res.get(0).getRuntime().getCpuSystemLoad() > 0D); + } + + + +} diff --git a/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientTempTests.java b/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientTempTests.java new file mode 100644 index 0000000000000000000000000000000000000000..7c4ddae173ac9c8d00e9b43ce9b41222bb06a63a --- /dev/null +++ b/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientTempTests.java @@ -0,0 +1,65 @@ +package com.tongtech.probe; + +import com.tongtech.probe.stat.StatCenterNode; +import com.tongtech.probe.stat.StatLicense; +import com.tongtech.probe.stat.StatSentinelNode; +import com.tongtech.probe.stat.StatService; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.util.List; + + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class RestCenterClientTempTests { + + //private static String client_url = "http://s0.v100.vip:33519"; + private static String client_url = "http://192.168.0.86:8086/"; + + private static RestCenterClient client = new RestCenterClient(client_url, + "abcef12376se790fowieaawew90qa8ew8e", true); + + @Test + @Order(1) + void testServices() throws IOException { + List res = client.getServices().getListData(); + System.out.println(res); + + Assertions.assertTrue(res.size() >= 1); + } + + + @Test + @Order(2) + void testSentinels() throws IOException { + List res = client.getSentinels().getListData(); + System.out.println(res); + + Assertions.assertEquals(res.size(), 1); + Assertions.assertEquals(res.get(0).getRemote(), "192.168.0.86"); + Assertions.assertTrue(res.get(0).getInstance() != null); + } + + @Test + @Order(3) + void testLicenseUsing() throws IOException { + StatLicense res = client.getLicenseUsing().getData(); + System.out.println(res); + + Assertions.assertEquals(res.getCode(), 200); + Assertions.assertEquals(res.getMsg(), "ok"); + } + + + @Test + @Order(4) + void testCenters() throws IOException { + List res = client.getCenters().getListData(); + System.out.println(res); + + Assertions.assertTrue(res.size() >= 1); + Assertions.assertTrue(res.get(0).getInstance() != null); + Assertions.assertEquals(res.get(0).getRemote(), "192.168.0.86"); + } + +} diff --git a/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientTests.java b/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientTests.java new file mode 100644 index 0000000000000000000000000000000000000000..664a7c134a5952816c9b337af7aa7a714b6f8577 --- /dev/null +++ b/rds-console/console-client/src/test/java/com/tongtech/probe/RestCenterClientTests.java @@ -0,0 +1,76 @@ +package com.tongtech.probe; + +import com.tongtech.probe.stat.StatCenterNode; +import com.tongtech.probe.stat.StatLicense; +import com.tongtech.probe.stat.StatSentinelNode; +import com.tongtech.probe.stat.StatService; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.util.*; + + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class RestCenterClientTests { + + //private RestCenterClient client = new RestCenterClient("http://localhost:8086/"); + private RestCenterClient client = new RestCenterClient("http://192.168.0.90:8806/", "abcef12376se790fowieaawew90qa8ew8e", true); + + + @Test + @Order(1) + void testServices() throws IOException { + List res = client.getServices().getListData(); + System.out.println(res); + + Assertions.assertTrue(res.size() >= 2); + Assertions.assertEquals(res.get(0).getType(), "SCALABLE"); + Assertions.assertTrue(res.get(0).getStatistics().getCpuSystemLoad() >= 0); + Assertions.assertTrue(res.get(0).getNodes().size() >= 2); + Assertions.assertTrue(res.get(0).getNodes().get(0).getInstance() != null); + Assertions.assertTrue(res.get(0).getNodes().get(0).getRunning() >= 10L); + Assertions.assertTrue(res.get(0).getNodes().get(0).getUsed().get(0).getUsed() >= 0L); + Assertions.assertEquals(res.get(0).getProxies().size(), 2); + + System.out.println("getProxies : " + res.get(0).getProxies()); + } + + + @Test + @Order(2) + void testSentinels() throws IOException { + List res = client.getSentinels().getListData(); + System.out.println(res); + + Assertions.assertEquals(res.size(), 2); + Assertions.assertEquals(res.get(0).getRemote(), "192.168.0.90"); + Assertions.assertTrue(res.get(0).getInstance() != null); + Assertions.assertTrue(res.get(0).getRuntime().getCpuSystemLoad() >= 0F); + + } + + @Test + @Order(3) + void testLicenseUsing() throws IOException { + StatLicense res = client.getLicenseUsing().getData(); + System.out.println(res); + + Assertions.assertEquals(res.getCode(), 200); + Assertions.assertEquals(res.getMsg(), "ok"); + Assertions.assertEquals(res.getPercent(), "0.0%"); + } + + + @Test + @Order(4) + void testCenters() throws IOException { + List res = client.getCenters().getListData(); + System.out.println(res); + + Assertions.assertEquals(res.size(), 3); + Assertions.assertTrue(res.get(0).getInstance() != null); + Assertions.assertEquals(res.get(0).getRemote(), "192.168.0.90"); + Assertions.assertTrue(res.get(0).getRuntime().getCpuSystemLoad() >= 0F); + } + +} diff --git a/rds-console/console-client/src/test/resources/json/centers.json b/rds-console/console-client/src/test/resources/json/centers.json new file mode 100644 index 0000000000000000000000000000000000000000..67e9d76831c233333064f24a039322c620b838af --- /dev/null +++ b/rds-console/console-client/src/test/resources/json/centers.json @@ -0,0 +1,53 @@ +[ + { + "expired": false, + "remote": "192.168.0.90", + "port": 10300, + "sentinelPort": 36379, + "runtime": { + "running": 18685, + "currentConnections": 16, + "totalConnections": 99, + "memoryUsed": 466500192, + "memoryFree": 211928480, + "memoryTotal": 678428672, + "memoryAvailable": 2863661056, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + }, + { + "expired": false, + "remote": "192.168.0.90", + "port": 10302, + "sentinelPort": 36381, + "runtime": { + "running": 18682, + "currentConnections": 16, + "totalConnections": 30, + "memoryUsed": 324234256, + "memoryFree": 417108976, + "memoryTotal": 741343232, + "memoryAvailable": 2863661056, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + }, + { + "expired": false, + "remote": "192.168.0.90", + "port": 10301, + "sentinelPort": 36380, + "runtime": { + "running": 18682, + "currentConnections": 14, + "totalConnections": 40, + "memoryUsed": 433443992, + "memoryFree": 277490536, + "memoryTotal": 710934528, + "memoryAvailable": 2863661056, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + } +] diff --git a/rds-console/console-client/src/test/resources/json/licenseUsing.json b/rds-console/console-client/src/test/resources/json/licenseUsing.json new file mode 100644 index 0000000000000000000000000000000000000000..cfe97f5344942a6619c5f1cfee5f5d6482086f07 --- /dev/null +++ b/rds-console/console-client/src/test/resources/json/licenseUsing.json @@ -0,0 +1,7 @@ +{ + "code": 200, + "msg": "ok", + "total": 137438953472, + "used": 2176272, + "percent": "0.0%" +} diff --git a/rds-console/console-client/src/test/resources/json/sentinels.json b/rds-console/console-client/src/test/resources/json/sentinels.json new file mode 100644 index 0000000000000000000000000000000000000000..e6be156a49a31822e1e35baff1a7749ec46bd83a --- /dev/null +++ b/rds-console/console-client/src/test/resources/json/sentinels.json @@ -0,0 +1,78 @@ +[ + { + "instance": "sentinel-1", + "group": "62C2AB3B4C19236B7686", + "expired": false, + "remote": "192.168.0.90", + "port": 27379, + "myself": false, + "runtime": { + "running": 5129, + "currentConnections": 1, + "totalConnections": 3, + "memoryUsed": 37874512, + "memoryFree": 289281200, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + }, + "services": { + "WebSession": [ + { + "endPoint": "192.168.0.90:7379", + "alive": true, + "master": true + }, + { + "endPoint": "192.168.0.90:7380", + "alive": true, + "master": false + }, + { + "endPoint": "192.168.0.90:7381", + "alive": true, + "master": false + } + ] + } + }, + { + "instance": "sentinel-2", + "group": "62C2AB3B4C19236B7686", + "expired": false, + "remote": "192.168.0.90", + "port": 27380, + "myself": false, + "runtime": { + "running": 5130, + "currentConnections": 1, + "totalConnections": 3, + "memoryUsed": 34091344, + "memoryFree": 293064368, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + }, + "services": { + "WebSession": [ + { + "endPoint": "192.168.0.90:7379", + "alive": true, + "master": true + }, + { + "endPoint": "192.168.0.90:7380", + "alive": true, + "master": false + }, + { + "endPoint": "192.168.0.90:7381", + "alive": true, + "master": false + } + ] + } + } +] diff --git a/rds-console/console-client/src/test/resources/json/services-bak.json b/rds-console/console-client/src/test/resources/json/services-bak.json new file mode 100644 index 0000000000000000000000000000000000000000..8f5fe7c92053747ccb358ea5c9f51b0eac3203a1 --- /dev/null +++ b/rds-console/console-client/src/test/resources/json/services-bak.json @@ -0,0 +1,904 @@ +[ + { + "name": "WebSession", + "type": "SCALABLE", + "message": "OK", + "alive": [ + "192.168.0.90:9200", + "192.168.0.90:9203", + "192.168.0.90:9204", + "192.168.0.90:9205", + "192.168.0.90:9202", + "192.168.0.90:9201" + ], + "statistics": { + "keys": 1, + "currentConnections": 12, + "totalConnections": 71, + "licenseOccupied": 1296006, + "memoryUsed": 125126816, + "memoryFree": 505591648, + "memoryTotal": 630718464, + "memoryAvailable": 19089326080, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 6 + }, + "nodes": [ + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9200, + "redisPort": 9379, + "secureLevel": 0, + "running": 34340, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 1, + "meanSquare": 74833, + "usedRate": "0.8%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6136 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9205, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34340, + "currentConnections": 6, + "totalConnections": 41, + "memoryUsed": 33652200, + "memoryFree": 282493464, + "memoryTotal": 316145664, + "memoryAvailable": 9544663040, + "keys": 1, + "commandResult": 20, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9201, + "redisPort": 9380, + "secureLevel": 0, + "running": 9179, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 0, + "average10": 0.0, + "average60": 0.0, + "lasthour": 0 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9200, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 9179, + "currentConnections": 0, + "totalConnections": 3, + "memoryUsed": 57942488, + "memoryFree": 255581736, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 20, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + }, + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9202, + "redisPort": 9381, + "secureLevel": 0, + "running": 9323, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 0, + "average10": 0.0, + "average60": 0.0, + "lasthour": 0 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9203, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 9323, + "currentConnections": 0, + "totalConnections": 4, + "memoryUsed": 60866592, + "memoryFree": 252657632, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 20, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 9203, + "redisPort": 9382, + "secureLevel": 0, + "running": 34268, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6134 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9204, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34268, + "currentConnections": 6, + "totalConnections": 30, + "memoryUsed": 91474616, + "memoryFree": 223098184, + "memoryTotal": 314572800, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 20, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9204, + "redisPort": 9383, + "secureLevel": 0, + "running": 34253, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.8, + "average60": 0.8, + "lasthour": 3069 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9203, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34253, + "currentConnections": 3, + "totalConnections": 11, + "memoryUsed": 83872488, + "memoryFree": 212874520, + "memoryTotal": 296747008, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 20, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9205, + "redisPort": 9384, + "secureLevel": 0, + "running": 34247, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 1, + "meanSquare": 74833, + "usedRate": "0.8%" + } + ], + "throughput": { + "current": 1, + "average10": 0.8, + "average60": 0.9, + "lasthour": 3069 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9200, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34247, + "currentConnections": 3, + "totalConnections": 14, + "memoryUsed": 84029736, + "memoryFree": 208522968, + "memoryTotal": 292552704, + "memoryAvailable": 9544663040, + "keys": 1, + "commandResult": 20, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + } + ], + "replicas": 2, + "hotspares": 2, + "status": "RUNNING", + "hotspare": [ + "192.168.0.90:9202", + "192.168.0.90:9201" + ], + "maxShards": 10, + "shard": [ + { + "endpoints": [ + "192.168.0.90:9200", + "192.168.0.90:9205" + ], + "slots": [ + "0-614" + ] + }, + { + "endpoints": [ + "192.168.0.90:9203", + "192.168.0.90:9204" + ], + "slots": [ + "614-1024" + ] + } + ], + "proxies": [ + { + "valid": true, + "expired": false, + "remote": "192.168.0.90", + "port": 6200, + "redisPort": 6379, + "secueLevel": 2, + "running": 1866, + "throughput": { + "current": 1, + "average10": 0.5, + "average60": 0.5, + "lasthour": 0 + }, + "runtime": { + "running": 1866, + "currentConnections": 1, + "totalConnections": 2, + "memoryUsed": 50382528, + "memoryFree": 276773184, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + } + ] + }, + { + "name": "Sentinel", + "type": "SENTINEL", + "message": "OK", + "alive": [ + "192.168.0.90:7200", + "192.168.0.90:7201", + "192.168.0.90:7202" + ], + "statistics": { + "keys": 737, + "currentConnections": 4, + "totalConnections": 10, + "licenseOccupied": 736266, + "memoryUsed": 79276824, + "memoryFree": 247878888, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + }, + "nodes": [ + { + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 7200, + "redisPort": 7379, + "secureLevel": 2, + "running": 34550, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 737, + "meanSquare": 16242, + "usedRate": "99.2%" + } + ], + "throughput": { + "current": 1, + "average10": 0.9, + "average60": 0.9, + "lasthour": 3258 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 7201, + "max": 0 + }, + { + "address": "127.0.0.1", + "port": 7202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34550, + "currentConnections": 4, + "totalConnections": 10, + "memoryUsed": 79276824, + "memoryFree": 247878888, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "keys": 737, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 7201, + "redisPort": 7380, + "secureLevel": 2, + "running": 34765, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 737, + "meanSquare": 16242, + "usedRate": "99.2%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6134 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 7200, + "max": 0 + }, + { + "address": "127.0.0.1", + "port": 7202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34765, + "currentConnections": 8, + "totalConnections": 25, + "memoryUsed": 24843104, + "memoryFree": 248835232, + "memoryTotal": 273678336, + "memoryAvailable": 9544663040, + "keys": 737, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 7202, + "redisPort": 7381, + "secureLevel": 2, + "running": 34773, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 737, + "meanSquare": 16242, + "usedRate": "99.2%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6134 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 7200, + "max": 0 + }, + { + "address": "127.0.0.1", + "port": 7201, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 34773, + "currentConnections": 8, + "totalConnections": 19, + "memoryUsed": 139472552, + "memoryFree": 187683160, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "keys": 737, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + } + ], + "master": "192.168.0.90:7200", + "syncList": [ + "localhost:7200", + "localhost:7201", + "localhost:7202" + ] + }, + { + "name": "Cluster", + "type": "CLUSTER", + "message": "OK", + "alive": [ + "192.168.0.90:8200", + "192.168.0.90:8201", + "192.168.0.90:8202", + "192.168.0.90:8203" + ], + "statistics": { + "keys": 0, + "currentConnections": 20, + "totalConnections": 46, + "licenseOccupied": 144000, + "memoryUsed": 175039832, + "memoryFree": 445717160, + "memoryTotal": 620756992, + "memoryAvailable": 19089326080, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 5 + }, + "nodes": [ + { + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 8200, + "redisPort": 8379, + "secureLevel": 0, + "running": 35390, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 9, + "average10": 9.4, + "average60": 9.4, + "lasthour": 33964 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8201, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 35390, + "currentConnections": 10, + "totalConnections": 24, + "memoryUsed": 85836448, + "memoryFree": 224542048, + "memoryTotal": 310378496, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + }, + { + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 8201, + "redisPort": 8380, + "secureLevel": 0, + "running": 35392, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 9, + "average10": 9.4, + "average60": 9.4, + "lasthour": 33964 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8200, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 35392, + "currentConnections": 10, + "totalConnections": 24, + "memoryUsed": 44883168, + "memoryFree": 241378080, + "memoryTotal": 286261248, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 2 + } + }, + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 8202, + "redisPort": 8381, + "secureLevel": 0, + "running": 35387, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 9, + "average10": 9.4, + "average60": 9.4, + "lasthour": 33964 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8203, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 35387, + "currentConnections": 10, + "totalConnections": 22, + "memoryUsed": 89203384, + "memoryFree": 221175112, + "memoryTotal": 310378496, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + }, + { + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 8203, + "redisPort": 8382, + "secureLevel": 0, + "running": 35387, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 10, + "average10": 9.4, + "average60": 9.4, + "lasthour": 33964 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 35387, + "currentConnections": 10, + "totalConnections": 22, + "memoryUsed": 88255656, + "memoryFree": 222122840, + "memoryTotal": 310378496, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 3 + } + } + ], + "shard": [ + { + "master": "192.168.0.90:8200", + "endpoints": [ + "localhost:8200", + "localhost:8201" + ], + "slots": [ + "0-8499" + ] + }, + { + "master": "192.168.0.90:8202", + "endpoints": [ + "localhost:8202", + "localhost:8203" + ], + "slots": [ + "8500-16383" + ] + } + ] + } +] diff --git a/rds-console/console-client/src/test/resources/json/services.json b/rds-console/console-client/src/test/resources/json/services.json new file mode 100644 index 0000000000000000000000000000000000000000..a4080876537ffcda3c083d2d251242a92b494e00 --- /dev/null +++ b/rds-console/console-client/src/test/resources/json/services.json @@ -0,0 +1,949 @@ +[ + { + "name": "WebSession", + "type": "SCALABLE", + "message": "OK", + "alive": [ + "192.168.0.90:9200", + "192.168.0.90:9201", + "192.168.0.90:9202", + "192.168.0.90:9203", + "192.168.0.90:9204", + "192.168.0.90:9205" + ], + "statistics": { + "keys": 0, + "currentConnections": 7, + "totalConnections": 28, + "licenseOccupied": 1296000, + "memoryUsed": 111146416, + "memoryFree": 515902032, + "memoryTotal": 627048448, + "memoryAvailable": 19089326080, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 28 + }, + "nodes": [ + { + "instance": "rds-1", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9200, + "redisPort": 9379, + "secureLevel": 0, + "running": 5189, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.9, + "average60": 0.9, + "lasthour": 3094 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9201, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5189, + "currentConnections": 4, + "totalConnections": 15, + "memoryUsed": 56478216, + "memoryFree": 257046008, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 24, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "rds-2", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9201, + "redisPort": 9380, + "secureLevel": 0, + "running": 5189, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.9, + "average60": 0.9, + "lasthour": 3083 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9200, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5189, + "currentConnections": 3, + "totalConnections": 15, + "memoryUsed": 49676016, + "memoryFree": 263848208, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 24, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "rds-3", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9202, + "redisPort": 9381, + "secureLevel": 0, + "running": 5189, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.9, + "average60": 0.9, + "lasthour": 3092 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9203, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5189, + "currentConnections": 3, + "totalConnections": 13, + "memoryUsed": 54668200, + "memoryFree": 258856024, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 8, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "rds-4", + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 9203, + "redisPort": 9382, + "secureLevel": 0, + "running": 5188, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.9, + "average60": 0.9, + "lasthour": 3083 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5188, + "currentConnections": 3, + "totalConnections": 13, + "memoryUsed": 48884040, + "memoryFree": 264640184, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 8, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "rds-5", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 9204, + "redisPort": 9383, + "secureLevel": 0, + "running": 5188, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.8, + "average60": 0.8, + "lasthour": 3075 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9205, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5188, + "currentConnections": 3, + "totalConnections": 6, + "memoryUsed": 46625192, + "memoryFree": 266899032, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "rds-6", + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 9205, + "redisPort": 9384, + "secureLevel": 0, + "running": 5194, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 1, + "average10": 0.8, + "average60": 0.9, + "lasthour": 3074 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "192.168.0.90", + "port": 9204, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5194, + "currentConnections": 3, + "totalConnections": 6, + "memoryUsed": 39583920, + "memoryFree": 273940304, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + } + ], + "replicas": 2, + "hotspares": 2, + "status": "RUNNING", + "hotspare": [ + "192.168.0.90:9204", + "192.168.0.90:9205" + ], + "maxShards": 10, + "shard": [ + { + "endpoints": [ + "192.168.0.90:9200", + "192.168.0.90:9201" + ], + "slots": [ + "0-614" + ] + }, + { + "endpoints": [ + "192.168.0.90:9202", + "192.168.0.90:9203" + ], + "slots": [ + "614-1024" + ] + } + ], + "proxies": [ + { + "instance": "proxy-1", + "valid": true, + "expired": false, + "remote": "192.168.0.90", + "port": 6200, + "redisPort": 6379, + "secureLevel": 2, + "running": 5757, + "throughput": { + "current": 2, + "average10": 1.8, + "average60": 1.9, + "lasthour": 6593 + }, + "runtime": { + "running": 5757, + "currentConnections": 8, + "totalConnections": 24, + "memoryUsed": 61038656, + "memoryFree": 266117056, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "cpuProcessLoad(%)": 6, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "proxy", + "valid": true, + "expired": false, + "remote": "192.168.0.90", + "port": 6201, + "redisPort": 6380, + "secureLevel": 2, + "running": 5757, + "throughput": { + "current": 2, + "average10": 1.8, + "average60": 1.9, + "lasthour": 6586 + }, + "runtime": { + "running": 5757, + "currentConnections": 8, + "totalConnections": 18, + "memoryUsed": 48713184, + "memoryFree": 278442528, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "cpuProcessLoad(%)": 6, + "cpuSystemLoad(%)": 14 + } + } + ] + }, + { + "name": "Sentinel", + "type": "SENTINEL", + "message": "OK", + "alive": [ + "192.168.0.90:7200", + "192.168.0.90:7201", + "192.168.0.90:7202" + ], + "statistics": { + "keys": 0, + "currentConnections": 8, + "totalConnections": 15, + "licenseOccupied": 648000, + "memoryUsed": 119965096, + "memoryFree": 207190616, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + }, + "nodes": [ + { + "instance": "mastslave-1", + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 7200, + "redisPort": 7379, + "secureLevel": 2, + "running": 5122, + "master": true, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6155 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 7201, + "max": 0 + }, + { + "address": "127.0.0.1", + "port": 7202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5122, + "currentConnections": 8, + "totalConnections": 15, + "memoryUsed": 119965096, + "memoryFree": 207190616, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "keys": 0, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "mastslave-2", + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 7201, + "redisPort": 7380, + "secureLevel": 2, + "running": 5121, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6155 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 7200, + "max": 0 + }, + { + "address": "127.0.0.1", + "port": 7202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5121, + "currentConnections": 8, + "totalConnections": 16, + "memoryUsed": 72626064, + "memoryFree": 240898160, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "mastslave-3", + "valid": true, + "expired": false, + "current": true, + "remote": "192.168.0.90", + "port": 7202, + "redisPort": 7381, + "secureLevel": 2, + "running": 5122, + "master": false, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 2, + "average10": 1.7, + "average60": 1.7, + "lasthour": 6154 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 7200, + "max": 0 + }, + { + "address": "127.0.0.1", + "port": 7201, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5122, + "currentConnections": 8, + "totalConnections": 15, + "memoryUsed": 89133360, + "memoryFree": 238022352, + "memoryTotal": 327155712, + "memoryAvailable": 10737418240, + "keys": 0, + "commandResult": 26, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + } + ], + "master": "192.168.0.90:7200", + "syncList": [ + "localhost:7200", + "localhost:7201", + "localhost:7202" + ] + }, + { + "name": "Cluster", + "type": "CLUSTER", + "message": "OK", + "alive": [ + "192.168.0.90:8200", + "192.168.0.90:8201", + "192.168.0.90:8202", + "192.168.0.90:8203" + ], + "statistics": { + "keys": 0, + "currentConnections": 43, + "totalConnections": 1790, + "licenseOccupied": 144000, + "memoryUsed": 82134984, + "memoryFree": 544913464, + "memoryTotal": 627048448, + "memoryAvailable": 19089326080, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 28 + }, + "nodes": [ + { + "instance": "cluster-0-0", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 8200, + "redisPort": 8379, + "secureLevel": 0, + "running": 5213, + "master": true, + "shardId": 0, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 11, + "average10": 9.9, + "average60": 9.8, + "lasthour": 35077 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8201, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5213, + "currentConnections": 21, + "totalConnections": 896, + "memoryUsed": 32710792, + "memoryFree": 280813432, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "cluster-0-1", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 8201, + "redisPort": 8380, + "secureLevel": 0, + "running": 5213, + "master": false, + "shardId": 0, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 11, + "average10": 9.9, + "average60": 9.8, + "lasthour": 35076 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8200, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5213, + "currentConnections": 21, + "totalConnections": 895, + "memoryUsed": 37232576, + "memoryFree": 276291648, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "cluster-1-0", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 8202, + "redisPort": 8381, + "secureLevel": 0, + "running": 5213, + "master": true, + "shardId": 1, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 10, + "average10": 9.8, + "average60": 9.8, + "lasthour": 35085 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8203, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5213, + "currentConnections": 22, + "totalConnections": 894, + "memoryUsed": 49424192, + "memoryFree": 264100032, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + }, + { + "instance": "cluster-1-1", + "valid": true, + "expired": false, + "current": false, + "remote": "192.168.0.90", + "port": 8203, + "redisPort": 8382, + "secureLevel": 0, + "running": 5212, + "master": false, + "shardId": 1, + "used": [ + { + "name": "table-1", + "capacity": 1000, + "used": 0, + "meanSquare": 0, + "usedRate": "0.0%" + } + ], + "throughput": { + "current": 10, + "average10": 9.8, + "average60": 9.8, + "lasthour": 35085 + }, + "sync": [ + { + "dataFrom": "table-1", + "sendTo": [ + { + "address": "127.0.0.1", + "port": 8202, + "max": 0 + } + ] + } + ], + "runtime": { + "running": 5212, + "currentConnections": 21, + "totalConnections": 894, + "memoryUsed": 37761384, + "memoryFree": 275762840, + "memoryTotal": 313524224, + "memoryAvailable": 9544663040, + "keys": 0, + "commandResult": 30, + "networkInputBytes": 0, + "inputPerSecond(KB/s)": 0.0, + "networkOutputBytes": 0, + "outputPerSecond(KB/s)": 0.0, + "cpuProcessLoad(%)": 0, + "cpuSystemLoad(%)": 14 + } + } + ], + "shard": [ + { + "master": "192.168.0.90:8200", + "endpoints": [ + "localhost:8200", + "localhost:8201" + ], + "slots": [ + "0-8499" + ] + }, + { + "master": "192.168.0.90:8202", + "endpoints": [ + "localhost:8202", + "localhost:8203" + ], + "slots": [ + "8500-16383" + ] + } + ] + } +] diff --git a/rds-console/console-common/pom.xml b/rds-console/console-common/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0aa87fb824fd5787a689b7433c7a018c7ae11dbf --- /dev/null +++ b/rds-console/console-common/pom.xml @@ -0,0 +1,172 @@ + + + + console + com.tongtech + 2.2.C.1 + + 4.0.0 + + console-common + + + common通用工具 + + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + commons-io + commons-io + + + + + commons-fileupload + commons-fileupload + + + + + org.apache.poi + poi-ooxml + + + + + org.yaml + snakeyaml + + + + + io.jsonwebtoken + jjwt + + + + + javax.xml.bind + jaxb-api + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + eu.bitwalker + UserAgentUtils + + + + + javax.servlet + javax.servlet-api + + + + + redis.clients + jedis + + + + org.junit.jupiter + junit-jupiter + test + + + + + + org.apache.velocity + velocity-engine-core + + + + + org.quartz-scheduler + quartz + + + com.mchange + c3p0 + + + + + + + diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Anonymous.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Anonymous.java new file mode 100644 index 0000000000000000000000000000000000000000..19a399eea46df27c6842a49268f70471df36cf85 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author XiaoZhangTongZhi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/DataSource.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/DataSource.java new file mode 100644 index 0000000000000000000000000000000000000000..24e7455df685e98d7d7d46e5ee12654ec6b89ef0 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/DataSource.java @@ -0,0 +1,29 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.tongtech.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author XiaoZhangTongZhi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Excel.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Excel.java new file mode 100644 index 0000000000000000000000000000000000000000..71019a09e94dfc8bfa41e44f425a0844cf2458dd --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Excel.java @@ -0,0 +1,188 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; + +import com.tongtech.common.utils.poi.ExcelHandlerAdapter; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * 自定义导出Excel数据注解 + * + * @author XiaoZhangTongZhi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 单位为字符 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽 单位为字符 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串 2图片) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Excels.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Excels.java new file mode 100644 index 0000000000000000000000000000000000000000..571f3c2ab8a0245427dc675c43bc773bc4772f9b --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author XiaoZhangTongZhi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + public Excel[] value(); +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Log.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Log.java new file mode 100644 index 0000000000000000000000000000000000000000..367c1c12bfa22f02ef8936c22b9395ecff52a4ba --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/Log.java @@ -0,0 +1,47 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.tongtech.common.enums.BusinessType; +import com.tongtech.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author XiaoZhangTongZhi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/RateLimiter.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/RateLimiter.java new file mode 100644 index 0000000000000000000000000000000000000000..4eab9d087f85f17c46ec4ea502175a1ad89adfcb --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/RateLimiter.java @@ -0,0 +1,41 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.enums.LimitType; + +/** + * 限流注解 + * + * @author XiaoZhangTongZhi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/annotation/RepeatSubmit.java b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/RepeatSubmit.java new file mode 100644 index 0000000000000000000000000000000000000000..49667b5349574ceefb531d2f4849b74b12ff1e9f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.tongtech.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author XiaoZhangTongZhi + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/config/AppHomeConfig.java b/rds-console/console-common/src/main/java/com/tongtech/common/config/AppHomeConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..f8c654f1153622746de725bd5740034508c25cea --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/config/AppHomeConfig.java @@ -0,0 +1,133 @@ +package com.tongtech.common.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; + +public class AppHomeConfig { + + private static final Logger logger = LoggerFactory.getLogger(AppHomeConfig.class); + + public static final String CENTER_PATH = "node-mgr"; + + public static final String TEMPLATE_PATH = toLocal("data/template"); + + public static final String PACKAGE_PATH = toLocal("data/package"); + + public static final String PACKAGE_VERSION_PATH = toLocal("data/package/version"); + + private static final String PROFILE_PATH = "data/profile"; + + public static String ETC_PATH = "config"; + + public static final String CENTER_LICENSE_FILE = toLocal("apps/center/center.lic"); + + public static final String DATABASE_PATH = toLocal("data/db"); + + public static final String DATABASE_NAME = "consoledb"; + + public static final String DATABASE_BACKUP_PATH = toLocal("data/dbbak"); + + private static File pwdDir = null; + + public static String getProfile() + { + return PROFILE_PATH; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } + + + + public static final File getAbsoluteFile(String baseDir, String fileName) + { + File desc = new File(getPWD(), baseDir + File.separator + fileName); + + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + + return desc; + } + + public static final String getAbsolutePath(String baseDir, String fileName) { + File f = getAbsoluteFile(baseDir, fileName); + return f.getAbsolutePath(); + } + + + public static final File getAbsoluteFile(String path) + { + File desc = new File(getPWD(), path); + + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + + return desc; + } + + public static final String getAbsolutePath(String path) { + File f = getAbsoluteFile(path); + return f.getAbsolutePath(); + } + + /** + * 得到当前工作路径 + * @return + */ + private static File getPWD() { + + if(pwdDir == null) { + try { + pwdDir = new File((new File(".")).getCanonicalPath()); + logger.info("System initialed application home directory at:" + pwdDir.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return pwdDir; + } + + + private static String toLocal(String path) { + return path.replace('/', File.separatorChar); + } + + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/config/UhConsoleConfig.java b/rds-console/console-common/src/main/java/com/tongtech/common/config/UhConsoleConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..484227f7c89c0cf9eb71b50999dbd58973a46514 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/config/UhConsoleConfig.java @@ -0,0 +1,136 @@ +package com.tongtech.common.config; + +import com.tongtech.common.enums.DeployEnvEnum; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author XiaoZhangTongZhi + */ +@Component +@ConfigurationProperties(prefix = "console") +public class UhConsoleConfig +{ + /** 项目名称 */ + private static String name; + + /** 版本 */ + private static String version; + + /** 版权年份 */ + private static String copyrightYear; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + /** 验证码类型 */ + private static String captchaType; + + /** SSH 连接时的超时时间, 单位毫秒 */ + private static int sshConnectTimeout = 10000; + + /** SSH 命令执行通道超时时间, 单位毫秒 */ + private static int sshChannelTimeout = 20000; + + /** 部署环境,"host" 主机,"k8s" 容器云 */ + private static String deployEnv; + + /** 集成嵌入 */ + private static boolean embedding; + + private static DeployEnvEnum deployEnvEnum; + + public static String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public static String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public static String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + UhConsoleConfig.addressEnabled = addressEnabled; + } + + public static String getCaptchaType() { + return captchaType; + } + + public void setCaptchaType(String captchaType) { + UhConsoleConfig.captchaType = captchaType; + } + + public static int getSshConnectTimeout() { + return sshConnectTimeout; + } + + public void setSshConnectTimeout(int sshConnectTimeout) { + UhConsoleConfig.sshConnectTimeout = sshConnectTimeout; + } + + public static int getSshChannelTimeout() { + return sshChannelTimeout; + } + + public void setSshChannelTimeout(int sshChannelTimeout) { + UhConsoleConfig.sshChannelTimeout = sshChannelTimeout; + } + + public static String getDeployEnv() { + return deployEnv; + } + + public static DeployEnvEnum getDeployEnvEnum() { + if(deployEnvEnum == null) { + deployEnvEnum = DeployEnvEnum.parse(deployEnv); + } + return deployEnvEnum; + } + + public void setDeployEnv(String deployEnv) { + if(deployEnv != null) { + UhConsoleConfig.deployEnv = deployEnv.trim().toLowerCase(); + } + else { + UhConsoleConfig.deployEnv = null; + } + } + + public static boolean isEmbedding() { + return embedding; + } + + public void setEmbedding(boolean embedding) { + UhConsoleConfig.embedding = embedding; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/CacheConstants.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/CacheConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..893b52df5b7fa6d227a6a992f88cdcab4f0a3b13 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/CacheConstants.java @@ -0,0 +1,46 @@ +package com.tongtech.common.constant; + +/** + * 缓存的key 常量 + * + * @author XiaoZhangTongZhi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + public static final String TEMP_CENTER_LICENSE_KEY = "temp_center_license_key"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/ConsoleConstants.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/ConsoleConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..9daf103a989529b0a443aedf43740042776832f7 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/ConsoleConstants.java @@ -0,0 +1,42 @@ +package com.tongtech.common.constant; + +/** + * 控制台中通用常量信息 + * + * @author Zhang Chenlong + */ +public class ConsoleConstants +{ + /** + * 节点管理器实例名前缀,实例名:实例名前缀 + 主机ID + * 如:HID18 + */ + public static final long CENTER_SERVICE_ID = 1L; + + //public static final long OPERATION_TIMEOUT = 1000 * 60 * 2; //超时时间2分钟,单位毫秒 + + public static final String STATUS_RUNNING = "RUNNING"; + + public static final String CONFIG_TYPE_LICENSE = "center-lic"; + + public static final String CONFIG_SYS_INITIALIZED_KEY = "sys.initialized"; + + public static final String CONFIG_SYS_DEVELOPMENT_MODE_KEY = "sys.development.mode"; + + + public final static String MAIN_ARG_RESTORE = "--restore"; + + public final static String MAIN_ARG_INITIALIZE = "--initialize"; + + public final static String MAIN_ARG_INITIALIZE_SHORT = "-i"; + + public final static String MAIN_ARG_DEVELOPMENT = "--development"; + + public final static String MAIN_ARG_DEVELOPMENT_SHORT = "-d"; + + public final static int STATISTIC_GROUPS = 10; //检测统计时,分组数量 + + public final static int STATISTIC_GROUP_MIN_SECOND = 600; //查询时间范围小于等于该值后,就逐条查询;大于就进行分组统计 + + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/Constants.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..b29580056f6001cff03024f3b003e7776c12c760 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/Constants.java @@ -0,0 +1,142 @@ +package com.tongtech.common.constant; + +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author XiaoZhangTongZhi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.tongtech" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.tongtech.common.utils.file" }; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/GenConstants.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/GenConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..b0625d13567b10e2d9ab328781f60d21860f2fc7 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.tongtech.common.constant; + +/** + * 代码生成通用常量 + * + * @author XiaoZhangTongZhi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/HttpStatus.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/HttpStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..00450a34c168aae1c2d8b6aec0d311d873d1ab17 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/HttpStatus.java @@ -0,0 +1,89 @@ +package com.tongtech.common.constant; + +/** + * 返回状态码 + * + * @author XiaoZhangTongZhi + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/ScheduleConstants.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/ScheduleConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..c6694e2b580f285f616985527695a8400d7c2b04 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.tongtech.common.constant; + +/** + * 任务调度通用常量 + * + * @author XiaoZhangTongZhi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/constant/UserConstants.java b/rds-console/console-common/src/main/java/com/tongtech/common/constant/UserConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..67ab4b3852fdc331517d7e34168c4de745970f84 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/constant/UserConstants.java @@ -0,0 +1,78 @@ +package com.tongtech.common.constant; + +/** + * 用户常量信息 + * + * @author XiaoZhangTongZhi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验返回结果码 */ + public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/ObjectCache.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/ObjectCache.java new file mode 100644 index 0000000000000000000000000000000000000000..3ea4d2f258d207b9aa7b4e632e05fd7e499b7eb8 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/ObjectCache.java @@ -0,0 +1,78 @@ +package com.tongtech.common.core.cache; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * 定义缓存的通用接口 + */ +public interface ObjectCache { + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + void setCacheObject(String key, T value); + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 超时时间 + * @param timeUnit 时间单位 + */ + void setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit); + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + T getCacheObject(String key); + + /** + * 删除单个对象 + * + * @param key + */ + boolean deleteObject(String key); + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return 删除的对象数量 + */ + boolean deleteObject(Collection collection); + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + Collection keys(final String pattern); + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + Boolean hasKey(String key); + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/impl/LocalMemCache.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/impl/LocalMemCache.java new file mode 100644 index 0000000000000000000000000000000000000000..c8ad5324607bf7d33f0cd18b11e1ce111b31ed89 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/impl/LocalMemCache.java @@ -0,0 +1,144 @@ +package com.tongtech.common.core.cache.impl; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import com.tongtech.common.core.cache.ObjectCache; +import org.springframework.data.redis.core.TimeoutUtils; +import org.springframework.stereotype.Component; + +@Component +public class LocalMemCache implements ObjectCache { + + private final static long CHECK_INTERVAL = 1000; //过期扫描的时间间隔 + + private long lastCheckingTime = System.currentTimeMillis(); //过期扫描的时间间隔 + + private Map data = new ConcurrentHashMap<>(); + + private Map dataExpire = new ConcurrentHashMap(); + + @Override + public void setCacheObject(String key, T value) { + removeExpired(); + + data.put(key, value); + } + + @Override + public void setCacheObject(String key, T value, Integer timeout, TimeUnit unit) { + + removeExpired(); + + //If timeout > 0 save it as expire time. + if(timeout > 0) { + Long expireTime = System.currentTimeMillis() + TimeoutUtils.toMillis(timeout, unit); + dataExpire.put(key, expireTime); + } + + data.put(key, value); + } + + + @Override + public T getCacheObject(String key) { + return (T)data.get(key); + } + + public boolean deleteObject(final String key) { + removeExpired(); + + if(dataExpire.containsKey(key)) dataExpire.remove(key); + return (data.remove(key) != null); + } + + @Override + public boolean deleteObject(Collection collection) { + removeExpired(); + + long deleted = 0; + for(Object keyObj : collection ) { + String key = keyObj.toString(); + + if(dataExpire.containsKey(key)) dataExpire.remove(key); + if (data.remove(key) != null) { + deleted ++; + } + } + + return deleted > 0; + } + + /** + * 获取匹配的缓存keys列表。 + * + * @param pattern 4种模式:1.后缀模式 "*suffix", 2.前缀模式 preffix*, 3.包含模式"contains" , 4 如果是""或null 返回所有key + * @return 对象列表 + * @throws IOException + */ + public Collection keys(final String pattern) { + Set allKeys = data.keySet(); + if(pattern == null || "".equals(pattern)) { + return allKeys; + } + else { + Set fillterKeys = new HashSet(); + + if(pattern.endsWith("*")) { + String prefixPatten = pattern.substring(0, pattern.length() - 2); + for (String key : allKeys) { + if(key.startsWith(prefixPatten)) { + fillterKeys.add(key); + } + } + } + else if(pattern.startsWith("*")) { + String suffixPatten = pattern.substring(1); + for (String key : allKeys) { + if(key.endsWith(suffixPatten)) { + fillterKeys.add(key); + } + } + } + else { + for (String key : allKeys) { + if(key.contains(pattern)) { + fillterKeys.add(key); + } + } + } + + return fillterKeys; + } + } + + @Override + public Boolean hasKey(String key) { + return data.containsKey(key); + } + + /** + * 删除所有过期的数据,针对设置了timeout的数据。 + * @return + */ + private void removeExpired() { + long time = System.currentTimeMillis(); + if( time > (lastCheckingTime + CHECK_INTERVAL) ) { + for(Map.Entry expireEn : dataExpire.entrySet()) { + Long expire = expireEn.getValue(); + if(time > expire) { + String removingKey = expireEn.getKey(); + data.remove(removingKey); + dataExpire.remove(removingKey); + } + } + + //Update the time stamp + lastCheckingTime = System.currentTimeMillis(); + } + + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/impl/RedisObjectCache.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/impl/RedisObjectCache.java new file mode 100644 index 0000000000000000000000000000000000000000..c4140b5d854dc464a59abd53dae51b4002da99f5 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/cache/impl/RedisObjectCache.java @@ -0,0 +1,264 @@ +package com.tongtech.common.core.cache.impl; + +import com.tongtech.common.core.cache.ObjectCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author XiaoZhangTongZhi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +public class RedisObjectCache implements ObjectCache +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/controller/BaseController.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/controller/BaseController.java new file mode 100644 index 0000000000000000000000000000000000000000..b76b453ecb5c1d3bcd28b14ddeb4fac660d137ce --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/controller/BaseController.java @@ -0,0 +1,180 @@ +package com.tongtech.common.core.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; + +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.core.page.PageDomain; +import com.tongtech.common.core.page.TableDataInfo; +import com.tongtech.common.core.page.TableSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.tongtech.common.constant.HttpStatus; +import com.tongtech.common.utils.DateUtils; +import com.tongtech.common.utils.PageUtils; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.sql.SqlUtil; + +/** + * web层通用数据处理 + * + * @author XiaoZhangTongZhi + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUsername(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/AjaxResult.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/AjaxResult.java new file mode 100644 index 0000000000000000000000000000000000000000..67c5c8e763bfbdde2a9eeca5ca125bd2468dc6fc --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/AjaxResult.java @@ -0,0 +1,162 @@ +package com.tongtech.common.core.domain; + +import java.util.HashMap; +import com.tongtech.common.constant.HttpStatus; +import com.tongtech.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author XiaoZhangTongZhi + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/BaseEntity.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/BaseEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..5646ac09d78afcefc0cccc46ff11580de73c3c1e --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/BaseEntity.java @@ -0,0 +1,114 @@ +package com.tongtech.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * Entity基类 + * + * @author XiaoZhangTongZhi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + protected Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/R.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/R.java new file mode 100644 index 0000000000000000000000000000000000000000..e2f54ebb6f422abc97e84cf1d60b46ed93e5a305 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/R.java @@ -0,0 +1,115 @@ +package com.tongtech.common.core.domain; + +import java.io.Serializable; +import com.tongtech.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author XiaoZhangTongZhi + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } + + public static Boolean isError(R ret) + { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) + { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/TreeEntity.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/TreeEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..5153d1f96304edfd40bfe63473f00c743cc9272c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.tongtech.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author XiaoZhangTongZhi + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/TreeSelect.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/TreeSelect.java new file mode 100644 index 0000000000000000000000000000000000000000..66780f842c15531e6f3853fb98199bf8a61762c1 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/TreeSelect.java @@ -0,0 +1,70 @@ +package com.tongtech.common.core.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.tongtech.common.core.domain.entity.SysMenu; + +/** + * Treeselect树结构实体类 + * + * @author XiaoZhangTongZhi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysDictData.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysDictData.java new file mode 100644 index 0000000000000000000000000000000000000000..64302a8ed025ad30d464a56c14a0e12043211f24 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysDictData.java @@ -0,0 +1,177 @@ +package com.tongtech.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.tongtech.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.annotation.Excel.ColumnType; +import com.tongtech.common.constant.UserConstants; + +/** + * 字典数据表 sys_dict_data + * + * @author XiaoZhangTongZhi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysDictType.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysDictType.java new file mode 100644 index 0000000000000000000000000000000000000000..bc21893fa1cba4506ea5b061f8de19d671202eb9 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysDictType.java @@ -0,0 +1,97 @@ +package com.tongtech.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import com.tongtech.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.annotation.Excel.ColumnType; + +/** + * 字典类型表 sys_dict_type + * + * @author XiaoZhangTongZhi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysMenu.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysMenu.java new file mode 100644 index 0000000000000000000000000000000000000000..742c672ac42ed6790f2ead8680be4eafc8db82eb --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysMenu.java @@ -0,0 +1,260 @@ +package com.tongtech.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.tongtech.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 菜单权限表 sys_menu + * + * @author XiaoZhangTongZhi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0显示 1隐藏) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List children = new ArrayList(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysRole.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysRole.java new file mode 100644 index 0000000000000000000000000000000000000000..3c99db632015501aa972a22c1cda6a48b882ccb9 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysRole.java @@ -0,0 +1,241 @@ +package com.tongtech.common.core.domain.entity; + +import java.util.Set; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.tongtech.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.annotation.Excel.ColumnType; + +/** + * 角色表 sys_role + * + * @author XiaoZhangTongZhi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + private String roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + private Long[] deptIds; + + /** 角色菜单权限 */ + private Set permissions; + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + @NotBlank(message = "显示顺序不能为空") + public String getRoleSort() + { + return roleSort; + } + + public void setRoleSort(String roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysUser.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysUser.java new file mode 100644 index 0000000000000000000000000000000000000000..e99fcec7991988773b22735a858f42100edb5fec --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/entity/SysUser.java @@ -0,0 +1,331 @@ +package com.tongtech.common.core.domain.entity; + +import java.util.Date; +import java.util.List; +import javax.validation.constraints.*; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.tongtech.common.core.domain.BaseEntity; +import com.tongtech.common.xss.Xss; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.annotation.Excel.ColumnType; +import com.tongtech.common.annotation.Excel.Type; + +/** + * 用户对象 sys_user + * + * @author XiaoZhangTongZhi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + private Long userId; + + + /** 用户账号 */ + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Excel(name = "手机号码") + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 帐号状态(0正常 1停用) */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** 登录重试次数,登录成功后重置为0 */ + private int loginRetries; + + /** 登录锁定(0正常 1锁定) */ + private String loginLocked; + + /** 密码过期时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") + private Date passwordExpired; + + /** 角色对象 */ + private List roles; + + /** 角色组 */ + private Long[] roleIds; + + /** 岗位组 */ + private Long[] postIds; + + /** 角色ID */ + private Long roleId; + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public int getLoginRetries() { + return loginRetries; + } + + public void setLoginRetries(int loginRetries) { + this.loginRetries = loginRetries; + } + + public String getLoginLocked() { + return loginLocked; + } + + public void setLoginLocked(String loginLocked) { + this.loginLocked = loginLocked; + } + + public Date getPasswordExpired() { + return passwordExpired; + } + + public void setPasswordExpired(Date passwordExpired) { + this.passwordExpired = passwordExpired; + } + + public List getRoles() + { + return roles; + } + + public void setRoles(List roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("loginRetries", getLoginRetries()) + .append("loginLocked", getLoginLocked()) + .append("passwordExpired", getPasswordExpired()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/LoginBody.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000000000000000000000000000000000000..9a85592cf102ccca5f442e989bcadff3373ca698 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/LoginBody.java @@ -0,0 +1,69 @@ +package com.tongtech.common.core.domain.model; + +/** + * 用户登录对象 + * + * @author XiaoZhangTongZhi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/LoginUser.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000000000000000000000000000000000000..7d694946d7197b34d4ef60bbfbf41d3f4b35230c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/LoginUser.java @@ -0,0 +1,253 @@ +package com.tongtech.common.core.domain.model; + +import java.util.Collection; +import java.util.Set; + +import com.tongtech.common.core.domain.entity.SysUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import com.alibaba.fastjson2.annotation.JSONField; + +/** + * 登录用户身份权限 + * + * @author XiaoZhangTongZhi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, SysUser user, Set permissions) + { + this.userId = userId; + this.user = user; + this.permissions = permissions; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection getAuthorities() + { + return null; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/RegisterBody.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000000000000000000000000000000000000..25faa3cb8e106a1069acfb557081d160c5e43cf7 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/domain/model/RegisterBody.java @@ -0,0 +1,11 @@ +package com.tongtech.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author XiaoZhangTongZhi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/page/PageDomain.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/page/PageDomain.java new file mode 100644 index 0000000000000000000000000000000000000000..736ccef9da77f7b5e53947931875642b06a71bb1 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.tongtech.common.core.page; + +import com.tongtech.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author XiaoZhangTongZhi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/page/TableDataInfo.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/page/TableDataInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..75122e7f37cd4a795db5130d40f39a7956dd0d59 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/page/TableDataInfo.java @@ -0,0 +1,85 @@ +package com.tongtech.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author XiaoZhangTongZhi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/page/TableSupport.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/page/TableSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..2dfe2c97a92d14c9cf4a8088da80d394b2a790c2 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.tongtech.common.core.page; + +import com.tongtech.common.core.text.Convert; +import com.tongtech.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author XiaoZhangTongZhi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/text/CharsetKit.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/text/CharsetKit.java new file mode 100644 index 0000000000000000000000000000000000000000..13ffe8788c3ff4b2a76e2a672c10d2f03e2f0c2c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.tongtech.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.tongtech.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author XiaoZhangTongZhi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/text/Convert.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/text/Convert.java new file mode 100644 index 0000000000000000000000000000000000000000..560aa9a0d0ca754bc52399fb58a69ab7928d93ac --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/text/Convert.java @@ -0,0 +1,1000 @@ +package com.tongtech.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.tongtech.common.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +/** + * 类型转换器 + * + * @author XiaoZhangTongZhi + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/core/text/StrFormatter.java b/rds-console/console-common/src/main/java/com/tongtech/common/core/text/StrFormatter.java new file mode 100644 index 0000000000000000000000000000000000000000..c6be9452295715b9da9a163989f117663142307d --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.tongtech.common.core.text; + +import com.tongtech.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author XiaoZhangTongZhi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/BusinessStatus.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/BusinessStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..c2b23417c18962003e6e18ba21a3b1385b46dde4 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.tongtech.common.enums; + +/** + * 操作状态 + * + * @author XiaoZhangTongZhi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/BusinessType.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/BusinessType.java new file mode 100644 index 0000000000000000000000000000000000000000..b7c0f21f121c94609c515a4a25f12c549cf46bf8 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.tongtech.common.enums; + +/** + * 业务操作类型 + * + * @author XiaoZhangTongZhi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/DataSourceType.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/DataSourceType.java new file mode 100644 index 0000000000000000000000000000000000000000..6b7fa25dc1e4de5f9d5ad526c361b6070eebce9f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.tongtech.common.enums; + +/** + * 数据源 + * + * @author XiaoZhangTongZhi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/DeployEnvEnum.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/DeployEnvEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..058eaa145ecd0172e035a318a1a61dd849339378 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/DeployEnvEnum.java @@ -0,0 +1,44 @@ +package com.tongtech.common.enums; + +import com.tongtech.common.utils.StringUtils; + +/** + * RDS 部署模式(单点、哨兵、集群、可伸缩集群) + */ +public enum DeployEnvEnum { + /**主机/虚机 */ + HOST("host"), + /** K8S容器云 */ + K8S("k8s"); + + private String name; + + DeployEnvEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public static DeployEnvEnum parse(String name) { + if(StringUtils.isNotEmpty(name)) { + String iName = name.toLowerCase(); + if (HOST.name.equals(iName)) { + return HOST; + } else if (K8S.name.equals(iName)) { + return K8S; + } else { + return null; + } + } + else { + return null; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/HttpMethod.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/HttpMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..9e161275a3694c5f50992a63b5d8dce62c999b22 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.tongtech.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author XiaoZhangTongZhi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/LimitType.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/LimitType.java new file mode 100644 index 0000000000000000000000000000000000000000..0bcef4da341a0e5ce1f7bc50de85b4a300f9a27c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/LimitType.java @@ -0,0 +1,20 @@ +package com.tongtech.common.enums; + +/** + * 限流类型 + * + * @author XiaoZhangTongZhi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/OperatorType.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/OperatorType.java new file mode 100644 index 0000000000000000000000000000000000000000..dd59ac39fd0a177f7cad53f18e22219421c7fb50 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.tongtech.common.enums; + +/** + * 操作人类别 + * + * @author XiaoZhangTongZhi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/TemplateNameEnum.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/TemplateNameEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..fa7a4c152de4d0110d948b7d930843a68c83b6ac --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/TemplateNameEnum.java @@ -0,0 +1,28 @@ +package com.tongtech.common.enums; + +/** + * 模板文件名和文件名枚举类 + */ +public enum TemplateNameEnum { + WORKER("工作节点默认模板", "serverNode.vm"), + SENTINEL("哨兵节点默认模板", "sentinelNode.vm"), + + CENTER("中心节点默认模板", "cluster.vm"), + DYNAMIC("服务节点动态配置", "dynamic.vm"); + + private String name; + private String fileName; + + TemplateNameEnum(String name, String fileName) { + this.name = name; + this.fileName = fileName; + } + + public String getName() { + return name; + } + + public String getFileName() { + return fileName; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/enums/UserStatus.java b/rds-console/console-common/src/main/java/com/tongtech/common/enums/UserStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..418688ba223974fd541ec53de59ab775847b87e1 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.tongtech.common.enums; + +/** + * 用户状态 + * + * @author XiaoZhangTongZhi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/DemoModeException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/DemoModeException.java new file mode 100644 index 0000000000000000000000000000000000000000..97f49f60d99371f59a3b0e3523740e2c3a713c3d --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.tongtech.common.exception; + +/** + * 演示模式异常 + * + * @author XiaoZhangTongZhi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/GlobalException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/GlobalException.java new file mode 100644 index 0000000000000000000000000000000000000000..7c8cfd0ffebcc686d8ea45c607f444324a26d305 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.tongtech.common.exception; + +/** + * 全局异常 + * + * @author XiaoZhangTongZhi + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/SSHProcessException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/SSHProcessException.java new file mode 100644 index 0000000000000000000000000000000000000000..bd9a94433789f7fe18c6659ca810ff0d7af89f23 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/SSHProcessException.java @@ -0,0 +1,29 @@ +package com.tongtech.common.exception; + +/** + * 在执行SSH操作时的异常 + * + * @author Zhang Chenlong + */ +public class SSHProcessException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public SSHProcessException() { + super(); + } + + public SSHProcessException(String message) { + super(message); + } + + public SSHProcessException(Throwable cause) { + super(cause); + } + + public SSHProcessException(String message, Throwable e) + { + super(message, e); + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/ServiceException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/ServiceException.java new file mode 100644 index 0000000000000000000000000000000000000000..aa054744c7be60450f544cf802e17c7bee967799 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.tongtech.common.exception; + +/** + * 业务异常 + * + * @author XiaoZhangTongZhi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/ShellCommandException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/ShellCommandException.java new file mode 100644 index 0000000000000000000000000000000000000000..3c3ce9ae83ee5ad86dd4f69e34a7a0f6eafcf5f5 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/ShellCommandException.java @@ -0,0 +1,29 @@ +package com.tongtech.common.exception; + +/** + * 在执行本地命令行时的异常 + * + * @author Zhang Chenlong + */ +public class ShellCommandException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public ShellCommandException() { + super(); + } + + public ShellCommandException(String message) { + super(message); + } + + public ShellCommandException(Throwable cause) { + super(cause); + } + + public ShellCommandException(String message, Throwable e) + { + super(message, e); + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/UtilException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/UtilException.java new file mode 100644 index 0000000000000000000000000000000000000000..e594398297335f5b3e3fe08b08deb52c81d305c0 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.tongtech.common.exception; + +/** + * 工具类异常 + * + * @author XiaoZhangTongZhi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/base/BaseException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/base/BaseException.java new file mode 100644 index 0000000000000000000000000000000000000000..9c9ae46bf83bf03d92596b63dd4e39e59937ad3f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.tongtech.common.exception.base; + +import com.tongtech.common.utils.MessageUtils; +import com.tongtech.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author XiaoZhangTongZhi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileException.java new file mode 100644 index 0000000000000000000000000000000000000000..4b7849109e5776174378a1116e2908bb8b31b30d --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.tongtech.common.exception.file; + +import com.tongtech.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author XiaoZhangTongZhi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileNameLengthLimitExceededException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000000000000000000000000000000000000..3f9d14f0afa9a7c5cb9f8c1b93c0eb691d374ed1 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.tongtech.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author XiaoZhangTongZhi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileSizeLimitExceededException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000000000000000000000000000000000000..53f1509e8afdc4f8d9516ebe10c57388e9bc786a --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.tongtech.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author XiaoZhangTongZhi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/InvalidExtensionException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000000000000000000000000000000000000..6d9d0f2d4f1864f556ad6e2877c0dc7d176ad292 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,81 @@ +package com.tongtech.common.exception.file; + +import java.util.Arrays; +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author XiaoZhangTongZhi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/job/TaskException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/job/TaskException.java new file mode 100644 index 0000000000000000000000000000000000000000..1faf335176908d2fefd55d452948a291c5df6a30 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.tongtech.common.exception.job; + +/** + * 计划策略异常 + * + * @author XiaoZhangTongZhi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/CaptchaException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/CaptchaException.java new file mode 100644 index 0000000000000000000000000000000000000000..7023d6ba901b4bc8bf925ef5338d8198338a7713 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.tongtech.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author XiaoZhangTongZhi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/CaptchaExpireException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000000000000000000000000000000000000..1b3f4ed2f96f9fae75a10439d33f4552670ad99c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.tongtech.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author XiaoZhangTongZhi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/UserException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/UserException.java new file mode 100644 index 0000000000000000000000000000000000000000..ecc8391f2317721df679f1a143ae7084f678784e --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.tongtech.common.exception.user; + +import com.tongtech.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author XiaoZhangTongZhi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/UserPasswordNotMatchException.java b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000000000000000000000000000000000000..2b4b2c7862f5cae895a8ca2056a16ec84eab6825 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.tongtech.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author XiaoZhangTongZhi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/filter/PropertyPreExcludeFilter.java b/rds-console/console-common/src/main/java/com/tongtech/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..d7aeb2d394a38084011e71870a09c1532bd56c4c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.tongtech.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author XiaoZhangTongZhi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/filter/RepeatableFilter.java b/rds-console/console-common/src/main/java/com/tongtech/common/filter/RepeatableFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..f6dfc679229d1dd3e5da8e529f344d42b34ce1a8 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/filter/RepeatableFilter.java @@ -0,0 +1,53 @@ +package com.tongtech.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import com.tongtech.common.utils.StringUtils; +import org.springframework.http.MediaType; + +/** + * Repeatable 过滤器 + * + * @author XiaoZhangTongZhi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/filter/RepeatedlyRequestWrapper.java b/rds-console/console-common/src/main/java/com/tongtech/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..8a81cd2436b902caadff5f1986b4575c3638b14e --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,77 @@ +package com.tongtech.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import com.tongtech.common.utils.http.HttpHelper; +import com.tongtech.common.constant.Constants; + +/** + * 构建可重复读取inputStream的request + * + * @author XiaoZhangTongZhi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/filter/XssFilter.java b/rds-console/console-common/src/main/java/com/tongtech/common/filter/XssFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..52a3efb6b86aed1924d742f1be4374e3401885df --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/filter/XssFilter.java @@ -0,0 +1,76 @@ +package com.tongtech.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.enums.HttpMethod; +import com.tongtech.common.utils.StringUtils; + +/** + * 防止XSS攻击的过滤器 + * + * @author XiaoZhangTongZhi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/filter/XssHttpServletRequestWrapper.java b/rds-console/console-common/src/main/java/com/tongtech/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..a336437975e2a1a0699527179c4ecd738f894a02 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,112 @@ +package com.tongtech.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.html.EscapeUtil; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +/** + * XSS过滤处理 + * + * @author XiaoZhangTongZhi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/test/ClassResourceUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/test/ClassResourceUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..b81bb02c755be2bcfed5c9ea6240088b6a0e1dd7 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/test/ClassResourceUtils.java @@ -0,0 +1,56 @@ +package com.tongtech.common.test; + +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + + + +public class ClassResourceUtils { + + + /** + * 获得classpath中的资源文件的路径,返回资源文件(java.io.File) + * @param resourceName + * @return + */ + public static File getResourceFile(String resourceName) { + File resFile = null; + try { + Enumeration urls = ClassResourceUtils.class.getClassLoader().getResources(resourceName); + while(urls.hasMoreElements()) { + URL url = urls.nextElement(); + resFile = new File(url.toURI()); + break; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return resFile; + } + + public static String loadResource(String resourceName) { + return loadResource(resourceName, "UTF-8"); + } + + /** + * 读取classpath中的资源文件的内容,返回为String类型。 + * @param resourceName + * @return + */ + public static String loadResource(String resourceName, String charsetName) { + String content = null; + try(InputStream licenseIn = ClassResourceUtils.class.getClassLoader().getResourceAsStream(resourceName)) { + content = IOUtils.toString(licenseIn, charsetName); + } + catch (Exception e) { + throw new RuntimeException(e); + } + return content; + } + + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/AESUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/AESUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..cae2f8bdfa725b1e1ffd6d41c6263d8a60891ebe --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/AESUtils.java @@ -0,0 +1,92 @@ +package com.tongtech.common.utils; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * AES加解密 + * + */ +public class AESUtils { + + /* + * 加密用的Key 可以用26个字母和数字组成 使用AES-128-CBC加密模式,key需要为16位。 + */ + private static final String key="hj7x89H$yuBI0456"; + private static final String iv ="NIfb&95GUY86Gfgh"; + /** + * @Description AES算法加密明文 + * @param data 明文 + * @return 密文 + */ + public static String encryptAES(String data) { + try { + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + int blockSize = cipher.getBlockSize(); + byte[] dataBytes = data.getBytes(); + int plaintextLength = dataBytes.length; + + if (plaintextLength % blockSize != 0) { + plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize)); + } + + byte[] plaintext = new byte[plaintextLength]; + System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length); + + SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); + IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); // CBC模式,需要一个向量iv,可增加加密算法的强度 + + cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); + byte[] encrypted = cipher.doFinal(plaintext); + + return new Base64().encodeToString(encrypted); // BASE64做转码。 + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * @Description AES算法解密密文 + * @param data 密文 + * @return 明文 + */ + public static String decryptAES(String data){ + try + { + byte[] encrypted1 = new Base64().decode(data);//先用base64解密 + + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); + IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); + + cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);// 初始化 + byte[] original = cipher.doFinal(encrypted1); + String originalString = new String(original); + return originalString.trim(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*public static void main(String[] args) { + try { + boolean matches = "Apassword123_@".matches(Constants.PASSWORD_RULE); + } catch (Exception e) { + e.printStackTrace(); + } + }*/ + + + /*public static void main(String[] args) { + String s = "^[\\u4e00-\\u9fa5_a-zA-Z0-9]+[\\u4e00-\\u9fa5_a-zA-Z0-9]$"; + String ss = "^(?!,)(?!.*?,$)(?!,)(?!.*?,$)+$"; + String s1 = "asjksdfhksdl"; + boolean matches = s1.matches(ss); + + }*/ +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/Arith.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/Arith.java new file mode 100644 index 0000000000000000000000000000000000000000..dc5f035b3610faed2a4d498e3cf06a3ac3d23988 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.tongtech.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author XiaoZhangTongZhi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = BigDecimal.ONE; + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/AssertUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/AssertUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..3dcab5f237283b473a0989e5b65355f908d1f479 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/AssertUtils.java @@ -0,0 +1,50 @@ +package com.tongtech.common.utils; + +import com.tongtech.common.exception.ServiceException; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 断言工具类 方便快速判空 并抛出异常 + */ +public class AssertUtils { + /** + * 非空对象 + * + * @param object 判空对象 + * @param exceptionString 抛出异常所携带的信息 + */ + public static void ObjectIsNull(@NotNull Object object, String exceptionString) { + if (object == null) + throw new ServiceException(exceptionString); + } + + /** + * String 类型判断空 并抛出异常 + * + * @param string String对象 + * @param exceptionString 抛出异常的信息 + */ + public static void StringIsNull(@NotNull @NotBlank String string, String exceptionString) { + if (StringUtils.isEmpty(string)) + throw new ServiceException(exceptionString); + } + + /** + * String 类型判断空 并抛出异常 + * + * @param list list 对象 + * @param exceptionString 抛出异常的信息 + */ + public static void ListIsNullOrSizeZero(@NotNull List list, String exceptionString) { + if (list == null || list.size() == 0) + throw new ServiceException(exceptionString); + } + + public static void IntegerValueGreaterZero(Integer integer, String exceptionString) { + if (integer <= 0) + throw new ServiceException(exceptionString); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/CRC64Utils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/CRC64Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..7ff8051df5b212f4447e49b5aea209ee1abd34eb --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/CRC64Utils.java @@ -0,0 +1,101 @@ +package com.tongtech.common.utils; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class CRC64Utils { + + private static final long POLY32REV = 0xEDB88320; + private static final long POLY64REV = 0x95AC9329AC4BC9B5L; + private static long[] Table = new long[256]; + private static final String crcFileSuffix = ".crc"; + + static { + for (int i = 0; i < 256; i++) { + long r = i; + for (int j = 0; j < 8; j++) + if ((r & 1) != 0) + r = (r >>> 1) ^ POLY64REV; + else + r >>>= 1; + Table[i] = r; + } + } + + public static String generateFileCRC(File file) throws IOException { + long nResult = 0; + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + byte[] buff = new byte[1024]; + CRC64Utils crc64 = new CRC64Utils(); + crc64.Init(); + while (true) { + int nRead = fis.read(buff); + if (nRead > 0) + crc64.Update(buff, 0, nRead); + if (nRead < 1024) + break; + } + nResult = crc64.GetDigest(); + } finally { + try { + fis.close(); + } catch (Throwable t2) { + } + } + return Long.toHexString(nResult); + } + + /** + * 在对应文件夹生成一个同名.crc文件。 + * @param file 源文件 + * @return 生成的CRC文件路径 + * @throws IOException + */ + public static File saveFileCRC(File file) throws IOException { + String content = generateFileCRC(file); + File crcFile = new File(buildCRCFileUrl(file.getAbsolutePath() )); + FileUtils.writeByteArrayToFile(crcFile, content.getBytes(StandardCharsets.UTF_8)); + + return crcFile; + } + + public static String readFileCRC(String filePath) throws IOException { + return new String(Files.readAllBytes(Paths.get(filePath))); + } + + private static String buildCRCFileUrl(String filePath) { + return filePath + crcFileSuffix; + } + + private long _value = -1; + + public void Init() { + _value = -1; + } + + public void Update(byte[] data) { + Update(data, 0, data.length); + } + + public void Update(byte[] data, int offset, int size) { + for (int i = 0; i < size; i++) { + UpdateByte(data[offset + i]); + } + } + + public void UpdateByte(int b) { + _value = Table[((int) _value ^ b) & 0xFF] ^ (_value >>> 8); + } + + public long GetDigest() { + return _value ^ (-1); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/CompareUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/CompareUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..1c9bba27ed63859544045b4088990772fad14df3 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/CompareUtils.java @@ -0,0 +1,59 @@ +package com.tongtech.common.utils; + +/** + * 用于对比两个值是否相同。 + */ +public class CompareUtils { + + public static boolean notEquals(String a, String b) { + return !isEquals(a, b); + } + + public static boolean isEquals(String a, String b) { + if(a == null) { + return (b == null) ? true : false; + } + else { + return a.equals(b); + } + } + + public static boolean notEquals(Integer a, Integer b) { + return !isEquals(a, b); + } + + public static boolean isEquals(Integer a, Integer b) { + if(a == null) { + return (b == null) ? true : false; + } + else { + return a.equals(b); + } + } + + public static boolean notEquals(Long a, Long b) { + return !isEquals(a, b); + } + + public static boolean isEquals(Long a, Long b) { + if(a == null) { + return (b == null) ? true : false; + } + else { + return a.equals(b); + } + } + + public static boolean notEquals(Boolean a, Boolean b) { + return !isEquals(a, b); + } + + public static boolean isEquals(Boolean a, Boolean b) { + if(a == null) { + return (b == null) ? true : false; + } + else { + return a.equals(b); + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/DateUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/DateUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..1b067ef46b59dd1a6c009d0a584543d5bcfea57e --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/DateUtils.java @@ -0,0 +1,196 @@ +package com.tongtech.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author XiaoZhangTongZhi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 获取未来的时间 timeUnit 取值 详见Calendar
+ * getFutureDate(Calendar.DATE,1) // 获取一天后的时间 + * + * @param timeUnit + * @param timescale + * @return + */ + public static Date getFutureDate(int timeUnit, int timescale) { + return getFutureDate(new Date(), timeUnit, timescale); + } + + /** + * 求一个指定日期未来时间 + * + * @param date + * @param timeUnit + * @param Timescale + * @return + */ + public static Date getFutureDate(Date date, int timeUnit, int Timescale) { + Calendar calendar = new GregorianCalendar(); + calendar.setTime(date); + // 把日期往后增加一天,整数 往后推,负数往前移动 + calendar.add(timeUnit, Timescale); + return calendar.getTime(); + } + + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/DictUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/DictUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..60c787fa99157cd8a17e58adbb8f7c1cc6505bfc --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/DictUtils.java @@ -0,0 +1,190 @@ +package com.tongtech.common.utils; + +import java.util.Collection; +import java.util.List; + +import com.tongtech.common.core.cache.ObjectCache; +import com.tongtech.common.core.domain.entity.SysDictData; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.utils.spring.SpringUtils; + +/** + * 字典工具类 + * + * @author XiaoZhangTongZhi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + SpringUtils.getBean(ObjectCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + //TODO 如果改回redis cache 需要使用此注释部分 +// JSONArray arrayCache = SpringUtils.getBean(ObjectCache.class).getCacheObject(getCacheKey(key)); +// if (StringUtils.isNotNull(arrayCache)) +// { +// return arrayCache.toList(SysDictData.class); +// } +// return null; + + ObjectCache cache = SpringUtils.getBean(ObjectCache.class); + return cache.getCacheObject(getCacheKey(key)); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StringUtils.isNotNull(datas)) + { + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictValue.equals(dict.getDictValue())) + { + return dict.getDictLabel(); + } + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictLabel.equals(dict.getDictLabel())) + { + return dict.getDictValue(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(ObjectCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + Collection keys = SpringUtils.getBean(ObjectCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(ObjectCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/ExceptionUtil.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ExceptionUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..e59d7af9b7520805e99b2125bdbd40b3262d99de --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.tongtech.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author XiaoZhangTongZhi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/LogUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/LogUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..18c752c5636922cc38a926ef4a2db1676e865285 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.tongtech.common.utils; + +/** + * 处理并记录日志文件 + * + * @author XiaoZhangTongZhi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/MessageUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/MessageUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..e622670f75752accd388752c87999f14ac2f19b7 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.tongtech.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.tongtech.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author XiaoZhangTongZhi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/ObjectSerializeUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ObjectSerializeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ed822dc02947c71ed67b45064f581668f5d0e246 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ObjectSerializeUtils.java @@ -0,0 +1,36 @@ +package com.tongtech.common.utils; + +import com.tongtech.common.utils.operation.ByteArrayOutputStream; +import org.apache.ibatis.cache.CacheException; +import org.apache.ibatis.cache.decorators.SerializedCache; + +import java.io.ByteArrayInputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class ObjectSerializeUtils { + + + public static byte[] serialize(Serializable value) { + try(ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos);) { + oos.writeObject(value); + oos.flush(); + return bos.toByteArray(); + } catch (Exception e) { + throw new CacheException("Error serializing object. Cause: " + e, e); + } + } + + + public static Serializable deserialize(byte[] value) { + try(ObjectInputStream ois = new SerializedCache + .CustomObjectInputStream(new ByteArrayInputStream(value))) { + return (Serializable) ois.readObject(); + } catch (Exception e) { + throw new CacheException("Error deserializing object. Cause: " + e, e); + } + } + + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/PageUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/PageUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..e1a9a663cd8c0b3f08bbbbc9fb2afe9239746f74 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/PageUtils.java @@ -0,0 +1,35 @@ +package com.tongtech.common.utils; + +import com.github.pagehelper.PageHelper; +import com.tongtech.common.core.page.PageDomain; +import com.tongtech.common.core.page.TableSupport; +import com.tongtech.common.utils.sql.SqlUtil; + +/** + * 分页工具类 + * + * @author XiaoZhangTongZhi + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/RedisUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/RedisUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2407160fe93a24c29cf30967c541b199620d48cf --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/RedisUtils.java @@ -0,0 +1,60 @@ +package com.tongtech.common.utils; + +import com.tongtech.common.exception.ServiceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; + +/** + * 测试 redis服务是否可用 + */ +public class RedisUtils { + + protected final static Logger logger = LoggerFactory.getLogger(RedisUtils.class); + + /** + * 检查redis是否存活 + * @param url 服务器地址 + * @param port 端口 + * @return + */ + public static void testRedis(String url, int port) { + String result; + try(Jedis jedis = new Jedis(url, port)) { + String ping = jedis.ping(); + if (ping.equalsIgnoreCase("PONG") == false) { + result = "Response: '" + ping + "'. Error, It should be 'PONG' !"; + throw new ServiceException(result); + } + } catch (Exception e) { + throw e; + } + } + + + + /** + * 检查redis是否存活 + * @param url 服务器地址 + * @param port 端口 + * @param password redis的密码 + * @return null 表示连接成功;失败会返回失败原因; + */ + public static void testRedis(String url, int port, String password) { + try(Jedis jedis = new Jedis(url, port)) { + jedis.auth(password);//密码 + String ping = jedis.ping(); + if (ping.equalsIgnoreCase("PONG") == false) { + String result = "Response: '" + ping + "'. Error, It should be 'PONG' !"; + throw new ServiceException(result); + } + } catch (Exception e) { + throw e; + } + } + + public static void main(String[] args) { + testRedis("127.0.0.1", 6379, "foobared"); + testRedis("127.0.0.1", 6379); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/SecurityUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/SecurityUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a739ab4de47aa72caa623667ecaa2e0d156442d1 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/SecurityUtils.java @@ -0,0 +1,106 @@ +package com.tongtech.common.utils; + +import com.tongtech.common.core.domain.model.LoginUser; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.tongtech.common.constant.HttpStatus; +import com.tongtech.common.exception.ServiceException; + +/** + * 安全服务工具类 + * + * @author XiaoZhangTongZhi + */ +public class SecurityUtils +{ + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/ServletUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ServletUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..677e3c316d56f8ce030cc4241637cb21fe7f5a80 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ServletUtils.java @@ -0,0 +1,187 @@ +package com.tongtech.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import com.tongtech.common.core.text.Convert; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.tongtech.common.constant.Constants; + +/** + * 客户端工具类 + * + * @author XiaoZhangTongZhi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/StringUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/StringUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..05cfa8eb25171c52af0da7747ecf59e07b55b5f7 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/StringUtils.java @@ -0,0 +1,655 @@ +package com.tongtech.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tongtech.common.core.text.StrFormatter; +import org.springframework.util.AntPathMatcher; +import com.tongtech.common.constant.Constants; + +/** + * 字符串工具类 + * + * @author XiaoZhangTongZhi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + + private static long SIZE_KB = 1024; + private static long SIZE_MB = 1048576; + private static long SIZE_GB = 1073741824; + private static long SIZE_TB = 1099511627776L; + + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param set 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 把内存和磁盘大小的整数值,显示为 KB、MB、GB、TB的字符串 + * + * @param size 字节大小 + * @return 转换后值 + */ + public static String toReadableSize(long size) + { + if(size >= SIZE_TB) { + float f = (float) size / SIZE_TB; + return String.format(f > 100 ? "%.0fTB" : "%.1fTB", f); + } + else if (size >= SIZE_GB) + { + float f = (float) size / SIZE_GB; + return String.format(f > 100 ? "%.0fGB" : "%.1fGB", f); + } + else if (size >= SIZE_MB) + { + float f = (float) size / SIZE_MB; + return String.format(f > 100 ? "%.0fMB" : "%.1fMB", f); + } + else if (size >= SIZE_KB) + { + float f = (float) size / SIZE_KB; + return String.format(f > 100 ? "%.0fKB" : "%.1fKB", f); + } + else + { + return String.format("%dB", size); + } + } + + + public static String trimQuote(String input) { + return input.replace('"',' ').trim(); + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/TemplateUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/TemplateUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..6372540c91f0f873f34fc23bfbb92c7921527ab4 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/TemplateUtils.java @@ -0,0 +1,21 @@ +package com.tongtech.common.utils; + +import com.tongtech.common.config.AppHomeConfig; +import com.tongtech.common.exception.ServiceException; +import com.tongtech.common.utils.file.FileUtils; + +import java.io.File; +import java.nio.charset.StandardCharsets; + +public class TemplateUtils { + public static String readLocalTemplate(String templateFileName) { + + try { + File templateFile = AppHomeConfig.getAbsoluteFile(AppHomeConfig.TEMPLATE_PATH, templateFileName); + return FileUtils.readFileToString(templateFile, StandardCharsets.UTF_8); + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException("read template file an exception occurs."); + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/Threads.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/Threads.java new file mode 100644 index 0000000000000000000000000000000000000000..f7f9820c0835b976ce5fa4fcc64117f14b418111 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.tongtech.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author XiaoZhangTongZhi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/bean/BeanUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d7b52fd95f4e1ed5990e2f0015d80a58fcd7747f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.tongtech.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author XiaoZhangTongZhi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/bean/BeanValidators.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/bean/BeanValidators.java new file mode 100644 index 0000000000000000000000000000000000000000..47efdf043d622af812074ea718fb4a76420c749c --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.tongtech.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author XiaoZhangTongZhi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileTypeUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..0858fc31ecde5bc7535c36cb80624f22de08a20d --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.tongtech.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author XiaoZhangTongZhi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileUploadUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ca3613f02ac97373d48ddf0bd110c50a6193e295 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileUploadUtils.java @@ -0,0 +1,273 @@ +package com.tongtech.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +import com.tongtech.common.utils.DateUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.config.AppHomeConfig; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.exception.file.FileNameLengthLimitExceededException; +import com.tongtech.common.exception.file.FileSizeLimitExceededException; +import com.tongtech.common.exception.file.InvalidExtensionException; +import com.tongtech.common.utils.uuid.Seq; + +/** + * 文件上传工具类 + * + * @author XiaoZhangTongZhi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认文本类文件最大 1M + */ + public static final long DEFAULT_MAX_TEXT_SIZE = 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = AppHomeConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + + /** + * 文件上传, MultipartFile file 中的文件名作为文件保存到 baseDir中。 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String uploadToDir(File baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = FilenameUtils.getName(file.getOriginalFilename()); + + File dest = new File(baseDir, fileName); + if(baseDir.exists() == false) { + baseDir.mkdirs(); + } + file.transferTo(dest); + return fileName; + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = AppHomeConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..e0f563f0b76c2f4b5bd52008a56c6b5f7e423317 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/FileUtils.java @@ -0,0 +1,299 @@ +package com.tongtech.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.utils.DateUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.config.AppHomeConfig; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import com.tongtech.common.utils.uuid.IdUtils; +import org.apache.commons.io.FilenameUtils; + +/** + * 文件处理工具类 + * + * @author XiaoZhangTongZhi + */ +public class FileUtils extends org.apache.commons.io.FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + writeBytes(new File(filePath), os); + } + + /** + * 输出指定文件的byte数组 + * + * @param file 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(File file, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + if (!file.exists()) + { + throw new FileNotFoundException(file.getAbsolutePath()); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, AppHomeConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + file.delete(); + flag = true; + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/ImageUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/ImageUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..c048ef281380357f30c37a05b59427f804240233 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/ImageUtils.java @@ -0,0 +1,99 @@ +package com.tongtech.common.utils.file; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.config.AppHomeConfig; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.tongtech.common.constant.Constants; + +/** + * 图片处理工具类 + * + * @author XiaoZhangTongZhi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = AppHomeConfig.getProfile(); + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/MimeTypeUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..60f1a1bf8a5c128c7d7ebd348db31910a17fc83d --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,61 @@ +package com.tongtech.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author XiaoZhangTongZhi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] GZIP_EXTENSION = { "gz", "tar.gz", "tgz" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/html/EscapeUtil.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..0d5af1cdbf8914fec3a92a72f8133b8791d70722 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.tongtech.common.utils.html; + +import com.tongtech.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author XiaoZhangTongZhi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/html/HTMLFilter.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..599e82b3a76260f891958cf8827606f51af0493f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.tongtech.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author XiaoZhangTongZhi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/http/HttpHelper.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/http/HttpHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..6bcb4b38488518d7264b58f02e484e46dabdf99a --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.tongtech.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author XiaoZhangTongZhi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/http/HttpUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/http/HttpUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..096c7a584f1af9e94a6b375984940794b1149677 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/http/HttpUtils.java @@ -0,0 +1,275 @@ +package com.tongtech.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import com.tongtech.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.tongtech.common.constant.Constants; + +/** + * 通用http发送方法 + * + * @author XiaoZhangTongZhi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/ip/AddressUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..6515fe8f3b2801c7d5dc13d3e9e36905c67521db --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package com.tongtech.common.utils.ip; + +import com.tongtech.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.tongtech.common.config.UhConsoleConfig; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author XiaoZhangTongZhi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (UhConsoleConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/ip/IpUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ip/IpUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ad218094e29bed4bed1c51175a9febe2e0ab87d4 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/ip/IpUtils.java @@ -0,0 +1,265 @@ +package com.tongtech.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; + +import com.tongtech.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author XiaoZhangTongZhi + */ +public class IpUtils +{ + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/ByteArrayOutputStream.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/ByteArrayOutputStream.java new file mode 100755 index 0000000000000000000000000000000000000000..2082f1b30364bc1ea68f3995c34ba78faeb789f3 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/ByteArrayOutputStream.java @@ -0,0 +1,379 @@ +package com.tongtech.common.utils.operation; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This class implements an output stream in which the data is + * written into a byte array. The buffer automatically grows as data + * is written to it. + *

+ * The data can be retrieved using toByteArray() and + * toString(). + *

+ * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + *

+ * This is an alternative implementation of the {@link java.io.ByteArrayOutputStream} + * class. The original implementation only allocates 32 bytes at the beginning. + * As this class is designed for heavy duty it starts at 1024 bytes. In contrast + * to the original it doesn't reallocate the whole memory block but allocates + * additional buffers. This way no buffers need to be garbage collected and + * the contents don't have to be copied to the new buffer. This class is + * designed to behave exactly like the original. The only exception is the + * deprecated toString(int) method that has been ignored. + * + * @version $Id: ByteArrayOutputStream.java 1304052 2012-03-22 20:55:29Z ggregory $ + */ +public class ByteArrayOutputStream extends OutputStream { + + /** A singleton empty byte array. */ + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + /** The list of buffers, which grows and never reduces. */ + private final List buffers = new ArrayList(); + /** The index of the current buffer. */ + private int currentBufferIndex; + /** The total count of bytes in all the filled buffers. */ + private int filledBufferSum; + /** The current buffer. */ + private byte[] currentBuffer; + /** The total count of bytes written. */ + private int count; + + /** + * Creates a new byte array output stream. The buffer capacity is + * initially 1024 bytes, though its size increases if necessary. + */ + public ByteArrayOutputStream() { + this(1024); + } + + /** + * Creates a new byte array output stream, with a buffer capacity of + * the specified size, in bytes. + * + * @param size the initial size + * @throws IllegalArgumentException if size is negative + */ + public ByteArrayOutputStream(int size) { + if (size < 0) { + throw new IllegalArgumentException( + "Negative initial size: " + size); + } + synchronized (this) { + needNewBuffer(size); + } + } + + /** + * Makes a new buffer available either by allocating + * a new one or re-cycling an existing one. + * + * @param newcount the size of the buffer if one is created + */ + private void needNewBuffer(int newcount) { + if (currentBufferIndex < buffers.size() - 1) { + //Recycling old buffer + filledBufferSum += currentBuffer.length; + + currentBufferIndex++; + currentBuffer = buffers.get(currentBufferIndex); + } else { + //Creating new buffer + int newBufferSize; + if (currentBuffer == null) { + newBufferSize = newcount; + filledBufferSum = 0; + } else { + newBufferSize = Math.max( + currentBuffer.length << 1, + newcount - filledBufferSum); + filledBufferSum += currentBuffer.length; + } + + currentBufferIndex++; + currentBuffer = new byte[newBufferSize]; + buffers.add(currentBuffer); + } + } + + /** + * Write the bytes to byte array. + * @param b the bytes to write + * @param off The start offset + * @param len The number of bytes to write + */ + @Override + public void write(byte[] b, int off, int len) { + if ((off < 0) + || (off > b.length) + || (len < 0) + || ((off + len) > b.length) + || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + synchronized (this) { + int newcount = count + len; + int remaining = len; + int inBufferPos = count - filledBufferSum; + while (remaining > 0) { + int part = Math.min(remaining, currentBuffer.length - inBufferPos); + System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part); + remaining -= part; + if (remaining > 0) { + needNewBuffer(newcount); + inBufferPos = 0; + } + } + count = newcount; + } + } + + /** + * Write a byte to byte array. + * @param b the byte to write + */ + @Override + public synchronized void write(int b) { + int inBufferPos = count - filledBufferSum; + if (inBufferPos == currentBuffer.length) { + needNewBuffer(count + 1); + inBufferPos = 0; + } + currentBuffer[inBufferPos] = (byte) b; + count++; + } + + /** + * Writes the entire contents of the specified input stream to this + * byte stream. Bytes from the input stream are read directly into the + * internal buffers of this streams. + * + * @param in the input stream to read from + * @return total number of bytes read from the input stream + * (and written to this stream) + * @throws IOException if an I/O error occurs while reading the input stream + * @since 1.4 + */ + public synchronized int write(InputStream in) throws IOException { + int readCount = 0; + int inBufferPos = count - filledBufferSum; + int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos); + while (n != -1) { + readCount += n; + inBufferPos += n; + count += n; + if (inBufferPos == currentBuffer.length) { + needNewBuffer(currentBuffer.length); + inBufferPos = 0; + } + n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos); + } + return readCount; + } + + /** + * Return the current size of the byte array. + * @return the current size of the byte array + */ + public synchronized int size() { + return count; + } + + /** + * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + * + * @throws IOException never (this method should not declare this exception + * but it has to now due to backwards compatability) + */ + @Override + public void close() throws IOException { + //nop + } + + /** + * @see java.io.ByteArrayOutputStream#reset() + */ + public synchronized void reset() { + count = 0; + filledBufferSum = 0; + currentBufferIndex = 0; + currentBuffer = buffers.get(currentBufferIndex); + } + + /** + * Writes the entire contents of this byte stream to the + * specified output stream. + * + * @param out the output stream to write to + * @throws IOException if an I/O error occurs, such as if the stream is closed + * @see java.io.ByteArrayOutputStream#writeTo(OutputStream) + */ + public synchronized void writeTo(OutputStream out) throws IOException { + int remaining = count; + for (byte[] buf : buffers) { + int c = Math.min(buf.length, remaining); + out.write(buf, 0, c); + remaining -= c; + if (remaining == 0) { + break; + } + } + } + + /** + * Fetches entire contents of an InputStream and represent + * same data as result InputStream. + *

+ * This method is useful where, + *

    + *
  • Source InputStream is slow.
  • + *
  • It has network resources associated, so we cannot keep it open for + * long time.
  • + *
  • It has network timeout associated.
  • + *
+ * It can be used in favor of {@link #toByteArray()}, since it + * avoids unnecessary allocation and copy of byte[].
+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input Stream to be fully buffered. + * @return A fully buffered stream. + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static InputStream toBufferedInputStream(InputStream input) + throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write(input); + return output.toBufferedInputStream(); + } + + /** + * Gets the current contents of this byte stream as a Input Stream. The + * returned stream is backed by buffers of this stream, + * avoiding memory allocation and copy, thus saving space and time.
+ * + * @return the current contents of this output stream. + * @see java.io.ByteArrayOutputStream#toByteArray() + * @see #reset() + * @since 2.0 + */ + private InputStream toBufferedInputStream() { + int remaining = count; + if (remaining == 0) { + return new ClosedInputStream(); + } + List list = new ArrayList(buffers.size()); + for (byte[] buf : buffers) { + int c = Math.min(buf.length, remaining); + list.add(new ByteArrayInputStream(buf, 0, c)); + remaining -= c; + if (remaining == 0) { + break; + } + } + return new SequenceInputStream(Collections.enumeration(list)); + } + + /** + * Gets the curent contents of this byte stream as a byte array. + * The result is independent of this stream. + * + * @return the current contents of this output stream, as a byte array + * @see java.io.ByteArrayOutputStream#toByteArray() + */ + public synchronized byte[] toByteArray() { + int remaining = count; + if (remaining == 0) { + return EMPTY_BYTE_ARRAY; + } + byte newbuf[] = new byte[remaining]; + int pos = 0; + for (byte[] buf : buffers) { + int c = Math.min(buf.length, remaining); + System.arraycopy(buf, 0, newbuf, pos, c); + pos += c; + remaining -= c; + if (remaining == 0) { + break; + } + } + return newbuf; + } + + /** + * Gets the curent contents of this byte stream as a string. + * @return the contents of the byte array as a String + * @see java.io.ByteArrayOutputStream#toString() + */ + @Override + public String toString() { + return new String(toByteArray()); + } + + /** + * Gets the curent contents of this byte stream as a string + * using the specified encoding. + * + * @param enc the name of the character encoding + * @return the string converted from the byte array + * @throws UnsupportedEncodingException if the encoding is not supported + * @see java.io.ByteArrayOutputStream#toString(String) + */ + public String toString(String enc) throws UnsupportedEncodingException { + return new String(toByteArray(), enc); + } + + + /** + * Closed input stream. This stream returns -1 to all attempts to read + * something from the stream. + *

+ * Typically uses of this class include testing for corner cases in methods + * that accept input streams and acting as a sentinel value instead of a + * {@code null} input stream. + * + * @version $Id: ClosedInputStream.java 1307459 2012-03-30 15:11:44Z ggregory $ + * @since 1.4 + */ + public class ClosedInputStream extends InputStream { + + /** + * Returns -1 to indicate that the stream is closed. + * + * @return always -1 + */ + @Override + public int read() { + return -1; + } + + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/Charsets.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/Charsets.java new file mode 100755 index 0000000000000000000000000000000000000000..4a8d2b3d2cfbb7eac95eea00c6ee153b491af343 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/Charsets.java @@ -0,0 +1,155 @@ +package com.tongtech.common.utils.operation; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +/** + * Charsets required of every implementation of the Java platform. + * + * From the Java documentation + * Standard charsets: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

+ * + *
    + *
  • US-ASCII
    + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • + *
  • ISO-8859-1
    + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • + *
  • UTF-8
    + * Eight-bit Unicode Transformation Format.
  • + *
  • UTF-16BE
    + * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • + *
  • UTF-16LE
    + * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • + *
  • UTF-16
    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)
  • + *
+ * + * @see Standard charsets + * @since 2.3 + * @version $Id: Charsets.java 1311751 2012-04-10 14:26:21Z ggregory $ + */ +public class Charsets { + // + // This class should only contain Charset instances for required encodings. This guarantees that it will load + // correctly and without delay on all Java platforms. + // + + /** + * Returns the given Charset or the default Charset if the given Charset is null. + * + * @param charset + * A charset or null. + * @return the given Charset or the default Charset if the given Charset is null + */ + public static Charset toCharset(Charset charset) { + return charset == null ? Charset.defaultCharset() : charset; + } + + /** + * Returns a Charset for the named charset. If the name is null, return the default Charset. + * + * @param charset + * The name of the requested charset, may be null. + * @return a Charset for the named charset + * @throws UnsupportedCharsetException + * If the named charset is unavailable + */ + public static Charset toCharset(String charset) { + return charset == null ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + + /** + *

+ * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final Charset US_ASCII = Charset.forName("US-ASCII"); + + /** + *

+ * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final Charset UTF_16 = Charset.forName("UTF-16"); + + /** + *

+ * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); + + /** + *

+ * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); + + /** + *

+ * Eight-bit Unicode Transformation Format. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final Charset UTF_8 = Charset.forName("UTF-8"); +} + diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/CommandUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/CommandUtils.java new file mode 100755 index 0000000000000000000000000000000000000000..3a6dfc9806835b32c839b290d99dd0c926482f23 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/CommandUtils.java @@ -0,0 +1,60 @@ +package com.tongtech.common.utils.operation; + +import com.tongtech.common.exception.ShellCommandException; + +import java.io.IOException; + + +/** + * 本地命令行执行工具,用于执行如DOS command和Unix(Linux) shell命令 + * + * @author Charlie Zhange + * + */ +public class CommandUtils { + + private static CommandUtilsHelper helper = new CommandUtilsHelper(); + + public static OSType getOSType() { + + try { + return helper.getOSType(); + } catch (IOException e) { + throw new ShellCommandException(e); + } + } + + /** + * 直接执行一个命令行 + * 如果发生错误自动记录到日志中 + * + * @param commandLine + * @return the exit value of the process. By convention, + * 0 indicates normal termination.
+ * More information please reference: Process.waitFor() return value.
+ */ + public static int execute(String commandLine, String outputCharset) throws IOException, InterruptedException { + return helper.execute(commandLine, outputCharset); + } + + /** + * + * 方法功能:得到上次执行命令后的 system out输出. + * @return + */ + public static String getLastOut() { + return helper.getLastOut(); + } + + /** + * + * 方法功能:得到上次执行命令后的 system err输出. + * @return + */ + public static String getLastErr() { + return helper.getLastErr(); + } + +} + + diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/CommandUtilsHelper.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/CommandUtilsHelper.java new file mode 100755 index 0000000000000000000000000000000000000000..c280777fba2c5fb4efa0a1822bb568b350ace6f9 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/CommandUtilsHelper.java @@ -0,0 +1,173 @@ +package com.tongtech.common.utils.operation; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + + +/** + * 本地命令行执行工具,用于执行如DOS command和Linux shell命令 + * + * @author Charlie Zhange + * + */ +public class CommandUtilsHelper { + + public static volatile long MAX_WAITING_MILLISECOND = 1000; + + private static Logger log = LoggerFactory.getLogger(CommandUtilsHelper.class); + + private Runtime runtime = null; + + private OSType ostype = null; + + private String lastOut; + + private String lastErr; + + private volatile int grabberCount = -1; + + + public synchronized OSType getOSType() throws IOException { + + if(ostype == null) { + String osname = System.getProperty("os.name").toLowerCase(); + if(osname.indexOf("windows") >= 0) { + ostype = OSType.WINDOWS; + } + else if(osname.indexOf("linux") >= 0) { + ostype = OSType.LINUX; + } + else if(osname.indexOf("unix") >= 0) { + ostype = OSType.UNIX; + } + else if(osname.indexOf("mac") >= 0) { + ostype = OSType.MAC; + } + else { + throw new IOException("Failed to get match OSType! os.name=" + osname); + } + } + + return ostype; + } + + /** + * 直接执行一个系统命令行,命令执行完成后该函数才返回 + * 如果发生错误自动记录到日志中 + * + * @param commandLine + * @return Process.waitFor() code. The exit value of the process. By convention, + * 0 indicates normal termination. + */ + public synchronized int execute(String commandLine, String outputCharset) throws IOException, InterruptedException{ + if(runtime == null) { + runtime = Runtime.getRuntime(); + } + + if (log.isDebugEnabled()) { + log.debug("[" + getOSType() + "]COMMAND>" + commandLine); + } + + String[] cmds = new String[3]; + switch(getOSType()) { + case UNIX: + case LINUX: + case MAC: + cmds[0] = "/bin/sh"; + cmds[1] = "-c"; //this options means: Carries out My_Command and then terminates + break; + case WINDOWS: + cmds[0] = "cmd.exe"; + cmds[1] = "/C"; //this options means: Carries out My_Command and then terminates + break; + default: + log.error("CommandUtils Not support OS System: " + System.getProperty("os.name")); + assert false; //should never reach here + break; + } + + cmds[2] = commandLine; + + Process proc = runtime.exec(cmds); + + InputStream stderr = proc.getErrorStream(); + InputStream stdout = proc.getInputStream(); + grabberCount = 2; + StreamGrabber errGrabber = new StreamGrabber(stderr, outputCharset, true); + StreamGrabber outGrabber = new StreamGrabber(stdout, outputCharset, false); + + //需要等待 sgErr 和 sgOut 线程停止。最长等待 MAX_WAITING_MILLISECOND设定的毫秒数 + errGrabber.join(MAX_WAITING_MILLISECOND); + outGrabber.join(MAX_WAITING_MILLISECOND); + + return proc.waitFor(); + } + + public synchronized String getLastOut() { + return lastOut; + } + + + public synchronized String getLastErr() { + return lastErr; + } + + + class StreamGrabber extends Thread { + + private InputStream input; + + private boolean isError; + + private String outputCharset; + + public StreamGrabber(InputStream input, String outputCharset, boolean isError) { + this.input = input; + this.isError = isError; + this.outputCharset = outputCharset; + this.setDaemon(true); + this.start(); + } + + public void run() { + try { + String outString = IOUtils.toString(input, getCharset()); + if(isError) { + lastErr = outString; + if(outString != null && outString.trim().length() > 0 && log.isDebugEnabled()) { + log.debug("COMMAND ERR[" + getCharset() + "]>" + outString); + } + } + else { + lastOut = outString; + if(outString != null && outString.trim().length() > 0 && log.isDebugEnabled()) { + log.debug("COMMAND OUT[" + getCharset() + "]>" + outString); + } + } + + grabberCount --; + } catch (Throwable ioe) { + log.error(ioe.getMessage(), ioe); + } + } + + private String getCharset() { + if(outputCharset != null) { + return outputCharset; + } + else { + return Charset.defaultCharset().name(); + } + } + } + + + +} + + diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/FileUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/FileUtils.java new file mode 100755 index 0000000000000000000000000000000000000000..25a23fe9ce640211e7d03b9a9f57442408a02648 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/FileUtils.java @@ -0,0 +1,93 @@ +package com.tongtech.common.utils.operation; + +import java.io.*; + +/** + * + * @author Zhang Chen Long + * @since 2012-7-24 + * + */ +public class FileUtils { + + /** + *

Get file name from file full path string.

+ * + * For example: parseFileName("d:\path\for\a\docFile.txt") will return "docFile.txt" + * + * @param filePathName + * @return + * @since 2012-7-24 + */ + public static String parseFileName(String filePathName) + { + int separatorIdx = filePathName.lastIndexOf('/'); + if(separatorIdx < 0) { + separatorIdx = filePathName.lastIndexOf('\\'); + } + + if(separatorIdx >= 0) + { + return filePathName.substring(separatorIdx + 1); + } + else { + return filePathName; + } + } + + + /** + * Write InputStream into a file + * @param file + * @param input + * @return file size in byte + * @throws IOException + * @since 2012-7-24 + */ + public static long writeToFile(File file, InputStream input) + throws IOException { + FileOutputStream output = null; + try { + output = new FileOutputStream(file); + + byte buffer[] = new byte[4096]; + long count = 0L; + for (int n = 0; -1 != (n = input.read(buffer));) { + output.write(buffer, 0, n); + count += n; + } + + return count; + } + finally { + if(output != null) output.close(); + } + } + + /** + * Open InputStream for a file. + * @param file + * @return + * @throws IOException + * @since 2012-7-24 + */ + public static FileInputStream openInputStream(File file) + throws IOException + { + if(file.exists()) { + if(file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + if(!file.canRead()) { + throw new IOException("File '" + file + "' cannot be read"); + } + else { + return new FileInputStream(file); + } + } + else { + throw new FileNotFoundException("File '" + file + "' does not exist"); + } + } + +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/IOUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/IOUtils.java new file mode 100755 index 0000000000000000000000000000000000000000..d95904e9d888ac1171a34cc3aba76abd00129384 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/IOUtils.java @@ -0,0 +1,2338 @@ +package com.tongtech.common.utils.operation; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.*; +import java.net.*; +import java.nio.channels.Selector; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + + +/** + * General IO stream manipulation utilities. + *

+ * This class provides static utility methods for input/output operations. + *

    + *
  • closeQuietly - these methods close a stream ignoring nulls and exceptions + *
  • toXxx/read - these methods read data from a stream + *
  • write - these methods write data to a stream + *
  • copy - these methods copy all the data from one stream to another + *
  • contentEquals - these methods compare the content of two streams + *
+ *

+ * The byte-to-char methods and char-to-byte methods involve a conversion step. + * Two methods are provided in each case, one that uses the platform default + * encoding and the other which allows you to specify an encoding. You are + * encouraged to always specify an encoding because relying on the platform + * default can lead to unexpected results, for example when moving from + * development to production. + *

+ * All the methods in this class that read a stream are buffered internally. + * This means that there is no cause to use a BufferedInputStream + * or BufferedReader. The default buffer size of 4K has been shown + * to be efficient in tests. + *

+ * Wherever possible, the methods in this class do not flush or close + * the stream. This is to avoid making non-portable assumptions about the + * streams' origin and further use. Thus the caller is still responsible for + * closing streams after use. + *

+ * Origin of code: Excalibur. + * + */ +public class IOUtils { + // NOTE: This class is focussed on InputStream, OutputStream, Reader and + // Writer. Each method should take at least one of these as a parameter, + // or return one of them. + + private static final int EOF = -1; + /** + * The Unix directory separator character. + */ + public static final char DIR_SEPARATOR_UNIX = '/'; + /** + * The Windows directory separator character. + */ + public static final char DIR_SEPARATOR_WINDOWS = '\\'; + /** + * The system directory separator character. + */ + public static final char DIR_SEPARATOR = File.separatorChar; + /** + * The Unix line separator string. + */ + public static final String LINE_SEPARATOR_UNIX = "\n"; + /** + * The Windows line separator string. + */ + public static final String LINE_SEPARATOR_WINDOWS = "\r\n"; + /** + * The system line separator string. + */ + public static final String LINE_SEPARATOR; + + static { + // avoid security issues + StringBuilderWriter buf = new StringBuilderWriter(4); + PrintWriter out = new PrintWriter(buf); + out.println(); + LINE_SEPARATOR = buf.toString(); + out.close(); + } + + /** + * The default buffer size ({@value}) to use for + * {@link #copyLarge(InputStream, OutputStream)} + * and + * {@link #copyLarge(Reader, Writer)} + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * The default buffer size to use for the skip() methods. + */ + private static final int SKIP_BUFFER_SIZE = 2048; + + // Allocated in the relevant skip method if necessary. + /* + * N.B. no need to synchronize these because: + * - we don't care if the buffer is created multiple times (the data is ignored) + * - we always use the same size buffer, so if it it is recreated it will still be OK + * (if the buffer size were variable, we would need to synch. to ensure some other thread + * did not create a smaller one) + */ + private static char[] SKIP_CHAR_BUFFER; + private static byte[] SKIP_BYTE_BUFFER; + + /** + * Instances should NOT be constructed in standard programming. + */ + public IOUtils() { + super(); + } + + //----------------------------------------------------------------------- + + /** + * Closes a URLConnection. + * + * @param conn the connection to close. + * @since 2.4 + */ + public static void close(URLConnection conn) { + if (conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).disconnect(); + } + } + + /** + * Unconditionally close an Reader. + *

+ * Equivalent to {@link Reader#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   char[] data = new char[1024];
+     *   Reader in = null;
+     *   try {
+     *       in = new FileReader("foo.txt");
+     *       in.read(data);
+     *       in.close(); //close errors are handled
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(in);
+     *   }
+     * 
+ * + * @param input the Reader to close, may be null or already closed + */ + public static void closeQuietly(Reader input) { + closeQuietly((Closeable)input); + } + + /** + * Unconditionally close a Writer. + *

+ * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Writer out = null;
+     *   try {
+     *       out = new StringWriter();
+     *       out.write("Hello World");
+     *       out.close(); //close errors are handled
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(out);
+     *   }
+     * 
+ * + * @param output the Writer to close, may be null or already closed + */ + public static void closeQuietly(Writer output) { + closeQuietly((Closeable)output); + } + + /** + * Unconditionally close an InputStream. + *

+ * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   byte[] data = new byte[1024];
+     *   InputStream in = null;
+     *   try {
+     *       in = new FileInputStream("foo.txt");
+     *       in.read(data);
+     *       in.close(); //close errors are handled
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(in);
+     *   }
+     * 
+ * + * @param input the InputStream to close, may be null or already closed + */ + public static void closeQuietly(InputStream input) { + closeQuietly((Closeable)input); + } + + /** + * Unconditionally close an OutputStream. + *

+ * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     * byte[] data = "Hello, World".getBytes();
+     *
+     * OutputStream out = null;
+     * try {
+     *     out = new FileOutputStream("foo.txt");
+     *     out.write(data);
+     *     out.close(); //close errors are handled
+     * } catch (IOException e) {
+     *     // error handling
+     * } finally {
+     *     IOUtils.closeQuietly(out);
+     * }
+     * 
+ * + * @param output the OutputStream to close, may be null or already closed + */ + public static void closeQuietly(OutputStream output) { + closeQuietly((Closeable)output); + } + + /** + * Unconditionally close a Closeable. + *

+ * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Closeable closeable = null;
+     *   try {
+     *       closeable = new FileReader("foo.txt");
+     *       // process closeable
+     *       closeable.close();
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(closeable);
+     *   }
+     * 
+ * + * @param closeable the object to close, may be null or already closed + * @since 2.0 + */ + public static void closeQuietly(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (IOException ioe) { + // ignore + } + } + + /** + * Unconditionally close a Socket. + *

+ * Equivalent to {@link Socket#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Socket socket = null;
+     *   try {
+     *       socket = new Socket("http://www.foo.com/", 80);
+     *       // process socket
+     *       socket.close();
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(socket);
+     *   }
+     * 
+ * + * @param sock the Socket to close, may be null or already closed + * @since 2.0 + */ + public static void closeQuietly(Socket sock){ + if (sock != null){ + try { + sock.close(); + } catch (IOException ioe) { + // ignored + } + } + } + + /** + * Unconditionally close a Selector. + *

+ * Equivalent to {@link Selector#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Selector selector = null;
+     *   try {
+     *       selector = Selector.open();
+     *       // process socket
+     *
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(selector);
+     *   }
+     * 
+ * + * @param selector the Selector to close, may be null or already closed + * @since 2.2 + */ + public static void closeQuietly(Selector selector){ + if (selector != null){ + try { + selector.close(); + } catch (IOException ioe) { + // ignored + } + } + } + + /** + * Unconditionally close a ServerSocket. + *

+ * Equivalent to {@link ServerSocket#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   ServerSocket socket = null;
+     *   try {
+     *       socket = new ServerSocket();
+     *       // process socket
+     *       socket.close();
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(socket);
+     *   }
+     * 
+ * + * @param sock the ServerSocket to close, may be null or already closed + * @since 2.2 + */ + public static void closeQuietly(ServerSocket sock){ + if (sock != null){ + try { + sock.close(); + } catch (IOException ioe) { + // ignored + } + } + } + + /** + * Fetches entire contents of an InputStream and represent + * same data as result InputStream. + *

+ * This method is useful where, + *

    + *
  • Source InputStream is slow.
  • + *
  • It has network resources associated, so we cannot keep it open for + * long time.
  • + *
  • It has network timeout associated.
  • + *
+ * It can be used in favor of {@link #toByteArray(InputStream)}, since it + * avoids unnecessary allocation and copy of byte[].
+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input Stream to be fully buffered. + * @return A fully buffered stream. + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static InputStream toBufferedInputStream(InputStream input) throws IOException { + return com.tongtech.common.utils.operation.ByteArrayOutputStream.toBufferedInputStream(input); + } + + /** + * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a toBufferedReader for the given + * reader. + * + * @param reader + * the reader to wrap or return + * @return the given reader or a new {@link BufferedReader} for the given reader + * @since 2.2 + */ + public static BufferedReader toBufferedReader(Reader reader) { + return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + } + + // read toByteArray + //----------------------------------------------------------------------- + /** + * Get the contents of an InputStream as a byte[]. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(InputStream input) throws IOException { + com.tongtech.common.utils.operation.ByteArrayOutputStream output = new com.tongtech.common.utils.operation.ByteArrayOutputStream(); + copy(input, output); + return output.toByteArray(); + } + + /** + * Get contents of an InputStream as a byte[]. + * Use this method instead of toByteArray(InputStream) + * when InputStream size is known. + * NOTE: the method checks that the length can safely be cast to an int without truncation + * before using {@link IOUtils#toByteArray(InputStream, int)} to read into the byte array. + * (Arrays can have no more than Integer.MAX_VALUE entries anyway) + * + * @param input the InputStream to read from + * @param size the size of InputStream + * @return the requested byte array + * @throws IOException if an I/O error occurs or InputStream size differ from parameter size + * @throws IllegalArgumentException if size is less than zero or size is greater than Integer.MAX_VALUE + * @see IOUtils#toByteArray(InputStream, int) + * @since 2.1 + */ + public static byte[] toByteArray(InputStream input, long size) throws IOException { + + if(size > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + size); + } + + return toByteArray(input, (int) size); + } + + /** + * Get the contents of an InputStream as a byte[]. + * Use this method instead of toByteArray(InputStream) + * when InputStream size is known + * @param input the InputStream to read from + * @param size the size of InputStream + * @return the requested byte array + * @throws IOException if an I/O error occurs or InputStream size differ from parameter size + * @throws IllegalArgumentException if size is less than zero + * @since 2.1 + */ + public static byte[] toByteArray(InputStream input, int size) throws IOException { + + if (size < 0) { + throw new IllegalArgumentException("Size must be equal or greater than zero: " + size); + } + + if (size == 0) { + return new byte[0]; + } + + byte[] data = new byte[size]; + int offset = 0; + int readed; + + while (offset < size && (readed = input.read(data, offset, size - offset)) != EOF) { + offset += readed; + } + + if (offset != size) { + throw new IOException("Unexpected readed size. current: " + offset + ", excepted: " + size); + } + + return data; + } + + /** + * Get the contents of a Reader as a byte[] + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(Reader input) throws IOException { + return toByteArray(input, Charset.defaultCharset()); + } + + /** + * Get the contents of a Reader as a byte[] + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @param encoding the encoding to use, null means platform default + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static byte[] toByteArray(Reader input, Charset encoding) throws IOException { + com.tongtech.common.utils.operation.ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(input, output, encoding); + return output.toByteArray(); + } + + /** + * Get the contents of a Reader as a byte[] + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @param encoding the encoding to use, null means platform default + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static byte[] toByteArray(Reader input, String encoding) throws IOException { + return toByteArray(input, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a URI as a byte[]. + * + * @param uri + * the URI to read + * @return the requested byte array + * @throws NullPointerException + * if the uri is null + * @throws IOException + * if an I/O exception occurs + * @since 2.4 + */ + public static byte[] toByteArray(URI uri) throws IOException { + return IOUtils.toByteArray(uri.toURL()); + } + + /** + * Get the contents of a URL as a byte[]. + * + * @param url + * the URL to read + * @return the requested byte array + * @throws NullPointerException + * if the input is null + * @throws IOException + * if an I/O exception occurs + * @since 2.4 + */ + public static byte[] toByteArray(URL url) throws IOException { + URLConnection conn = url.openConnection(); + try { + return IOUtils.toByteArray(conn); + } finally { + close(conn); + } + } + + /** + * Get the contents of a URLConnection as a byte[]. + * + * @param urlConn + * the URLConnection to read + * @return the requested byte array + * @throws NullPointerException + * if the urlConn is null + * @throws IOException + * if an I/O exception occurs + * @since 2.4 + */ + public static byte[] toByteArray(URLConnection urlConn) throws IOException { + InputStream inputStream = urlConn.getInputStream(); + try { + return IOUtils.toByteArray(inputStream); + } finally { + inputStream.close(); + } + } + + // read char[] + //----------------------------------------------------------------------- + /** + * Get the contents of an InputStream as a character array + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream to read from + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static char[] toCharArray(InputStream is) throws IOException { + return toCharArray(is, Charset.defaultCharset()); + } + + /** + * Get the contents of an InputStream as a character array + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static char[] toCharArray(InputStream is, Charset encoding) + throws IOException { + CharArrayWriter output = new CharArrayWriter(); + copy(is, output, encoding); + return output.toCharArray(); + } + + /** + * Get the contents of an InputStream as a character array + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static char[] toCharArray(InputStream is, String encoding) throws IOException { + return toCharArray(is, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a Reader as a character array. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static char[] toCharArray(Reader input) throws IOException { + CharArrayWriter sw = new CharArrayWriter(); + copy(input, sw); + return sw.toCharArray(); + } + + // read toString + //----------------------------------------------------------------------- + /** + * Get the contents of an InputStream as a String + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static String toString(InputStream input) throws IOException { + return toString(input, Charset.defaultCharset()); + } + + /** + * Get the contents of an InputStream as a String + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * @param input the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static String toString(InputStream input, Charset encoding) throws IOException { + StringBuilderWriter sw = new StringBuilderWriter(); + copy(input, sw, encoding); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + */ + public static String toString(InputStream input, String encoding) + throws IOException { + return toString(input, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a Reader as a String. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static String toString(Reader input) throws IOException { + StringBuilderWriter sw = new StringBuilderWriter(); + copy(input, sw); + return sw.toString(); + } + + /** + * Gets the contents at the given URI. + * + * @param uri + * The URI source. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.1 + */ + public static String toString(URI uri) throws IOException { + return toString(uri, Charset.defaultCharset()); + } + + /** + * Gets the contents at the given URI. + * + * @param uri + * The URI source. + * @param encoding + * The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.3. + */ + public static String toString(URI uri, Charset encoding) throws IOException { + return toString(uri.toURL(), Charsets.toCharset(encoding)); + } + + /** + * Gets the contents at the given URI. + * + * @param uri + * The URI source. + * @param encoding + * The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.1 + */ + public static String toString(URI uri, String encoding) throws IOException { + return toString(uri, Charsets.toCharset(encoding)); + } + + /** + * Gets the contents at the given URL. + * + * @param url + * The URL source. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.1 + */ + public static String toString(URL url) throws IOException { + return toString(url, Charset.defaultCharset()); + } + + /** + * Gets the contents at the given URL. + * + * @param url + * The URL source. + * @param encoding + * The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.3 + */ + public static String toString(URL url, Charset encoding) throws IOException { + InputStream inputStream = url.openStream(); + try { + return toString(inputStream, encoding); + } finally { + inputStream.close(); + } + } + + /** + * Gets the contents at the given URL. + * + * @param url + * The URL source. + * @param encoding + * The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.1 + */ + public static String toString(URL url, String encoding) throws IOException { + return toString(url, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a byte[] as a String + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @param input the byte array to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs (never occurs) + */ + public static String toString(byte[] input, String encoding) throws IOException { + return new String(input, Charsets.toCharset(encoding)); + } + + // readLines + //----------------------------------------------------------------------- + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from, not null + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static List readLines(InputStream input) throws IOException { + return readLines(input, Charset.defaultCharset()); + } + + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from, not null + * @param encoding the encoding to use, null means platform default + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static List readLines(InputStream input, Charset encoding) throws IOException { + InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(encoding)); + return readLines(reader); + } + + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from, not null + * @param encoding the encoding to use, null means platform default + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static List readLines(InputStream input, String encoding) throws IOException { + return readLines(input, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a Reader as a list of Strings, + * one entry per line. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from, not null + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static List readLines(Reader input) throws IOException { + BufferedReader reader = toBufferedReader(input); + List list = new ArrayList(); + String line = reader.readLine(); + while (line != null) { + list.add(line); + line = reader.readLine(); + } + return list; + } + + + + //----------------------------------------------------------------------- + /** + * Convert the specified CharSequence to an input stream, encoded as bytes + * using the default character encoding of the platform. + * + * @param input the CharSequence to convert + * @return an input stream + * @since 2.0 + */ + public static InputStream toInputStream(CharSequence input) { + return toInputStream(input, Charset.defaultCharset()); + } + + /** + * Convert the specified CharSequence to an input stream, encoded as bytes + * using the specified character encoding. + * + * @param input the CharSequence to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @since 2.3 + */ + public static InputStream toInputStream(CharSequence input, Charset encoding) { + return toInputStream(input.toString(), encoding); + } + + /** + * Convert the specified CharSequence to an input stream, encoded as bytes + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @param input the CharSequence to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @throws IOException if the encoding is invalid + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.0 + */ + public static InputStream toInputStream(CharSequence input, String encoding) throws IOException { + return toInputStream(input, Charsets.toCharset(encoding)); + } + + //----------------------------------------------------------------------- + /** + * Convert the specified string to an input stream, encoded as bytes + * using the default character encoding of the platform. + * + * @param input the string to convert + * @return an input stream + * @since 1.1 + */ + public static InputStream toInputStream(String input) { + return toInputStream(input, Charset.defaultCharset()); + } + + /** + * Convert the specified string to an input stream, encoded as bytes + * using the specified character encoding. + * + * @param input the string to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @since 2.3 + */ + public static InputStream toInputStream(String input, Charset encoding) { + return new ByteArrayInputStream(input.getBytes(Charsets.toCharset(encoding))); + } + + /** + * Convert the specified string to an input stream, encoded as bytes + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @param input the string to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @throws IOException if the encoding is invalid + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static InputStream toInputStream(String input, String encoding) throws IOException { + byte[] bytes = input.getBytes(Charsets.toCharset(encoding)); + return new ByteArrayInputStream(bytes); + } + + // write byte[] + //----------------------------------------------------------------------- + /** + * Writes bytes from a byte[] to an OutputStream. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(byte[] data, OutputStream output) + throws IOException { + if (data != null) { + output.write(data); + } + } + + /** + * Writes bytes from a byte[] to chars on a Writer + * using the default character encoding of the platform. + *

+ * This method uses {@link String#String(byte[])}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(byte[] data, Writer output) throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes bytes from a byte[] to chars on a Writer + * using the specified character encoding. + *

+ * This method uses {@link String#String(byte[], String)}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(byte[] data, Writer output, Charset encoding) throws IOException { + if (data != null) { + output.write(new String(data, Charsets.toCharset(encoding))); + } + } + + /** + * Writes bytes from a byte[] to chars on a Writer + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#String(byte[], String)}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void write(byte[] data, Writer output, String encoding) throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write char[] + //----------------------------------------------------------------------- + /** + * Writes chars from a char[] to a Writer + * using the default character encoding of the platform. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(char[] data, Writer output) throws IOException { + if (data != null) { + output.write(data); + } + } + + /** + * Writes chars from a char[] to bytes on an + * OutputStream. + *

+ * This method uses {@link String#String(char[])} and + * {@link String#getBytes()}. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(char[] data, OutputStream output) + throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes chars from a char[] to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#String(char[])} and + * {@link String#getBytes(String)}. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(char[] data, OutputStream output, Charset encoding) throws IOException { + if (data != null) { + output.write(new String(data).getBytes(Charsets.toCharset(encoding))); + } + } + + /** + * Writes chars from a char[] to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#String(char[])} and + * {@link String#getBytes(String)}. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void write(char[] data, OutputStream output, String encoding) + throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write CharSequence + //----------------------------------------------------------------------- + /** + * Writes chars from a CharSequence to a Writer. + * + * @param data the CharSequence to write, null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static void write(CharSequence data, Writer output) throws IOException { + if (data != null) { + write(data.toString(), output); + } + } + + /** + * Writes chars from a CharSequence to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the CharSequence to write, null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static void write(CharSequence data, OutputStream output) + throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes chars from a CharSequence to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the CharSequence to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(CharSequence data, OutputStream output, Charset encoding) throws IOException { + if (data != null) { + write(data.toString(), output, encoding); + } + } + + /** + * Writes chars from a CharSequence to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the CharSequence to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.0 + */ + public static void write(CharSequence data, OutputStream output, String encoding) throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write String + //----------------------------------------------------------------------- + /** + * Writes chars from a String to a Writer. + * + * @param data the String to write, null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(String data, Writer output) throws IOException { + if (data != null) { + output.write(data); + } + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(String data, OutputStream output) + throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(String data, OutputStream output, Charset encoding) throws IOException { + if (data != null) { + output.write(data.getBytes(Charsets.toCharset(encoding))); + } + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void write(String data, OutputStream output, String encoding) + throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + + // writeLines + //----------------------------------------------------------------------- + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the default character + * encoding of the platform and the specified line ending. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param output the OutputStream to write to, not null, not closed + * @throws NullPointerException if the output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void writeLines(Collection lines, String lineEnding, + OutputStream output) throws IOException { + writeLines(lines, lineEnding, output, Charset.defaultCharset()); + } + + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the specified character + * encoding and the specified line ending. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param output the OutputStream to write to, not null, not closed + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void writeLines(Collection lines, String lineEnding, OutputStream output, Charset encoding) + throws IOException { + if (lines == null) { + return; + } + if (lineEnding == null) { + lineEnding = LINE_SEPARATOR; + } + Charset cs = Charsets.toCharset(encoding); + for (Object line : lines) { + if (line != null) { + output.write(line.toString().getBytes(cs)); + } + output.write(lineEnding.getBytes(cs)); + } + } + + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the specified character + * encoding and the specified line ending. + *

+ * Character encoding names can be found at + * IANA. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param output the OutputStream to write to, not null, not closed + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void writeLines(Collection lines, String lineEnding, + OutputStream output, String encoding) throws IOException { + writeLines(lines, lineEnding, output, Charsets.toCharset(encoding)); + } + + /** + * Writes the toString() value of each item in a collection to + * a Writer line by line, using the specified line ending. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param writer the Writer to write to, not null, not closed + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void writeLines(Collection lines, String lineEnding, + Writer writer) throws IOException { + if (lines == null) { + return; + } + if (lineEnding == null) { + lineEnding = LINE_SEPARATOR; + } + for (Object line : lines) { + if (line != null) { + writer.write(line.toString()); + } + writer.write(lineEnding); + } + } + + // copy from InputStream + //----------------------------------------------------------------------- + /** + * Copy bytes from an InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Large streams (over 2GB) will return a bytes copied value of + * -1 after the copy has completed since the correct + * number of bytes cannot be returned as an int. For large streams + * use the copyLarge(InputStream, OutputStream) method. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static int copy(InputStream input, OutputStream output) throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + /** + * Copy bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.3 + */ + public static long copyLarge(InputStream input, OutputStream output) + throws IOException { + return copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param buffer the buffer to use for the copy + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(InputStream input, OutputStream output, byte[] buffer) + throws IOException { + long count = 0; + int n = 0; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copy some or all bytes from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input bytes. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param inputOffset : number of bytes to skip from input before copying + * -ve values are ignored + * @param length : number of bytes to copy. -ve means all + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(InputStream input, OutputStream output, long inputOffset, long length) + throws IOException { + return copyLarge(input, output, inputOffset, length, new byte[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy some or all bytes from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input bytes. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param inputOffset : number of bytes to skip from input before copying + * -ve values are ignored + * @param length : number of bytes to copy. -ve means all + * @param buffer the buffer to use for the copy + * + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(InputStream input, OutputStream output, + final long inputOffset, final long length, byte[] buffer) throws IOException { + if (inputOffset > 0) { + skipFully(input, inputOffset); + } + if (length == 0) { + return 0; + } + final int bufferLength = buffer.length; + int bytesToRead = bufferLength; + if (length > 0 && length < bufferLength) { + bytesToRead = (int) length; + } + int read; + long totalRead = 0; + while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) { + output.write(buffer, 0, read); + totalRead += read; + if (length > 0) { // only adjust length if not reading to the end + // Note the cast must work because buffer.length is an integer + bytesToRead = (int) Math.min(length - totalRead, bufferLength); + } + } + return totalRead; + } + + /** + * Copy bytes from an InputStream to chars on a + * Writer using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void copy(InputStream input, Writer output) + throws IOException { + copy(input, output, Charset.defaultCharset()); + } + + /** + * Copy bytes from an InputStream to chars on a + * Writer using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void copy(InputStream input, Writer output, Charset encoding) throws IOException { + InputStreamReader in = new InputStreamReader(input, Charsets.toCharset(encoding)); + copy(in, output); + } + + /** + * Copy bytes from an InputStream to chars on a + * Writer using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void copy(InputStream input, Writer output, String encoding) throws IOException { + copy(input, output, Charsets.toCharset(encoding)); + } + + // copy from Reader + //----------------------------------------------------------------------- + /** + * Copy chars from a Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Large streams (over 2GB) will return a chars copied value of + * -1 after the copy has completed since the correct + * number of chars cannot be returned as an int. For large streams + * use the copyLarge(Reader, Writer) method. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static int copy(Reader input, Writer output) throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + /** + * Copy chars from a large (over 2GB) Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.3 + */ + public static long copyLarge(Reader input, Writer output) throws IOException { + return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy chars from a large (over 2GB) Reader to a Writer. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedReader. + *

+ * + * @param input the Reader to read from + * @param output the Writer to write to + * @param buffer the buffer to be used for the copy + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(Reader input, Writer output, char [] buffer) throws IOException { + long count = 0; + int n = 0; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copy some or all chars from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input chars. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @param inputOffset : number of chars to skip from input before copying + * -ve values are ignored + * @param length : number of chars to copy. -ve means all + * @return the number of chars copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(Reader input, Writer output, final long inputOffset, final long length) + throws IOException { + return copyLarge(input, output, inputOffset, length, new char[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy some or all chars from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input chars. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedReader. + *

+ * + * @param input the Reader to read from + * @param output the Writer to write to + * @param inputOffset : number of chars to skip from input before copying + * -ve values are ignored + * @param length : number of chars to copy. -ve means all + * @param buffer the buffer to be used for the copy + * @return the number of chars copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(Reader input, Writer output, final long inputOffset, final long length, char [] buffer) + throws IOException { + if (inputOffset > 0) { + skipFully(input, inputOffset); + } + if (length == 0) { + return 0; + } + int bytesToRead = buffer.length; + if (length > 0 && length < buffer.length) { + bytesToRead = (int) length; + } + int read; + long totalRead = 0; + while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) { + output.write(buffer, 0, read); + totalRead += read; + if (length > 0) { // only adjust length if not reading to the end + // Note the cast must work because buffer.length is an integer + bytesToRead = (int) Math.min(length - totalRead, buffer.length); + } + } + return totalRead; + } + + /** + * Copy chars from a Reader to bytes on an + * OutputStream using the default character encoding of the + * platform, and calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ * This method uses {@link OutputStreamWriter}. + * + * @param input the Reader to read from + * @param output the OutputStream to write to + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void copy(Reader input, OutputStream output) + throws IOException { + copy(input, output, Charset.defaultCharset()); + } + + /** + * Copy chars from a Reader to bytes on an + * OutputStream using the specified character encoding, and + * calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ *

+ * This method uses {@link OutputStreamWriter}. + *

+ * + * @param input the Reader to read from + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void copy(Reader input, OutputStream output, Charset encoding) throws IOException { + OutputStreamWriter out = new OutputStreamWriter(output, Charsets.toCharset(encoding)); + copy(input, out); + // XXX Unless anyone is planning on rewriting OutputStreamWriter, + // we have to flush here. + out.flush(); + } + + /** + * Copy chars from a Reader to bytes on an + * OutputStream using the specified character encoding, and + * calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Character encoding names can be found at + * IANA. + *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ * This method uses {@link OutputStreamWriter}. + * + * @param input the Reader to read from + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void copy(Reader input, OutputStream output, String encoding) throws IOException { + copy(input, output, Charsets.toCharset(encoding)); + } + + // content equals + //----------------------------------------------------------------------- + /** + * Compare the contents of two Streams to determine if they are equal or + * not. + *

+ * This method buffers the input internally using + * BufferedInputStream if they are not already buffered. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't + * exist, false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + */ + public static boolean contentEquals(InputStream input1, InputStream input2) + throws IOException { + if (!(input1 instanceof BufferedInputStream)) { + input1 = new BufferedInputStream(input1); + } + if (!(input2 instanceof BufferedInputStream)) { + input2 = new BufferedInputStream(input2); + } + + int ch = input1.read(); + while (EOF != ch) { + int ch2 = input2.read(); + if (ch != ch2) { + return false; + } + ch = input1.read(); + } + + int ch2 = input2.read(); + return ch2 == EOF; + } + + /** + * Compare the contents of two Readers to determine if they are equal or + * not. + *

+ * This method buffers the input internally using + * BufferedReader if they are not already buffered. + * + * @param input1 the first reader + * @param input2 the second reader + * @return true if the content of the readers are equal or they both don't + * exist, false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static boolean contentEquals(Reader input1, Reader input2) + throws IOException { + + input1 = toBufferedReader(input1); + input2 = toBufferedReader(input2); + + int ch = input1.read(); + while (EOF != ch) { + int ch2 = input2.read(); + if (ch != ch2) { + return false; + } + ch = input1.read(); + } + + int ch2 = input2.read(); + return ch2 == EOF; + } + + /** + * Compare the contents of two Readers to determine if they are equal or + * not, ignoring EOL characters. + *

+ * This method buffers the input internally using + * BufferedReader if they are not already buffered. + * + * @param input1 the first reader + * @param input2 the second reader + * @return true if the content of the readers are equal (ignoring EOL differences), false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static boolean contentEqualsIgnoreEOL(Reader input1, Reader input2) + throws IOException { + BufferedReader br1 = toBufferedReader(input1); + BufferedReader br2 = toBufferedReader(input2); + + String line1 = br1.readLine(); + String line2 = br2.readLine(); + while (line1 != null && line2 != null && line1.equals(line2)) { + line1 = br1.readLine(); + line2 = br2.readLine(); + } + return line1 == null ? line2 == null ? true : false : line1.equals(line2); + } + + /** + * Skip bytes from an input byte stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input byte stream to skip + * @param toSkip number of bytes to skip. + * @return number of bytes actually skipped. + * + * @see InputStream#skip(long) + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @since 2.0 + */ + public static long skip(InputStream input, long toSkip) throws IOException { + if (toSkip < 0) { + throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); + } + /* + * N.B. no need to synchronize this because: - we don't care if the buffer is created multiple times (the data + * is ignored) - we always use the same size buffer, so if it it is recreated it will still be OK (if the buffer + * size were variable, we would need to synch. to ensure some other thread did not create a smaller one) + */ + if (SKIP_BYTE_BUFFER == null) { + SKIP_BYTE_BUFFER = new byte[SKIP_BUFFER_SIZE]; + } + long remain = toSkip; + while (remain > 0) { + long n = input.read(SKIP_BYTE_BUFFER, 0, (int) Math.min(remain, SKIP_BUFFER_SIZE)); + if (n < 0) { // EOF + break; + } + remain -= n; + } + return toSkip - remain; + } + + /** + * Skip characters from an input character stream. + * This implementation guarantees that it will read as many characters + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input character stream to skip + * @param toSkip number of characters to skip. + * @return number of characters actually skipped. + * + * @see Reader#skip(long) + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @since 2.0 + */ + public static long skip(Reader input, long toSkip) throws IOException { + if (toSkip < 0) { + throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); + } + /* + * N.B. no need to synchronize this because: - we don't care if the buffer is created multiple times (the data + * is ignored) - we always use the same size buffer, so if it it is recreated it will still be OK (if the buffer + * size were variable, we would need to synch. to ensure some other thread did not create a smaller one) + */ + if (SKIP_CHAR_BUFFER == null) { + SKIP_CHAR_BUFFER = new char[SKIP_BUFFER_SIZE]; + } + long remain = toSkip; + while (remain > 0) { + long n = input.read(SKIP_CHAR_BUFFER, 0, (int) Math.min(remain, SKIP_BUFFER_SIZE)); + if (n < 0) { // EOF + break; + } + remain -= n; + } + return toSkip - remain; + } + + /** + * Skip the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#skip(long)} may + * not skip as many bytes as requested (most likely because of reaching EOF). + * + * @param input stream to skip + * @param toSkip the number of bytes to skip + * @see InputStream#skip(long) + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @throws EOFException if the number of bytes skipped was incorrect + * @since 2.0 + */ + public static void skipFully(InputStream input, long toSkip) throws IOException { + if (toSkip < 0) { + throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip); + } + long skipped = skip(input, toSkip); + if (skipped != toSkip) { + throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped); + } + } + + /** + * Skip the requested number of characters or fail if there are not enough left. + *

+ * This allows for the possibility that {@link Reader#skip(long)} may + * not skip as many characters as requested (most likely because of reaching EOF). + * + * @param input stream to skip + * @param toSkip the number of characters to skip + * @see Reader#skip(long) + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @throws EOFException if the number of characters skipped was incorrect + * @since 2.0 + */ + public static void skipFully(Reader input, long toSkip) throws IOException { + long skipped = skip(input, toSkip); + if (skipped != toSkip) { + throw new EOFException("Chars to skip: " + toSkip + " actual: " + skipped); + } + } + + + /** + * Read characters from an input character stream. + * This implementation guarantees that it will read as many characters + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(Reader input, char[] buffer, int offset, int length) throws IOException { + if (length < 0) { + throw new IllegalArgumentException("Length must not be negative: " + length); + } + int remaining = length; + while (remaining > 0) { + int location = length - remaining; + int count = input.read(buffer, offset + location, remaining); + if (EOF == count) { // EOF + break; + } + remaining -= count; + } + return length - remaining; + } + + /** + * Read characters from an input character stream. + * This implementation guarantees that it will read as many characters + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input where to read input from + * @param buffer destination + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(Reader input, char[] buffer) throws IOException { + return read(input, buffer, 0, buffer.length); + } + + /** + * Read bytes from an input stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link InputStream}. + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(InputStream input, byte[] buffer, int offset, int length) throws IOException { + if (length < 0) { + throw new IllegalArgumentException("Length must not be negative: " + length); + } + int remaining = length; + while (remaining > 0) { + int location = length - remaining; + int count = input.read(buffer, offset + location, remaining); + if (EOF == count) { // EOF + break; + } + remaining -= count; + } + return length - remaining; + } + + /** + * Read bytes from an input stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link InputStream}. + * + * @param input where to read input from + * @param buffer destination + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(InputStream input, byte[] buffer) throws IOException { + return read(input, buffer, 0, buffer.length); + } + + /** + * Read the requested number of characters or fail if there are not enough left. + *

+ * This allows for the possibility that {@link Reader#read(char[], int, int)} may + * not read as many characters as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of characters read was incorrect + * @since 2.2 + */ + public static void readFully(Reader input, char[] buffer, int offset, int length) throws IOException { + int actual = read(input, buffer, offset, length); + if (actual != length) { + throw new EOFException("Length to read: " + length + " actual: " + actual); + } + } + + /** + * Read the requested number of characters or fail if there are not enough left. + *

+ * This allows for the possibility that {@link Reader#read(char[], int, int)} may + * not read as many characters as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of characters read was incorrect + * @since 2.2 + */ + public static void readFully(Reader input, char[] buffer) throws IOException { + readFully(input, buffer, 0, buffer.length); + } + + /** + * Read the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#read(byte[], int, int)} may + * not read as many bytes as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of bytes read was incorrect + * @since 2.2 + */ + public static void readFully(InputStream input, byte[] buffer, int offset, int length) throws IOException { + int actual = read(input, buffer, offset, length); + if (actual != length) { + throw new EOFException("Length to read: " + length + " actual: " + actual); + } + } + + /** + * Read the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#read(byte[], int, int)} may + * not read as many bytes as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of bytes read was incorrect + * @since 2.2 + */ + public static void readFully(InputStream input, byte[] buffer) throws IOException { + readFully(input, buffer, 0, buffer.length); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/OSType.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/OSType.java new file mode 100755 index 0000000000000000000000000000000000000000..5a2d803e08a1007213866a516e1a996bf5cec923 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/OSType.java @@ -0,0 +1,11 @@ +package com.tongtech.common.utils.operation; + +/** + * 功能描述: 操作系统类型定义 . + */ +public enum OSType { + LINUX, + UNIX, + WINDOWS, + MAC +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/StringBuilderWriter.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/StringBuilderWriter.java new file mode 100755 index 0000000000000000000000000000000000000000..d2e459ce1389f8b3c9d9402f90a147739ee9dc07 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/operation/StringBuilderWriter.java @@ -0,0 +1,161 @@ +package com.tongtech.common.utils.operation; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.Serializable; +import java.io.Writer; + +/** + * {@link Writer} implementation that outputs to a {@link StringBuilder}. + *

+ * NOTE: This implementation, as an alternative to + * java.io.StringWriter, provides an un-synchronized + * (i.e. for use in a single thread) implementation for better performance. + * For safe usage with multiple {@link Thread}s then + * java.io.StringWriter should be used. + * + * @version $Id: StringBuilderWriter.java 1304052 2012-03-22 20:55:29Z ggregory $ + * @since 2.0 + */ +public class StringBuilderWriter extends Writer implements Serializable { + + private final StringBuilder builder; + + /** + * Construct a new {@link StringBuilder} instance with default capacity. + */ + public StringBuilderWriter() { + this.builder = new StringBuilder(); + } + + /** + * Construct a new {@link StringBuilder} instance with the specified capacity. + * + * @param capacity The initial capacity of the underlying {@link StringBuilder} + */ + public StringBuilderWriter(int capacity) { + this.builder = new StringBuilder(capacity); + } + + /** + * Construct a new instance with the specified {@link StringBuilder}. + * + * @param builder The String builder + */ + public StringBuilderWriter(StringBuilder builder) { + this.builder = builder != null ? builder : new StringBuilder(); + } + + /** + * Append a single character to this Writer. + * + * @param value The character to append + * @return This writer instance + */ + @Override + public Writer append(char value) { + builder.append(value); + return this; + } + + /** + * Append a character sequence to this Writer. + * + * @param value The character to append + * @return This writer instance + */ + @Override + public Writer append(CharSequence value) { + builder.append(value); + return this; + } + + /** + * Append a portion of a character sequence to the {@link StringBuilder}. + * + * @param value The character to append + * @param start The index of the first character + * @param end The index of the last character + 1 + * @return This writer instance + */ + @Override + public Writer append(CharSequence value, int start, int end) { + builder.append(value, start, end); + return this; + } + + /** + * Closing this writer has no effect. + */ + @Override + public void close() { + } + + /** + * Flushing this writer has no effect. + */ + @Override + public void flush() { + } + + + /** + * Write a String to the {@link StringBuilder}. + * + * @param value The value to write + */ + @Override + public void write(String value) { + if (value != null) { + builder.append(value); + } + } + + /** + * Write a portion of a character array to the {@link StringBuilder}. + * + * @param value The value to write + * @param offset The index of the first character + * @param length The number of characters to write + */ + @Override + public void write(char[] value, int offset, int length) { + if (value != null) { + builder.append(value, offset, length); + } + } + + /** + * Return the underlying builder. + * + * @return The underlying builder + */ + public StringBuilder getBuilder() { + return builder; + } + + /** + * Returns {@link StringBuilder#toString()}. + * + * @return The contents of the String builder. + */ + @Override + public String toString() { + return builder.toString(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/poi/ExcelHandlerAdapter.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c307c557f61f1589010155a0e1161af9386c43f0 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,19 @@ +package com.tongtech.common.utils.poi; + +/** + * Excel数据格式处理适配器 + * + * @author XiaoZhangTongZhi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args); +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/poi/ExcelUtil.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..5836a524c6e81f7205274a036d57ab3d2935adf5 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1670 @@ +package com.tongtech.common.utils.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; + +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.text.Convert; +import com.tongtech.common.utils.DateUtils; +import com.tongtech.common.utils.DictUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.config.AppHomeConfig; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFPicture; +import org.apache.poi.hssf.usermodel.HSSFPictureData; +import org.apache.poi.hssf.usermodel.HSSFShape; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.PictureData; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.annotation.Excel.ColumnType; +import com.tongtech.common.annotation.Excel.Type; +import com.tongtech.common.annotation.Excels; +import com.tongtech.common.exception.UtilException; +import com.tongtech.common.utils.file.FileTypeUtils; +import com.tongtech.common.utils.file.FileUtils; +import com.tongtech.common.utils.file.ImageUtils; +import com.tongtech.common.utils.reflect.ReflectUtils; + +/** + * Excel相关处理 + * + * @author XiaoZhangTongZhi + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * @throws Exception + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) + { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) throws Exception + { + return importExcel(is, 0); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) throws Exception + { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map pictures; + if (isXSSFWorkbook) + { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } + else + { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else + { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + else if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (StringUtils.isNotEmpty(attr.dictType())) + { + val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr); + } + else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) + { + PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey()); + if (image == null) + { + val = ""; + } + else + { + byte[] data = image.getData(); + val = FileUtils.writeImportBytes(data); + } + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) + { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) + { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + throw new UtilException("导出Excel失败,请联系网站管理员!"); + } + finally + { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo; i < endNo; i++) + { + rowNo = i > 1 ? rowNo + 1 : rowNo + i; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) + { + if (isSubListValue(vo)) + { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } + else + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + int column = 0; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + { + boolean subFirst = false; + for (Object obj : subList) + { + if (subFirst) + { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) + { + if (subField.isAnnotationPresent(Excel.class)) + { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } + else + { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + styles.put(key, style); + } + } + return styles; + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) + { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr)); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) + { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) + { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel) + { + try + { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class }); + value = formatMethod.invoke(instance, value, excel.args()); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) + { + filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx"; + return filename; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) + { + String downloadPath = AppHomeConfig.getDownloadPath() + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) + { + if (row == null) + { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) + { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) + { + return false; + } + } + return true; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + { + Map sheetIndexPicMap = new HashMap(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty()) + { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) + { + HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); + if (shape instanceof HSSFPicture) + { + HSSFPicture pic = (HSSFPicture) shape; + int pictureIndex = pic.getPictureIndex() - 1; + HSSFPictureData picData = pictures.get(pictureIndex); + String picIndex = String.valueOf(anchor.getRow1()) + "_" + String.valueOf(anchor.getCol1()); + sheetIndexPicMap.put(picIndex, picData); + } + } + return sheetIndexPicMap; + } + else + { + return sheetIndexPicMap; + } + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + { + Map sheetIndexPicMap = new HashMap(); + for (POIXMLDocumentPart dr : sheet.getRelations()) + { + if (dr instanceof XSSFDrawing) + { + XSSFDrawing drawing = (XSSFDrawing) dr; + List shapes = drawing.getShapes(); + for (XSSFShape shape : shapes) + { + if (shape instanceof XSSFPicture) + { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.put(picIndex, pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/reflect/ReflectUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..42dde3a9074eab71b080864df400590c0daa3858 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,411 @@ +package com.tongtech.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; + +import com.tongtech.common.core.text.Convert; +import com.tongtech.common.utils.DateUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author XiaoZhangTongZhi + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/sign/Base64.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/sign/Base64.java new file mode 100644 index 0000000000000000000000000000000000000000..6ff1bfb8e43935408f91b489c3ece720e0c26f32 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.tongtech.common.utils.sign; + +/** + * Base64工具类 + * + * @author XiaoZhangTongZhi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/sign/Md5Utils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/sign/Md5Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..5620858cfeea7d1db588eeda1cb48a784f1c05bd --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package com.tongtech.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author XiaoZhangTongZhi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/spring/SpringUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d6ba4b254d3639631f7af12aba684d19c53e75b9 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/spring/SpringUtils.java @@ -0,0 +1,158 @@ +package com.tongtech.common.utils.spring; + +import com.tongtech.common.utils.StringUtils; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author XiaoZhangTongZhi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/sql/SqlUtil.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..2e6b9ac17f3ced030b1dc28ca343e2ea2a86ad9f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/sql/SqlUtil.java @@ -0,0 +1,61 @@ +package com.tongtech.common.utils.sql; + +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.exception.UtilException; + +/** + * sql操作工具类 + * + * @author XiaoZhangTongZhi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/IdUtils.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..1eb162021186f9ef6d9ddddfc0e956463f23fbaf --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.tongtech.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author XiaoZhangTongZhi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/Seq.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/Seq.java new file mode 100644 index 0000000000000000000000000000000000000000..3c416a78d1f40e21ea7511e6ab2fbd08a19bef84 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/Seq.java @@ -0,0 +1,87 @@ +package com.tongtech.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; + +import com.tongtech.common.utils.DateUtils; +import com.tongtech.common.utils.StringUtils; + +/** + * @author XiaoZhangTongZhi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/UUID.java b/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/UUID.java new file mode 100644 index 0000000000000000000000000000000000000000..ac30e83f8f41899744537e2c01434c5b67694082 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package com.tongtech.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.tongtech.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author XiaoZhangTongZhi + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/xss/Xss.java b/rds-console/console-common/src/main/java/com/tongtech/common/xss/Xss.java new file mode 100644 index 0000000000000000000000000000000000000000..62da763c2d171df3b0a5e33e047969c501d79736 --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.tongtech.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author XiaoZhangTongZhi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/rds-console/console-common/src/main/java/com/tongtech/common/xss/XssValidator.java b/rds-console/console-common/src/main/java/com/tongtech/common/xss/XssValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..b75d5ca9dc794d81879f8797ac98eb1e4e82361f --- /dev/null +++ b/rds-console/console-common/src/main/java/com/tongtech/common/xss/XssValidator.java @@ -0,0 +1,34 @@ +package com.tongtech.common.xss; + +import com.tongtech.common.utils.StringUtils; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author XiaoZhangTongZhi + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} diff --git a/rds-console/console-framework/pom.xml b/rds-console/console-framework/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c0d18f7c6b357cf3c08dce48c43b02cfe1dbcf4 --- /dev/null +++ b/rds-console/console-framework/pom.xml @@ -0,0 +1,64 @@ + + + + console + com.tongtech + 2.2.C.1 + + 4.0.0 + + console-framework + + + framework框架核心 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.alibaba + druid-spring-boot-starter + + + + + com.github.penggle + kaptcha + + + javax.servlet-api + javax.servlet + + + + + + + com.github.oshi + oshi-core + + + + + com.tongtech + console-system + + + + + diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/DataSourceAspect.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000000000000000000000000000000000000..e1e0c734f36141cef68c434a458bf72fd67886f2 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package com.tongtech.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.tongtech.common.annotation.DataSource; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author XiaoZhangTongZhi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.tongtech.common.annotation.DataSource)" + + "|| @within(com.tongtech.common.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/LogAspect.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/LogAspect.java new file mode 100644 index 0000000000000000000000000000000000000000..1018bfdc4ca2ea373f33c9a55affe6b0c7d552c7 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/LogAspect.java @@ -0,0 +1,229 @@ +package com.tongtech.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; +import com.alibaba.fastjson2.JSON; +import com.tongtech.common.annotation.Log; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.enums.BusinessStatus; +import com.tongtech.common.enums.HttpMethod; +import com.tongtech.common.filter.PropertyPreExcludeFilter; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.ip.IpUtils; +import com.tongtech.framework.manager.AsyncManager; +import com.tongtech.framework.manager.factory.AsyncFactory; +import com.tongtech.system.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author XiaoZhangTongZhi + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("==前置通知异常=="); + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception + { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) + { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter()); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter() + { + return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/RateLimiterAspect.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000000000000000000000000000000000000..016ae2945ad88f5d4cb872dfddad4c8d490d67ec --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,90 @@ +package com.tongtech.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import com.tongtech.common.annotation.RateLimiter; +import com.tongtech.common.enums.LimitType; +import com.tongtech.common.exception.ServiceException; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.ip.IpUtils; + +/** + * 限流处理 + * + * @author XiaoZhangTongZhi + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ApplicationConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ApplicationConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..b3cfdd8d478ddfe802efb092c096098dfccd91b3 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package com.tongtech.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author XiaoZhangTongZhi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.tongtech.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/CaptchaConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/CaptchaConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..2f9ce6d1a56667539b29a1246f6f178748aad078 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.tongtech.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.tongtech.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/DruidConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/DruidConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..45d9e5c6cede9fc979e106cb369ee1d4d2b85394 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +package com.tongtech.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.tongtech.common.enums.DataSourceType; +import com.tongtech.common.utils.spring.SpringUtils; +import com.tongtech.framework.config.properties.DruidProperties; +import com.tongtech.framework.datasource.DynamicDataSource; + +/** + * druid 配置多数据源 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/FastJson2JsonRedisSerializer.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..272c8f0f0de258b750c9cbcab0513300c660e6c7 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,48 @@ +package com.tongtech.framework.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; + +/** + * Redis使用FastJson序列化 + * + * @author XiaoZhangTongZhi + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/FilterConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/FilterConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..231c49a97286c34aa40eb825abf5d35c74c5ad3d --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/FilterConfig.java @@ -0,0 +1,58 @@ +package com.tongtech.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.tongtech.common.filter.RepeatableFilter; +import com.tongtech.common.filter.XssFilter; +import com.tongtech.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/KaptchaTextCreator.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/KaptchaTextCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..607094e6afbba05caab607011eda4bcd2239af6e --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.tongtech.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author XiaoZhangTongZhi + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/MyBatisConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/MyBatisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..ed09669fc9f702cf952ca2f9cd63d923644ec23a --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/MyBatisConfig.java @@ -0,0 +1,132 @@ +package com.tongtech.framework.config; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import javax.sql.DataSource; +import org.apache.ibatis.io.VFS; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.ClassUtils; +import com.tongtech.common.utils.StringUtils; + +/** + * Mybatis支持*匹配扫描包 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class MyBatisConfig +{ + @Autowired + private Environment env; + + static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + + public static String setTypeAliasesPackage(String typeAliasesPackage) + { + ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); + MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); + List allResult = new ArrayList(); + try + { + for (String aliasesPackage : typeAliasesPackage.split(",")) + { + List result = new ArrayList(); + aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; + Resource[] resources = resolver.getResources(aliasesPackage); + if (resources != null && resources.length > 0) + { + MetadataReader metadataReader = null; + for (Resource resource : resources) + { + if (resource.isReadable()) + { + metadataReader = metadataReaderFactory.getMetadataReader(resource); + try + { + result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); + } + catch (ClassNotFoundException e) + { + e.printStackTrace(); + } + } + } + } + if (result.size() > 0) + { + HashSet hashResult = new HashSet(result); + allResult.addAll(hashResult); + } + } + if (allResult.size() > 0) + { + typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); + } + else + { + throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + return typeAliasesPackage; + } + + public Resource[] resolveMapperLocations(String[] mapperLocations) + { + ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); + List resources = new ArrayList(); + if (mapperLocations != null) + { + for (String mapperLocation : mapperLocations) + { + try + { + Resource[] mappers = resourceResolver.getResources(mapperLocation); + resources.addAll(Arrays.asList(mappers)); + } + catch (IOException e) + { + // ignore + } + } + } + return resources.toArray(new Resource[resources.size()]); + } + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception + { + String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); + String mapperLocations = env.getProperty("mybatis.mapperLocations"); + String configLocation = env.getProperty("mybatis.configLocation"); + typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); + VFS.addImplClass(SpringBootVFS.class); + + final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + sessionFactory.setTypeAliasesPackage(typeAliasesPackage); + sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); + sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); + return sessionFactory.getObject(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/RedisConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/RedisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..d6382bde6f1b52690d6c7c468204ab51658f267e --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/RedisConfig.java @@ -0,0 +1,69 @@ +package com.tongtech.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author XiaoZhangTongZhi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ResourcesConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ResourcesConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..f14b29f22942bb9dce73587b5cc24226f4a802e3 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ResourcesConfig.java @@ -0,0 +1,86 @@ +package com.tongtech.framework.config; + +import com.tongtech.common.config.AppHomeConfig; +import com.tongtech.framework.interceptor.ContextPathInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.tongtech.common.constant.Constants; +import com.tongtech.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Autowired + private ContextPathInterceptor contextPathInterceptor; + +// @Override +// public void addResourceHandlers(ResourceHandlerRegistry registry) +// { +// /** 本地文件上传路径 */ +// registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") +// .addResourceLocations("file:" + RdsConsoleConfig.getProfile() + "/"); +// } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + + /** 本地文件上传路径 */ + registry.addResourceHandler("/web-api/" + Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + AppHomeConfig.getProfile() + "/"); + + /** 加入静态资源的映射 */ + registry.addResourceHandler("/index.html").addResourceLocations("classpath:/public/index.html"); + registry.addResourceHandler("/static/**").addResourceLocations("classpath:/public/static/"); + registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/public/favicon.ico"); + + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + //registry.addInterceptor(contextPathInterceptor).addPathPatterns("/**/*.js"); + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ScheduleConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ScheduleConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..a82b15f959a9b6b7a06b4a35e73018c70a1a84ce --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.tongtech.quartz.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author XiaoZhangTongZhi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/SecurityConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/SecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..5cbe126b557fc1e1fd2b0bbc3577cc375bd88181 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/SecurityConfig.java @@ -0,0 +1,188 @@ +package com.tongtech.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; +import com.tongtech.framework.config.properties.PermitAllUrlProperties; +import com.tongtech.framework.security.filter.JwtAuthenticationTokenFilter; +import com.tongtech.framework.security.handle.AuthenticationEntryPointImpl; +import com.tongtech.framework.security.handle.LogoutSuccessHandlerImpl; + +/** + * spring security配置 + * + * @author XiaoZhangTongZhi + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception + { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ +// @Override +// protected void configure(HttpSecurity httpSecurity) throws Exception +// { +// // 注解标记允许匿名访问的url +// ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); +// permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); +// +// httpSecurity +// // CSRF禁用,因为不使用session +// .csrf().disable() +// // 认证失败处理类 +// .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() +// // 基于token,所以不需要session +// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() +// // 过滤请求 +// .authorizeRequests() +// // 对于登录login 注册register 验证码captchaImage 允许匿名访问 +// .antMatchers("/login", "/register", "/captchaImage").anonymous() +// // 静态资源,可匿名访问 +// .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() +// .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() +// // 除上面外的所有请求全部需要鉴权认证 +// .anyRequest().authenticated() +// .and() +// .headers().frameOptions().disable(); +// // 添加Logout filter +// httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); +// // 添加JWT filter +// httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); +// // 添加CORS filter +// httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); +// httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); +// } + + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception + { + // 注解标记允许匿名访问的url + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); + + httpSecurity + // CSRF禁用,因为不使用session + .csrf().disable() + // 认证失败处理类 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + .antMatchers("/web-api/login", "/web-api/register", "/web-api/captchaImage","/web-api/test/testHomeController/**").anonymous() + // 对于重定向到index.html的路径 允许匿名访问, 请对照 SysIndexController 中相同的路径配置。 + .antMatchers("/index", "/login", "/logout", "/system/**", "/user/**", "/monitor/**", "/tool/**", "/rds/**", "/console/**", "/log/**").anonymous() + .antMatchers(HttpMethod.GET, "/web-api/profile/**").permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/" , "/*.html", "/*.ico", + "/static/**/*.ico", "/static/**/*.gif", "/static/**/*.png", + "/static/**/*.jpg", "/static/**/*.ttf", "/static/**/*.woff", + "/**/*.html", "/**/*.css", "/**/*.js", "/**/*.svg").permitAll() + //TODO 不确认此处如何修改 + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**", "/database/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable(); + // 添加Logout filter + httpSecurity.logout().logoutUrl("/web-api/logout").logoutSuccessHandler(logoutSuccessHandler); + // 添加JWT filter + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception + { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ServerConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ServerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..5dd4391aecb1d2f159b6f38f8cef4b17af1d6fa9 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.tongtech.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.tongtech.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author XiaoZhangTongZhi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ThreadPoolConfig.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..44edcf0346bfb900f860268afe02422a9171f13b --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/ThreadPoolConfig.java @@ -0,0 +1,63 @@ +package com.tongtech.framework.config; + +import com.tongtech.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author XiaoZhangTongZhi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/properties/DruidProperties.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..3c0c812ac9504ce83d23ae39929f5f94845164bb --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/properties/DruidProperties.java @@ -0,0 +1,77 @@ +package com.tongtech.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/config/properties/PermitAllUrlProperties.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..13e9a4e06e0de7350b2d48575caba82be3db8187 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,72 @@ +package com.tongtech.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import com.tongtech.common.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/datasource/DynamicDataSource.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000000000000000000000000000000000000..99b3b57bca97ce09345bc90f8a13ba9421a69bc8 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.tongtech.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author XiaoZhangTongZhi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/datasource/DynamicDataSourceContextHolder.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..189c25aed21566e914d492fc9e616f8bfeb809ab --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.tongtech.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author XiaoZhangTongZhi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/ContextPathInterceptor.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/ContextPathInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..9e8ba6b9e6b8cec65c86df1a9ff0ae0038c7d4ad --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/ContextPathInterceptor.java @@ -0,0 +1,61 @@ +package com.tongtech.framework.interceptor; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; + +@Component +public class ContextPathInterceptor implements HandlerInterceptor { + + @Value("${server.servlet.context-path}") + private String contextPath; + + private final static String PATH_TOBE = "/uhrds"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + int startIdx = contextPath.length() + 1; + String uri = request.getRequestURI(); + String resourcePath = "/public/" + uri.substring(startIdx); + ClassPathResource jsRes = new ClassPathResource(resourcePath); + if(jsRes.exists() ) { + String content = StreamUtils.copyToString(jsRes.getInputStream(), StandardCharsets.UTF_8); + if(contains(content)) { + //System.out.println("~~~~~~~~~~ Replacing resourcePath:" + resourcePath); + response.setHeader("Content-Type", "application/javascript;charset=utf-8"); + PrintWriter writer = response.getWriter(); + writer.write(replace(content)); + writer.flush(); + return false; + } + } + + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + HandlerInterceptor.super.afterCompletion(request, response, handler, ex); + } + + public boolean contains(String inputContent) { + return inputContent.contains(PATH_TOBE); + } + + public String replace(String inputContent) { + return inputContent.replace(PATH_TOBE, contextPath); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/RepeatSubmitInterceptor.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..d4831620dbada37d0b06c2adeaa6f80b90bd1821 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,55 @@ +package com.tongtech.framework.interceptor; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import com.tongtech.common.annotation.RepeatSubmit; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author XiaoZhangTongZhi + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/impl/SameUrlDataInterceptor.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..882cba97bbe9549dfe5739ed0224d9eea7cede8b --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,110 @@ +package com.tongtech.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.tongtech.common.annotation.RepeatSubmit; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.core.cache.ObjectCache; +import com.tongtech.common.filter.RepeatedlyRequestWrapper; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.http.HttpHelper; +import com.tongtech.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author XiaoZhangTongZhi + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private ObjectCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/AsyncManager.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/AsyncManager.java new file mode 100644 index 0000000000000000000000000000000000000000..980b440c65a610b0c73a4a06f1b15b53e0e4c99a --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package com.tongtech.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.tongtech.common.utils.Threads; +import com.tongtech.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author XiaoZhangTongZhi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/ShutdownManager.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/ShutdownManager.java new file mode 100644 index 0000000000000000000000000000000000000000..7e33a14f2a57a48a06ee5ec32cb08761f50da23a --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.tongtech.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author XiaoZhangTongZhi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/factory/AsyncFactory.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0894bd1385e32385dcc7edd5ccac06ca0f21d0f2 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package com.tongtech.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.utils.LogUtils; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.ip.AddressUtils; +import com.tongtech.common.utils.ip.IpUtils; +import com.tongtech.common.utils.spring.SpringUtils; +import com.tongtech.system.domain.SysLogininfor; +import com.tongtech.system.domain.SysOperLog; +import com.tongtech.system.service.ISysLogininforService; +import com.tongtech.system.service.ISysOperLogService; +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author XiaoZhangTongZhi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/security/context/AuthenticationContextHolder.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..a87a4dc0499d3c6d8349d1b233e74ff2be72d3ce --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.tongtech.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author XiaoZhangTongZhi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/security/context/PermissionContextHolder.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..32f43086ead5452bd3f260b43794ef27dedbfbde --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.tongtech.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.tongtech.common.core.text.Convert; + +/** + * 权限信息 + * + * @author XiaoZhangTongZhi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/security/filter/JwtAuthenticationTokenFilter.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..3552500c991fa95a61cedc76cdaa8a094f755476 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,44 @@ +package com.tongtech.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author XiaoZhangTongZhi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/security/handle/AuthenticationEntryPointImpl.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..655e647b55a498ab33653b8b14633c9eb437859f --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.tongtech.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.tongtech.common.constant.HttpStatus; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author XiaoZhangTongZhi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/security/handle/LogoutSuccessHandlerImpl.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..9944b5486479d5c741d6f55274db8e7a8360c763 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,53 @@ +package com.tongtech.framework.security.handle; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson2.JSON; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.constant.HttpStatus; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.framework.manager.AsyncManager; +import com.tongtech.framework.manager.factory.AsyncFactory; +import com.tongtech.framework.web.service.TokenService; + +/** + * 自定义退出处理类 返回成功 + * + * @author XiaoZhangTongZhi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功"))); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/Server.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/Server.java new file mode 100644 index 0000000000000000000000000000000000000000..3388f1bd6e4177a8657f985a4acb1f0e159e6f92 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package com.tongtech.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.tongtech.common.utils.Arith; +import com.tongtech.common.utils.ip.IpUtils; +import com.tongtech.framework.web.domain.server.Cpu; +import com.tongtech.framework.web.domain.server.Jvm; +import com.tongtech.framework.web.domain.server.Mem; +import com.tongtech.framework.web.domain.server.Sys; +import com.tongtech.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author XiaoZhangTongZhi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Cpu.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000000000000000000000000000000000000..80c2bbbb9d75550249700f9a46560632c15a499c --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.tongtech.framework.web.domain.server; + +import com.tongtech.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author XiaoZhangTongZhi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Jvm.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000000000000000000000000000000000000..ce0a54304445a4caa8c238c5201560d77ec988ca --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package com.tongtech.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.tongtech.common.utils.Arith; +import com.tongtech.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author XiaoZhangTongZhi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Mem.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Mem.java new file mode 100644 index 0000000000000000000000000000000000000000..f35ec578fc640bcef7af0397e7674c9a558e89bf --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.tongtech.framework.web.domain.server; + +import com.tongtech.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author XiaoZhangTongZhi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Sys.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Sys.java new file mode 100644 index 0000000000000000000000000000000000000000..beb211d9267bbda108e7db3fe4b03b234f52193d --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.tongtech.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author XiaoZhangTongZhi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/SysFile.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000000000000000000000000000000000000..4445857d97deaa561949ee8de68b2d330e150e0a --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.tongtech.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author XiaoZhangTongZhi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/exception/GlobalExceptionHandler.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..65aa5275c25e7f45fc76fef21c517f9e8be113d8 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,114 @@ +package com.tongtech.framework.web.exception; + +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import com.tongtech.common.constant.HttpStatus; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.common.exception.DemoModeException; +import com.tongtech.common.exception.ServiceException; +import com.tongtech.common.utils.StringUtils; + +/** + * 全局异常处理器 + * + * @author XiaoZhangTongZhi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/PermissionService.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/PermissionService.java new file mode 100644 index 0000000000000000000000000000000000000000..d07ffbbcaf1ef8878fdd067a1c80eac243d88263 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/PermissionService.java @@ -0,0 +1,168 @@ +package com.tongtech.framework.web.service; + +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.tongtech.common.core.domain.entity.SysRole; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.framework.security.context.PermissionContextHolder; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author XiaoZhangTongZhi + */ +@Service("ss") +public class PermissionService +{ + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + private static final String ROLE_DELIMETER = ","; + + private static final String PERMISSION_DELIMETER = ","; + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysLoginService.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysLoginService.java new file mode 100644 index 0000000000000000000000000000000000000000..e7ce9b3b2a1622306f5c0d37d2b28d28cf8c0da8 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysLoginService.java @@ -0,0 +1,209 @@ +package com.tongtech.framework.web.service; + +import javax.annotation.Resource; + +import com.tongtech.common.config.UhConsoleConfig; +import com.tongtech.common.core.domain.AjaxResult; +import com.tongtech.system.service.SysObjectCacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.exception.ServiceException; +import com.tongtech.common.exception.user.CaptchaException; +import com.tongtech.common.exception.user.CaptchaExpireException; +import com.tongtech.common.exception.user.UserPasswordNotMatchException; +import com.tongtech.common.utils.DateUtils; +import com.tongtech.common.utils.MessageUtils; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.ip.IpUtils; +import com.tongtech.framework.manager.AsyncManager; +import com.tongtech.framework.manager.factory.AsyncFactory; +import com.tongtech.framework.security.context.AuthenticationContextHolder; +import com.tongtech.system.service.ISysConfigService; +import com.tongtech.system.service.ISysUserService; + +import java.util.Date; + +/** + * 登录校验方法 + * + * @author XiaoZhangTongZhi + */ +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private SysObjectCacheService cacheService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + // 验证码开关 + if (captchaEnabled) + { + validateCaptcha(username, code, uuid); + } + // 用户验证 + Authentication authentication = null; + try + { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally + { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = cacheService.getCacheObject(verifyKey); + cacheService.deleteObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateLoginInfo(sysUser); + } + + + + + + /** + * 判断用户密码是否过期 + * @param userId + * @return + */ + public boolean isUserExpired(Long userId) { + if(UhConsoleConfig.isEmbedding() == true) { //嵌入模式下,不判断密码过期, + return false; //返回未过期 + } + + SysUser user = userService.selectUserById(userId); + Date expireDate = user.getPasswordExpired(); + if(expireDate != null) { + if(System.currentTimeMillis() > expireDate.getTime()) { + return true; + } + else { + return false; + } + } + else { + return false; + } + } + + /** + * 返回用户密码修改建议 + * @param userId + * @return + * 密码过期: msg="密码已过期,请设置新的密码!",data="expired"; + * 首次登录: msg="首次登录,为保证您的账号安全,请更改密码!",data="initial" + * 无需修改: msg="none", data="none" + */ + public AjaxResult getPasswordSuggestion(Long userId) { + if(UhConsoleConfig.isEmbedding() == true) { //嵌入模式下,不判断用户密码修改状态 + return AjaxResult.success("none", "none"); + } + + + SysUser user = userService.selectUserById(userId); + Date expireDate = user.getPasswordExpired(); + boolean expired = false; + if(expireDate != null && System.currentTimeMillis() > expireDate.getTime()) { + expired = true; + } + + if(user.getUpdateTime() == null) { + return AjaxResult.success(MessageUtils.message("user.password.initial"), "initial"); + } + else if(expired) { + return AjaxResult.success(MessageUtils.message("user.password.expired"), "expired"); + } + else { + return AjaxResult.success("none", "none"); + } + + } + +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysPasswordService.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysPasswordService.java new file mode 100644 index 0000000000000000000000000000000000000000..12c952d945c72f73b06f78e7061c313c473c43b5 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysPasswordService.java @@ -0,0 +1,111 @@ +package com.tongtech.framework.web.service; + +import java.util.Date; + +import com.tongtech.common.exception.ServiceException; +import com.tongtech.system.service.ISysConfigService; +import com.tongtech.system.service.ISysUserService; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.utils.MessageUtils; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.framework.manager.AsyncManager; +import com.tongtech.framework.manager.factory.AsyncFactory; +import com.tongtech.framework.security.context.AuthenticationContextHolder; + +import javax.annotation.Resource; + +/** + * 登录密码方法 + * + * @author XiaoZhangTongZhi + */ +@Component +public class SysPasswordService { + + private final static long DAY_TIME = 24 * 60 * 60 * 1000; + + @Resource + private ISysUserService sysUserService; + + @Resource + private ISysConfigService sysConfigService; + + public void validate(SysUser user) { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + SysUser sysUser = sysUserService.selectUserByUserName(username); + + SysUser userToUpdate = new SysUser(); + userToUpdate.setUserId(sysUser.getUserId()); + + int loginRetries = sysUser.getLoginRetries(); + //获取系统配置中登录尝试次数账号锁定的值 + int maxLoginRetries = Integer.parseInt(sysConfigService.selectConfigByKey("sys.login.maxRetries")); + + if (loginRetries >= maxLoginRetries) { + String msg = MessageUtils.message("user.login.retries.exceed", maxLoginRetries); + // 异步日志记录 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, msg)); + + throw new ServiceException(msg); + } + else if (!matches(user, password)) { + loginRetries ++; + userToUpdate.setLoginRetries(loginRetries); + if (loginRetries >= maxLoginRetries) { //已超出认证重试次数 + userToUpdate.setLoginLocked("1"); + sysUserService.updateUser(userToUpdate); + String msg = MessageUtils.message("user.login.retries.exceed", maxLoginRetries); + throw new ServiceException(msg); + } + else { //未到达认证重试次数 + userToUpdate.setLoginLocked("0"); + sysUserService.updateUser(userToUpdate); + } + + } else { + userToUpdate.setLoginLocked("0"); + userToUpdate.setLoginRetries(0); + sysUserService.updateUser(userToUpdate); + } + + } + + public boolean matches(SysUser user, String rawPassword) { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + + /** + * 清楚登录锁定 + * @param userId + */ + public void clearLoginLocked(Long userId) { + SysUser u = new SysUser(); + u.setUserId(userId); + u.setLoginLocked("0"); + u.setLoginRetries(0); + sysUserService.updateUser(u); + } + + /** + * 已当前时间为准更新到下一个密码过期时间 + * @param userId + */ + public SysUser renewPasswordExpiredTime(Long userId) { + SysUser u = new SysUser(); + int expireDays = Integer.parseInt(sysConfigService.selectConfigByKey("sys.password.expireDays")); + long expireTime = System.currentTimeMillis() + expireDays * DAY_TIME; + u.setUserId(userId); + u.setPasswordExpired(new Date(expireTime)); + sysUserService.updateUser(u); + return u; + } + + +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysPermissionService.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysPermissionService.java new file mode 100644 index 0000000000000000000000000000000000000000..d85aa3c0789846295dfe90466fb35f2ab1f496a0 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysPermissionService.java @@ -0,0 +1,82 @@ +package com.tongtech.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.tongtech.common.core.domain.entity.SysRole; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.system.service.ISysMenuService; +import com.tongtech.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author XiaoZhangTongZhi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + List roles = user.getRoles(); + if (!roles.isEmpty() && roles.size() > 1) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysRegisterService.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysRegisterService.java new file mode 100644 index 0000000000000000000000000000000000000000..5426f123f4a3cdca02a24a1147b6733da01e89e8 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/SysRegisterService.java @@ -0,0 +1,115 @@ +package com.tongtech.framework.web.service; + +import com.tongtech.system.service.SysObjectCacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.constant.UserConstants; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.domain.model.RegisterBody; +import com.tongtech.common.exception.user.CaptchaException; +import com.tongtech.common.exception.user.CaptchaExpireException; +import com.tongtech.common.utils.MessageUtils; +import com.tongtech.common.utils.SecurityUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.framework.manager.AsyncManager; +import com.tongtech.framework.manager.factory.AsyncFactory; +import com.tongtech.system.service.ISysConfigService; +import com.tongtech.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author XiaoZhangTongZhi + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private SysObjectCacheService redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + + // 验证码开关 + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); + } + + if (StringUtils.isEmpty(username)) + { + msg = "用户名不能为空"; + } + else if (StringUtils.isEmpty(password)) + { + msg = "用户密码不能为空"; + } + else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + msg = "账户长度必须在2到20个字符之间"; + } + else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + msg = "密码长度必须在5到20个字符之间"; + } + else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) + { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } + else + { + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) + { + msg = "注册失败,请联系系统管理人员"; + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/TokenService.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/TokenService.java new file mode 100644 index 0000000000000000000000000000000000000000..99396b133378a2d41e90bbd3718111745b556e51 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/TokenService.java @@ -0,0 +1,227 @@ +package com.tongtech.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; + +import com.tongtech.system.service.SysObjectCacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.tongtech.common.constant.CacheConstants; +import com.tongtech.common.constant.Constants; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.utils.ServletUtils; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.common.utils.ip.AddressUtils; +import com.tongtech.common.utils.ip.IpUtils; +import com.tongtech.common.utils.uuid.IdUtils; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author XiaoZhangTongZhi + */ +@Component +public class TokenService +{ + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private SysObjectCacheService cacheService; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = cacheService.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + cacheService.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + cacheService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/UserDetailsServiceImpl.java b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..943bbe8f0ee5502ecac381c8b4dfc9cc1936e300 --- /dev/null +++ b/rds-console/console-framework/src/main/java/com/tongtech/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,60 @@ +package com.tongtech.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.tongtech.common.core.domain.entity.SysUser; +import com.tongtech.common.core.domain.model.LoginUser; +import com.tongtech.common.enums.UserStatus; +import com.tongtech.common.exception.ServiceException; +import com.tongtech.common.utils.StringUtils; +import com.tongtech.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author XiaoZhangTongZhi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException("登录用户:" + username + " 不存在"); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + + if (!user.getUserId().equals(1L)) { + passwordService.validate(user); + } + + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) { + return new LoginUser(user.getUserId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/rds-console/console-release/pom.xml b/rds-console/console-release/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..242a9faf21a638b2405084edc866612c9419fab9 --- /dev/null +++ b/rds-console/console-release/pom.xml @@ -0,0 +1,124 @@ + + + 4.0.0 + + com.tongtech + console-release + 1.0-SNAPSHOT + + + ${project.basedir}/resources/tongtech/apphome + ${project.basedir}/../apphome + 11 + 11 + + + + + + org.slf4j + slf4j-api + 1.7.36 + + + org.apache.logging.log4j + log4j-api + 2.17.2 + + + org.apache.logging.log4j + log4j-api + 2.17.2 + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + id.clean + clean + + run + + + + Delete apphome.distdir: ${apphome.distdir} + + + + + + + + + id.compile + compile + + run + + + + + [==== Copy apphome resources ====] + + + + + + + + + + + + id.prepare-package + prepare-package + + run + + + + [==== console-admin.jar to apphome ====] + + + + + + + + + + maven-assembly-plugin + + + dist + package + + single + + + console + + ${project.basedir}/src/main/assembly/binary.xml + + + + + + true + false + false + + + + + + + diff --git a/rds-console/console-release/resources/tongtech/apphome/bin/console.bat b/rds-console/console-release/resources/tongtech/apphome/bin/console.bat new file mode 100644 index 0000000000000000000000000000000000000000..96696126ede2ba850d3131e25d487165b872ec25 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/apphome/bin/console.bat @@ -0,0 +1,73 @@ +@echo off + +rem jarƽĿ¼ +set AppName=console-admin.jar + +rem JVM +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m" + + +rem õǰĿ¼ +cd .. +set "APP_HOME=%cd%" +cd "%APP_HOME%" +ECHO. APP_HOME:%APP_HOME% + +ECHO. + ECHO. [1] %AppName% + ECHO. [2] ر%AppName% + ECHO. [3] %AppName% + ECHO. [4] ״̬ %AppName% + ECHO. [5] +ECHO. + +ECHO.ѡĿ: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar lib/%AppName% + +echo starting +echo Start %AppName% success... +goto:eof + +rem stopͨjpspid +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem ݽIDkill + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/rds-console/console-release/resources/tongtech/apphome/bin/console.sh b/rds-console/console-release/resources/tongtech/apphome/bin/console.sh new file mode 100755 index 0000000000000000000000000000000000000000..2261cc23c758e3328be8a69d864a66442704e3d0 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/apphome/bin/console.sh @@ -0,0 +1,130 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=console-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m" + + +# resolve links - $0 may be a softlink +PRG="$0" + +while [ -h "$PRG" ]; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`/"$link" + fi +done + +# Get standard environment variables +PRGDIR=`dirname "$PRG"` + +# Set APP_HOME as current working directory +APP_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log +cd $APP_HOME + +echo "APP_HOME: $APP_HOME" + + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|run|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar lib/$AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function run() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + if [ ! -d "data/db" ]; then + echo "data/ do not contained data files! try to initial it!" + if [ ! -d "data" ]; then + mkdir data + fi + cp -a data-init/* data/ + fi + + java $JVM_OPTS -jar lib/$AppName + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + run) + run;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/rds-console/console-release/resources/tongtech/apphome/config/application.yml b/rds-console/console-release/resources/tongtech/apphome/config/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..24604490710617bc22e98578446fd12bb733a20f --- /dev/null +++ b/rds-console/console-release/resources/tongtech/apphome/config/application.yml @@ -0,0 +1,21 @@ +# 项目相关配置 +console: + # 验证码类型 rds-math 数组计算 rds-char 字符验证 easy-char 简单字符验证 + captchaType: easy-char + #SSH 连接时的超时时间, 单位毫秒 + sshConnectTimeout: 10000 + #SSH 命令执行通道超时时间, 单位毫秒 + sshChannelTimeout: 20000 + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8083 + port: 8083 + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 diff --git a/rds-console/console-release/resources/tongtech/apphome/config/probe.config b/rds-console/console-release/resources/tongtech/apphome/config/probe.config new file mode 100644 index 0000000000000000000000000000000000000000..f5e13d4dd732229ab4d31539a7869c4016793f36 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/apphome/config/probe.config @@ -0,0 +1,4 @@ +#Local Probe Setting +probe.instance_name=${PROBE_INSTANCE_NAME\:ID1} +probe.log_level=${PROBE_LOG_LEVEL\:info} +probe.server_port=${PROBE_SERVER_PORT\:9090} diff --git a/rds-console/console-release/resources/tongtech/apphome/data/db/consoledb.mv.db b/rds-console/console-release/resources/tongtech/apphome/data/db/consoledb.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..f79d07884d3241df2182384d6552b93bad9a7321 Binary files /dev/null and b/rds-console/console-release/resources/tongtech/apphome/data/db/consoledb.mv.db differ diff --git a/rds-console/console-release/resources/tongtech/apphome/data/dbbak/README.txt b/rds-console/console-release/resources/tongtech/apphome/data/dbbak/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..19a1f8db3f1039f1bc359391ceabde2c3b25d660 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/apphome/data/dbbak/README.txt @@ -0,0 +1 @@ +database file backup directory diff --git a/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/application-druid.yml b/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/application-druid.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b46d80c0a677000e7909d9bb83b01b9c378cbd8 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/application-druid.yml @@ -0,0 +1,58 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: org.h2.Driver + druid: + # 主库数据源 + master: + url: jdbc:h2:file:./data/db/consoledb;MODE=MySQL + username: root + password: 123456 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: false + statViewServlet: + enabled: false + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: + login-password: + filter: + stat: + enabled: false + # 慢SQL记录 + log-slow-sql: false + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true diff --git a/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/banner.txt b/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/banner.txt new file mode 100644 index 0000000000000000000000000000000000000000..85d7ff6739c4bbea4556c3f497c0e4ef8eb080e0 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/banner.txt @@ -0,0 +1,6 @@ + _____ _____ _____ _____ _ +| __ \| __ \ / ____| / ____| | | +| |__) | | | | (___ ______ | | ___ _ __ ___ ___ | | ___ +| _ /| | | |\___ \ |______| | | / _ \| '_ \/ __|/ _ \| |/ _ \ +| | \ \| |__| |____) | | |___| (_) | | | \__ \ (_) | | __/ +|_| \_\_____/|_____/ \_____\___/|_| |_|___/\___/|_|\___| diff --git a/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/logback.xml b/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..c3f2ecc315c548377c292c2508067e126d9a19c1 --- /dev/null +++ b/rds-console/console-release/resources/tongtech/console-admin/src/main/resources/logback.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/system.log + + + + ${log.path}/system.%d{yyyy-MM-dd}.log + + 30 + + + ${log.pattern} + + + + + + ${log.path}/user-access.log + + + ${log.path}/user-access.%d{yyyy-MM-dd}.log + + 30 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + diff --git a/rds-console/console-release/src/main/assembly/binary.xml b/rds-console/console-release/src/main/assembly/binary.xml new file mode 100644 index 0000000000000000000000000000000000000000..cf7a312db950f83e9889a9d19413bfb2450bc44d --- /dev/null +++ b/rds-console/console-release/src/main/assembly/binary.xml @@ -0,0 +1,27 @@ + + + dist + + tar.gz + + + + ${project.basedir}/../apphome + + **/*.sh + + + + + ${project.basedir}/../apphome + + **/*.sh + + 0744 + + + + diff --git a/rds-console/console-system/pom.xml b/rds-console/console-system/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fcb5909736af958fadabd7cdbf8f5ebebb81f4d5 --- /dev/null +++ b/rds-console/console-system/pom.xml @@ -0,0 +1,57 @@ + + + + console + com.tongtech + 2.2.C.1 + + 4.0.0 + + console-system + + + system系统模块 + + + + + + com.h2database + h2 + + + + mysql + mysql-connector-java + + + + com.tongtech + console-common + + + + com.tongtech + console-client + + + + org.junit.jupiter + junit-jupiter + test + + + + + + + + diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/CenterLicenseInfo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/CenterLicenseInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..1c353b68758b6613c6249f4557bb9cd1c8d8fac9 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/CenterLicenseInfo.java @@ -0,0 +1,120 @@ +package com.tongtech.console.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.tongtech.common.utils.StringUtils; + +import java.util.Arrays; +import java.util.Date; + +/** + * 中心节点信息 + */ +public class CenterLicenseInfo { + + private static String KEY_USERNAME = "enduser"; + private static String KEY_PRODUCT = "product"; + private static String KEY_TYPE = "licenseType"; + private static String KEY_PROJECT = "project"; + + private long type; //license的类型码,大于等于 100 是企业版,小于100是标准版 + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date expiredTime; //到期时间,如:Tue Jun 14 17:47:30 CST 2022 + private long totalMemory; //内存用量,字节数 + private String totalMemoryDesc; //内存用量的描述,如:20KB,30GB,50GB, 1.2TB + private String userName; //用户名称, 如:"某某有限公司" + private String product; //产品名称,如:"TongRDS 2.2" + + private String project; //产品所使用的项目 如:"上海电力公司项目" + + private String typeDesc; //证书类型描述,如:"临时证书 90 天授权" + + private String[] context; //license中的context原始数据, 如:[最终用户:东方通, 产品名称:TongRDS 2.2, 证书类型:临时证书 90 天授权] + + public CenterLicenseInfo() {} + + public CenterLicenseInfo(long licenseType, long expiredTime, long totalMemory, String[] context) { + this.expiredTime = new Date(expiredTime); + this.totalMemory = totalMemory; + this.totalMemoryDesc = StringUtils.toReadableSize(totalMemory); + this.type = licenseType; + this.context = context; + for(String item : context) { + if(item != null && item.length() > 0) { + int idx = item.indexOf(":"); + String key="" , value = ""; + if(idx > 0) { + key = item.substring(0, idx); + idx++; + if(item.length() > idx) { + value = item.substring(idx).trim(); + } + } + + if(KEY_USERNAME.equals(key)) { + this.userName = value; + } + else if(KEY_PRODUCT.equals(key)) { + this.product = value; + } + else if(KEY_TYPE.equals(key)) { + this.typeDesc = value; + } + else if(KEY_PROJECT.equals((key))) { + this.project = value; + } + } + } + } + + public Date getExpiredTime() { + return expiredTime; + } + + public long getTotalMemory() { + return totalMemory; + } + + public String getTotalMemoryDesc() { + return totalMemoryDesc; + } + + public String getUserName() { + return userName; + } + + public String getProduct() { + return product; + } + + public long getType() { + return type; + } + + public String getProject() { + return project; + } + + public String getTypeDesc() { + return typeDesc; + } + + public String[] getContext() { + return context; + } + + @Override + public String toString() { + return "CenterLicenseInfo{" + "\n" + + "type=" + type + "\n" + + ", expiredTime=" + expiredTime + "\n" + + ", totalMemory=" + totalMemory + "\n" + + ", totalMemoryDesc='" + totalMemoryDesc + '\'' + "\n" + + ", userName='" + userName + '\'' + "\n" + + ", product='" + product + '\'' + "\n" + + ", project='" + project + '\'' + "\n" + + ", typeDesc='" + typeDesc + '\'' + "\n" + + ", context=" + Arrays.toString(context) + "\n" + + '}'; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/CenterStatSrc.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/CenterStatSrc.java new file mode 100644 index 0000000000000000000000000000000000000000..ab8b53ae2a5aaa7c7f75369219ebcd2fab19d74a --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/CenterStatSrc.java @@ -0,0 +1,103 @@ +package com.tongtech.console.domain; + +import com.tongtech.common.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 中心节点统计信息的原始报文对象 cnsl_center_stat_src + * + * @author Zhang ChenLong + * @date 2023-03-15 + */ +public class CenterStatSrc extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 源ID */ + private Long srcId; + + private String centersSrc; + + private String servicesSrc; + + private String sentinelsSrc; + + private String licenseSrc; + + /** 执行时长(毫秒) */ + private Long duration; + + public CenterStatSrc() {} + + public CenterStatSrc(String centersSrc, String servicesSrc, String sentinelsSrc, String licenseSrc, Long duration, Date createTime) { + this.centersSrc = centersSrc; + this.servicesSrc = servicesSrc; + this.sentinelsSrc = sentinelsSrc; + this.licenseSrc = licenseSrc; + } + + public void setSrcId(Long srcId) + { + this.srcId = srcId; + } + + public Long getSrcId() + { + return srcId; + } + + public void setDuration(Long duration) + { + this.duration = duration; + } + + public Long getDuration() + { + return duration; + } + + public String getCentersSrc() { + return centersSrc; + } + + public void setCentersSrc(String centersSrc) { + this.centersSrc = centersSrc; + } + + public String getServicesSrc() { + return servicesSrc; + } + + public void setServicesSrc(String servicesSrc) { + this.servicesSrc = servicesSrc; + } + + public String getSentinelsSrc() { + return sentinelsSrc; + } + + public void setSentinelsSrc(String sentinelsSrc) { + this.sentinelsSrc = sentinelsSrc; + } + + public String getLicenseSrc() { + return licenseSrc; + } + + public void setLicenseSrc(String licenseSrc) { + this.licenseSrc = licenseSrc; + } + + @Override + public String toString() { + return "CenterStatSrc{" + + "srcId=" + srcId + + ", centersSrc='" + centersSrc + '\'' + + ", servicesSrc='" + servicesSrc + '\'' + + ", sentinelsSrc='" + sentinelsSrc + '\'' + + ", licenseSrc='" + licenseSrc + '\'' + + ", duration=" + duration + + '}'; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/NodeConfig.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/NodeConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..fa47dc04c4ad9ce09bb985654ad4b21d46cbbd4c --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/NodeConfig.java @@ -0,0 +1,81 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +/** + * 节点配置信息对象 cnsl_node_config + * + * @author Zhang ChenLong + * @date 2023-02-27 + */ +public class NodeConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + @Excel(name = "节点ID") + private Long nodeId; + + /** 使用的模版ID */ + @Excel(name = "使用的模版ID") + private Long templateId; + + /** 对应模版类型: cnsl_template_type */ + @Excel(name = "对应模版类型: cnsl_template_type") + private String tempType; + + /** 配置内容 */ + @Excel(name = "配置内容") + private String confContent; + + public void setNodeId(Long nodeId) + { + this.nodeId = nodeId; + } + + public Long getNodeId() + { + return nodeId; + } + public void setTemplateId(Long templateId) + { + this.templateId = templateId; + } + + public Long getTemplateId() + { + return templateId; + } + public void setTempType(String tempType) + { + this.tempType = tempType; + } + + public String getTempType() + { + return tempType; + } + public void setConfContent(String confContent) + { + this.confContent = confContent; + } + + public String getConfContent() + { + return confContent; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("nodeId", getNodeId()) + .append("templateId", getTemplateId()) + .append("tempType", getTempType()) + .append("confContent", getConfContent()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/NodeStat.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/NodeStat.java new file mode 100644 index 0000000000000000000000000000000000000000..2636e51069839ad54663d80f88557fffa2fa3604 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/NodeStat.java @@ -0,0 +1,393 @@ +package com.tongtech.console.domain; + +import com.tongtech.probe.stat.StatBaseNode; +import com.tongtech.probe.stat.StatRuntime; +import com.tongtech.probe.stat.StatWorkerNode; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.core.domain.BaseEntity; + +import java.util.Date; + +import static com.tongtech.console.enums.NodeStatusEnum.START; +import static com.tongtech.console.enums.NodeStatusEnum.STOP; + +/** + * 节点监控信息对象 cnsl_node_stat + * + * @author Zhang ChenLong + * @date 2023-03-15 + */ +public class NodeStat extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 监控信息ID */ + private Long statId; + /** 源ID */ + private Long srcId; + /** 节点ID */ + private Long nodeId; + /** 服务ID */ + private Long serviceId; + /** 节点名称 */ + private String name; + /** 节点类型 */ + private String nodeType; + /** 实例名 */ + private String instance; + /** 是否过期 */ + private Boolean expired; + /** 启动以来运行了多少秒 */ + private Long running; + /** 当前连接数 */ + private Long currentConnections = 0L; + /** 启动以来一共连接次数 */ + private Long totalConnections = 0L; + /** 当前key总数 */ + private Long currentKeys = 0L; + /** 当前内存使用量 */ + private Long memoryUsed = 0L; + /** 当前内存剩余量 */ + private Long memoryFree = 0L; + /** 实际占用内存总量 */ + private Long memoryTotal = 0L; + /** 最大可用内存量 */ + private Long memoryAvailable = 0L; + /** 启动以来返回结果的命令数累计 */ + private Long commandResult = 0L; + /** 网络IO入流量总和 */ + private Long networkInputBytes = 0L; + /** 网络IO每秒入流量 */ + private Double inputPerSecond = 0D; + /** 网络IO出流量总和 */ + private Long networkOutputBytes = 0L; + /** 网络IO每秒出流量 */ + private Double outputPerSecond = 0D; + /** 当前程序CPU使用率 */ + private Double cpuProcessLoad = 0D; + /** 当前系统CPU使用率 */ + private Double cpuSystemLoad = 0D; + /** 每秒平均流量-前10秒内 */ + private Double throughputAverage10 = 0D; + /** 每秒平均流量-前60秒内 */ + private Double throughputAverage60 = 0D; + /** 显示专用属性, 节点状态,stop start */ + private String status; + /** 创建时间秒(从1970 到现在的秒数)*/ + protected Long createSecond; + + public NodeStat() { + + } + + public NodeStat(Long srcId, Long serviceId) { + this.srcId = srcId; + this.serviceId = serviceId; + } + + public NodeStat(Long srcId, RdsNode node, StatBaseNode statNode) { + this.srcId = srcId; + this.nodeId = node.getNodeId(); + this.serviceId = node.getServiceId(); + this.name = node.getNodeName(); + this.nodeType = node.getNodeType(); + this.instance = statNode.getInstance(); + this.setExpired(statNode.getExpired()); + StatRuntime rt = statNode.getRuntime(); + this.running = rt.getRunning(); + this.currentConnections = rt.getCurrentConnections(); + this.totalConnections = rt.getTotalConnections(); + this.currentKeys = rt.getKeys(); + this.memoryUsed = rt.getMemoryUsed(); + this.memoryFree = rt.getMemoryFree(); + this.memoryTotal = rt.getMemoryTotal(); + this.memoryAvailable = rt.getMemoryAvailable(); + this.commandResult = rt.getCommandResult(); + this.networkInputBytes = rt.getNetworkInputBytes(); + this.inputPerSecond = rt.getInputPerSecond(); + this.networkOutputBytes = rt.getNetworkOutputBytes(); + this.outputPerSecond = rt.getOutputPerSecond(); + this.cpuProcessLoad = rt.getCpuProcessLoad(); + this.cpuSystemLoad = rt.getCpuSystemLoad(); + + if(statNode instanceof StatWorkerNode) { + StatWorkerNode worker = (StatWorkerNode)statNode; + this.throughputAverage10 = worker.getThroughput().getAverage10(); + this.throughputAverage60 = worker.getThroughput().getAverage60(); + } + + } + + public void setStatId(Long statId) + { + this.statId = statId; + } + + public Long getStatId() + { + return statId; + } + public void setNodeId(Long nodeId) + { + this.nodeId = nodeId; + } + + public Long getNodeId() + { + return nodeId; + } + public void setNodeType(String nodeType) + { + this.nodeType = nodeType; + } + + public Long getSrcId() { + return srcId; + } + + public void setSrcId(Long srcId) { + this.srcId = srcId; + } + + public Long getServiceId() { + return serviceId; + } + + public void setServiceId(Long serviceId) { + this.serviceId = serviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public String getNodeType() + { + return nodeType; + } + public void setExpired(Boolean expired) + { + this.expired = expired; + this.status = (this.expired) ? STOP.getName() : START.getName(); + } + + public Boolean getExpired() + { + return expired; + } + public void setRunning(Long running) + { + this.running = running; + } + + public Long getRunning() + { + return running; + } + public void setCurrentConnections(Long currentConnections) + { + this.currentConnections = currentConnections; + } + + public Long getCurrentConnections() + { + return currentConnections; + } + public void setTotalConnections(Long totalConnections) + { + this.totalConnections = totalConnections; + } + + public Long getTotalConnections() + { + return totalConnections; + } + public void setCurrentKeys(Long currentKeys) + { + this.currentKeys = currentKeys; + } + + public Long getCurrentKeys() + { + return currentKeys; + } + public void setMemoryUsed(Long memoryUsed) + { + this.memoryUsed = memoryUsed; + } + + public Long getMemoryUsed() + { + return memoryUsed; + } + public void setMemoryFree(Long memoryFree) + { + this.memoryFree = memoryFree; + } + + public Long getMemoryFree() + { + return memoryFree; + } + public void setMemoryTotal(Long memoryTotal) + { + this.memoryTotal = memoryTotal; + } + + public Long getMemoryTotal() + { + return memoryTotal; + } + public void setMemoryAvailable(Long memoryAvailable) + { + this.memoryAvailable = memoryAvailable; + } + + public Long getMemoryAvailable() + { + return memoryAvailable; + } + public void setCommandResult(Long commandResult) + { + this.commandResult = commandResult; + } + + public Long getCommandResult() + { + return commandResult; + } + public void setNetworkInputBytes(Long networkInputBytes) + { + this.networkInputBytes = networkInputBytes; + } + + public Long getNetworkInputBytes() + { + return networkInputBytes; + } + + public void setNetworkOutputBytes(Long networkOutputBytes) + { + this.networkOutputBytes = networkOutputBytes; + } + + public Long getNetworkOutputBytes() + { + return networkOutputBytes; + } + + public Double getInputPerSecond() { + return inputPerSecond; + } + + public void setInputPerSecond(Double inputPerSecond) { + this.inputPerSecond = inputPerSecond; + } + + public Double getOutputPerSecond() { + return outputPerSecond; + } + + public void setOutputPerSecond(Double outputPerSecond) { + this.outputPerSecond = outputPerSecond; + } + + public Double getCpuProcessLoad() { + return cpuProcessLoad; + } + + public void setCpuProcessLoad(Double cpuProcessLoad) { + this.cpuProcessLoad = cpuProcessLoad; + } + + public Double getCpuSystemLoad() { + return cpuSystemLoad; + } + + public void setCpuSystemLoad(Double cpuSystemLoad) { + this.cpuSystemLoad = cpuSystemLoad; + } + + public Double getThroughputAverage10() { + return throughputAverage10; + } + + public void setThroughputAverage10(Double throughputAverage10) { + this.throughputAverage10 = throughputAverage10; + } + + public Double getThroughputAverage60() { + return throughputAverage60; + } + + public void setThroughputAverage60(Double throughputAverage60) { + this.throughputAverage60 = throughputAverage60; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Long getCreateSecond() { + return createSecond; + } + + public void setCreateSecond(Long createSecond) { + this.createSecond = createSecond; + } + + @Override + public void setCreateTime(Date createTime) { + super.setCreateTime(createTime); + this.createSecond = createTime.getTime() / 1000; //同时更新创建秒 + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("statId", getStatId()) + .append("srcId", getSrcId()) + .append("nodeId", getNodeId()) + .append("serviceId", getServiceId()) + .append("name", getName()) + .append("instance", getInstance()) + .append("nodeType", getNodeType()) + .append("expired", getExpired()) + .append("running", getRunning()) + .append("currentConnections", getCurrentConnections()) + .append("totalConnections", getTotalConnections()) + .append("currentKeys", getCurrentKeys()) + .append("memoryUsed", getMemoryUsed()) + .append("memoryFree", getMemoryFree()) + .append("memoryTotal", getMemoryTotal()) + .append("memoryAvailable", getMemoryAvailable()) + .append("commandResult", getCommandResult()) + .append("networkInputBytes", getNetworkInputBytes()) + .append("inputPerSecond", getInputPerSecond()) + .append("networkOutputBytes", getNetworkOutputBytes()) + .append("outputPerSecond", getOutputPerSecond()) + .append("cpuProcessLoad", getCpuProcessLoad()) + .append("cpuSystemLoad", getCpuSystemLoad()) + .append("throughputAverage10", getThroughputAverage10()) + .append("throughputAverage60", getThroughputAverage60()) + .append("createTime", getCreateTime()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsNode.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsNode.java new file mode 100644 index 0000000000000000000000000000000000000000..93bce4081e5a2811949ee2a5e71fab75a4577915 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsNode.java @@ -0,0 +1,403 @@ +package com.tongtech.console.domain; + +import com.tongtech.console.enums.NodeStatusEnum; +import com.tongtech.console.enums.NodeTypeEnum; +import com.tongtech.probe.stat.*; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +import java.util.List; + +import static com.tongtech.common.utils.CompareUtils.notEquals; + +/** + * 节点信息对象 cnsl_rds_node + * + * @author Zhang ChenLong + * @date 2023-01-24 + */ +public class RdsNode extends BaseEntity implements Comparable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + protected Long nodeId; + + /** 节点管理器ID */ + protected Long managerId; + + /** + * 节点管理器名称(查询显示用) + */ + protected String managerName; + + /** 服务ID */ + protected Long serviceId; + + /** 节点名称 */ + @Excel(name = "节点名称") + protected String nodeName; + + protected String instance; + + /** 节点类型 */ + @Excel(name = "节点类型") + protected String nodeType; + + /** 节点地址 */ + @Excel(name = "节点地址") + protected String hostAddress; + + /** 节点状态 */ + @Excel(name = "节点状态") + protected String nodeStatus; + + protected NodeStatusEnum nodeStatusEnum; + + /** RDS端口 */ + protected Integer servicePort; + + /** redis端口 */ + protected Integer redisPort; + + /** center中的管理端口 */ + protected Integer adminPort; + + /** 主节点 */ + protected boolean masterNode; + + /** 是否为热备节点 */ + protected boolean hotSpares; + + /** 分片的插槽范围 */ + protected String slot; + + /** 分片的编号 */ + protected Integer shard; + + /** + * 前端传入的变化的属性名称列表(主要对比提交前后的变化属性) + */ + private List changedProps; + + public RdsNode() { + } + + public RdsNode(Long serviceId, StatBaseNode statNode) { + this.serviceId = serviceId; + this.managerId = null; + this.instance = statNode.getInstance(); + this.nodeName = statNode.getInstance(); + this.hostAddress = statNode.getRemote(); + this.servicePort = statNode.getPort(); + + if(statNode.getExpired()) { + this.nodeStatus = NodeStatusEnum.STOP.getName(); + } + else { + this.nodeStatus = NodeStatusEnum.START.getName(); + } + + if(statNode instanceof StatCenterNode) { + this.nodeType = NodeTypeEnum.CENTER.getName(); + } + else if(statNode instanceof StatSentinelNode) { + this.nodeType = NodeTypeEnum.SENTINEL.getName(); + StatSentinelNode ssNode = (StatSentinelNode)statNode; + } + else if(statNode instanceof StatWorkerNode) { + StatWorkerNode swNode = ((StatWorkerNode)statNode); + this.nodeType = NodeTypeEnum.WORKER.getName(); + this.redisPort = swNode.getRedisPort(); + this.masterNode = swNode.getMaster(); + this.hotSpares = swNode.isHotSpares(); + this.shard = swNode.getShard(); + this.slot = swNode.getSlot(); + } + else if(statNode instanceof StatProxyNode) { + this.nodeType = NodeTypeEnum.PROXY.getName(); + this.redisPort = ((StatProxyNode)statNode).getRedisPort(); + } + else { + throw new RuntimeException("Unexpect node type class:" + statNode.getClass()); + } + + } + + public RdsNode(String hostAddress, Integer servicePort) { + this.hostAddress = hostAddress; + this.servicePort = servicePort; + } + + public RdsNode(String hostAddress, Integer servicePort, Integer adminPort) { + this.hostAddress = hostAddress; + this.servicePort = servicePort; + this.adminPort = adminPort; + } + + public RdsNode(String hostAddress, NodeTypeEnum nodeType, Integer servicePort, Integer redisPort, Integer shard, String slot, boolean masterNode) { + this.hostAddress = hostAddress; + this.nodeType = nodeType.getName(); + this.servicePort = servicePort; + this.redisPort = redisPort; + this.shard = shard; + this.slot = slot; + this.masterNode = masterNode; + } + + public RdsNode(String instance, String hostAddress, NodeTypeEnum nodeType, Integer servicePort, Integer redisPort, boolean masterNode) { + this.instance = instance; + this.hostAddress = hostAddress; + this.nodeType = nodeType.getName(); + this.servicePort = servicePort; + this.redisPort = redisPort; + this.masterNode = masterNode; + } + + /** + * 从另外一个RdsNode对象中获得配置属性和状态属性 + */ + public void setFrom(RdsNode other) { + this.hostAddress = other.hostAddress; + this.servicePort = other.servicePort; + this.redisPort = other.redisPort; + this.masterNode = other.masterNode; + this.hotSpares = other.hotSpares; + this.shard = other.shard; + this.slot = other.slot; + this.nodeStatus = other.nodeStatus; + } + + public void setNodeId(Long nodeId) + { + this.nodeId = nodeId; + } + + public Long getNodeId() + { + return nodeId; + } + public void setManagerId(Long managerId) + { + this.managerId = managerId; + } + + public Long getManagerId() + { + return managerId; + } + + public String getManagerName() { + return managerName; + } + + public void setManagerName(String managerName) { + this.managerName = managerName; + } + + public void setServiceId(Long serviceId) + { + this.serviceId = serviceId; + } + + public Long getServiceId() + { + return serviceId; + } + public void setNodeName(String nodeName) + { + this.nodeName = nodeName; + } + + public String getNodeName() + { + return nodeName; + } + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public void setNodeType(String nodeType) + { + this.nodeType = nodeType; + } + + public String getNodeType() { + return nodeType; + } + + public NodeTypeEnum getNodeTypeEnum() { + return NodeTypeEnum.parse(this.nodeType); + } + + public NodeStatusEnum getNodeStatusEnum() { + if(nodeStatusEnum == null && nodeStatus != null) { + this.nodeStatusEnum = NodeStatusEnum.parse(this.nodeStatus); + } + return this.nodeStatusEnum; + } + + /** + * 返回形如:hostAddress:servicePort 格式的地址 + * @return + */ + public String getServiceEndpoint() { + if(this.hostAddress != null && this.servicePort != null) { + StringBuilder buf = new StringBuilder(this.hostAddress.length() + 9); + buf.append(this.hostAddress).append(':').append(this.servicePort); + return buf.toString(); + } + else { + return ""; + } + } + + /** + * 返回形如:hostAddress:redisPort 格式的地址 + * @return + */ + public String getRedisEndpoint() { + if(this.hostAddress != null && this.redisPort != null) { + StringBuilder buf = new StringBuilder(this.hostAddress.length() + 9); + buf.append(this.hostAddress).append(':').append(this.redisPort); + return buf.toString(); + } + else { + return ""; + } + } + + + + public void setHostAddress(String hostAddress) + { + this.hostAddress = hostAddress; + } + + public String getHostAddress() + { + return hostAddress; + } + public void setNodeStatus(String nodeStatus) + { + this.nodeStatus = nodeStatus; + } + + public String getNodeStatus() + { + return nodeStatus; + } + + public Integer getServicePort() { + return servicePort; + } + + public void setServicePort(Integer servicePort) { + this.servicePort = servicePort; + } + + public Integer getRedisPort() { + return redisPort; + } + + public void setRedisPort(Integer redisPort) { + this.redisPort = redisPort; + } + + public Integer getAdminPort() { + return adminPort; + } + + public void setAdminPort(Integer adminPort) { + this.adminPort = adminPort; + } + + public boolean isMasterNode() { + return masterNode; + } + + public void setMasterNode(boolean masterNode) { + this.masterNode = masterNode; + } + + public boolean isHotSpares() { + return hotSpares; + } + + public void setHotSpares(boolean hotSpares) { + this.hotSpares = hotSpares; + } + + public String getSlot() { + return slot; + } + + public void setSlot(String slot) { + this.slot = slot; + } + + public Integer getShard() { + return shard; + } + + public void setShard(Integer shard) { + this.shard = shard; + } + + public List getChangedProps() { + return changedProps; + } + + public void setChangedProps(List changedProps) { + this.changedProps = changedProps; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("nodeId", getNodeId()) + .append("managerId", getManagerId()) + .append("managerName", getManagerName()) + .append("serviceId", getServiceId()) + .append("nodeName", getNodeName()) + .append("nodeType", getNodeType()) + .append("hostAddress", getHostAddress()) + .append("servicePort", getServicePort()) + .append("redisPort", getRedisPort()) + .append("adminPort", getAdminPort()) + .append("masterNode", isMasterNode()) + .append("hostSpares", isHotSpares()) + .append("slot", getSlot()) + .append("shard", getShard()) + .append("nodeStatus", getNodeStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } + + /** + * 按 nodeType , nodeName 顺序进行排序 + * @param o the object to be compared. + * @return + */ + @Override + public int compareTo(RdsNode o) { + int ret = this.nodeType.compareTo(o.getNodeType()); + if(ret == 0) { + return this.nodeName.compareTo(o.getNodeName()); + } + else { + return ret; + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsService.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsService.java new file mode 100644 index 0000000000000000000000000000000000000000..1e7800ca42a5699180d74a574003cda207c83ef2 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsService.java @@ -0,0 +1,248 @@ +package com.tongtech.console.domain; + +import com.tongtech.common.exception.ServiceException; + +import com.tongtech.console.enums.DeployModeEnum; +import com.tongtech.probe.stat.StatService; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +import java.util.List; + +/** + * RDS服务对象 cnsl_rds_service + * + * @author Zhang ChenLong + * @date 2023-01-26 + */ +public class RdsService extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 服务ID */ + private Long serviceId; + + /** 哨兵服务ID */ + private Long sentinelServiceId; + + /** 服务名称 */ + @Excel(name = "服务名称") + private String serviceName; + + /** 服务地址 */ + private String hostAddress; + + /** 管理端口 */ + private Integer adminPort; + + /** 是否可以手工维护 */ + private boolean manualAdmin; + + /** 部署模式 */ + @Excel(name = "部署模式") + private String deployMode; + + /** 部署模式 */ + private DeployModeEnum deployModeEnum; + + /** 认证方式 */ + private Integer secureMode; + + /** 版本 */ + private Long versionId; + + /** 模版组ID */ + private Long groupId; + + /** 认证密码 */ + private String password; + + /** 版本显示名称,显示专用 */ + private String versionDesc; + + /** 模版组名称,显示专用 */ + private String groupName; + + /** + * 前端传入的变化的属性名称列表(主要对比提交前后的变化属性) + */ + private List changedProps; + + + public RdsService() {} + + public RdsService(StatService statServ, RdsVersion version) { + this(statServ.getName(), DeployModeEnum.parse(statServ.getType()), version); + } + + public RdsService(String serviceName, DeployModeEnum deployMode, RdsVersion version) { + this.serviceName = serviceName; + this.manualAdmin = false; + this.deployModeEnum = deployMode; + if(this.deployModeEnum != null) { + this.deployMode = deployModeEnum.getName(); + } + else { + throw new ServiceException("RdsService(serviceName:" + serviceName + ") creating error! deployMode is null!"); + } + + this.secureMode = 0; //secureMode 会在节点信息解析时被赋值,这里设置为0(不需要认证) + this.versionId = version.getVersionId(); + this.groupId = version.getDefaultGroupId(); + } + + + public void setServiceId(Long serviceId) + { + this.serviceId = serviceId; + } + + public Long getServiceId() + { + return serviceId; + } + + public Long getSentinelServiceId() { + return sentinelServiceId; + } + + public void setSentinelServiceId(Long sentinelServiceId) { + this.sentinelServiceId = sentinelServiceId; + } + + public void setServiceName(String serviceName) + { + this.serviceName = serviceName; + } + + public String getServiceName() + { + return serviceName; + } + public void setDeployMode(String deployMode) + { + this.deployMode = deployMode; + } + + public String getDeployMode() + { + return deployMode; + } + + public DeployModeEnum getDeployModeEnum() { + if(this.deployModeEnum == null) { + this.deployModeEnum = DeployModeEnum.parse(this.deployMode); + } + + return this.deployModeEnum; + } + + public void setSecureMode(Integer secureMode) + { + this.secureMode = secureMode; + } + + public Integer getSecureMode() + { + return secureMode; + } + public void setVersionId(Long versionId) + { + this.versionId = versionId; + } + + public Long getVersionId() + { + return versionId; + } + public void setGroupId(Long groupId) + { + this.groupId = groupId; + } + + public Long getGroupId() + { + return groupId; + } + public void setPassword(String password) + { + this.password = password; + } + + public String getPassword() + { + return password; + } + + public String getVersionDesc() { + return versionDesc; + } + + public void setVersionDesc(String versionDesc) { + this.versionDesc = versionDesc; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public List getChangedProps() { + return changedProps; + } + + public void setChangedProps(List changedProps) { + this.changedProps = changedProps; + } + + public String getHostAddress() { + return hostAddress; + } + + public void setHostAddress(String hostAddress) { + this.hostAddress = hostAddress; + } + + public Integer getAdminPort() { + return adminPort; + } + + public void setAdminPort(Integer adminPort) { + this.adminPort = adminPort; + } + + public boolean isManualAdmin() { + return manualAdmin; + } + + public void setManualAdmin(boolean manualAdmin) { + this.manualAdmin = manualAdmin; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("serviceId", getServiceId()) + .append("sentinelServiceId", getSentinelServiceId()) + .append("serviceName", getServiceName()) + .append("hostAddress", getHostAddress()) + .append("adminPort", getAdminPort()) + .append("deployMode", getDeployMode()) + .append("secureMode", getSecureMode()) + .append("versionId", getVersionId()) + .append("versionDesc", getVersionDesc()) + .append("groupId", getGroupId()) + .append("password", getPassword()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsVersion.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsVersion.java new file mode 100644 index 0000000000000000000000000000000000000000..2d2e34629741463814a851088096af8b3c230e36 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsVersion.java @@ -0,0 +1,141 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +/** + * 版本信息对象 cnsl_rds_version + * + * @author Zhang ChenLong + * @date 2023-01-12 + */ +public class RdsVersion extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 版本ID */ + private Long versionId; + + /** 软件名称 */ + @Excel(name = "软件名称") + private String softwareName; + + /** 版本编号 */ + @Excel(name = "版本编号") + private String versionNo; + + /** 模版组ID */ + private Long defaultGroupId; + + /** 默认模板组名称(仅用于查询显示) */ + private String defaultGroupName; + + /** 是否为默认版本 */ + @Excel(name = "是否为默认版本") + private boolean defaultVersion; + + /** 状态(1正常 0停用) */ + @Excel(name = "状态", readConverterExp = "1=正常,0=停用") + private String status; + + private String packageCount; + + public void setVersionId(Long versionId) + { + this.versionId = versionId; + } + + public Long getVersionId() + { + return versionId; + } + public void setSoftwareName(String softwareName) + { + this.softwareName = softwareName; + } + + public String getSoftwareName() + { + return softwareName; + } + public void setVersionNo(String versionNo) + { + this.versionNo = versionNo; + } + + public String getVersionNo() + { + return versionNo; + } + + public String getVersionDesc() { + if(softwareName != null && versionNo != null) { + return softwareName + " " + versionNo; + } + else { + return ""; + } + } + + public boolean isDefaultVersion() { + return defaultVersion; + } + + public void setDefaultVersion(boolean defaultVersion) { + this.defaultVersion = defaultVersion; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public Long getDefaultGroupId() { + return defaultGroupId; + } + + public void setDefaultGroupId(Long defaultGroupId) { + this.defaultGroupId = defaultGroupId; + } + + public String getDefaultGroupName() { + return defaultGroupName; + } + + public void setDefaultGroupName(String defaultGroupName) { + this.defaultGroupName = defaultGroupName; + } + + public String getPackageCount() { + return packageCount; + } + + public void setPackageCount(String packageCount) { + this.packageCount = packageCount; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("versionId", getVersionId()) + .append("softwareName", getSoftwareName()) + .append("versionNo", getVersionNo()) + .append("packageCount", getPackageCount()) + .append("defaultGroupId", getDefaultGroupId()) + .append("defaultVersion", isDefaultVersion()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsVersionPkg.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsVersionPkg.java new file mode 100644 index 0000000000000000000000000000000000000000..053a23f5a0e049387b91fe2050a6b98bfcc14750 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/RdsVersionPkg.java @@ -0,0 +1,122 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +import static com.tongtech.common.utils.StringUtils.toReadableSize; + +/** + * 安装包信息对象 cnsl_rds_version_pkg + * + * @author Zhang ChenLong + * @date 2023-01-12 + */ +public class RdsVersionPkg extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 安装包ID */ + private Long packageId; + + /** 版本ID */ + @Excel(name = "版本ID") + private Long versionId; + + /** 包名称 */ + @Excel(name = "包名称") + private String pkgName; + + /** 包类类型 */ + @Excel(name = "包类类型") + private String pkgType; + + /** 文件名称 */ + private String fileName; + + /** 文件大小 */ + @Excel(name = "文件大小") + private Long fileSize; + + public void setPackageId(Long packageId) + { + this.packageId = packageId; + } + + public Long getPackageId() + { + return packageId; + } + public void setVersionId(Long versionId) + { + this.versionId = versionId; + } + + public Long getVersionId() + { + return versionId; + } + public void setPkgName(String pkgName) + { + this.pkgName = pkgName; + } + + public String getPkgName() + { + return pkgName; + } + public void setPkgType(String pkgType) + { + this.pkgType = pkgType; + } + + public String getPkgType() + { + return pkgType; + } + public void setFileName(String fileName) + { + this.fileName = fileName; + } + + public String getFileName() + { + return fileName; + } + public void setFileSize(Long fileSize) + { + this.fileSize = fileSize; + } + + public Long getFileSize() + { + return fileSize; + } + + public String getFileSizeDesc() { + if(fileSize != null) { + return toReadableSize(fileSize); + } + else { + return ""; + } + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("packageId", getPackageId()) + .append("versionId", getVersionId()) + .append("pkgName", getPkgName()) + .append("pkgType", getPkgType()) + .append("fileName", getFileName()) + .append("fileSize", getFileSize()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/ServiceConfig.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/ServiceConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..21127fe06c74251bdb6b32579dd198bb1e4836ee --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/ServiceConfig.java @@ -0,0 +1,88 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +/** + * 服务配置信息对象 cnsl_service_config + * + * @author Zhang ChenLong + * @date 2023-01-11 + */ +public class ServiceConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 服务ID */ + private Long serviceId; + + /** 模版类型 */ + private String confType; + + /** 使用的模版ID */ + @Excel(name = "使用的模版ID") + private Long templateId; + + /** 配置内容 */ + @Excel(name = "配置内容") + private String confContent; + + public ServiceConfig() { + + } + + public ServiceConfig(Long serviceId, String confType) { + this.serviceId = serviceId; + this.confType = confType; + } + + public void setServiceId(Long serviceId) + { + this.serviceId = serviceId; + } + + public Long getServiceId() + { + return serviceId; + } + + public String getConfType() { + return confType; + } + + public void setConfType(String confType) { + this.confType = confType; + } + + public void setTemplateId(Long templateId) + { + this.templateId = templateId; + } + + public Long getTemplateId() + { + return templateId; + } + public void setConfContent(String confContent) + { + this.confContent = confContent; + } + + public String getConfContent() + { + return confContent; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("serviceId", getServiceId()) + .append("tempType", getConfType()) + .append("templateId", getTemplateId()) + .append("confContent", getConfContent()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/ServiceStat.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/ServiceStat.java new file mode 100644 index 0000000000000000000000000000000000000000..d624432d60e743e34ef7dde1fecfd2a488820d52 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/ServiceStat.java @@ -0,0 +1,358 @@ +package com.tongtech.console.domain; + +import com.tongtech.common.core.domain.BaseEntity; +import com.tongtech.probe.stat.StatRuntime; +import com.tongtech.probe.stat.StatService; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.util.Date; +import java.util.List; +import java.util.OptionalDouble; + + +/** + * 服务监控信息对象 cnsl_service_stat + * + * @author Zhang ChenLong + * @date 2023-03-18 + */ +public class ServiceStat extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 监控信息ID */ + protected Long statId; + /** 源ID */ + protected Long srcId; + /** 节点ID */ + protected Long serviceId; + /** 部署模式, 字典类型 cnsl_deploy_mode */ + protected String deployMode; + /** 服务名称 */ + protected String name; + /** 当前连接数 */ + protected Long currentConnections; + /** 启动以来一共连接次数 */ + protected Long totalConnections; + /** 当前key总数 */ + protected Long currentKeys; + /** 当前内存使用量 */ + protected Long memoryUsed; + /** 当前内存剩余量 */ + protected Long memoryFree; + /** 实际占用内存总量,JVM当前使用的堆内存总量 */ + protected Long memoryTotal; + /** 最大可用内存量,JVM -Xmx 的参数值 */ + protected Long memoryAvailable; + /** 启动以来返回结果的命令数累计 */ + protected Long commandResult; + /** 网络IO入流量总和 */ + protected Long networkInputBytes; + /** 网络IO每秒入流量 */ + protected Double inputPerSecond; + /** 网络IO出流量总和 */ + protected Long networkOutputBytes; + /** 网络IO每秒出流量 */ + protected Double outputPerSecond; + /** 当前程序CPU使用率,百分比值 */ + protected Double cpuProcessLoad; + /** 当前系统CPU使用率, 百分比值 */ + protected Double cpuSystemLoad; + /** 创建时间秒(从1970 到现在的秒数)*/ + protected Long createSecond; + + public ServiceStat() {} + + public ServiceStat(Long srcId, RdsService serv, StatService statServ) { + this.srcId = srcId; + this.serviceId = serv.getServiceId(); + this.deployMode = serv.getDeployMode(); + this.name = serv.getServiceName(); + + StatRuntime rt = statServ.getStatistics(); + /* 当前连接数 */ + this.currentConnections = rt.getCurrentConnections(); + /* 启动以来一共连接次数 */ + this.totalConnections = rt.getTotalConnections(); + /* 当前key总数 */ + this.currentKeys = rt.getKeys(); + /* 当前内存使用量 */ + this.memoryUsed = rt.getMemoryUsed(); + /* 当前内存剩余量 */ + this.memoryFree = rt.getMemoryFree(); + /* 实际占用内存总量,JVM当前使用的堆内存总量 */ + this.memoryTotal = rt.getMemoryTotal(); + /* 最大可用内存量,JVM -Xmx 的参数值 */ + this.memoryAvailable = rt.getMemoryAvailable(); + /* 启动以来返回结果的命令数累计 */ + this.commandResult = rt.getCommandResult(); + /* 网络IO入流量总和 */ + this.networkInputBytes = rt.getNetworkInputBytes(); + /* 网络IO每秒入流量 */ + this.inputPerSecond = rt.getInputPerSecond(); + /* 网络IO出流量总和 */ + this.networkOutputBytes = rt.getNetworkOutputBytes(); + /* 网络IO每秒出流量 */ + this.outputPerSecond = rt.getOutputPerSecond(); + /* 当前程序CPU使用率,百分比值 */ + this.cpuProcessLoad = rt.getCpuProcessLoad(); + /* 当前系统CPU使用率, 百分比值 */ + this.cpuSystemLoad = rt.getCpuSystemLoad(); + } + + + public ServiceStat(Long srcId, RdsService serv, List nodeStats) { + this.srcId = srcId; + this.serviceId = serv.getServiceId(); + this.deployMode = serv.getDeployMode(); + this.name = serv.getServiceName(); + + if(nodeStats != null && nodeStats.size() > 0) { + /* 当前连接数 */ + this.currentConnections = nodeStats.stream().mapToLong(NodeStat::getCurrentConnections).sum(); + /* 启动以来一共连接次数 */ + this.totalConnections = nodeStats.stream().mapToLong(NodeStat::getTotalConnections).sum(); + /* 当前key总数 */ + this.currentKeys = 0L; + /* 当前内存使用量 */ + this.memoryUsed = nodeStats.stream().mapToLong(NodeStat::getMemoryUsed).sum(); + /* 当前内存剩余量 */ + this.memoryFree = nodeStats.stream().mapToLong(NodeStat::getMemoryFree).sum(); + /* 实际占用内存总量,JVM当前使用的堆内存总量 */ + this.memoryTotal = nodeStats.stream().mapToLong(NodeStat::getMemoryTotal).sum(); + /* 最大可用内存量,JVM -Xmx 的参数值 */ + this.memoryAvailable = nodeStats.stream().mapToLong(NodeStat::getMemoryAvailable).sum(); + /* 启动以来返回结果的命令数累计 */ + this.commandResult = 0L; + /* 网络IO入流量总和 */ + this.networkInputBytes = 0L; + /* 网络IO每秒入流量 */ + this.inputPerSecond = 0D; + /* 网络IO出流量总和 */ + this.networkOutputBytes = 0L; + /* 网络IO每秒出流量 */ + this.outputPerSecond = 0D; + /* 当前程序CPU使用率,百分比值 */ + OptionalDouble optCpuProcessLoad = nodeStats.stream().mapToDouble(NodeStat::getCpuProcessLoad).average(); + this.cpuProcessLoad = optCpuProcessLoad.getAsDouble(); + /* 当前系统CPU使用率, 百分比值 */ + OptionalDouble optCpuSystemLoad = nodeStats.stream().mapToDouble(NodeStat::getCpuSystemLoad).average(); + this.cpuSystemLoad = optCpuSystemLoad.getAsDouble(); + + } + } + + public void setStatId(Long statId) + { + this.statId = statId; + } + + public Long getStatId() + { + return statId; + } + public void setSrcId(Long srcId) + { + this.srcId = srcId; + } + + public Long getSrcId() + { + return srcId; + } + public void setServiceId(Long serviceId) + { + this.serviceId = serviceId; + } + + public Long getServiceId() + { + return serviceId; + } + public void setDeployMode(String deployMode) + { + this.deployMode = deployMode; + } + + public String getDeployMode() + { + return deployMode; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setCurrentConnections(Long currentConnections) + { + this.currentConnections = currentConnections; + } + + public Long getCurrentConnections() + { + return currentConnections; + } + public void setTotalConnections(Long totalConnections) + { + this.totalConnections = totalConnections; + } + + public Long getTotalConnections() + { + return totalConnections; + } + public void setCurrentKeys(Long currentKeys) + { + this.currentKeys = currentKeys; + } + + public Long getCurrentKeys() + { + return currentKeys; + } + public void setMemoryUsed(Long memoryUsed) + { + this.memoryUsed = memoryUsed; + } + + public Long getMemoryUsed() + { + return memoryUsed; + } + public void setMemoryFree(Long memoryFree) + { + this.memoryFree = memoryFree; + } + + public Long getMemoryFree() + { + return memoryFree; + } + public void setMemoryTotal(Long memoryTotal) + { + this.memoryTotal = memoryTotal; + } + + public Long getMemoryTotal() + { + return memoryTotal; + } + public void setMemoryAvailable(Long memoryAvailable) + { + this.memoryAvailable = memoryAvailable; + } + + public Long getMemoryAvailable() + { + return memoryAvailable; + } + public void setCommandResult(Long commandResult) + { + this.commandResult = commandResult; + } + + public Long getCommandResult() + { + return commandResult; + } + public void setNetworkInputBytes(Long networkInputBytes) + { + this.networkInputBytes = networkInputBytes; + } + + public Long getNetworkInputBytes() + { + return networkInputBytes; + } + public void setInputPerSecond(Double inputPerSecond) + { + this.inputPerSecond = inputPerSecond; + } + + public Double getInputPerSecond() + { + return inputPerSecond; + } + public void setNetworkOutputBytes(Long networkOutputBytes) + { + this.networkOutputBytes = networkOutputBytes; + } + + public Long getNetworkOutputBytes() + { + return networkOutputBytes; + } + public void setOutputPerSecond(Double outputPerSecond) + { + this.outputPerSecond = outputPerSecond; + } + + public Double getOutputPerSecond() + { + return outputPerSecond; + } + public void setCpuProcessLoad(Double cpuProcessLoad) + { + this.cpuProcessLoad = cpuProcessLoad; + } + + public Double getCpuProcessLoad() + { + return cpuProcessLoad; + } + public void setCpuSystemLoad(Double cpuSystemLoad) + { + this.cpuSystemLoad = cpuSystemLoad; + } + + public Double getCpuSystemLoad() + { + return cpuSystemLoad; + } + + public Long getCreateSecond() { + return createSecond; + } + + public void setCreateSecond(Long createSecond) { + this.createSecond = createSecond; + } + + @Override + public void setCreateTime(Date createTime) { + super.setCreateTime(createTime); + this.createSecond = createTime.getTime() / 1000; //同时更新创建秒 + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("statId", getStatId()) + .append("srcId", getSrcId()) + .append("serviceId", getServiceId()) + .append("deployMode", getDeployMode()) + .append("name", name) + .append("currentConnections", getCurrentConnections()) + .append("totalConnections", getTotalConnections()) + .append("currentKeys", getCurrentKeys()) + .append("memoryUsed", getMemoryUsed()) + .append("memoryFree", getMemoryFree()) + .append("memoryTotal", getMemoryTotal()) + .append("memoryAvailable", getMemoryAvailable()) + .append("commandResult", getCommandResult()) + .append("networkInputBytes", getNetworkInputBytes()) + .append("inputPerSecond", getInputPerSecond()) + .append("networkOutputBytes", getNetworkOutputBytes()) + .append("outputPerSecond", getOutputPerSecond()) + .append("cpuProcessLoad", getCpuProcessLoad()) + .append("cpuSystemLoad", getCpuSystemLoad()) + .append("createTime", getCreateTime()) + .toString(); + } + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/Template.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/Template.java new file mode 100644 index 0000000000000000000000000000000000000000..dab918a77b15ef19d4d867227b78984aebadfeb3 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/Template.java @@ -0,0 +1,97 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +/** + * 配置模版对象 cnsl_template + * + * @author Zhang ChenLong + * @date 2023-01-15 + */ +public class Template extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 模版ID */ + private Long templateId; + + /** 模版组ID */ + private Long groupId; + + /** 模版名称 */ + @Excel(name = "模版名称") + private String tempName; + + /** 模版内容 */ + @Excel(name = "模版内容") + private String tempContent; + + /** 模版类型 */ + @Excel(name = "模版类型") + private String tempType; + + public void setTemplateId(Long templateId) + { + this.templateId = templateId; + } + + public Long getTemplateId() + { + return templateId; + } + public void setGroupId(Long groupId) + { + this.groupId = groupId; + } + + public Long getGroupId() + { + return groupId; + } + public void setTempName(String tempName) + { + this.tempName = tempName; + } + + public String getTempName() + { + return tempName; + } + public void setTempContent(String tempContent) + { + this.tempContent = tempContent; + } + + public String getTempContent() + { + return tempContent; + } + public void setTempType(String tempType) + { + this.tempType = tempType; + } + + public String getTempType() + { + return tempType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("templateId", getTemplateId()) + .append("groupId", getGroupId()) + .append("tempName", getTempName()) + .append("tempContent", getTempContent()) + .append("tempType", getTempType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/TemplateGroup.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/TemplateGroup.java new file mode 100644 index 0000000000000000000000000000000000000000..c70f4e92865611ff91d79205d5593b9e3f07294c --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/TemplateGroup.java @@ -0,0 +1,90 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +/** + * 配置模版对象 cnsl_template_group + * + * @author Zhang ChenLong + * @date 2023-01-16 + */ +public class TemplateGroup extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 模版组ID */ + private Long groupId; + + /** 模版组名称 */ + @Excel(name = "模版组名称") + private String groupName; + + /** 版本信息,用于显示 */ + private String versions; + + private Long[] versionIds; + + private String templateCount; + + public void setGroupId(Long groupId) + { + this.groupId = groupId; + } + + public Long getGroupId() + { + return groupId; + } + public void setGroupName(String groupName) + { + this.groupName = groupName; + } + + public String getGroupName() + { + return groupName; + } + + public String getVersions() { + return versions; + } + + public void setVersions(String versions) { + this.versions = versions; + } + + public Long[] getVersionIds() { + return versionIds; + } + + public void setVersionIds(Long[] versionIds) { + this.versionIds = versionIds; + } + + public String getTemplateCount() { + return templateCount; + } + + public void setTemplateCount(String templateCount) { + this.templateCount = templateCount; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("groupId", getGroupId()) + .append("groupName", getGroupName()) + .append("versions", getVersions()) + .append("versionIds", getVersionIds()) + .append("templateCount", getTemplateCount()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/TemplateGroupVersion.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/TemplateGroupVersion.java new file mode 100644 index 0000000000000000000000000000000000000000..5a67ca3a3b96ab926cfae3f2e0bd04cc5e4fc5b7 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/TemplateGroupVersion.java @@ -0,0 +1,58 @@ +package com.tongtech.console.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.tongtech.common.annotation.Excel; +import com.tongtech.common.core.domain.BaseEntity; + +/** + * 模版组适用版本对象 cnsl_template_group_version + * + * @author Zhang ChenLong + * @date 2023-01-16 + */ +public class TemplateGroupVersion extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 模版组ID */ + private Long groupId; + + /** 版本ID */ + @Excel(name = "版本ID") + private Long versionId; + + public TemplateGroupVersion() {} + + public TemplateGroupVersion(Long groupId, Long versionId) { + this.groupId = groupId; + this.versionId = versionId; + } + + public void setGroupId(Long groupId) + { + this.groupId = groupId; + } + + public Long getGroupId() + { + return groupId; + } + public void setVersionId(Long versionId) + { + this.versionId = versionId; + } + + public Long getVersionId() + { + return versionId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("groupId", getGroupId()) + .append("versionId", getVersionId()) + .toString(); + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/NodeStatQueryVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/NodeStatQueryVo.java new file mode 100644 index 0000000000000000000000000000000000000000..658ff0ca5c9060bd45f064269a8003244c839175 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/NodeStatQueryVo.java @@ -0,0 +1,58 @@ +package com.tongtech.console.domain.vo; + + +import java.io.Serializable; + +/** + * 用来节点统计进行查询的查询条件 + */ +public class NodeStatQueryVo implements Serializable +{ + private static final long serialVersionUID = 1L; + + private Long nodeId; + + private Integer groupSeconds; // 查询时分组的时间间隔(秒) + + private Long beginCreateSecond; //开始时间(秒) + + private Long endCreateSecond; //结束时间(秒) + + public Long getBeginCreateSecond() { + return beginCreateSecond; + } + + public void setBeginCreateSecond(Long beginCreateSecond) { + this.beginCreateSecond = beginCreateSecond; + } + + public Long getEndCreateSecond() { + return endCreateSecond; + } + + public void setEndCreateSecond(Long endCreateSecond) { + this.endCreateSecond = endCreateSecond; + } + + public NodeStatQueryVo(Long nodeId, Long beginCreateSecond, Integer groupSeconds) { + this.nodeId = nodeId; + this.beginCreateSecond = beginCreateSecond; + this.groupSeconds = groupSeconds; + } + + public Long getNodeId() { + return nodeId; + } + + public void setNodeId(Long nodeId) { + this.nodeId = nodeId; + } + + public Integer getGroupSeconds() { + return groupSeconds; + } + + public void setGroupSeconds(Integer groupSeconds) { + this.groupSeconds = groupSeconds; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/OperationResult.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/OperationResult.java new file mode 100644 index 0000000000000000000000000000000000000000..ac43b7e2291b68d484527cdf2399f13c8bc119a9 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/OperationResult.java @@ -0,0 +1,147 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.console.domain.RdsNode; +import com.tongtech.probe.pojo.CmdsResult; + +import java.util.*; + +/** + * 记录一批节点操作的执行结果。 + */ +public class OperationResult { + + private Long serviceId; + + private String serviceName; + + private Map> nodesResults; + + private Set errorNodeNames; + + private int resultCount; + + private int errorCount; + + public Long getServiceId() { + return serviceId; + } + + public String getServiceName() { + return serviceName; + } + + public Map> getNodesResults() { + return nodesResults; + } + + public int getResultCount() { + return resultCount; + } + + public int getErrorCount() { + return errorCount; + } + + public Set getErrorNodeNames() { + return errorNodeNames; + } + + public OperationResult() { + this(null, null); + } + + public OperationResult(Long serviceId, String serviceName) { + this.serviceId = serviceId; + this.serviceName = serviceName; + this.nodesResults = new LinkedHashMap<>(); //保证map中的元素按插入顺序排列 + this.errorNodeNames = new LinkedHashSet<>(); + this.resultCount = 0; + this.errorCount = 0; + } + + public List addNodeResult(RdsNode node, CmdsResult.Result res) { + return addNodeResult(new NodeResult(node, res)); + } + + public List addNodeResult(NodeResult res) { + Long nodeId = res.getNodeId(); + List list = nodesResults.get(nodeId); + if(list == null) { + list = new ArrayList<>(); + nodesResults.put(nodeId, list); + } + + list.add(res); + this.resultCount ++; + if(res.isSuccess() == false) { + this.errorCount ++; + errorNodeNames.add(res.getNodeName()); + } + return list; + } + + public static class NodeResult { + private Long nodeId; + private String nodeName; + private String nodeType; + private String cmd; + private boolean success; + private String msg; + + public NodeResult(RdsNode node, CmdsResult.Result cmdRes) { + this.nodeId = node.getNodeId(); + this.nodeName = node.getNodeName(); + this.nodeType = node.getNodeType(); + this.success = cmdRes.isSuccess(); + this.cmd = cmdRes.getCmd(); + this.msg = cmdRes.getMsg(); + } + + public Long getNodeId() { + return nodeId; + } + + public String getNodeName() { + return nodeName; + } + + public String getNodeType() { + return nodeType; + } + + public String getCmd() { + return cmd; + } + + public boolean isSuccess() { + return success; + } + + public String getMsg() { + return msg; + } + + @Override + public String toString() { + return "NodeResult{" + + ((success) ? "SUCCESS" : "FAILED") + + ", nodeId=" + nodeId + + ", nodeName='" + nodeName + '\'' + + ", nodeType='" + nodeType + '\'' + + ", cmd='" + cmd + '\'' + + ", msg='" + msg + '\'' + + '}'; + } + } + + @Override + public String toString() { + return "OperationResult{" + + "serviceId=" + serviceId + + ", serviceName='" + serviceName + '\'' + + ", nodesResults=" + nodesResults + + ", resultCount=" + resultCount + + ", errorCount=" + errorCount + + '}'; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsMonitorQueryVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsMonitorQueryVo.java new file mode 100644 index 0000000000000000000000000000000000000000..0771ecbdec7d9544b86be5d37bfdf0109106682e --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsMonitorQueryVo.java @@ -0,0 +1,63 @@ +package com.tongtech.console.domain.vo; + + +import java.io.Serializable; +import java.util.Date; + +/** + * 服务监控的查询条件 + */ +public class RdsMonitorQueryVo implements Serializable +{ + private static final long serialVersionUID = 1L; + + private Long srcId; + + private Long serviceId; + + private Integer pastSecond; //过去时间,单位秒 + + private Long beginCreateSecond; //开始时间(秒) + + private Long endCreateSecond; //结束时间(秒) + + public Long getSrcId() { + return srcId; + } + + public void setSrcId(Long srcId) { + this.srcId = srcId; + } + + public Long getServiceId() { + return serviceId; + } + + public void setServiceId(Long serviceId) { + this.serviceId = serviceId; + } + + public Integer getPastSecond() { + return pastSecond; + } + + public void setPastSecond(Integer pastSecond) { + this.pastSecond = pastSecond; + } + + public Long getBeginCreateSecond() { + return beginCreateSecond; + } + + public void setBeginCreateSecond(Long beginCreateSecond) { + this.beginCreateSecond = beginCreateSecond; + } + + public Long getEndCreateSecond() { + return endCreateSecond; + } + + public void setEndCreateSecond(Long endCreateSecond) { + this.endCreateSecond = endCreateSecond; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsNodeStatsVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsNodeStatsVo.java new file mode 100644 index 0000000000000000000000000000000000000000..dcadbec9ce9cceab9166ea2778c461e4353fc660 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsNodeStatsVo.java @@ -0,0 +1,55 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.console.domain.NodeStat; +import com.tongtech.console.domain.RdsNode; + +import java.util.List; + +/** + * 节点信息对象 cnsl_rds_node + * + * @author Zhang ChenLong + * @date 2023-01-24 + */ +public class RdsNodeStatsVo extends RdsNode +{ + private static final long serialVersionUID = 1L; + + private List stats; + + public RdsNodeStatsVo(RdsNode node) { + this.nodeId = node.getNodeId(); + this.managerId = node.getManagerId(); + this.managerName = node.getManagerName(); + this.serviceId = node.getServiceId(); + this.nodeName = node.getNodeName(); + this.instance = node.getInstance(); + this.nodeType = node.getNodeType(); + this.hostAddress = node.getHostAddress(); + this.nodeStatus = node.getNodeStatus(); + this.nodeStatusEnum = node.getNodeStatusEnum(); + this.servicePort = node.getServicePort(); + this.redisPort = node.getRedisPort(); + this.adminPort = node.getAdminPort(); + this.masterNode = node.isMasterNode(); + this.hotSpares = node.isHotSpares(); + this.slot = node.getSlot(); + this.shard = node.getShard(); + } + + /** + * 为了界面显示使用,和nodeStatus属性一致。 + * @return + */ + public String getStatus() { + return this.nodeStatus; + } + + public List getStats() { + return stats; + } + + public void setStats(List stats) { + this.stats = stats; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceNodesCfg.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceNodesCfg.java new file mode 100644 index 0000000000000000000000000000000000000000..85640273023394aafb3c2f6d4b91e3e4bd32b4e1 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceNodesCfg.java @@ -0,0 +1,210 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.common.exception.ServiceException; +import com.tongtech.console.domain.RdsNode; +import com.tongtech.console.domain.RdsService; +import com.tongtech.console.enums.NodeTypeEnum; +import com.tongtech.console.enums.DeployModeEnum; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import static com.tongtech.console.enums.DeployModeEnum.*; + +/** + * 在通过模版生成正式配置,用于转换为RdsService相关的模版配置信息 + */ +public class RdsServiceNodesCfg implements Serializable { + + private static final long serialVersionUID = 1L; + + private String serviceName; + + private String deployMode; + + private Long groupId; + + private Integer secureMode; + + private String password; + + private List shards; + + private List nodes; //格式 "localhost:6241" 第一个是主节点 + + public RdsServiceNodesCfg(RdsServiceNodesVo vo) { + this(vo.getService(), vo.getNodes()); + } + + public RdsServiceNodesCfg(RdsService serv, List nodes) { + + this.serviceName = serv.getServiceName(); + this.deployMode = convertDeployMode(serv); + this.groupId = serv.getGroupId(); + this.secureMode = serv.getSecureMode(); + this.password = serv.getPassword(); + + DeployModeEnum mode = serv.getDeployModeEnum(); + + this.nodes = new ArrayList(); + shards = new ArrayList(); + if(mode == SINGLE) { + for(RdsNode node : nodes) { + this.nodes.add(node); + } + } + else if(mode == SENTINEL_WORKER) { + List noneMasterNodes = new ArrayList(); + RdsNode masterNode = null; + for( RdsNode node : nodes ) { + NodeTypeEnum nodeType = NodeTypeEnum.parse(node.getNodeType()); + if(masterNode == null && node.isMasterNode()) { + masterNode = node; + } + else { + noneMasterNodes.add(node); + } + } + + //通过此操作保证 masterNode 是放在列表最前面。 + if(masterNode != null) this.nodes.add(masterNode); + for(RdsNode node : noneMasterNodes) { + this.nodes.add(node); + } + } + else if(mode == CLUSTER) { + //int len = vo.getNodes().size(); + //for(int i = 0 ; i < len ; i++) { + for( RdsNode node : nodes ){ + Shard shard = getShard(node); + if(node.isMasterNode()) { + shard.masterNode = node.getServiceEndpoint(); + } + else { + shard.noneMasterNodes.add(node.getServiceEndpoint()); + } + } + + //对每一个shard进行赋值 + for(Shard shard : this.shards) { + StringBuilder buf = new StringBuilder(); + buf.append(shard.masterNode); + for(String node : shard.noneMasterNodes) { + buf.append(',').append(node); + } + + shard.endpoints = buf.toString(); + } + } + else { + throw new ServiceException("Unsupport service deployMode '" + mode + "' in RdsServiceNodesCfg"); + } + //TODO 未实现 SCALABLE 模式 + } + + /** + * 配置文件中的 DeployMode 和软件中定义不一致! + * 此函数把系统存放的DeployMode转换为配置需要的值。 + * @param serv + * @return + */ + private String convertDeployMode(RdsService serv) { + DeployModeEnum m = serv.getDeployModeEnum(); + if(m == SINGLE) { + return "default"; + } + else if(m == SENTINEL_WORKER) { + return "sentinel"; + } + else { + return serv.getDeployMode(); + } + } + + public String getServiceName() { + return serviceName; + } + + /** + * 获取所有工作节点列表的字符串,格式如: "192.168.0.81:6379,192.168.0.82:6379,192.168.0.83:6379" + * 其中第一个是主节点, 端口为redisPort + * @return + */ + public String getEndpoints() { + StringBuilder buf = new StringBuilder(""); + for(RdsNode node : this.nodes) { + buf.append(node.getRedisEndpoint()).append(','); + } + if(buf.length() > 1) { //删除最后一个逗号 + buf.deleteCharAt(buf.length() - 1); + } + return buf.toString(); + } + + public String getDeployMode() { + return deployMode; + } + + public Long getGroupId() { + return groupId; + } + + public Integer getSecureMode() { + return secureMode; + } + + public String getPassword() { + return password; + } + + public List getShards() { + return shards; + } + + public List getNodes() { + return nodes; + } + + // public List getWorkerNodes() { +// return workerNodes; +// } + + private Shard getShard(RdsNode node) { + int size = this.shards.size(); + int index = node.getShard(); + if(index < size) { + return this.shards.get(index); + } + else { + Shard s = new Shard(); + s.index = index; + s.slot = node.getSlot(); + this.shards.add(s); + return s; + } + } + + public static class Shard { + private Integer index; + private String endpoints; //192.168.0.60:6200,192.168.0.60:6200 第一个是主节点 + private String slot; + + private String masterNode; + private List noneMasterNodes = new ArrayList(); + + public Integer getIndex() { + return index; + } + + public String getEndpoints() { + return endpoints; + } + + public String getSlot() { + return slot; + } + + } + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceNodesVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceNodesVo.java new file mode 100644 index 0000000000000000000000000000000000000000..4f39aab6d2495befcca7aa65fec8912f5564c2e5 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceNodesVo.java @@ -0,0 +1,72 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.console.domain.RdsNode; +import com.tongtech.console.domain.RdsService; + +import java.io.Serializable; +import java.util.List; + +/** + * RdsService 和其下属 nodes 的集合对象,用于前端显示和赋值 + */ +public class RdsServiceNodesVo implements Serializable +{ + private static final long serialVersionUID = 1L; + + private RdsService service; + + private List nodes; + + private List reloadNodes; //需要重启的节点名称 + + private List deleteNodeIds; //需要删除的节点名称 + + private Boolean reloadable; //是否重启那些需要重启的节点 + + public RdsServiceNodesVo() {} + + public RdsServiceNodesVo(RdsService service, List nodes) { + this.service = service; + this.nodes = nodes; + } + + public RdsService getService() { + return service; + } + + public void setService(RdsService service) { + this.service = service; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public List getReloadNodes() { + return reloadNodes; + } + + public void setReloadNodes(List reloadNodes) { + this.reloadNodes = reloadNodes; + } + + public List getDeleteNodeIds() { + return deleteNodeIds; + } + + public void setDeleteNodeIds(List deleteNodeIds) { + this.deleteNodeIds = deleteNodeIds; + } + + public Boolean getReloadable() { + return reloadable; + } + + public void setReloadable(Boolean reloadable) { + this.reloadable = reloadable; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceQueryVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceQueryVo.java new file mode 100644 index 0000000000000000000000000000000000000000..8dd337b59c9f432f059679ebcec35d249c1d7a17 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsServiceQueryVo.java @@ -0,0 +1,71 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.console.domain.RdsNode; +import com.tongtech.console.domain.RdsService; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用于RdsService的查询条件,和查询结果 + */ +public class RdsServiceQueryVo extends RdsService { + + /** + * 服务下的节点列表,列表显示时需要 + */ + private List nodes; + + /** + * 查询条件时可以指定多个deployMode + */ + private List deployModes; + + /** + * 哨兵服务时,使用哨兵服务的主从服务数据量 + */ + private Integer workerServices; + + public RdsServiceQueryVo() { + this.deployModes = new ArrayList(); + } + + public RdsServiceQueryVo(String[] deployModes) { + this(); + addDeployModes(deployModes); + } + + public List getDeployModes() { + return deployModes; + } + + public void setDeployModes(List deployModes) { + this.deployModes = deployModes; + } + + public boolean addDeployMode(String deployMode) { + return this.deployModes.add(deployMode); + } + + public void addDeployModes(String[] deployModes) { + for(String deployMode : deployModes) { + this.deployModes.add(deployMode); + } + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public Integer getWorkerServices() { + return workerServices; + } + + public void setWorkerServices(Integer workerServices) { + this.workerServices = workerServices; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsVersionVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsVersionVo.java new file mode 100644 index 0000000000000000000000000000000000000000..15b69fc28d762dc5375de3d51f1c3fd5dfe429f0 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/RdsVersionVo.java @@ -0,0 +1,35 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.console.domain.RdsVersion; +import com.tongtech.console.domain.TemplateGroup; + +import java.util.List; + +public class RdsVersionVo extends RdsVersion { + + public RdsVersionVo(RdsVersion rv, List templateGroups) { + this.setVersionId(rv.getVersionId()); + this.setVersionNo(rv.getVersionNo()); + this.setDefaultGroupId(rv.getDefaultGroupId()); + this.setDefaultVersion(rv.isDefaultVersion()); + this.setStatus(rv.getStatus()); + this.setSoftwareName(rv.getSoftwareName()); + + this.setCreateBy(rv.getCreateBy()); + this.setCreateTime(rv.getCreateTime()); + this.setUpdateBy(rv.getUpdateBy()); + this.setUpdateTime(rv.getUpdateTime()); + this.setRemark(rv.getRemark()); + this.setTemplateGroups(templateGroups); + } + + private List templateGroups; + + public List getTemplateGroups() { + return templateGroups; + } + + public void setTemplateGroups(List templateGroups) { + this.templateGroups = templateGroups; + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/ServiceStatVo.java b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/ServiceStatVo.java new file mode 100644 index 0000000000000000000000000000000000000000..826591871f6f03cca3ba5d34afdfae3c65cbd957 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/domain/vo/ServiceStatVo.java @@ -0,0 +1,123 @@ +package com.tongtech.console.domain.vo; + +import com.tongtech.common.utils.DateUtils; +import com.tongtech.console.domain.NodeStat; +import com.tongtech.console.domain.RdsService; +import com.tongtech.console.domain.ServiceStat; +import java.util.List; + +import static com.tongtech.console.enums.NodeStatusEnum.*; + +public class ServiceStatVo extends ServiceStat { + + private List children; //子节点统计信息 + + private List nodes; //子节点,和子节点下的统计 RdsNodeStatsVo.nodeStats + + private String status; //状态 + + public ServiceStatVo() {} + + public ServiceStatVo(RdsService serv) { + this.serviceId = serv.getServiceId(); + this.deployMode = serv.getDeployMode(); + this.name = serv.getServiceName(); + this.setCreateTime(DateUtils.getNowDate()); + } + + public ServiceStatVo(ServiceStat orgStat) { + this.statId = orgStat.getStatId(); + this.srcId = orgStat.getSrcId(); + this.serviceId = orgStat.getServiceId(); + this.deployMode = orgStat.getDeployMode(); + this.name = orgStat.getName(); + this.currentConnections = orgStat.getCurrentConnections(); + this.totalConnections = orgStat.getTotalConnections(); + this.currentKeys = orgStat.getCurrentKeys(); + this.memoryUsed = orgStat.getMemoryUsed(); + this.memoryFree = orgStat.getMemoryFree(); + this.memoryTotal = orgStat.getMemoryTotal(); + this.memoryAvailable = orgStat.getMemoryAvailable(); + this.commandResult = orgStat.getCommandResult(); + this.networkInputBytes = orgStat.getNetworkInputBytes(); + this.inputPerSecond = orgStat.getInputPerSecond(); + this.networkOutputBytes = orgStat.getNetworkOutputBytes(); + this.outputPerSecond = orgStat.getOutputPerSecond(); + this.cpuProcessLoad = orgStat.getCpuProcessLoad(); + this.cpuSystemLoad = orgStat.getCpuSystemLoad(); + this.setCreateTime(orgStat.getCreateTime()); + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + boolean hasStart = false; + boolean hasStop = false; + //遍历 child node,通过它们状态的汇总得出,服务的状态 + for(NodeStat ns : children) { + if(STOP.getName().equals(ns.getStatus())) { + hasStop = true; + } + else if(START.getName().equals(ns.getStatus())) { + hasStart = true; + } + } + + if( hasStart == true && hasStop == false ) { + this.status = START.getName(); + } + else if( hasStart == false && hasStop == true ) { + this.status = STOP.getName(); + } + else if( hasStart == true && hasStop == true ) { + this.status = START_PART.getName(); + } + else { + this.status = STOP.getName(); + } + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + + boolean hasStart = false; + boolean hasStop = false; + //遍历 child node,通过它们状态的汇总得出,服务的状态 + for(RdsNodeStatsVo ns : nodes) { + if(STOP.getName().equals(ns.getNodeStatus())) { + hasStop = true; + } + else if(START.getName().equals(ns.getNodeStatus())) { + hasStart = true; + } + } + + if( hasStart == true && hasStop == false ) { + this.status = START.getName(); + } + else if( hasStart == false && hasStop == true ) { + this.status = STOP.getName(); + } + else if( hasStart == true && hasStop == true ) { + this.status = START_PART.getName(); + } + else { + this.status = STOP.getName(); + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/CommandResultEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/CommandResultEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..57a1d9e1671d114af09b04598811ae9ccdfa6cf2 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/CommandResultEnum.java @@ -0,0 +1,36 @@ +package com.tongtech.console.enums; + +/** + * 命令执行是否成功 + */ +public enum CommandResultEnum { + DONE("done"), + FAILED("failed"); + + private String name; + + CommandResultEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public static CommandResultEnum parse(String name) { + if(DONE.name.equals(name)) { + return DONE; + } + else if(FAILED.name.equals(name)) { + return FAILED; + } + else { + return null; + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/DeployModeEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/DeployModeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..652608a0b337ddcb9928c91f85d91cd81b9f90d5 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/DeployModeEnum.java @@ -0,0 +1,61 @@ +package com.tongtech.console.enums; + +import com.tongtech.common.utils.StringUtils; + +/** + * RDS 部署模式(单点、哨兵、集群、可伸缩集群) + */ +public enum DeployModeEnum { + SINGLE("single", "default"), + SENTINEL("sentinel"), + SENTINEL_WORKER("sentinel_worker"), + CLUSTER("cluster"), + SCALABLE("scalable"), + CENTER("center"); + + private String name; + + private String name2; + + DeployModeEnum(String name) { + this.name = name; + } + + DeployModeEnum(String name, String name2) { + this.name = name; + this.name2 = name2; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public static DeployModeEnum parse(String name) { + if(StringUtils.isNotEmpty(name)) { + String iName = name.trim().toLowerCase(); + if (SINGLE.name.equals(iName) || SINGLE.name2.equals(iName)) { + return SINGLE; + } else if (SENTINEL_WORKER.name.equals(iName)) { + return SENTINEL_WORKER; + } else if (SENTINEL.name.equals(iName)) { + return SENTINEL; + } else if (CLUSTER.name.equals(iName)) { + return CLUSTER; + } else if (SCALABLE.name.equals(iName)) { + return SCALABLE; + } else if (CENTER.name.equals(iName)) { + return CENTER; + } else { + return null; + } + } + else { + return null; + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeManagerModeEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeManagerModeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..181d58b1bd7f0e5c403ba216f973941895e509f0 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeManagerModeEnum.java @@ -0,0 +1,48 @@ +package com.tongtech.console.enums; + +/** + * 节点管理器管理模式 + */ +public enum NodeManagerModeEnum { + + PROBE("probe"), //手动联机 + + SSH("ssh"), //SSH联机 + + INNER("inner"), //控制台内嵌 + + K8S("k8s"); //Kubernetes + + private String name; + + NodeManagerModeEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public static NodeManagerModeEnum parse(String name) { + if(PROBE.name.equals(name)) { + return PROBE; + } + else if(SSH.name.equals(name)) { + return SSH; + } + else if(INNER.name.equals(name)) { + return INNER; + } + else if(K8S.name.equals(name)) { + return K8S; + } + else { + return null; + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeManagerStatusEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeManagerStatusEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..c5c641edda5bdce7786037d036ace06dc34e4db0 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeManagerStatusEnum.java @@ -0,0 +1,40 @@ +package com.tongtech.console.enums; + +/** + * 命令执行是否成功 + */ +public enum NodeManagerStatusEnum { + NONE("none"), + RUNNING("running"), + STOPPED("stopped"); + + private String name; + + NodeManagerStatusEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public static NodeManagerStatusEnum parse(String name) { + if(NONE.name.equals(name)) { + return NONE; + } + else if(RUNNING.name.equals(name)) { + return RUNNING; + } + else if(STOPPED.name.equals(name)) { + return STOPPED; + } + else { + return null; + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeSecureEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeSecureEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..815f064faf4a1e0311584953594436abd65dcff2 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeSecureEnum.java @@ -0,0 +1,51 @@ +package com.tongtech.console.enums; + +public enum NodeSecureEnum { + /** + * Telnet none password + */ + ANONYMOUS(0), + + /** + * SSL none password + */ + SSL(1), + + /** + * Telnet and password + */ + PASSWORD(2), + + /** + * SSL and password + */ + SSL_PASSWORD(3); + + + + + private int secure; + + NodeSecureEnum(int secure) { + this.secure = secure; + } + + public Integer getSecure() { + return secure; + } + + public static NodeSecureEnum parse(int secure) { + switch(secure) { + case 0: + return ANONYMOUS; + case 1: + return SSL; + case 2: + return PASSWORD; + case 3: + return SSL_PASSWORD; + default: + return ANONYMOUS; + } + } +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeStatusEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeStatusEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..6337980343ed8fffad9ad535d81cc473760380bf --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeStatusEnum.java @@ -0,0 +1,55 @@ +package com.tongtech.console.enums; + +public enum NodeStatusEnum { + + NONE("none", "未安装"), + + START("start", "运行"), + + STARTING("starting", "启动中"), + + START_PART("start-part", "部分启动"), + + STOP("stop", "停止"), + + STOPPING("stopping", "停止中"); + + private String name; + + private String info; + + + NodeStatusEnum(String name, String info) { + this.name = name; + this.info = info; + } + + public String getName() { + return name; + } + + public String getInfo() { return info; } + + public static NodeStatusEnum parse(String name) { + if(NONE.name.equals(name)) { + return NONE; + } + else if(START.name.equals(name)) { + return START; + } + else if(STARTING.name.equals(name)) { + return STARTING; + } + else if(STOP.name.equals(name)) { + return STOP; + } + else if(STOPPING.name.equals(name)) { + return STOPPING; + } + else { + return null; + } + + } + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeTypeEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeTypeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..c8835afb284767d6decd05faa28554590b8d9c2e --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/NodeTypeEnum.java @@ -0,0 +1,45 @@ +package com.tongtech.console.enums; + +public enum NodeTypeEnum { + WORKER("worker", "工作节点"), + SENTINEL("sentinel", "哨兵节点"), + CENTER("center", "中心节点"), + PROXY("proxy", "代理节点"); + + + private String name; + + private String info; + + + NodeTypeEnum(String name, String info) { + this.name = name; + this.info = info; + } + + public String getName() { + return name; + } + + public String getInfo() { return info; } + + public static NodeTypeEnum parse(String name) { + if(WORKER.name.equals(name)) { + return WORKER; + } + else if(SENTINEL.name.equals(name)) { + return SENTINEL; + } + else if(CENTER.name.equals(name)) { + return CENTER; + } + else if(PROXY.name.equals(name)) { + return PROXY; + } + else { + return null; + } + + } + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/PackageTypeEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/PackageTypeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..c71dcb04c6968ba5e618c3e083f32dca93527d22 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/PackageTypeEnum.java @@ -0,0 +1,54 @@ +package com.tongtech.console.enums; + +/** + * 安装包类型 + */ +public enum PackageTypeEnum { + + WORKER("worker"), //工作哨兵节点 + CENTER("center"), //中心节点 + PROXY("proxy"); //代理节点 + + private String name; + + PackageTypeEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static PackageTypeEnum parseByNodeType(String nodeType) { + if("worker".equals(nodeType) || "sentinel".equals(nodeType)) { + return WORKER; + } + else if("center".equals(nodeType)) { + return CENTER; + } + else if("proxy".equals(nodeType)) { + return PROXY; + } + else { + return null; + } + + } + + public static PackageTypeEnum parse(String name) { + if(WORKER.name.equals(name)) { + return WORKER; + } + else if(CENTER.name.equals(name)) { + return CENTER; + } + else if(PROXY.name.equals(name)) { + return PROXY; + } + else { + return null; + } + + } + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/enums/TemplateTypeEnum.java b/rds-console/console-system/src/main/java/com/tongtech/console/enums/TemplateTypeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..7295fc2f58c926b1d656e6630a3669ec2895ef0c --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/enums/TemplateTypeEnum.java @@ -0,0 +1,83 @@ +package com.tongtech.console.enums; + + +/** + * Center 配置 + this.deployableFiles.put("vmoptions", new DeployableFile("vmoptions", new File(this.parent, "bin/external.vmoptions"))); + this.deployableFiles.put("cfg", new DeployableFile("cfg", new File(this.parent, "etc/config.properties"))); + this.deployableFiles.put("sync", new DeployableFile("sync", new File(this.parent, "etc/sync.properties"))); + this.deployableFiles.put("cluster", new DeployableFile("cluster", new File(this.parent, "etc/cluster.properties"))); + this.deployableFiles.put("active", new DeployableFile("active", new File(this.parent, "etc/active.properties"))); + this.deployableFiles.put("acl", new DeployableFile("acl", new File(this.parent, "etc/acl.properties"))); + this.deployableFiles.put("lic", new DeployableFile("lic", new File(this.parent, "center.lic"))); + + * Worker 配置 + this.deployableFiles.put("vmoptions", new DeployableFile("vmoptions", new File(this.parent, "bin/external.vmoptions"))); + this.deployableFiles.put("cfg", new DeployableFile("cfg", new File(this.parent, "etc/cfg.xml"))); + this.deployableFiles.put("dynamic", new DeployableFile("dynamic", new File(this.parent, "etc/dynamic.xml"))); + this.deployableFiles.put("lic", new DeployableFile("lic", new File(this.parent, "license.dat"))); + + * Sentinel 配置 + this.deployableFiles.put("cfg", new DeployableFile("cfg", new File(this.parent, "etc/sentinel.xml"))); + + + */ +public enum TemplateTypeEnum { + WORKER_CFG("worker-cfg", "工作节点-配置"), + WORKER_DYNAMIC("worker-dynamic", "工作节点-动态配置"), + SENTINEL_CFG("sentinel-cfg", "哨兵节点-配置"), + PROXY_CFG("proxy-cfg", "代理节点-配置"), + CENTER_CFG("center-cfg", "中心节点-配置"), + CENTER_CLUSTER("center-cluster", "中心节点-部署管理配置"), + CENTER_SYNC("center-sync", "中心节点-同步配置"), + CENTER_ALC("center-alc", "中心节点-用户访问控制"); + + + private String name; + + private String info; + + + TemplateTypeEnum(String name, String info) { + this.name = name; + this.info = info; + } + + public String getName() { + return name; + } + + public String getInfo() { return info; } + + public static TemplateTypeEnum parse(String name) { + if(WORKER_CFG.name.equals(name)) { + return WORKER_CFG; + } + else if(WORKER_DYNAMIC.name.equals(name)) { + return WORKER_DYNAMIC; + } + else if(SENTINEL_CFG.name.equals(name)) { + return SENTINEL_CFG; + } + else if(PROXY_CFG.name.equals(name)) { + return PROXY_CFG; + } + else if(CENTER_CFG.name.equals(name)) { + return CENTER_CFG; + } + else if(CENTER_CLUSTER.name.equals(name)) { + return CENTER_CLUSTER; + } + else if(CENTER_SYNC.name.equals(name)) { + return CENTER_SYNC; + } + else if(CENTER_ALC.name.equals(name)) { + return CENTER_ALC; + } + else { + return null; + } + + } + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/CenterStatSrcMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/CenterStatSrcMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..2bd5fc823ee9ffe66c6cd0a9217edf80c8b0df80 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/CenterStatSrcMapper.java @@ -0,0 +1,60 @@ +package com.tongtech.console.mapper; + +import java.util.Date; + +import com.tongtech.console.domain.CenterStatSrc; + +/** + * 中心节点统计信息的原始报文Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-03-15 + */ +public interface CenterStatSrcMapper +{ + /** + * 查询中心节点统计信息的原始报文 + * + * @param srcId 中心节点统计信息的原始报文主键 + * @return 中心节点统计信息的原始报文 + */ + public CenterStatSrc selectCenterStatSrcBySrcId(Long srcId); + + /** + * 新增中心节点统计信息的原始报文 + * + * @param centerStatSrc 中心节点统计信息的原始报文 + * @return 结果 + */ + public int insertCenterStatSrc(CenterStatSrc centerStatSrc); + + /** + * 获取最后一次插入的srcId; + * @return + */ + public Long selectLastSrcId(); + + + /** + * 删除中心节点统计信息的原始报文 + * + * @param srcId 中心节点统计信息的原始报文主键 + * @return 结果 + */ + public int deleteCenterStatSrcBySrcId(Long srcId); + + /** + * 批量删除中心节点统计信息的原始报文 + * + * @param srcIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteCenterStatSrcBySrcIds(Long[] srcIds); + + /** + * 删除小于 createTime 参数时间之前的数据 + * @param createTime + * @return + */ + public int deleteByCreateTime(Date createTime); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/NodeConfigMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/NodeConfigMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..f904cad9e83c3e203c8fb1a625f9a5ba5baf0557 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/NodeConfigMapper.java @@ -0,0 +1,70 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.NodeConfig; + +/** + * 节点配置信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-02-27 + */ +public interface NodeConfigMapper +{ + /** + * 查询节点配置信息 + * + * @param nodeId 节点配置信息主键 + * @return 节点配置信息 + */ + public NodeConfig selectNodeConfigByNodeId(Long nodeId); + + /** + * 查询节点配置信息列表 + * + * @param nodeConfig 节点配置信息 + * @return 节点配置信息集合 + */ + public List selectNodeConfigList(NodeConfig nodeConfig); + + /** + * 新增节点配置信息 + * + * @param nodeConfig 节点配置信息 + * @return 结果 + */ + public int insertNodeConfig(NodeConfig nodeConfig); + + + /** + * 新增或更新节点配置信息,新增时如果主键冲突(node_id, temp_type),则变为新增。 + * + * @param nodeConfig 节点配置信息 + * @return 结果 + */ + public int insertUpdateNodeConfig(NodeConfig nodeConfig); + + /** + * 修改节点配置信息 + * + * @param nodeConfig 节点配置信息 + * @return 结果 + */ + public int updateNodeConfig(NodeConfig nodeConfig); + + /** + * 删除节点配置信息 + * + * @param nodeId 节点配置信息主键 + * @return 结果 + */ + public int deleteNodeConfigByNodeId(Long nodeId); + + /** + * 批量删除节点配置信息 + * + * @param nodeIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteNodeConfigByNodeIds(Long[] nodeIds); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/NodeStatMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/NodeStatMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..5f13db5179a541cec909093dc3fbb8d2ebd5bb3e --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/NodeStatMapper.java @@ -0,0 +1,100 @@ +package com.tongtech.console.mapper; + +import java.util.Date; +import java.util.List; + +import com.tongtech.console.domain.vo.NodeStatQueryVo; +import com.tongtech.console.domain.NodeStat; + +/** + * 节点监控信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-03-15 + */ +public interface NodeStatMapper +{ + /** + * 查询节点监控信息 + * + * @param statId 节点监控信息主键 + * @return 节点监控信息 + */ + public NodeStat selectNodeStatByStatId(Long statId); + + /** + * 查询节点监控信息列表 + * + * @param nodeStat 节点监控信息 + * @return 节点监控信息集合 + */ + public List selectNodeStatList(NodeStat nodeStat); + + /** + * 监控查询某一个节点的状态信息, 按时间段分组 + * nodeId 节点ID + * groupSeconds; 查询时分组的时间间隔(秒) + * beginCreateSecond; 开始时间(秒) + * @param queryVo + * @return + */ + public List selectMonitorGroupList(NodeStatQueryVo queryVo); + + + /** + * 监控查询某一个节点的状态信息 + * nodeId 节点ID + * beginCreateSecond; 开始时间(秒) + * @param queryVo + * @return + */ + public List selectMonitorList(NodeStatQueryVo queryVo); + + /** + * 新增节点监控信息 + * + * @param nodeStat 节点监控信息 + * @return 结果 + */ + public int insertNodeStat(NodeStat nodeStat); + + /** + * 修改节点监控信息 + * + * @param nodeStat 节点监控信息 + * @return 结果 + */ + public int updateNodeStat(NodeStat nodeStat); + + /** + * 删除节点监控信息 + * + * @param statId 节点监控信息主键 + * @return 结果 + */ + public int deleteNodeStatByStatId(Long statId); + + /** + * 批量删除节点监控信息 + * + * @param statIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteNodeStatByStatIds(Long[] statIds); + + + /** + * 删除节点监控信息, 通过节点ID + * + * @param nodeId + * @return 结果 + */ + public int deleteNodeStatByNodeId(Long nodeId); + + /** + * 删除小于 createTime 参数时间之前的数据 + * @param createTime + * @return + */ + public int deleteByCreateTime(Date createTime); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsNodeMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsNodeMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..1dd8c2501760c427afc57ade8c30ba75dc052c8f --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsNodeMapper.java @@ -0,0 +1,142 @@ +package com.tongtech.console.mapper; + +import java.util.Date; +import java.util.List; + +import com.tongtech.console.domain.RdsNode; +import com.tongtech.console.domain.RdsService; + +/** + * 节点信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-24 + */ +public interface RdsNodeMapper +{ + /** + * 查询节点信息 + * + * @param nodeId 节点信息主键 + * @return 节点信息 + */ + public RdsNode selectNodeByNodeId(Long nodeId); + + /** + * 获得节点信息 + * + * @param instance 节点实例名称 + * @return 节点信息 + */ + public RdsNode selectNodeByInstance(String instance); + + /** + * 查询服务serivce下的RDS节点信息列表 + * @return + */ + public List selectNodesByServiceId(Long serviceId); + + /** + * node.managerId , node.nodeName 来唯一获得一个 RdsNode对象 + * @return + */ + public RdsNode selectNodeByManagerIdAndNodeName(RdsNode node); + + + /** + * 查询 nodeId + * @param rdsService 查询参数:updateTime, manualAdmin, serviceId(可选) + * @return + */ + public List selectNodeIdList(RdsService rdsService); + + /** + * 更新nodes状态 , 只要指定时间前没有更新(update_time)的节点, 状态不是 'none' and 'stop' + * 更新状态为 stop + * @param updateTime + * @return + */ + public int updateNoneStopNodes(Date updateTime); + + /** + * 查询节点信息列表 + * + * @param rdsNode 节点信息 + * @return 节点信息集合 + */ + public List selectNodeList(RdsNode rdsNode); + + /** + * 查询是否有相同名称的节点在同一个节点管理器中 + * + * @param rdsNode 节点信息 只有两个参数可用:managerId(必须), nodeName(必须) + * @return 节点信息集合 + */ + public List selectSameNameNodeList(RdsNode rdsNode); + + /** + * 查询是否有相同端口的节点,在相同主机地址的情况下。 + * 可选传入 serviceId 属性,用来在搜索范围中排除 serviceId + * + * host_address = #{hostAddress} + * and n.service_id != #{serviceId} + * and (service_port = #{servicePort} or redis_port = #{servicePort}) + * + * @param rdsNode hostAddress(必须), serviceId(可选,排除),servicePort(必须, 同时去匹配 servicePort 和 redisPort) + * @return + */ + public List selectSamePortNodeList(RdsNode rdsNode); + + + /** + * 新增节点信息 + * + * @param rdsNode 节点信息 + * @return 结果 + */ + public int insertNode(RdsNode rdsNode); + + /** + * 修改节点信息 + * + * @param rdsNode 节点信息 + * @return 结果 + */ + public int updateNode(RdsNode rdsNode); + + /** + * 节点状态信息 + * + * @param rdsNode 节点信息 + * @return 结果 + */ + public int updateNodeStatus(RdsNode rdsNode); + + /** + * 删除节点信息 + * + * @param nodeId 节点信息主键 + * @return 结果 + */ + public int deleteNodeByNodeId(Long nodeId); + + /** + * 删除node 信息 by serviceId + * @param serviceId + * @return + */ + public int deleteNodesByServiceId(Long serviceId); + + /** + * 批量删除节点信息 + * + * @param nodeIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteNodeByNodeIds(Long[] nodeIds); + + + public int updateExpiredNodeStatus(String[] runningInstances); + + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsServiceMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsServiceMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d00f3a2bf80013b95c5db4fd424cd3a29f559e8f --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsServiceMapper.java @@ -0,0 +1,102 @@ +package com.tongtech.console.mapper; + +import java.util.List; + +import com.tongtech.console.domain.vo.RdsServiceQueryVo; +import com.tongtech.console.domain.RdsService; + +/** + * RDS服务Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-26 + */ +public interface RdsServiceMapper +{ + /** + * 查询RDS服务 + * + * @param serviceId RDS服务主键 + * @return RDS服务 + */ + public RdsServiceQueryVo selectRdsServiceByServiceId(Long serviceId); + + /** + * 通过serviceName获取服务信息 + * @param serviceName + * @return + */ + public RdsServiceQueryVo selectByServiceName(String serviceName); + + /** + * 查询RDS服务列表 + * + * @param rdsService RDS服务, 其中的serviceName和deployMode两个属性可用 + * @return RDS服务集合 + */ + public List selectRdsServiceList(RdsServiceQueryVo rdsService); + + public Integer selectRdsServiceCount(RdsServiceQueryVo queryVo); + + /** + * 查询serviceId列表,其下没有节点的service + * + * @param rdsService RDS服务 + * @return RDS服务集合 + */ + public List selectNoneNodeServiceIdList(RdsServiceQueryVo rdsService); + + /** + * 查询包括(in)多个部署模式的列表 + * @param queryVo 根据 queryVo.deployModes 和 queryVo.serviceName进行查询 + * @return + */ + public List selectListInDeployModes(RdsServiceQueryVo queryVo); + + + /** + * 新增RDS服务 + * + * @param rdsService RDS服务 + * @return 结果 + */ + public int insertRdsService(RdsService rdsService); + + /** + * 修改RDS服务 + * + * @param rdsService RDS服务 + * @return 结果 + */ + public int updateRdsService(RdsService rdsService); + + /** + * 重置服务配置 + * @param serviceId + * @return + */ + public int resetRdsService(Long serviceId); + + /** + * 修改服务密码 + * @param rdsService + * @return + */ + public int updateServicePassword(RdsService rdsService); + + /** + * 删除RDS服务 + * + * @param serviceId RDS服务主键 + * @return 结果 + */ + public int deleteRdsServiceByServiceId(Long serviceId); + + /** + * 批量删除RDS服务 + * + * @param serviceIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteRdsServiceByServiceIds(Long[] serviceIds); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsVersionMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsVersionMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..fafeef87c4a47377eb55336d8e123891e517ecca --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsVersionMapper.java @@ -0,0 +1,101 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.RdsVersion; + +/** + * 版本信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-12 + */ +public interface RdsVersionMapper +{ + /** + * 查询版本信息 + * + * @param versionId 版本信息主键 + * @return 版本信息 + */ + public RdsVersion selectRdsVersionByVersionId(Long versionId); + + /** + * 查询版本信息列表 + * + * @param rdsVersion 版本信息 + * @return 版本信息集合 + */ + public List selectRdsVersionList(RdsVersion rdsVersion); + + /** + * 查询默认版本 + * @return 版本信息集合 + */ + public RdsVersion selectDefaultVersion(); + + /** + * 查询所有版本,by status + * @param status "1" 启用状态,"0" 停用状态, null 查询所有版本 + * @return + */ + public List selectListByStatus(String status); + + /** + * 新增版本信息 + * + * @param rdsVersion 版本信息 + * @return 结果 + */ + public int insertRdsVersion(RdsVersion rdsVersion); + + /** + * 修改版本信息 + * + * @param rdsVersion 版本信息 + * @return 结果 + */ + public int updateRdsVersion(RdsVersion rdsVersion); + + /** + * 删除版本信息 + * + * @param versionId 版本信息主键 + * @return 结果 + */ + public int deleteRdsVersionByVersionId(Long versionId); + + /** + * 批量删除版本信息 + * + * @param versionIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteRdsVersionByVersionIds(Long[] versionIds); + + /** + * 更新版本状态 + * @param rdsVersion + * @return + */ + public int updateStatus(RdsVersion rdsVersion); + + /** + * 更新 defaultVersion + * @return + */ + public int updateDefaultVersion(RdsVersion rdsVersion); + + /** + * 找到所有 defaultVersion = true 的记录,全部变更为 defaultVersiion = false + * @param username 更新的用户名称 + * @return + */ + public int updateToNoDefaultVersion(String username); + + /** + * 清除表中default_group_id = groupId 的列值,设置为Null。 + * @param groupId + * @return + */ + public int clearDefaultGroupId(Long groupId); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsVersionPkgMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsVersionPkgMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d13cff6428e0322cc541b52d61a78454be20600f --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/RdsVersionPkgMapper.java @@ -0,0 +1,61 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.RdsVersionPkg; + +/** + * 安装包信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-12 + */ +public interface RdsVersionPkgMapper +{ + /** + * 查询安装包信息 + * + * @param packageId 安装包信息主键 + * @return 安装包信息 + */ + public RdsVersionPkg selectRdsVersionPkgByPackageId(Long packageId); + + /** + * 查询安装包信息列表 + * + * @param rdsVersionPkg 安装包信息 + * @return 安装包信息集合 + */ + public List selectRdsVersionPkgList(RdsVersionPkg rdsVersionPkg); + + /** + * 新增安装包信息 + * + * @param rdsVersionPkg 安装包信息 + * @return 结果 + */ + public int insertRdsVersionPkg(RdsVersionPkg rdsVersionPkg); + + /** + * 修改安装包信息 + * + * @param rdsVersionPkg 安装包信息 + * @return 结果 + */ + public int updateRdsVersionPkg(RdsVersionPkg rdsVersionPkg); + + /** + * 删除安装包信息 + * + * @param packageId 安装包信息主键 + * @return 结果 + */ + public int deleteRdsVersionPkgByPackageId(Long packageId); + + /** + * 批量删除安装包信息 + * + * @param packageIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteRdsVersionPkgByPackageIds(Long[] packageIds); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/ServiceConfigMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/ServiceConfigMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..de9db61b85a17da07d97d9ef71ff3969d895f7b3 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/ServiceConfigMapper.java @@ -0,0 +1,64 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.ServiceConfig; + +/** + * 服务配置信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-11 + */ +public interface ServiceConfigMapper +{ + /** + * 查询服务配置信息 + * where service_id = #{serviceId} and conf_type = #{confType} + * @param serviceConfig 服务配置信息主键 service_id, conf_type + * @return 服务配置信息 + */ + public ServiceConfig selectServiceConfig(ServiceConfig serviceConfig); + + /** + * 查询服务配置信息列表 + * + * @param serviceConfig 服务配置信息 + * @return 服务配置信息集合 + */ + public List selectServiceConfigList(ServiceConfig serviceConfig); + + /** + * 新增服务配置信息 + * + * @param serviceConfig 服务配置信息 + * @return 结果 + */ + public int insertServiceConfig(ServiceConfig serviceConfig); + + public int insertUpdateServiceConfig(ServiceConfig serviceConfig); + + + /** + * 修改服务配置信息 + * + * @param serviceConfig 服务配置信息 + * @return 结果 + */ + public int updateServiceConfig(ServiceConfig serviceConfig); + + /** + * 删除服务配置信息 + * + * @param serviceId 服务配置信息主键 + * @return 结果 + */ + public int deleteServiceConfigByServiceId(Long serviceId); + + /** + * 批量删除服务配置信息 + * + * @param serviceIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteServiceConfigByServiceIds(Long[] serviceIds); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/ServiceStatMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/ServiceStatMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..0840f5d85f1674ea69035c2d34584e37cab07d11 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/ServiceStatMapper.java @@ -0,0 +1,88 @@ +package com.tongtech.console.mapper; + +import java.util.Date; +import java.util.List; + +import com.tongtech.console.domain.vo.RdsMonitorQueryVo; +import com.tongtech.console.domain.ServiceStat; + +/** + * 服务监控信息Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-03-18 + */ +public interface ServiceStatMapper +{ + /** + * 查询服务监控信息 + * + * @param statId 服务监控信息主键 + * @return 服务监控信息 + */ + public ServiceStat selectServiceStatByStatId(Long statId); + + /** + * 查询服务监控信息列表 + * + * @param serviceStat 服务监控信息 + * @return 服务监控信息集合 + */ + public List selectServiceStatList(ServiceStat serviceStat); + + //public List selectMonitorList(RdsMonitorQueryVo queryVo); + + /** + * 查询以一个服务ID,一个时间段的监控信息汇总平局 + * @param queryVo + * @return + */ + public ServiceStat selectSummaryServiceStat(RdsMonitorQueryVo queryVo); + + /** + * 新增服务监控信息 + * + * @param serviceStat 服务监控信息 + * @return 结果 + */ + public int insertServiceStat(ServiceStat serviceStat); + + /** + * 修改服务监控信息 + * + * @param serviceStat 服务监控信息 + * @return 结果 + */ + public int updateServiceStat(ServiceStat serviceStat); + + /** + * 删除服务监控信息 + * + * @param statId 服务监控信息主键 + * @return 结果 + */ + public int deleteServiceStatByStatId(Long statId); + + /** + * 批量删除服务监控信息 + * + * @param statIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteServiceStatByStatIds(Long[] statIds); + + /** + * 删除服务监控信息信息, 通过serviceId + * + * @param serviceId 服务监控信息主键 + * @return 结果 + */ + public int deleteServiceStatByServiceId(Long serviceId); + + /** + * 删除小于 createTime 参数时间之前的数据 + * @param createTime + * @return + */ + public int deleteByCreateTime(Date createTime); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateGroupMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateGroupMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..fd753dfd36ac4f52fc6340c50c46b88432e21108 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateGroupMapper.java @@ -0,0 +1,69 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.TemplateGroup; + +/** + * 配置模版Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-16 + */ +public interface TemplateGroupMapper +{ + /** + * 查询配置模版 + * + * @param groupId 配置模版主键 + * @return 配置模版 + */ + public TemplateGroup selectTemplateGroupByGroupId(Long groupId); + + /** + * 查询版本相关配置模版列表 + * @param versionId 版本ID + * @return + */ + public List selectTemplateGroupByVersionId(Long versionId); + + /** + * 查询配置模版列表 + * + * @param templateGroup 配置模版 + * @return 配置模版集合 + */ + public List selectTemplateGroupList(TemplateGroup templateGroup); + + /** + * 新增配置模版 + * + * @param templateGroup 配置模版 + * @return 结果 + */ + public int insertTemplateGroup(TemplateGroup templateGroup); + + /** + * 修改配置模版 + * + * @param templateGroup 配置模版 + * @return 结果 + */ + public int updateTemplateGroup(TemplateGroup templateGroup); + + + /** + * 删除配置模版 + * + * @param groupId 配置模版主键 + * @return 结果 + */ + public int deleteTemplateGroupByGroupId(Long groupId); + + /** + * 批量删除配置模版 + * + * @param groupIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTemplateGroupByGroupIds(Long[] groupIds); +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateGroupVersionMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateGroupVersionMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d4536a078a581f62f7826ddbca0b60ddbc269f17 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateGroupVersionMapper.java @@ -0,0 +1,53 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.TemplateGroupVersion; + +/** + * 模版组适用版本Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-16 + */ +public interface TemplateGroupVersionMapper +{ + /** + * 查询模版组适用版本 + * + * @param groupId 模版组适用版本主键 + * @return 模版组适用版本 + */ + public List selectByGroupId(Long groupId); + + /** + * 查询模版组适用版本列表 + * + * @param templateGroupVersion 模版组适用版本 + * @return 模版组适用版本集合 + */ + public List selectList(TemplateGroupVersion templateGroupVersion); + + /** + * 新增模版组适用版本 + * + * @param templateGroupVersion 模版组适用版本 + * @return 结果 + */ + public int insertGroupVersion(TemplateGroupVersion templateGroupVersion); + + /** + * 删除模版组相关的 + * + * @param groupId 模版组适用版本主键 + * @return 结果 + */ + public int deleteByGroupId(Long groupId); + + /** + * 删除版本相关的 + * @param versionId + * @return + */ + public int deleteByVersionId(Long versionId); + +} diff --git a/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateMapper.java b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..18f83e241d810b1f8a6bb7a67ff08fd618c03b63 --- /dev/null +++ b/rds-console/console-system/src/main/java/com/tongtech/console/mapper/TemplateMapper.java @@ -0,0 +1,68 @@ +package com.tongtech.console.mapper; + +import java.util.List; +import com.tongtech.console.domain.Template; + +/** + * 配置模版Mapper接口 + * + * @author Zhang ChenLong + * @date 2023-01-15 + */ +public interface TemplateMapper +{ + /** + * 查询配置模版 + * + * @param templateId 配置模版主键 + * @return 配置模版 + */ + public Template selectTemplateByTemplateId(Long templateId); + + /** + * 查询配置模版列表 + * + * @param template 配置模版 + * @return 配置模版集合 + */ + public List