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运行环境。
+ 
+ 
+
+#### 通过命令行运行工程 ####
+在根目录中
+```
+# 在根目录下编译相关工程
+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/ 在浏览器上进行访问。如果控制台启动正常会显示登录页面,如下图:
+
+
+
+初次启动后,请使用默认的管理员用户进行登录,用户名:admin 密码:admin123。
+
+为了提高系统安全性建议尽快对admin用户的密码进行更改,设置有一定安全强度的密码。更改密码可以通过,个人中心进行更改。
+
+### 5.1.2中心节点的配置
+
+容器云环境下是通过中心节点服务完整对容器云中RDS节点的注册发现、节点服务运行状态监控,节点弹性扩缩容等操作。
+
+因此首先要配置中心服务的管理地址和端口,通过连接中心节点服务后来拉取相关的配置信息。
+
+配置方式如下:
+
+
+
+在中心服务菜单功能中点击"修改"按钮。
+
+
+
+再修改界面中,部署环境要选为"k8s容器云","服务地址"请填写中心服务的访问地址,"管理关口"是中心服务的管理端口。
+
+
+当填写完成并确定后,系统会去中心节点拉取配置信息,在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. 所有服务的监控数据以列表的形式展现,列表支持根据服务名称/服务状态进行搜索,如下图所示。
+
+
+
+点击列表中的服务名字,会跳转至此服务的监控详情页面,在这个页面可以查看服务及节点的运行状态/内存/连接数/每秒请求数,监控数据以图表的形式展示,如下图所示
+
+
+
+### 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