This commit is contained in:
六如
2024-11-04 21:46:18 +08:00
parent f72fd07ea4
commit 5956af3564
201 changed files with 84 additions and 8702 deletions

199
README.md
View File

@@ -1,144 +1,113 @@
# SOP(Simple Open Platform) # SOP(Simple Open Platform)
一个开放平台解决方案项目基于dubbo实现目标让用户快速搭建自己的开放平台。
一个开放平台解决方案项目基于Spring Cloud实现目标让用户快速搭建自己的开放平台。
通过简单的配置后,你的项目就具备了和支付宝开放平台的一样的接口提供能力。 通过简单的配置后,你的项目就具备了和支付宝开放平台的一样的接口提供能力。
SOP封装了开放平台大部分功能包括签名验证、统一异常处理、统一返回内容 、业务参数验证JSR-303、秘钥管理等未来还会实现更多功能。 SOP封装了开放平台大部分功能包括签名验证、统一异常处理、统一返回内容 、业务参数验证JSR-303、秘钥管理等未来还会实现更多功能。
## 项目特点
- 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后在方法上加上注解即可。 ## 项目特点
- 架构松耦合业务代码实现在各自微服务上SOP不参与业务实现这也是Spring Cloud微服务体系带来的好处 + 接入方式简单,与老项目不冲突,老项目注册到注册中心,然后在方法上加上注解即可。
- 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等 + 架构松耦合业务代码实现在各自微服务上SOP不参与业务实现这也是dubbo微服务体系带来的好处
+ 扩展简单,开放平台对应的功能各自独立,可以自定义实现自己的需求,如:更改参数,更改签名规则等。
## 谁可以使用这个项目 ## 谁可以使用这个项目
+ 有现成的项目,想改造成开放平台供他人调用
- 有现成的项目,想改造成开放平台供他人调用 + 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用
- 有现成的项目,想暴露其中几个接口并通过开放平台供他人调用 + 想搭一个开放平台新项目,并结合微服务的方式去维护
- 想搭一个开放平台新项目,并结合微服务的方式去维护 + 对开放平台感兴趣的朋友
- 对开放平台感兴趣的朋友
以上情况都可以考虑使用SOP 以上情况都可以考虑使用SOP
## 例子 ## 例子
开放接口定义
```java ```java
// 加一个注解即可 /**
@Open("story.get") * 支付接口
@RequestMapping("/get") *
public StoryResult get() { * @author 六如
StoryResult result = new StoryResult(); */
result.setId(1L); @Api("支付接口")
result.setName("海底小纵队"); public interface OpenPayment {
return result;
@ApiOperation(
value = "alipay.trade.wap.pay(手机网站支付接口2.0)",
notes = "该接口是页面跳转接口,用于生成用户访问支付宝的跳转链接。" +
"请在服务端执行支付宝SDK中pageExecute方法读取响应中的body()结果。" +
"该结果用于跳转到支付宝页面,返回到用户浏览器渲染或重定向跳转到支付宝页面。" +
"具体使用方法请参考 <a href=\"https://torna.cn\" target=\"_blank\">接入指南</a>"
)
@Open(value = "alipay.trade.wap.pay", version = "2.0")
AlipayTradeWapPayResponse tradeWapPay(AlipayTradeWapPayRequest request);
} }
``` ```
接口实现
```java
/**
* 开放接口实现
*
* @author 六如
*/
@DubboService(validation = "true")
public class OpenPaymentImpl implements OpenPayment {
@Override
public AlipayTradeWapPayResponse tradeWapPay(AlipayTradeWapPayRequest request) {
AlipayTradeWapPayResponse alipayTradeWapPayResponse = new AlipayTradeWapPayResponse();
alipayTradeWapPayResponse.setPageRedirectionData(UUID.randomUUID().toString());
return alipayTradeWapPayResponse;
}
}
```
调用: 调用:
```java ```java
// 公共请求参数 @Test
Map<String, String> params = new HashMap<String, String>(); public void testGet() throws Exception {
params.put("app_id", appId);
params.put("method", "story.get");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数 // 公共请求参数
Map<String, String> bizContent = new HashMap<>(); Map<String, String> params = new HashMap<String, String>();
bizContent.put("id", "1"); params.put("app_id", appId);
bizContent.put("name", "葫芦娃"); params.put("method", "alipay.trade.wap.pay");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "2.0");
params.put("biz_content", JSON.toJSONString(bizContent)); // 业务参数
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no", "70501111111S001111119");
bizContent.put("total_amount", "9.00");
bizContent.put("subject", "衣服");
bizContent.put("product_code", "QUICK_WAP_WAY");
System.out.println("----------- 请求信息 -----------"); params.put("biz_content", JSON.toJSONString(bizContent));
System.out.println("请求参数:" + buildParamQuery(params)); String content = AlipaySignature.getSignContent(params);
System.out.println("商户秘钥:" + privateKey); String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
String content = AlipaySignature.getSignContent(params); params.put("sign", sign);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign)" + sign);
params.put("sign", sign); System.out.println("----------- 请求信息 -----------");
System.out.println("URL参数:" + buildUrlQuery(params)); System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
System.out.println("待签名内容:" + content);
System.out.println("签名(sign)" + sign);
System.out.println("URL参数" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------"); System.out.println("----------- 返回结果 -----------");
String responseData = get(url, params);// 发送请求 String responseData = postJson(url, params);// 发送请求
System.out.println(responseData); System.out.println(responseData);
}
``` ```
## 架构 ## 整体架构
![整体架构](./asset/arc.jpg)
![架构图](https://images.gitee.com/uploads/images/2019/1227/145216_c9b45109_332975.png "sop3.png")
> 如上图所示,整个系统运行后,开发者只需关注微服务中的业务代码,接口变更后重新部署微服务应用即可
## 已完成列表
- 签名验证
- 统一异常处理
- 统一返回内容
- session管理
- 秘钥管理
- 微服务端自动验证JSR-303
- Admin管理平台统一管理微服务配置管理路由管理微服务上下线
- 门户网站,提供用户注册账号
- 接入方管理+秘钥管理
- 接口权限分配
- 文件上传/下载
- 提供基础SDKJava,C++,C#,Python,Go,Rust,Nodejs
- 接口限流
- 文档整合
- 应用授权
- 监控日志
- 注册中心支持nacos/eureka
- 网关动态修改参数
- 预发布/灰度环境切换
## 界面预览
![服务列表](https://images.gitee.com/uploads/images/2020/1016/134354_c1915902_332975.png "service.png")
![路由管理](https://images.gitee.com/uploads/images/2020/1016/134039_bed1608d_332975.png "route.png")
![限流管理](https://images.gitee.com/uploads/images/2020/1016/134102_f2dcfb25_332975.png "limit.png")
![秘钥信息](https://images.gitee.com/uploads/images/2019/0711/174921_bd817533_332975.png "秘钥信息")
- 门户网站
![首页2](https://images.gitee.com/uploads/images/2021/0318/195935_1d610da8_332975.png "portal-vue.png")
![文档页](https://images.gitee.com/uploads/images/2020/1107/104342_d44849a9_332975.png "portal1.png")
## 工程说明
> 运行环境JDK8Maven3[Nacos](https://nacos.io/zh-cn/docs/what-is-nacos.html)Mysql
- doc开发文档
- sop-common公共模块封装常用功能包含签名校验、错误处理、限流等功能
- sop-gateway网关统一访问入口`Spring Cloud Zuul``Spring Cloud Gateway`实现
- sop-example微服务示例含springboot,springmvc示例
- sop-website开放平台对应网站提供文档API、沙箱测试等内容
- sop-auth应用授权服务示例
- sop-admin后台管理
- sop-sdk基础sdk含Java、C#版本
- sop-test接口调用测试用例
## 分支说明
- master发版分支
- develop日常开发分支
- eureka使用eureka注册中心
- pr接受PR的分支提交PR请提交到此分支
[更新说明](./changelog.md)
## 相关文档
[开发文档](http://durcframework.gitee.io/sop)

BIN
asset/arc.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -14,18 +14,13 @@
<version>5.0.0-SNAPSHOT</version> <version>5.0.0-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<description>一个开放平台解决方案项目基于Spring Cloud实现目标是能够让用户快速得搭建起自己的开放平台</description> <description>一个开放平台解决方案项目基于Spring Cloud实现目标是能够让用户快速得搭建起自己的开放平台</description>
<url></url>
<modules> <modules>
<module>doc</module> <module>doc</module>
<!-- <module>sop-common</module>-->
<!-- <module>sop-auth</module>-->
<module>sop-example</module> <module>sop-example</module>
<module>sop-admin</module> <module>sop-admin</module>
<!-- <module>sop-gateway</module>-->
<module>sop-test</module> <module>sop-test</module>
<module>sop-sdk</module> <module>sop-sdk</module>
<!-- <module>sop-website</module>-->
<module>sop-gateway</module> <module>sop-gateway</module>
<module>sop-registry</module> <module>sop-registry</module>
<module>sop-support</module> <module>sop-support</module>

View File

@@ -1,155 +0,0 @@
CREATE DATABASE IF NOT EXISTS `sop` DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
USE `sop`;
DROP TABLE IF EXISTS `api_info`;
CREATE TABLE `api_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`application` varchar(64) NOT NULL DEFAULT '' COMMENT '应用名称',
`api_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名称',
`api_version` varchar(16) NOT NULL DEFAULT '1.0' COMMENT '版本号',
`description` varchar(64) DEFAULT '' COMMENT '接口描述',
`remark` text COMMENT '备注',
`interface_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口class',
`method_name` varchar(128) NOT NULL DEFAULT '' COMMENT '方法名称',
`param_info` text COMMENT '参数信息',
`is_permission` tinyint(4) NOT NULL DEFAULT '0' COMMENT '接口是否需要授权访问',
`is_need_Token` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否需要appAuthToken',
`reg_source` tinyint(4) NOT NULL DEFAULT '1' COMMENT '注册来源1-系统注册,2-手动注册',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '1启用0禁用',
`add_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_apiname_version` (`api_name`,`api_version`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf32 COMMENT='接口信息表';
DROP TABLE IF EXISTS `isv_info`;
CREATE TABLE `isv_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`app_id` varchar(100) NOT NULL COMMENT 'appKey',
`status` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '1启用2禁用',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`add_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_appid` (`app_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf32 COMMENT='isv信息表';
DROP TABLE IF EXISTS `isv_keys`;
CREATE TABLE `isv_keys` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`isv_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'isv_info.id',
`key_format` tinyint(4) NOT NULL DEFAULT '1' COMMENT '秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)',
`public_key_isv` text NOT NULL COMMENT '开发者生成的公钥',
`private_key_isv` text NOT NULL COMMENT '开发者生成的私钥(交给开发者)',
`public_key_platform` text NOT NULL COMMENT '平台生成的公钥(交给开发者)',
`private_key_platform` text NOT NULL COMMENT '平台生成的私钥',
`add_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_appid` (`isv_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COMMENT='ISV秘钥管理';
DROP TABLE IF EXISTS `perm_group`;
CREATE TABLE `perm_group` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`group_name` varchar(64) NOT NULL COMMENT '分组描述',
`is_deleted` tinyint(4) DEFAULT '0' COMMENT '是否删除',
`add_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf32 COMMENT='分组表';
DROP TABLE IF EXISTS `perm_group_permission`;
CREATE TABLE `perm_group_permission` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`group_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'perm_group.id',
`api_id` varchar(64) NOT NULL COMMENT 'api_info.id',
`add_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code_apiid` (`group_id`,`api_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf32 COMMENT='组权限表';
DROP TABLE IF EXISTS `perm_isv_group`;
CREATE TABLE `perm_isv_group` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`isv_id` bigint(20) NOT NULL COMMENT 'isv_info表id',
`group_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'perm_group.id',
`add_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_role` (`isv_id`,`group_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf32 COMMENT='isv分组';
DROP TABLE IF EXISTS `sys_admin_user`;
CREATE TABLE `sys_admin_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
`nickname` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
`email` varchar(128) NOT NULL DEFAULT '' COMMENT '邮箱',
`avatar` varchar(128) NOT NULL DEFAULT '' COMMENT '头像',
`status` tinyint(4) unsigned NOT NULL DEFAULT '1' COMMENT '状态1启用2禁用',
`reg_type` varchar(32) NOT NULL DEFAULT '1' COMMENT '注册类型1-系统2-手动',
`add_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf32 COMMENT='系统用户表';
DROP TABLE IF EXISTS `sys_config`;
CREATE TABLE `sys_config` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`config_key` varchar(64) NOT NULL DEFAULT '',
`config_value` varchar(256) NOT NULL DEFAULT '',
`remark` varchar(128) NOT NULL DEFAULT '',
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
`add_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_configkey` (`config_key`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf32 COMMENT='系统配置表';
-- insert
INSERT INTO `api_info` (`id`, `application`, `api_name`, `api_version`, `description`, `remark`, `interface_class_name`, `method_name`, `param_info`, `is_permission`, `is_need_Token`, `reg_source`, `status`, `add_time`, `update_time`) VALUES
(1, 'story-service', 'story.get', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'getById', '[{\"name\":\"id\",\"type\":\"java.lang.Integer\"}]', 0, 0, 1, 1, '2024-09-10 18:34:05', '2024-10-09 09:12:07'),
(2, 'story-service', 'story.save', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'save', '[{\"name\":\"storySaveDTO\",\"type\":\"com.gitee.sop.storyweb.open.req.StorySaveDTO\"}]', 0, 0, 1, 1, '2024-09-10 18:34:05', '2024-10-18 20:06:07'),
(3, 'story-service', 'story.find', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'getById', '[{\"name\":\"id\",\"type\":\"java.lang.Integer\"},{\"name\":\"name\",\"type\":\"java.lang.String\"}]', 0, 0, 1, 1, '2024-09-10 18:38:27', '2024-10-09 09:12:07'),
(4, 'story-service', 'story.find2', '1.0', '', '', 'com.gitee.sop.storyweb.open.StoryService', 'getById2', '[{\"name\":\"id\",\"type\":\"java.lang.Integer\"},{\"name\":\"name\",\"type\":\"java.lang.String\"}]', 0, 0, 1, 1, '2024-09-10 18:44:51', '2024-09-10 18:44:51'),
(5, 'story-service', 'story.find3', '1.0', '', '', 'com.gitee.sop.storyweb.open.StoryService', 'getById2', '[{\"name\":\"id\",\"type\":\"java.lang.Integer\"},{\"name\":\"name\",\"type\":\"java.lang.String\"}]', 0, 0, 1, 1, '2024-09-10 18:49:14', '2024-09-10 18:49:14'),
(6, 'story-service', 'story.update', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'update', '[{\"name\":\"id\",\"type\":\"java.lang.Integer\"},{\"name\":\"storySaveDTO\",\"type\":\"com.gitee.sop.storyweb.open.req.StorySaveDTO\"}]', 0, 0, 1, 1, '2024-09-10 23:00:07', '2024-10-09 09:12:07'),
(7, 'story-service', 'story.upload', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'upload', '[{\"name\":\"storySaveDTO\",\"type\":\"com.gitee.sop.storyweb.open.req.StorySaveDTO\"},{\"name\":\"file\",\"type\":\"com.gitee.sop.support.dto.FileData\"}]', 0, 0, 1, 1, '2024-09-13 09:14:03', '2024-10-09 09:12:07'),
(8, 'story-service', 'story.upload.more', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'upload2', '[{\"name\":\"storySaveDTO\",\"type\":\"com.gitee.sop.storyweb.open.req.StorySaveDTO\"},{\"name\":\"idCardFront\",\"type\":\"com.gitee.sop.support.dto.FileData\"},{\"name\":\"idCardBack\",\"type\":\"com.gitee.sop.support.dto.FileData\"}]', 0, 0, 1, 1, '2024-09-13 09:22:05', '2024-10-09 09:12:07'),
(9, 'story-service', 'story.upload.list', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'upload3', '[{\"name\":\"storySaveDTO\",\"type\":\"com.gitee.sop.storyweb.open.req.StorySaveDTO\"},{\"actualType\":\"com.gitee.sop.support.dto.FileData\",\"name\":\"files\",\"type\":\"java.util.List\"}]', 0, 0, 1, 1, '2024-09-13 09:28:01', '2024-10-09 09:12:07'),
(10, 'story-service', 'story.get', '2.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'getByIdV2', '[{\"name\":\"id\",\"type\":\"java.lang.Long\"}]', 0, 0, 1, 1, '2024-09-14 10:40:13', '2024-10-09 09:12:07'),
(11, 'story-service', 'story.download', '1.0', '', '', 'com.gitee.sop.storyweb.open.OpenStory', 'download', '[{\"name\":\"id\",\"type\":\"java.lang.Integer\"}]', 0, 0, 2, 1, '2024-09-16 20:56:09', '2024-10-18 20:06:07'),
(14, 'q1', 'q11', 'q', 'q1', '备注,内容有点长内容有点长内容有点长内容有点长内容有点长内容有点长', '', '', NULL, 1, 0, 1, 1, '2024-10-06 09:51:47', '2024-10-19 11:47:54'),
(15, 'server', 'goods.get', '1.0', '1111', '2222225555', '', '', NULL, 0, 0, 2, 2, '2024-10-10 10:38:59', '2024-10-20 15:51:09');
INSERT INTO `isv_info` (`id`, `app_id`, `status`, `remark`, `add_time`, `update_time`) VALUES
(1, '2019032617262200001', 1, NULL, '2024-09-10 09:23:43', '2024-10-14 11:32:58'),
(2, '224', 1, '334', '2024-10-10 10:48:40', '2024-10-10 10:50:39'),
(3, '202410111294229455501262848', 1, '杭州限行公司', '2024-10-11 09:17:28', '2024-10-15 09:32:35'),
(4, '202410191297295138816524288', 1, 'xx公司', '2024-10-19 20:19:24', '2024-10-21 14:09:42');
INSERT INTO `isv_keys` (`id`, `isv_id`, `key_format`, `public_key_isv`, `private_key_isv`, `public_key_platform`, `private_key_platform`, `add_time`, `update_time`) VALUES
(16, 1, 1, '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', '2024-09-10 09:26:33', '2024-10-19 20:00:37'),
(17, 3, 1, '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', '', '', '2024-10-11 09:17:28', '2024-10-11 09:17:28'),
(18, 2, 1, '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', '2024-10-19 20:08:11', '2024-10-19 20:08:11'),
(19, 4, 1, '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', '2024-10-19 20:23:38', '2024-10-19 20:23:38');
INSERT INTO `perm_group` (`id`, `group_name`, `is_deleted`, `add_time`, `update_time`) VALUES
(1, 'VIP', 0, '2024-10-20 16:23:31', '2024-10-22 09:21:07'),
(2, 'VIP2', 1, '2024-10-20 16:25:11', '2024-10-21 14:13:41');
INSERT INTO `perm_isv_group` (`id`, `isv_id`, `group_id`, `add_time`, `update_time`) VALUES
(8, 4, 1, '2024-10-21 14:10:18', '2024-10-21 14:10:18');
INSERT INTO `sys_admin_user` (`id`, `username`, `password`, `nickname`, `email`, `avatar`, `status`, `reg_type`, `add_time`, `update_time`) VALUES
(1, 'admin', '$2a$10$6LWUuQQ8V/rszLo50.qrsuBlNmbYY/q2KSC1BmrxxN5czvOxmF3qK', 'admin', '', '', 1, 'backend', '2024-10-03 08:37:03', '2024-10-03 19:08:48');
INSERT INTO `sys_config` (`id`, `config_key`, `config_value`, `remark`, `is_deleted`, `add_time`, `update_time`) VALUES
(1, 'admin.jwt.secret', 'UXJC8R3NmAvj9kFKWwBgYu6y3naXh7', '', 0, '2024-10-02 21:20:41', '2024-10-02 21:20:41'),
(2, 'admin.password-salt', 'Khq9RnXZCGsWz', '', 0, '2024-10-02 21:20:41', '2024-10-02 21:20:41');

View File

@@ -1,25 +0,0 @@
target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
/local-config/

View File

@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-parent</artifactId>
<version>5.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-admin-server</artifactId>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<!-- easyopen starter -->
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>easyopen-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
<!-- optional-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>maven2</id>
<name>maven2</name>
<url>https://repo1.maven.org/maven2</url>
</repository>
</repositories>
</project>

View File

@@ -1,22 +0,0 @@
# sop admin服务端
# 使用步骤
- 打开`application-dev.yml`,修改数据库`username/password`指定nacos地址
- 运行`SopAdminServerApplication.java`
- 访问:`http://localhost:8082`
登录账号admin/123456
后台用户表admin_user_info
- 密码保存规则:`md5(username + md5(password) + username)`
```text
username: admin
password: 123456
md5("admin" + md5(123456) + "admin")
```
详见com.gitee.sop.adminserver.AccountTest

View File

@@ -1,12 +0,0 @@
package com.gitee.sop.adminserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SopAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(SopAdminServerApplication.class, args);
}
}

View File

@@ -1,18 +0,0 @@
package com.gitee.sop.adminserver.api;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Getter
@Setter
public class IdParam {
@NotNull(message = "id不能为空")
@ApiDocField(description = "id")
private Long id;
}

View File

@@ -1,51 +0,0 @@
package com.gitee.sop.adminserver.api.demo;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.sop.adminserver.api.demo.param.GoodsParam;
import com.gitee.sop.adminserver.api.demo.result.Goods;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
/**
* 业务类
*
* @author 六如
*
*/
@ApiService
@ApiDoc("商品模块")
public class GoodsApi {
@Api(name = "goods.get")
@ApiDocMethod(description = "获取商品")
public Goods getGoods(GoodsParam param) {
Goods goods = new Goods();
goods.setId(1L);
goods.setGoods_name("苹果iPhoneX");
goods.setPrice(new BigDecimal(8000));
return goods;
}
@Api(name = "goods.list", version = "2.0")
@ApiDocMethod(description = "获取商品列表", results = {
@ApiDocField(description = "商品列表", name = "list", elementClass = Goods.class) })
public List<Goods> listGoods(GoodsParam param) {
Goods goods = new Goods();
goods.setId(1L);
goods.setGoods_name("iPhoneX");
goods.setPrice(new BigDecimal(8000));
Goods goods2 = new Goods();
goods2.setId(2L);
goods2.setGoods_name("三星");
goods2.setPrice(new BigDecimal(7000));
return Arrays.asList(goods, goods2);
}
}

View File

@@ -1,23 +0,0 @@
package com.gitee.sop.adminserver.api.demo.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
public class GoodsParam {
@ApiDocField(description = "商品名称", required = true, example = "iphoneX")
@NotEmpty(message = "商品名称不能为空")
@Length(min = 3, max = 20, message = "{goods.name.length}=3,20")
private String goods_name;
public String getGoods_name() {
return goods_name;
}
public void setGoods_name(String goods_name) {
this.goods_name = goods_name;
}
}

View File

@@ -1,48 +0,0 @@
package com.gitee.sop.adminserver.api.demo.result;
import com.gitee.easyopen.doc.DataType;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.math.BigDecimal;
@XStreamAlias("goods")
public class Goods {
@ApiDocField(description = "id")
private Long id;
@ApiDocField(description = "商品名称")
private String goods_name;
@ApiDocField(description = "价格", dataType = DataType.FLOAT)
private BigDecimal price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getGoods_name() {
return goods_name;
}
public void setGoods_name(String goods_name) {
this.goods_name = goods_name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
@Override
public String toString() {
return "Goods [id=" + id + ", goods_name=" + goods_name + ", price=" + price + "]";
}
}

View File

@@ -1,298 +0,0 @@
package com.gitee.sop.adminserver.api.isv;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.DataType;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.IdParam;
import com.gitee.sop.adminserver.api.isv.param.IsvInfoFormAdd;
import com.gitee.sop.adminserver.api.isv.param.IsvInfoFormUpdate;
import com.gitee.sop.adminserver.api.isv.param.IsvKeysFormUpdate;
import com.gitee.sop.adminserver.api.isv.param.IsvKeysGen;
import com.gitee.sop.adminserver.api.isv.param.IsvPageParam;
import com.gitee.sop.adminserver.api.isv.result.IsvDetailDTO;
import com.gitee.sop.adminserver.api.isv.result.IsvInfoVO;
import com.gitee.sop.adminserver.api.isv.result.IsvKeysGenVO;
import com.gitee.sop.adminserver.api.isv.result.IsvKeysVO;
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.common.IdGen;
import com.gitee.sop.adminserver.common.RSATool;
import com.gitee.sop.adminserver.entity.IsvInfo;
import com.gitee.sop.adminserver.entity.IsvKeys;
import com.gitee.sop.adminserver.entity.PermIsvRole;
import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.mapper.IsvInfoMapper;
import com.gitee.sop.adminserver.mapper.IsvKeysMapper;
import com.gitee.sop.adminserver.mapper.PermIsvRoleMapper;
import com.gitee.sop.adminserver.mapper.PermRoleMapper;
import com.gitee.sop.adminserver.service.ConfigPushService;
import com.gitee.sop.adminserver.service.RoutePermissionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotBlank;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@ApiService
@ApiDoc("ISV管理")
@Slf4j
public class IsvApi {
public static final byte SIGN_TYPE_RSA = 1;
public static final byte SIGN_TYPE_MD5 = 2;
static Map<String, Byte> SIGN_TYPE_MAP = new HashMap<>();
static {
SIGN_TYPE_MAP.put("rsa", (byte) SIGN_TYPE_RSA);
SIGN_TYPE_MAP.put("md5", (byte) SIGN_TYPE_MD5);
}
@Autowired
IsvInfoMapper isvInfoMapper;
@Autowired
IsvKeysMapper isvKeysMapper;
@Autowired
PermIsvRoleMapper permIsvRoleMapper;
@Autowired
PermRoleMapper permRoleMapper;
@Autowired
RoutePermissionService routePermissionService;
@Autowired
private ConfigPushService configPushService;
@Value("${sop.sign-type}")
private String sopSignType;
@Api(name = "isv.info.page")
@ApiDocMethod(description = "isv列表", results = {
@ApiDocField(name = "pageIndex", description = "第几页", dataType = DataType.INT, example = "1"),
@ApiDocField(name = "pageSize", description = "每页几条数据", dataType = DataType.INT, example = "10"),
@ApiDocField(name = "total", description = "每页几条数据", dataType = DataType.LONG, example = "100"),
@ApiDocField(name = "rows", description = "数据", dataType = DataType.ARRAY, elementClass = IsvInfoVO.class)
})
PageInfo<IsvInfoVO> pageIsv(IsvPageParam param) {
Query query = Query.build(param);
query.orderby("id", Sort.DESC);
PageInfo<IsvInfo> pageInfo = MapperUtil.query(isvInfoMapper, query);
List<IsvInfo> list = pageInfo.getList();
List<IsvInfoVO> retList = list.stream()
.map(isvInfo -> {
return buildIsvVO(isvInfo);
})
.collect(Collectors.toList());
PageInfo<IsvInfoVO> pageInfoRet = new PageInfo<>();
pageInfoRet.setTotal(pageInfo.getTotal());
pageInfoRet.setList(retList);
return pageInfoRet;
}
@Api(name = "isv.info.get")
@ApiDocMethod(description = "获取isv")
IsvInfoVO getIsvVO(IdParam param) {
IsvInfo isvInfo = isvInfoMapper.getById(param.getId());
return buildIsvVO(isvInfo);
}
@Api(name = "isv.keys.get")
@ApiDocMethod(description = "获取isv2")
IsvKeysVO getIsvKeys(@NotBlank(message = "appKey不能为空")
@ApiDocField(description = "appKey")
String appKey) {
IsvKeys isvKeys = isvKeysMapper.getByColumn("app_key", appKey);
IsvKeysVO isvDetailVO = new IsvKeysVO();
if (isvKeys != null) {
CopyUtil.copyProperties(isvKeys, isvDetailVO);
}
isvDetailVO.setAppKey(appKey);
isvDetailVO.setSignType(getSignType());
return isvDetailVO;
}
private IsvInfoVO buildIsvVO(IsvInfo isvInfo) {
if (isvInfo == null) {
return null;
}
IsvInfoVO vo = new IsvInfoVO();
CopyUtil.copyProperties(isvInfo, vo);
vo.setRoleList(this.buildIsvRole(isvInfo));
return vo;
}
/**
* 构建ISV拥有的角色
*
* @param permClient
* @return
*/
List<RoleVO> buildIsvRole(IsvInfo permClient) {
List<String> roleCodeList = routePermissionService.listClientRoleCode(permClient.getId());
if (CollectionUtils.isEmpty(roleCodeList)) {
return Collections.emptyList();
}
List<PermRole> list = permRoleMapper.list(new Query().in("role_code", roleCodeList));
return list.stream()
.map(permRole -> {
RoleVO vo = new RoleVO();
CopyUtil.copyProperties(permRole, vo);
return vo;
})
.collect(Collectors.toList());
}
@Api(name = "isv.info.add")
@ApiDocMethod(description = "添加isv")
@Transactional(rollbackFor = Exception.class)
public void addIsv(IsvInfoFormAdd param) throws Exception {
String appKey = new SimpleDateFormat("yyyyMMdd").format(new Date()) + IdGen.nextId();
IsvInfo rec = new IsvInfo();
rec.setAppKey(appKey);
CopyUtil.copyPropertiesIgnoreNull(param, rec);
isvInfoMapper.saveIgnoreNull(rec);
if (CollectionUtils.isNotEmpty(param.getRoleCode())) {
this.saveIsvRole(rec, param.getRoleCode());
}
IsvKeysGenVO isvKeysGenVO = this.createIsvKeys();
IsvKeys isvKeys = new IsvKeys();
isvKeys.setAppKey(appKey);
isvKeys.setSignType(getSignType());
CopyUtil.copyPropertiesIgnoreNull(isvKeysGenVO, isvKeys);
isvKeysMapper.saveIgnoreNull(isvKeys);
this.sendChannelMsg(rec.getAppKey());
}
private byte getSignType() {
return SIGN_TYPE_MAP.getOrDefault(sopSignType, SIGN_TYPE_RSA);
}
@Api(name = "isv.info.update")
@ApiDocMethod(description = "修改isv")
@Transactional(rollbackFor = Exception.class)
public void updateIsv(IsvInfoFormUpdate param) {
IsvInfo rec = isvInfoMapper.getById(param.getId());
CopyUtil.copyPropertiesIgnoreNull(param, rec);
isvInfoMapper.updateIgnoreNull(rec);
this.saveIsvRole(rec, param.getRoleCode());
this.sendChannelMsg(rec.getAppKey());
}
@Api(name = "isv.keys.update")
@ApiDocMethod(description = "修改isv")
public void updateIsvKeys(IsvKeysFormUpdate param) {
IsvKeys isvKeys = isvKeysMapper.getByColumn("app_key", param.getAppKey());
if (isvKeys == null) {
isvKeys = new IsvKeys();
CopyUtil.copyPropertiesIgnoreNull(param, isvKeys);
isvKeys.setSignType(getSignType());
isvKeysMapper.saveIgnoreNull(isvKeys);
} else {
CopyUtil.copyPropertiesIgnoreNull(param, isvKeys);
isvKeysMapper.updateIgnoreNull(isvKeys);
}
this.sendChannelMsg(isvKeys.getAppKey());
}
private void sendChannelMsg(String appKey) {
IsvDetailDTO isvDetail = isvInfoMapper.getIsvDetail(appKey);
if (isvDetail == null) {
return;
}
ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.ISV_INFO_UPDATE, isvDetail);
configPushService.publishConfig(NacosConfigs.DATA_ID_ISV, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
private IsvKeysGenVO createIsvKeys() throws Exception {
IsvKeysGenVO isvFormVO = new IsvKeysGenVO();
String secret = IdGen.uuid();
isvFormVO.setSecret(secret);
RSATool rsaToolIsv = new RSATool(RSATool.KeyFormat.PKCS8, RSATool.KeyLength.LENGTH_2048);
RSATool.KeyStore keyStoreIsv = rsaToolIsv.createKeys();
isvFormVO.setPublicKeyIsv(keyStoreIsv.getPublicKey());
isvFormVO.setPrivateKeyIsv(keyStoreIsv.getPrivateKey());
isvFormVO.setPublicKeyPlatform("");
isvFormVO.setPrivateKeyPlatform("");
return isvFormVO;
}
@Api(name = "isv.keys.gen")
@ApiDocMethod(description = "生成公私钥")
RSATool.KeyStore createPubPriKey(IsvKeysGen param) throws Exception {
RSATool.KeyFormat format = RSATool.KeyFormat.PKCS8;
Byte keyFormat = param.getKeyFormat();
if (keyFormat != null && keyFormat == 2) {
format = RSATool.KeyFormat.PKCS1;
}
RSATool rsaTool = new RSATool(format, RSATool.KeyLength.LENGTH_2048);
return rsaTool.createKeys();
}
@Api(name = "isv.secret.gen")
@ApiDocMethod(description = "生成MD秘钥")
String createSecret() throws Exception {
return IdGen.uuid();
}
void saveIsvRole(IsvInfo isvInfo, List<String> roleCodeList) {
Query query = new Query();
Long isvId = isvInfo.getId();
query.eq("isv_id", isvId);
permIsvRoleMapper.deleteByQuery(query);
List<PermIsvRole> tobeSaveList = roleCodeList.stream()
.map(roleCode -> {
PermIsvRole rec = new PermIsvRole();
rec.setIsvId(isvId);
rec.setRoleCode(roleCode);
return rec;
})
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(tobeSaveList)) {
permIsvRoleMapper.saveBatch(tobeSaveList);
}
try {
routePermissionService.sendIsvRolePermissionMsg(isvInfo.getAppKey(), roleCodeList);
} catch (Exception e) {
log.error("同步角色失败isvInfo:{}, roleCodeList:{}", isvInfo, roleCodeList);
throw new BizException("同步角色失败,请查看网关日志");
}
}
}

View File

@@ -1,87 +0,0 @@
package com.gitee.sop.adminserver.api.isv;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.isv.param.RoleForm;
import com.gitee.sop.adminserver.api.isv.param.RolePageParam;
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.entity.PermRolePermission;
import com.gitee.sop.adminserver.mapper.PermRoleMapper;
import com.gitee.sop.adminserver.mapper.PermRolePermissionMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@ApiService
@ApiDoc("ISV管理")
@Slf4j
public class RoleApi {
@Autowired
PermRoleMapper permRoleMapper;
@Autowired
PermRolePermissionMapper permRolePermissionMapper;
@Api(name = "role.listall")
List<RoleVO> roleListall() {
Query query = new Query();
query.orderby("id", Sort.ASC);
return permRoleMapper.list(query).stream()
.map(permRole -> {
RoleVO vo = new RoleVO();
CopyUtil.copyProperties(permRole, vo);
return vo;
})
.collect(Collectors.toList());
}
@ApiDocMethod(description = "获取角色,分页")
@Api(name = "role.page")
PageEasyui<RoleVO> pageRole(RolePageParam rolePage) {
Query query = Query.build(rolePage);
return MapperUtil.queryForEasyuiDatagrid(permRoleMapper, query, RoleVO.class);
}
@Api(name = "role.add")
void addRole(RoleForm roleForm) {
PermRole rec = permRoleMapper.getByColumn("role_code", roleForm.getRoleCode());
if (rec != null) {
throw new BizException("角色码已存在");
}
PermRole permRole = new PermRole();
CopyUtil.copyPropertiesIgnoreNull(roleForm, permRole);
permRoleMapper.saveIgnoreNull(permRole);
}
@Api(name = "role.update")
void updateRole(RoleForm roleForm) {
PermRole rec = permRoleMapper.getById(roleForm.getId());
rec.setDescription(roleForm.getDescription());
permRoleMapper.updateIgnoreNull(rec);
}
@Api(name = "role.del")
void delRole(long id) {
PermRole rec = permRoleMapper.getById(id);
PermRolePermission rolePermission = permRolePermissionMapper.getByColumn("role_code", rec.getRoleCode());
if (rolePermission != null) {
throw new BizException("该角色已使用,无法删除");
}
permRoleMapper.deleteById(id);
}
}

View File

@@ -1,26 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import java.util.Collections;
import java.util.List;
/**
* @author 六如
*/
@Data
public class IsvInfoForm {
/** 0启用1禁用, 数据库字段status */
@ApiDocField(description = "状态0启用1禁用")
private Byte status = 0;
@ApiDocField(description = "备注")
@Length(max = 100, message = "长度不得唱过100")
private String remark;
@ApiDocField(description = "roleCode数组", elementClass = String.class)
private List<String> roleCode = Collections.emptyList();
}

View File

@@ -1,12 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
public class IsvInfoFormAdd extends IsvInfoForm {
}

View File

@@ -1,18 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Getter
@Setter
public class IsvInfoFormUpdate extends IsvInfoForm {
@ApiDocField(description = "id")
@NotNull(message = "id不能为空")
private Long id;
}

View File

@@ -1,39 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class IsvKeysFormUpdate {
/** appKey, 数据库字段app_key */
@ApiDocField(description = "appKey", example = "aaaa")
@NotBlank(message = "appKey不能为空")
@Length(max = 100,message = "appKey长度不能超过100")
private String appKey;
/** secret, 数据库字段secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用), 数据库字段key_format */
@ApiDocField(description = "秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)", example = "1")
@Min(value = 1, message = "秘钥格式错误")
@Max(value = 2, message = "秘钥格式错误")
private Byte keyFormat;
/** 平台生成的公钥(交给开发者), 数据库字段public_key_platform */
@ApiDocField(description = "平台生成的公钥")
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段private_key_platform */
@ApiDocField(description = "平台生成的私钥")
private String privateKeyPlatform;
}

View File

@@ -1,18 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
/**
* @author 六如
*/
@Data
public class IsvKeysGen {
@ApiDocField(description = "秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)", example = "1")
@Min(value = 1, message = "秘钥格式错误")
@Max(value = 2, message = "秘钥格式错误")
private Byte keyFormat;
}

View File

@@ -1,24 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.DataType;
import com.gitee.easyopen.doc.annotation.ApiDocBean;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
@ApiDocBean(fields = {
@ApiDocField(name = "pageIndex", description = "第几页", dataType = DataType.INT, example = "1"),
@ApiDocField(name = "pageSize", description = "每页几条数据", dataType = DataType.INT, example = "10"),
})
public class IsvPageParam extends PageParam {
@ApiDocField(name = "appKey", description = "appKey", dataType = DataType.STRING, example = "111111")
@Condition(ignoreEmptyString = true)
private String appKey;
}

View File

@@ -1,24 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class RoleForm {
@ApiDocField(description = "id")
private Long id;
@ApiDocField(description = "角色码")
@NotBlank(message = "roleCode不能为空")
@Length(max = 64)
private String roleCode;
@ApiDocField(description = "描述")
private String description;
}

View File

@@ -1,19 +0,0 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
public class RolePageParam extends PageParam {
@ApiDocField(description = "角色码")
@Condition(operator = Operator.like)
private String roleCode;
}

View File

@@ -1,11 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvAppKeyGenVO {
private String appKey;
}

View File

@@ -1,34 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvDetailDTO {
/** appKey, 数据库字段app_key */
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
/** 0启用1禁用, 数据库字段status */
@ApiDocField(description = "状态0启用1禁用")
private Byte status;
/** secret, 数据库字段secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 开发者生成的公钥, 数据库字段public_key_isv */
@ApiDocField(description = "开发者生成的公钥")
private String publicKeyIsv;
/** 平台生成的私钥, 数据库字段private_key_platform */
@ApiDocField(description = "平台生成的私钥")
private String privateKeyPlatform;
@ApiDocField(description = "签名类型1:RSA2,2:MD5")
private Byte signType;
}

View File

@@ -1,44 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author 六如
*/
@Data
public class IsvInfoVO {
/** 数据库字段id */
@ApiDocField(description = "id", example = "1")
private Long id;
/** appKey, 数据库字段app_key */
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
/** 0启用1禁用, 数据库字段status */
@ApiDocField(description = "状态0启用1禁用")
private Byte status;
@ApiDocField(description = "签名类型1:RSA2,2:MD5")
private Byte signType;
@ApiDocField(description = "备注")
private String remark;
private Long userId;
/** 数据库字段gmt_create */
@ApiDocField(description = "添加时间")
private Date gmtCreate;
/** 数据库字段gmt_modified */
@ApiDocField(description = "修改时间")
private Date gmtModified;
@ApiDocField(description = "角色列表")
private List<RoleVO> roleList;
}

View File

@@ -1,25 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvKeysGenVO {
/** secret, 数据库字段secret */
private String secret;
/** 开发者生成的公钥, 数据库字段public_key_isv */
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段private_key_isv */
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段public_key_platform */
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段private_key_platform */
private String privateKeyPlatform;
}

View File

@@ -1,38 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class IsvKeysVO {
/** appKey, 数据库字段app_key */
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
/** secret, 数据库字段secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用), 数据库字段key_format */
private Byte keyFormat = 1;
/** 开发者生成的公钥, 数据库字段public_key_isv */
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段private_key_isv */
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段public_key_platform */
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段private_key_platform */
private String privateKeyPlatform;
@ApiDocField(description = "签名类型1:RSA2,2:MD5")
private Byte signType = 1;
}

View File

@@ -1,27 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import java.util.Date;
/**
* @author 六如
*/
@Data
public class RoleVO {
@ApiDocField(description = "id")
private Long id;
@ApiDocField(description = "角色码")
private String roleCode;
@ApiDocField(description = "描述")
private String description;
@ApiDocField(description = "创建时间")
private Date gmtCreate;
@ApiDocField(description = "修改时间")
private Date gmtModified;
}

View File

@@ -1,94 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlackForm;
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlacklistPageParam;
import com.gitee.sop.adminserver.api.service.result.ConfigIpBlacklistVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.entity.ConfigIpBlacklist;
import com.gitee.sop.adminserver.mapper.ConfigIpBlacklistMapper;
import com.gitee.sop.adminserver.service.ConfigPushService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author 六如
*/
@ApiService
@ApiDoc("IP黑名单管理")
@Slf4j
public class IPBlacklistApi {
@Autowired
ConfigIpBlacklistMapper configIpBlacklistMapper;
@Autowired
private ConfigPushService configPushService;
@ApiDocMethod(description = "获取IP黑名单分页")
@Api(name = "ip.blacklist.page")
PageEasyui<ConfigIpBlacklistVO> page(ConfigIpBlacklistPageParam form) {
Query query = Query.build(form);
query.orderby("id", Sort.DESC);
return MapperUtil.queryForEasyuiDatagrid(configIpBlacklistMapper, query, ConfigIpBlacklistVO.class);
}
@ApiDocMethod(description = "IP黑名单--新增")
@Api(name = "ip.blacklist.add")
void add(ConfigIpBlackForm form) {
ConfigIpBlacklist rec = configIpBlacklistMapper.getByColumn("ip", form.getIp());
if (rec != null) {
throw new BizException("IP已添加");
}
rec = new ConfigIpBlacklist();
CopyUtil.copyPropertiesIgnoreNull(form, rec);
configIpBlacklistMapper.saveIgnoreNull(rec);
try {
this.sendIpBlacklistMsg(rec, ChannelOperation.BLACKLIST_ADD);
} catch (Exception e) {
log.error("推送IP黑名单失败, rec:{}",rec, e);
throw new BizException("推送IP黑名单失败");
}
}
@ApiDocMethod(description = "IP黑名单--修改")
@Api(name = "ip.blacklist.update")
void update(ConfigIpBlackForm form) {
ConfigIpBlacklist rec = configIpBlacklistMapper.getById(form.getId());
CopyUtil.copyPropertiesIgnoreNull(form, rec);
configIpBlacklistMapper.updateIgnoreNull(rec);
}
@ApiDocMethod(description = "IP黑名单--删除")
@Api(name = "ip.blacklist.del")
void del(long id) {
ConfigIpBlacklist rec = configIpBlacklistMapper.getById(id);
if (rec == null) {
return;
}
configIpBlacklistMapper.deleteById(id);
try {
this.sendIpBlacklistMsg(rec, ChannelOperation.BLACKLIST_DELETE);
} catch (Exception e) {
log.error("推送IP黑名单失败, rec:{}",rec, e);
throw new BizException("推送IP黑名单失败");
}
}
public void sendIpBlacklistMsg(ConfigIpBlacklist configIpBlacklist, ChannelOperation channelOperation) {
ChannelMsg channelMsg = new ChannelMsg(channelOperation, configIpBlacklist);
configPushService.publishConfig(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
}

View File

@@ -1,95 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.service.param.LimitNewAddParam;
import com.gitee.sop.adminserver.api.service.param.LimitNewParam;
import com.gitee.sop.adminserver.api.service.param.LimitNewUpdateParam;
import com.gitee.sop.adminserver.api.service.result.LimitNewVO;
import com.gitee.sop.adminserver.bean.ConfigLimitDto;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.ConfigLimit;
import com.gitee.sop.adminserver.mapper.ConfigLimitMapper;
import com.gitee.sop.adminserver.service.RouteConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
/**
* 限流
*
* @author 六如
*/
@ApiService
@ApiDoc("服务管理-限流管理")
@Slf4j
public class LimitNewApi {
@Autowired
RouteConfigService routeConfigService;
@Autowired
ConfigLimitMapper configLimitMapper;
@Api(name = "config.limit.list")
@ApiDocMethod(description = "限流列表(新)", elementClass = LimitNewVO.class)
PageInfo<ConfigLimit> listLimit(LimitNewParam param) {
Query query = Query.build(param);
query.orderby("route_id", Sort.ASC)
.orderby("app_key", Sort.ASC)
.orderby("order_index", Sort.ASC);
PageInfo<ConfigLimit> pageInfo = MapperUtil.query(configLimitMapper, query);
return pageInfo;
}
@Api(name = "config.limit.add")
@ApiDocMethod(description = "新增限流(新)")
@Transactional(rollbackFor = Exception.class)
public void createLimtit(LimitNewAddParam param) {
ConfigLimit configLimit = new ConfigLimit();
CopyUtil.copyPropertiesIgnoreNull(param, configLimit);
configLimitMapper.save(configLimit);
ConfigLimitDto configLimitDto = buildConfigLimitDto(configLimit);
try {
routeConfigService.sendLimitConfigMsg(configLimitDto);
} catch (Exception e) {
log.error("推送限流消息错误, param:{}", param, e);
throw new BizException("新增失败,请查看日志");
}
}
@Api(name = "config.limit.update")
@ApiDocMethod(description = "修改限流(新)")
@Transactional(rollbackFor = Exception.class)
public void updateLimtit(LimitNewUpdateParam param) {
ConfigLimit configLimit = configLimitMapper.getById(param.getId());
if (configLimit == null) {
configLimit = new ConfigLimit();
CopyUtil.copyPropertiesIgnoreNull(param, configLimit);
configLimitMapper.save(configLimit);
} else {
CopyUtil.copyPropertiesIgnoreNull(param, configLimit);
configLimitMapper.update(configLimit);
}
ConfigLimitDto configLimitDto = buildConfigLimitDto(configLimit);
try {
routeConfigService.sendLimitConfigMsg(configLimitDto);
} catch (Exception e) {
log.error("推送限流消息错误, param:{}", param, e);
throw new BizException("修改失败,请查看日志");
}
}
private ConfigLimitDto buildConfigLimitDto(ConfigLimit configLimit) {
ConfigLimitDto configLimitDto = new ConfigLimitDto();
CopyUtil.copyPropertiesIgnoreNull(configLimit, configLimitDto);
return configLimitDto;
}
}

View File

@@ -1,173 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.verify.DefaultMd5Verifier;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.service.param.LogMonitorInstanceAddParam;
import com.gitee.sop.adminserver.api.service.result.LogMonitorInstanceVO;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.QueryUtil;
import com.gitee.sop.adminserver.entity.ConfigCommon;
import com.gitee.sop.adminserver.mapper.ConfigCommonMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@ApiService
@ApiDoc("服务管理-日志监控")
@Slf4j
public class LogApi {
public static final String LOG_MONITOR_INSTANCE = "log.monitor.instance";
public static final String CODE_SUCCESS = "10000";
private static final String CODE_KEY = "code";
public static final String SOP_LIST_ERRORS_PATH = "/sop/listErrors";
public static final String SOP_CLEAR_ERRORS_PATH = "/sop/clearErrors";
@Autowired
ConfigCommonMapper configCommonMapper;
RestTemplate restTemplate = new RestTemplate();
@Value("${sop.secret}")
private String secret;
@Api(name = "monitor.log.list")
@ApiDocMethod(description = "获取监控日志")
List<LogMonitorInstanceVO> listLog() {
List<ConfigCommon> configCommonList = configCommonMapper.listByColumn("config_group", LOG_MONITOR_INSTANCE);
List<LogMonitorInstanceVO> ret = new ArrayList<>();
int id = 1;
for (ConfigCommon configCommon : configCommonList) {
int pid = id++;
String ipPort = configCommon.getConfigKey();
// 父节点
LogMonitorInstanceVO logMonitorInstanceVOParent = new LogMonitorInstanceVO();
logMonitorInstanceVOParent.setRawId(configCommon.getId());
logMonitorInstanceVOParent.setTreeId(pid);
logMonitorInstanceVOParent.setMonitorName(configCommon.getContent());
ret.add(logMonitorInstanceVOParent);
try {
String logData = this.requestLogServer(ipPort, SOP_LIST_ERRORS_PATH);
JSONObject jsonObject = JSON.parseObject(logData);
if (CODE_SUCCESS.equals(jsonObject.getString("code"))) {
int errorTotal = 0;
List<LogMonitorInstanceVO> data = JSON.parseArray(jsonObject.getString("data"), LogMonitorInstanceVO.class);
for (LogMonitorInstanceVO instanceVO : data) {
instanceVO.setTreeId(id++);
instanceVO.setParentId(pid);
errorTotal += instanceVO.getCount();
}
ret.addAll(data);
logMonitorInstanceVOParent.setCount(errorTotal);
}
} catch (Exception e) {
log.error("获取日志信息出错", e);
logMonitorInstanceVOParent.setMonitorName(logMonitorInstanceVOParent.getMonitorName() + "(请求出错)");
}
}
Collections.sort(ret, Comparator.comparing(LogMonitorInstanceVO::getCount));
return ret;
}
@Api(name = "monitor.log.clear")
@ApiDocMethod(description = "清空日志")
void clearLog(@NotNull(message = "id不能为空") Long id) {
ConfigCommon configCommon = configCommonMapper.getById(id);
if (configCommon == null) {
return;
}
try {
String ipPort = configCommon.getConfigKey();
this.requestLogServer(ipPort, SOP_CLEAR_ERRORS_PATH);
} catch (Exception e) {
throw new BizException("清除失败");
}
}
@Api(name = "monitor.instance.list")
@ApiDocMethod(description = "获取已添加的监控实例")
List<String> listServiceInstance() {
List<ConfigCommon> configCommonList = configCommonMapper.listByColumn("config_group", LOG_MONITOR_INSTANCE);
return configCommonList.stream()
.map(ConfigCommon::getConfigKey)
.collect(Collectors.toList());
}
@Api(name = "monitor.instance.add")
@ApiDocMethod(description = "添加监控实例")
void addServiceInstance(LogMonitorInstanceAddParam param) {
String ipPort = param.getIpPort();
this.checkInstance(ipPort);
Query query = new Query();
query.eq("config_group", LOG_MONITOR_INSTANCE)
.eq("config_key", ipPort);
ConfigCommon rec = configCommonMapper.getByQuery(query);
if (rec != null) {
throw new BizException("该实例已添加");
}
ConfigCommon configCommon = new ConfigCommon();
configCommon.setConfigGroup(LOG_MONITOR_INSTANCE);
configCommon.setConfigKey(ipPort);
configCommon.setContent(param.getServiceId() + "(" + ipPort + ")");
configCommonMapper.saveIgnoreNull(configCommon);
}
private void checkInstance(String ipPort) {
try {
String json = this.requestLogServer(ipPort, SOP_LIST_ERRORS_PATH);
JSONObject jsonObject = JSON.parseObject(json);
if (!CODE_SUCCESS.equals(jsonObject.getString(CODE_KEY))) {
log.error("请求结果:{}", json);
throw new BizException("添加失败");
}
} catch (Exception e) {
log.error("添加失败", e);
throw new BizException("添加失败");
}
}
private String requestLogServer(String ipPort, String path) throws Exception {
DefaultMd5Verifier md5Verifier = new DefaultMd5Verifier();
Map<String, Object> params = new HashMap<>(16);
params.put("time", System.currentTimeMillis());
String sign = md5Verifier.buildSign(params, secret);
params.put("sign", sign);
String query = QueryUtil.buildQueryString(params);
path = path.startsWith("/") ? path.substring(1) : path;
String url = "http://" + ipPort + "/" + path + "?" + query;
ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
if (entity.getStatusCode() != HttpStatus.OK) {
throw new IllegalAccessException("无权访问");
}
return entity.getBody();
}
@Api(name = "monitor.instance.del")
@ApiDocMethod(description = "删除监控实例")
void delServiceInstance(@NotNull(message = "id不能为空") Long id) {
configCommonMapper.deleteById(id);
}
}

View File

@@ -1,166 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.sop.adminserver.api.service.param.RouteParam;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.api.service.param.ServiceSearchParam;
import com.gitee.sop.adminserver.api.service.result.MonitorInfoVO;
import com.gitee.sop.adminserver.api.service.result.MonitorResult;
import com.gitee.sop.adminserver.api.service.result.ServiceInstanceVO;
import com.gitee.sop.adminserver.common.QueryUtil;
import com.gitee.sop.adminserver.service.ServerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* @author 六如
* @deprecated use com.gitee.sop.adminserver.api.service.MonitorNewApi
* @see MonitorNewApi
*/
@Deprecated
//@ApiService
@ApiDoc("服务管理-监控")
@Slf4j
public class MonitorApi {
private static final String GATEWAY_MONITOR_PATH = "/sop/getMonitorData";
@Autowired
private ServerService serverService;
@Value("${sop.secret}")
private String secret;
@Api(name = "monitor.data.list")
@ApiDocMethod(description = "获取监控数据")
public MonitorResult listMonitorData(RouteParam param) {
ServiceSearchParam serviceSearchParam = new ServiceSearchParam();
serviceSearchParam.setServiceId("sop-gateway");
String searchRouteId = param.getRouteId();
List<MonitorInfoVO> monitorInfoList = new ArrayList<>();
List<ServiceInstanceVO> serviceInstanceVOS = serverService.listService(serviceSearchParam);
for (ServiceInstanceVO serviceInstanceVO : serviceInstanceVOS) {
if (StringUtils.isBlank(serviceInstanceVO.getInstanceId())) {
continue;
}
String ipPort = serviceInstanceVO.getIpPort();
try {
String data = QueryUtil.requestServer(ipPort, GATEWAY_MONITOR_PATH, secret);
JSONObject jsonObject = JSON.parseObject(data);
List<MonitorInfoVO> monitorInfoVOList = this.buildMonitorInfoVO(serviceInstanceVO, jsonObject.getJSONObject("data"));
List<MonitorInfoVO> newList = monitorInfoVOList.stream()
.filter(monitorInfoVO -> StringUtils.isBlank(searchRouteId)
|| StringUtils.containsIgnoreCase(monitorInfoVO.getRouteId(), searchRouteId))
.collect(Collectors.toList());
monitorInfoList.addAll(newList);
} catch (Exception e) {
log.error("请求服务失败, ipPort:{}, path:{}", ipPort, GATEWAY_MONITOR_PATH, e);
throw new ApiException("请求数据失败");
}
}
MonitorResult monitorResult = new MonitorResult();
List<MonitorInfoVO> monitorInfoTreeData = this.buildTreeData(monitorInfoList);
monitorResult.setMonitorInfoData(monitorInfoTreeData);
return monitorResult;
}
private List<MonitorInfoVO> buildTreeData(List<MonitorInfoVO> monitorInfoList) {
AtomicInteger id = new AtomicInteger();
List<MonitorInfoVO> treeData = new ArrayList<>(8);
monitorInfoList.stream()
.collect(Collectors.groupingBy(MonitorInfoVO::getRouteId))
.forEach((routeId, items) -> {
MonitorInfoVO monitorInfoVOTotal = getStatistics(items, id);
monitorInfoVOTotal.setId(id.incrementAndGet());
treeData.add(monitorInfoVOTotal);
});
Comparator<MonitorInfoVO> comparator = Comparator
// 根据错误次数降序
.comparing(MonitorInfoVO::getErrorCount).reversed()
// 然后根据请求耗时降序
.thenComparing(Comparator.comparing(MonitorInfoVO::getAvgTime).reversed());
treeData.sort(comparator);
return treeData;
}
private MonitorInfoVO getStatistics(List<MonitorInfoVO> children, AtomicInteger id) {
long totalRequestDataSize = 0;
long totalResponseDataSize = 0;
long maxTime = 0;
long minTime = 0;
long totalTime = 0;
long totalCount = 0;
long successCount = 0;
long errorCount = 0;
List<String> errorMsgList = new ArrayList<>();
String name = null,version = null, serviceId = null;
for (MonitorInfoVO child : children) {
name = child.getName();
version = child.getVersion();
serviceId = child.getServiceId();
child.setId(id.incrementAndGet());
totalRequestDataSize += child.getTotalRequestDataSize();
totalResponseDataSize += child.getTotalResponseDataSize();
if (minTime == 0 || child.getMinTime() < minTime) {
minTime = child.getMinTime();
}
if (child.getMaxTime() > maxTime) {
maxTime = child.getMaxTime();
}
totalTime += child.getTotalTime();
totalCount += child.getTotalCount();
successCount += child.getSuccessCount();
errorCount += child.getErrorCount();
errorMsgList.addAll(child.getErrorMsgList());
}
MonitorInfoVO total = new MonitorInfoVO();
total.setName(name);
total.setVersion(version);
total.setServiceId(serviceId);
total.setErrorCount(errorCount);
total.setMaxTime(maxTime);
total.setMinTime(minTime);
total.setSuccessCount(successCount);
total.setTotalCount(totalCount);
total.setTotalRequestDataSize(totalRequestDataSize);
total.setTotalResponseDataSize(totalResponseDataSize);
total.setTotalTime(totalTime);
total.setChildren(children);
total.setErrorMsgList(errorMsgList);
return total;
}
private List<MonitorInfoVO> buildMonitorInfoVO(ServiceInstanceVO serviceInstanceVO, JSONObject monitorData) {
Set<String> routeIdList = monitorData.keySet();
List<MonitorInfoVO> ret = new ArrayList<>(routeIdList.size());
routeIdList.forEach(routeId -> {
JSONObject monitorInfo = monitorData.getJSONObject(routeId);
MonitorInfoVO monitorInfoVO = monitorInfo.toJavaObject(MonitorInfoVO.class);
monitorInfoVO.setInstanceId(serviceInstanceVO.getIpPort());
ret.add(monitorInfoVO);
});
return ret;
}
}

View File

@@ -1,80 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.service.param.InstanceMonitorSearchParam;
import com.gitee.sop.adminserver.api.service.param.MonitorErrorMsgParam;
import com.gitee.sop.adminserver.api.service.param.MonitorInfoErrorSolveParam;
import com.gitee.sop.adminserver.api.service.param.MonitorSearchParam;
import com.gitee.sop.adminserver.bean.RouteErrorCount;
import com.gitee.sop.adminserver.entity.MonitorInfoError;
import com.gitee.sop.adminserver.entity.MonitorSummary;
import com.gitee.sop.adminserver.mapper.MonitorInfoErrorMapper;
import com.gitee.sop.adminserver.mapper.MonitorInfoMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 路由监控
* @author 六如
*/
@ApiService
public class MonitorNewApi {
@Autowired
private MonitorInfoMapper monitorInfoMapper;
@Autowired
private MonitorInfoErrorMapper monitorInfoErrorMapper;
@Api(name = "monitornew.data.page")
PageInfo<Object> listMonitor(MonitorSearchParam param) {
Query query = Query.build(param);
query.orderby("errorCount", Sort.DESC)
.orderby("avgTime", Sort.DESC);
return PageHelper.offsetPage(query.getStart(), query.getLimit())
.doSelectPage(() -> monitorInfoMapper.listMonitorSummary(query))
.toPageInfo();
}
@Api(name = "monitornew.routeid.data.get")
List<MonitorSummary> listInstanceMonitor(InstanceMonitorSearchParam param) {
Query query = Query.build(param);
query.orderby("errorCount", Sort.DESC)
.orderby("avgTime", Sort.DESC);
return monitorInfoMapper.listInstanceMonitorInfo(query);
}
private Map<String, Integer> getRouteErrorCount() {
List<RouteErrorCount> routeErrorCounts = monitorInfoErrorMapper.listRouteErrorCount();
return routeErrorCounts.stream()
.collect(Collectors.toMap(RouteErrorCount::getRouteId, RouteErrorCount::getCount));
}
@Api(name = "monitornew.error.page")
PageEasyui<MonitorInfoError> listError(MonitorErrorMsgParam param) {
Query query = param.toQuery()
.orderby("gmt_modified", Sort.DESC);
return MapperUtil.queryForEasyuiDatagrid(monitorInfoErrorMapper, query);
}
@Api(name = "monitornew.error.solve")
void solve(MonitorInfoErrorSolveParam param) {
Query query = Query.build(param);
Map<String, Object> set = new HashMap<>(4);
set.put("is_deleted", 1);
set.put("count", 0);
monitorInfoErrorMapper.updateByMap(set, query);
}
}

View File

@@ -1,54 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.sop.adminserver.api.system.param.IspPageParam;
import com.gitee.sop.adminserver.api.system.param.IspResourceParam;
import com.gitee.sop.adminserver.common.CopyUtil;
import com.gitee.sop.adminserver.entity.IspResource;
import com.gitee.sop.adminserver.mapper.IspResourceMapper;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author 六如
*/
@ApiService
public class ResourceApi {
public static final byte RESOURCE_TYPE_SDK = (byte) 0;
@Autowired
private IspResourceMapper ispResourceMapper;
@Api(name = "isp.sdk.list")
List<IspResource> list(IspPageParam param) {
return ispResourceMapper.list(param.toQuery());
}
@Api(name = "isp.sdk.add")
void addSdk(IspResourceParam param) {
IspResource ispResource = CopyUtil.copyBean(param, IspResource::new);
ispResource.setType(RESOURCE_TYPE_SDK);
ispResourceMapper.saveIgnoreNull(ispResource);
}
@Api(name = "isp.sdk.update")
void updateSdk(IspResourceParam param) {
IspResource resource = ispResourceMapper.getById(param.getId());
CopyUtil.copyProperties(param, resource);
ispResourceMapper.update(resource);
}
@Api(name = "isp.sdk.delete")
void deleteSdk(@NotNull Long id) {
IspResource resource = ispResourceMapper.getById(id);
if (resource != null) {
ispResourceMapper.deleteById(id);
}
}
}

View File

@@ -1,259 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.api.service.param.RouteAddParam;
import com.gitee.sop.adminserver.api.service.param.RouteDeleteParam;
import com.gitee.sop.adminserver.api.service.param.RoutePermissionParam;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.api.service.param.RouteStatusUpdateParam;
import com.gitee.sop.adminserver.api.service.param.RouteUpdateParam;
import com.gitee.sop.adminserver.api.service.result.RouteVO;
import com.gitee.sop.adminserver.bean.RouteConfigDto;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.ConfigRouteBase;
import com.gitee.sop.adminserver.entity.ConfigServiceRoute;
import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.entity.PermRolePermission;
import com.gitee.sop.adminserver.entity.RouteRoleDTO;
import com.gitee.sop.adminserver.mapper.ConfigRouteBaseMapper;
import com.gitee.sop.adminserver.mapper.ConfigServiceRouteMapper;
import com.gitee.sop.adminserver.mapper.PermRoleMapper;
import com.gitee.sop.adminserver.mapper.PermRolePermissionMapper;
import com.gitee.sop.adminserver.service.RouteConfigService;
import com.gitee.sop.adminserver.service.RoutePermissionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@ApiService
@ApiDoc("服务管理-路由管理")
@Slf4j
public class RouteApi {
@Autowired
PermRolePermissionMapper permRolePermissionMapper;
@Autowired
PermRoleMapper permRoleMapper;
@Autowired
ConfigRouteBaseMapper configRouteBaseMapper;
@Autowired
private ConfigServiceRouteMapper configServiceRouteMapper;
@Autowired
RoutePermissionService routePermissionService;
@Autowired
RouteConfigService routeConfigService;
@ApiDocMethod(description = "路由列表,分页")
@Api(name = "route.page")
PageEasyui<RouteVO> page(RouteSearchParam form) {
Query query = Query.build(form);
query.orderby("id", Sort.ASC);
PageEasyui<RouteVO> datagrid = MapperUtil.queryForEasyuiDatagrid(configServiceRouteMapper, query, RouteVO.class);
List<String> routeIdList = datagrid.getRows()
.parallelStream()
.map(RouteVO::getId)
.collect(Collectors.toList());
if (routeIdList.isEmpty()) {
return datagrid;
}
Map<String, Byte> routeIdStatusMap = configRouteBaseMapper
.list(new Query().in("route_id", routeIdList))
.stream()
.collect(Collectors.toMap(ConfigRouteBase::getRouteId, ConfigRouteBase::getStatus));
Map<String, List<RouteRoleDTO>> routeIdRoleMap = permRolePermissionMapper.listRouteRole(routeIdList)
.parallelStream()
.collect(Collectors.groupingBy(RouteRoleDTO::getRouteId));
datagrid.getRows().forEach(vo -> {
String routeId = vo.getId();
List<RouteRoleDTO> routeRoleDTOS = routeIdRoleMap.getOrDefault(routeId, Collections.emptyList());
vo.setRoles(routeRoleDTOS);
Byte status = routeIdStatusMap.getOrDefault(routeId, vo.getStatus());
vo.setStatus(status);
});
return datagrid;
}
@Api(name = "route.list", version = "1.2")
@ApiDocMethod(description = "路由列表1.2")
List<ConfigServiceRoute> listRoute2(RouteSearchParam param) {
String serviceId = param.getServiceId();
if (StringUtils.isBlank(serviceId)) {
return Collections.emptyList();
}
Query query = Query.build(param);
return configServiceRouteMapper.list(query);
}
@Api(name = "route.add")
@ApiDocMethod(description = "新增路由")
void addRoute(RouteAddParam param) {
// TODO: 新增路由
}
@Api(name = "route.update")
@ApiDocMethod(description = "修改路由")
void updateRoute(RouteUpdateParam param) {
this.updateRouteConfig(param);
}
@Api(name = "route.status.update")
@ApiDocMethod(description = "修改路由状态")
void updateRouteStatus(RouteStatusUpdateParam param) {
String routeId = param.getId();
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId);
boolean doSave = configRouteBase == null;
if (doSave) {
configRouteBase = new ConfigRouteBase();
configRouteBase.setRouteId(routeId);
}
configRouteBase.setStatus(param.getStatus());
int i = doSave ? configRouteBaseMapper.save(configRouteBase)
: configRouteBaseMapper.update(configRouteBase);
if (i > 0) {
this.sendMsg(configRouteBase);
}
}
@Api(name = "route.del")
@ApiDocMethod(description = "删除路由")
void delRoute(RouteDeleteParam param) {
// TODO: 删除路由
}
private void updateRouteConfig(RouteUpdateParam routeUpdateParam) {
String routeId = routeUpdateParam.getId();
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId);
boolean doSave = configRouteBase == null;
if (doSave) {
configRouteBase = new ConfigRouteBase();
configRouteBase.setRouteId(routeId);
}
configRouteBase.setStatus(routeUpdateParam.getStatus().byteValue());
int i = doSave ? configRouteBaseMapper.save(configRouteBase)
: configRouteBaseMapper.update(configRouteBase);
if (i > 0) {
this.sendMsg(configRouteBase);
}
}
private void sendMsg(ConfigRouteBase routeDefinition) {
RouteConfigDto routeConfigDto = new RouteConfigDto();
routeConfigDto.setRouteId(routeDefinition.getRouteId());
routeConfigDto.setStatus(routeDefinition.getStatus().intValue());
routeConfigService.sendRouteConfigMsg(routeConfigDto);
}
@Api(name = "route.role.get")
@ApiDocMethod(description = "获取路由对应的角色", elementClass = RoleVO.class)
List<RoleVO> getRouteRole(RouteSearchParam param) {
if (StringUtils.isBlank(param.getId())) {
throw new BizException("id不能为空");
}
return this.getRouteRole(param.getId());
}
/**
* 获取路由对应的角色
*
* @param routeIdList routeIdList
* @return
*/
private List<RoleVO> getRouteRole(List<String> routeIdList) {
// key:routeId, value: roleCode
Map<String, List<String>> routeIdRoleCodeMap = permRolePermissionMapper.list(new Query().in("route_id", routeIdList))
.stream()
.collect(Collectors.groupingBy(PermRolePermission::getRouteId,
Collectors.mapping(PermRolePermission::getRoleCode, Collectors.toList())));
return permRolePermissionMapper.list(new Query().in("route_id", routeIdList))
.stream()
.map(permRolePermission -> {
RoleVO vo = new RoleVO();
String roleCode = permRolePermission.getRoleCode();
PermRole permRole = permRoleMapper.getByColumn("role_code", roleCode);
BeanUtils.copyProperties(permRole, vo);
return vo;
})
.collect(Collectors.toList());
}
/**
* 获取路由对应的角色
*
* @param id routeId
* @return
*/
private List<RoleVO> getRouteRole(String id) {
return permRolePermissionMapper.listByColumn("route_id", id)
.stream()
.map(permRolePermission -> {
RoleVO vo = new RoleVO();
String roleCode = permRolePermission.getRoleCode();
PermRole permRole = permRoleMapper.getByColumn("role_code", roleCode);
BeanUtils.copyProperties(permRole, vo);
return vo;
})
.collect(Collectors.toList());
}
@Api(name = "route.role.update")
@ApiDocMethod(description = "更新路由对应的角色")
public void updateRouteRole(RoutePermissionParam param) {
RoutePermissionParam oldRoutePermission = this.buildOldRoutePermission(param.getRouteId());
routePermissionService.updateRoutePermission(param);
try {
routePermissionService.sendRoutePermissionReloadMsg(oldRoutePermission);
} catch (Exception e) {
log.error("消息推送--路由权限(reload)失败", e);
// 回滚
routePermissionService.updateRoutePermission(oldRoutePermission);
throw new BizException(e.getMessage());
}
}
private RoutePermissionParam buildOldRoutePermission(String routeId) {
List<RoleVO> routeRole = this.getRouteRole(routeId);
List<String> roleCodeList = routeRole.stream()
.map(RoleVO::getRoleCode)
.collect(Collectors.toList());
RoutePermissionParam routePermissionParam = new RoutePermissionParam();
routePermissionParam.setRouteId(routeId);
routePermissionParam.setRoleCode(roleCodeList);
return routePermissionParam;
}
}

View File

@@ -1,227 +0,0 @@
package com.gitee.sop.adminserver.api.service;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.sop.adminserver.api.service.param.ServiceAddParam;
import com.gitee.sop.adminserver.api.service.param.ServiceGrayConfigParam;
import com.gitee.sop.adminserver.api.service.param.ServiceIdParam;
import com.gitee.sop.adminserver.api.service.param.ServiceInstanceParam;
import com.gitee.sop.adminserver.api.service.param.ServiceSearchParam;
import com.gitee.sop.adminserver.api.service.result.ServiceInfoVo;
import com.gitee.sop.adminserver.api.service.result.ServiceInstanceVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.MetadataEnum;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.bean.ServiceGrayDefinition;
import com.gitee.sop.adminserver.bean.ServiceInstance;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.common.StatusEnum;
import com.gitee.sop.adminserver.entity.ConfigGray;
import com.gitee.sop.adminserver.entity.ConfigGrayInstance;
import com.gitee.sop.adminserver.mapper.ConfigGrayInstanceMapper;
import com.gitee.sop.adminserver.mapper.ConfigGrayMapper;
import com.gitee.sop.adminserver.mapper.ConfigServiceRouteMapper;
import com.gitee.sop.adminserver.service.ConfigPushService;
import com.gitee.sop.adminserver.service.RegistryService;
import com.gitee.sop.adminserver.service.ServerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@ApiService
@ApiDoc("服务管理-服务列表")
@Slf4j
public class ServiceApi {
@Autowired
private RegistryService registryService;
@Autowired
private ServerService serverService;
@Autowired
private ConfigGrayMapper configGrayMapper;
@Autowired
private ConfigGrayInstanceMapper configGrayInstanceMapper;
@Autowired
private ConfigServiceRouteMapper configServiceRouteMapper;
@Autowired
private ConfigPushService configPushService;
@Api(name = "registry.service.list")
@ApiDocMethod(description = "路由配置中的服务列表", elementClass = String.class)
List<String> listServiceInfo(ServiceSearchParam param) {
List<String> allServiceId = configServiceRouteMapper.listAllServiceId();
return allServiceId
.stream()
.filter(serviceId -> {
if (StringUtils.isBlank(param.getServiceId())) {
return true;
} else {
return serviceId.contains(param.getServiceId());
}
})
.sorted()
.collect(Collectors.toList());
}
@Api(name = "service.custom.add")
@ApiDocMethod(description = "添加服务")
void addService(ServiceAddParam param) {
throw new BizException("该功能已下线");
}
@Api(name = "service.custom.del")
@ApiDocMethod(description = "删除自定义服务")
void delService(ServiceSearchParam param) {
throw new BizException("该功能已下线");
}
@Api(name = "service.instance.list")
@ApiDocMethod(description = "获取注册中心的服务列表", elementClass = ServiceInfoVo.class)
List<ServiceInstanceVO> listService(ServiceSearchParam param) {
return serverService.listService(param);
}
@Api(name = "service.instance.offline")
@ApiDocMethod(description = "服务禁用")
void serviceOffline(ServiceInstanceParam param) {
try {
registryService.offlineInstance(param.buildServiceInstance());
} catch (Exception e) {
log.error("服务禁用失败param:{}", param, e);
throw new BizException("服务禁用失败,请查看日志");
}
}
@Api(name = "service.instance.online")
@ApiDocMethod(description = "服务启用")
void serviceOnline(ServiceInstanceParam param) throws IOException {
try {
registryService.onlineInstance(param.buildServiceInstance());
} catch (Exception e) {
log.error("服务启用失败param:{}", param, e);
throw new BizException("服务启用失败,请查看日志");
}
}
@Api(name = "service.instance.env.pre.open")
@ApiDocMethod(description = "预发布")
void serviceEnvPre(ServiceInstanceParam param) throws IOException {
try {
MetadataEnum envPre = MetadataEnum.ENV_PRE;
registryService.setMetadata(param.buildServiceInstance(), envPre.getKey(), envPre.getValue());
} catch (Exception e) {
log.error("预发布失败param:{}", param, e);
throw new BizException("预发布失败,请查看日志");
}
}
@Api(name = "service.gray.config.get")
@ApiDocMethod(description = "灰度配置--获取")
ConfigGray serviceEnvGrayConfigGet(ServiceIdParam param) throws IOException {
return this.getConfigGray(param.getServiceId());
}
@Api(name = "service.gray.config.save")
@ApiDocMethod(description = "灰度配置--保存")
void serviceEnvGrayConfigSave(ServiceGrayConfigParam param) throws IOException {
String serviceId = param.getServiceId().toLowerCase();
ConfigGray configGray = configGrayMapper.getByColumn("service_id", serviceId);
if (configGray == null) {
configGray = new ConfigGray();
configGray.setServiceId(serviceId);
configGray.setNameVersionContent(param.getNameVersionContent());
configGray.setUserKeyContent(param.getUserKeyContent());
configGrayMapper.save(configGray);
} else {
configGray.setNameVersionContent(param.getNameVersionContent());
configGray.setUserKeyContent(param.getUserKeyContent());
configGrayMapper.update(configGray);
}
this.sendServiceGrayMsg(serviceId, ChannelOperation.GRAY_USER_KEY_SET);
}
@Api(name = "service.instance.env.gray.open")
@ApiDocMethod(description = "开启灰度发布")
void serviceEnvGray(ServiceInstanceParam param) throws IOException {
String serviceId = param.getServiceId().toLowerCase();
ConfigGray configGray = this.getConfigGray(serviceId);
if (configGray == null) {
throw new BizException("请先设置灰度参数");
}
try {
MetadataEnum envPre = MetadataEnum.ENV_GRAY;
registryService.setMetadata(param.buildServiceInstance(), envPre.getKey(), envPre.getValue());
String instanceId = param.getInstanceId();
ConfigGrayInstance configGrayInstance = configGrayInstanceMapper.getByColumn("instance_id", instanceId);
if (configGrayInstance == null) {
configGrayInstance = new ConfigGrayInstance();
configGrayInstance.setServiceId(serviceId);
configGrayInstance.setInstanceId(instanceId);
configGrayInstance.setStatus(StatusEnum.STATUS_ENABLE.getStatus());
configGrayInstanceMapper.save(configGrayInstance);
} else {
configGrayInstance.setStatus(StatusEnum.STATUS_ENABLE.getStatus());
configGrayInstance.setServiceId(serviceId);
configGrayInstanceMapper.update(configGrayInstance);
}
this.sendServiceGrayMsg(instanceId, serviceId, ChannelOperation.GRAY_USER_KEY_OPEN);
} catch (Exception e) {
log.error("灰度发布失败param:{}", param, e);
throw new BizException("灰度发布失败,请查看日志");
}
}
@Api(name = "service.instance.env.online")
@ApiDocMethod(description = "上线")
void serviceEnvOnline(ServiceInstance param) throws IOException {
try {
MetadataEnum envPre = MetadataEnum.ENV_ONLINE;
registryService.setMetadata(param, envPre.getKey(), envPre.getValue());
ConfigGrayInstance configGrayInstance = configGrayInstanceMapper.getByColumn("instance_id", param.getInstanceId());
if (configGrayInstance != null) {
configGrayInstance.setStatus(StatusEnum.STATUS_DISABLE.getStatus());
configGrayInstanceMapper.update(configGrayInstance);
this.sendServiceGrayMsg(param.getInstanceId(), param.getServiceId().toLowerCase(), ChannelOperation.GRAY_USER_KEY_CLOSE);
}
} catch (Exception e) {
log.error("上线失败param:{}", param, e);
throw new BizException("上线失败,请查看日志");
}
}
private void sendServiceGrayMsg(String serviceId, ChannelOperation channelOperation) {
this.sendServiceGrayMsg(null, serviceId, channelOperation);
}
private void sendServiceGrayMsg(String instanceId, String serviceId, ChannelOperation channelOperation) {
ServiceGrayDefinition serviceGrayDefinition = new ServiceGrayDefinition();
serviceGrayDefinition.setInstanceId(instanceId);
serviceGrayDefinition.setServiceId(serviceId);
ChannelMsg channelMsg = new ChannelMsg(channelOperation, serviceGrayDefinition);
configPushService.publishConfig(NacosConfigs.DATA_ID_GRAY, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
private ConfigGray getConfigGray(String serviceId) {
return configGrayMapper.getByColumn("service_id", serviceId);
}
}

View File

@@ -1,28 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* 表名config_ip_black
* 备注IP黑名单
*
* @author 六如
*/
@Data
public class ConfigIpBlackForm {
/** 数据库字段id */
private Long id;
/** ip, 数据库字段ip */
@NotBlank(message = "不能为空")
@Length(max = 30, message = "ip长度太长")
private String ip;
/** 备注, 数据库字段remark */
@Length(max = 100, message = "备注长度太长")
private String remark;
}

View File

@@ -1,19 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
public class ConfigIpBlacklistPageParam extends PageParam {
@ApiDocField(description = "IP")
@Condition(operator = Operator.like)
private String ip;
}

View File

@@ -1,15 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
public class InstanceMonitorSearchParam {
@Condition(column = "t.route_id")
private String routeId;
}

View File

@@ -1,75 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 限流
* @author 六如
*/
@Data
public class LimitNewAddParam {
/** 路由id, 数据库字段route_id */
@ApiDocField(description = "routeId")
private String routeId;
/** 数据库字段app_key */
@ApiDocField(description = "appKey")
private String appKey;
/** 限流ip多个用英文逗号隔开, 数据库字段limit_ip */
@ApiDocField(description = "limitIp")
private String limitIp;
@ApiDocField(description = "serviceId")
@NotBlank(message = "serviceId can not null")
private String serviceId;
/** 限流策略1窗口策略2令牌桶策略, 数据库字段limit_type */
@ApiDocField(description = "限流策略1窗口策略2令牌桶策略")
@NotNull(message = "limitType不能为空")
private Byte limitType;
/** 每秒可处理请求数, 数据库字段exec_count_per_second */
@ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond;
/** 限流持续时间默认1秒即每durationSeconds秒允许多少请求当limit_type=1时有效 */
@ApiDocField(description = "限流持续时间默认1秒即每durationSeconds秒允许多少请求当limit_type=1时有效")
@NotNull(message = "durationSeconds不能为空")
@Min(value = 1, message = "durationSeconds最小是1")
private Integer durationSeconds;
/** 返回的错误码, 数据库字段limit_code */
@ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64")
private String limitCode;
/** 返回的错误信息, 数据库字段limit_msg */
@ApiDocField(description = "返回的错误信息")
@Length(max = 100, message = "limitMsg长度不能超过100")
private String limitMsg;
/** 令牌桶容量, 数据库字段token_bucket_count */
@ApiDocField(description = "令牌桶容量")
private Integer tokenBucketCount;
/** 1:开启0关闭, 数据库字段limit_status */
@ApiDocField(description = "1:开启0关闭")
@NotNull(message = "limitStatus不能为空")
private Byte limitStatus;
@ApiDocField(description = "排序字段")
@NotNull(message = "orderIndex不能为空")
@Min(value = 0, message = "orderIndex必须大于等于0")
private Integer orderIndex;
@ApiDocField(description = "备注")
@Length(max = 128, message = "备注不能超过128")
private String remark;
}

View File

@@ -1,32 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author 六如
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class LimitNewParam extends PageParam {
@ApiDocField(description = "服务名serviceId")
private String serviceId;
@ApiDocField(description = "路由id")
@Condition(operator = Operator.like)
private String routeId;
/** 数据库字段app_key */
@ApiDocField(description = "appKey")
@Condition(operator = Operator.like)
private String appKey;
/** 限流ip多个用英文逗号隔开, 数据库字段limit_ip */
@ApiDocField(description = "限流ip多个用英文逗号隔开")
@Condition(operator = Operator.like)
private String limitIp;
}

View File

@@ -1,80 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 限流
* @author 六如
*/
@Data
public class LimitNewUpdateParam {
@ApiDocField(description = "id")
@NotNull(message = "id can not null")
@Min(value = 1, message = "id不正确")
private Long id;
/** 路由id, 数据库字段route_id */
@ApiDocField(description = "routeId")
private String routeId;
/** 数据库字段app_key */
@ApiDocField(description = "appKey")
private String appKey;
/** 限流ip多个用英文逗号隔开, 数据库字段limit_ip */
@ApiDocField(description = "limitIp")
private String limitIp;
@ApiDocField(description = "serviceId")
@NotBlank(message = "serviceId can not null")
private String serviceId;
/** 限流策略1窗口策略2令牌桶策略, 数据库字段limit_type */
@ApiDocField(description = "限流策略1窗口策略2令牌桶策略")
@NotNull(message = "limitType不能为空")
private Byte limitType;
/** 每秒可处理请求数, 数据库字段exec_count_per_second */
@ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond;
/** 限流持续时间默认1秒即每durationSeconds秒允许多少请求当limit_type=1时有效 */
@ApiDocField(description = "限流持续时间默认1秒即每durationSeconds秒允许多少请求当limit_type=1时有效")
@NotNull(message = "durationSeconds不能为空")
@Min(value = 1, message = "durationSeconds最小是1")
private Integer durationSeconds;
/** 返回的错误码, 数据库字段limit_code */
@ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64")
private String limitCode;
/** 返回的错误信息, 数据库字段limit_msg */
@ApiDocField(description = "返回的错误信息")
@Length(max = 100, message = "limitMsg长度不能超过100")
private String limitMsg;
/** 令牌桶容量, 数据库字段token_bucket_count */
@ApiDocField(description = "令牌桶容量")
private Integer tokenBucketCount;
/** 1:开启0关闭, 数据库字段limit_status */
@ApiDocField(description = "1:开启0关闭")
@NotNull(message = "limitStatus不能为空")
private Byte limitStatus;
@ApiDocField(description = "排序字段")
@NotNull(message = "orderIndex不能为空")
@Min(value = 0, message = "orderIndex必须大于等于0")
private Integer orderIndex;
@ApiDocField(description = "备注")
@Length(max = 128, message = "备注不能超过128")
private String remark;
}

View File

@@ -1,48 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 限流
* @author 六如
*/
@Data
public class LimitParam {
@ApiDocField(description = "routeId")
@NotBlank(message = "routeId can not null")
private String routeId;
@ApiDocField(description = "serviceId")
@NotBlank(message = "serviceId can not null")
private String serviceId;
/** 限流策略1窗口策略2令牌桶策略, 数据库字段limit_type */
@ApiDocField(description = "限流策略1窗口策略2令牌桶策略")
@NotNull
private Byte limitType;
/** 每秒可处理请求数, 数据库字段exec_count_per_second */
@ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond;
/** 返回的错误码, 数据库字段limit_code */
@ApiDocField(description = "返回的错误码")
private String limitCode;
/** 返回的错误信息, 数据库字段limit_msg */
@ApiDocField(description = "返回的错误信息")
private String limitMsg;
/** 令牌桶容量, 数据库字段token_bucket_count */
@ApiDocField(description = "令牌桶容量")
private Integer tokenBucketCount;
/** 1:开启0关闭, 数据库字段limit_status */
@ApiDocField(description = "1:开启0关闭")
@NotNull(message = "limitStatus不能为空")
private Byte limitStatus;
}

View File

@@ -1,17 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class LogMonitorInstanceAddParam {
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@NotBlank(message = "ipPort不能为空")
private String ipPort;
}

View File

@@ -1,21 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Getter
@Setter
public class MonitorErrorMsgParam extends PageParam {
@NotBlank(message = "routeId不能为空")
private String routeId;
@Condition(ignoreEmptyString = true)
private String instanceId;
}

View File

@@ -1,19 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class MonitorInfoErrorSolveParam {
/** 错误id,md5(error_msg), 数据库字段error_id */
@NotBlank
@Condition(index = 1)
private String errorId;
}

View File

@@ -1,23 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
public class MonitorSearchParam extends PageParam {
@ApiDocField(description = "服务名serviceId")
@Condition(column = "service_id", operator = Operator.like, ignoreEmptyString = true)
private String serviceId;
@ApiDocField(description = "路由id")
@Condition(column = "route_id", operator = Operator.like, ignoreEmptyString = true)
private String routeId;
}

View File

@@ -1,76 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Getter
@Setter
public class RouteAddParam {
@NotBlank(message = "serviceId不能为空")
@Length(max = 100, message = "serviceId长度不能超过100")
@ApiDocField(description = "serviceId")
private String serviceId;
/**
* 接口名
*/
@NotBlank(message = "接口名不能为空")
@Length(max = 100, message = "name长度不能超过100")
@ApiDocField(description = "接口名")
private String name;
/**
* 版本号
*/
@NotBlank(message = "版本号不能为空")
@Length(max = 100, message = "version长度不能超过100")
@ApiDocField(description = "版本号")
private String version;
/**
* 路由规则转发的目标uri
*/
@NotBlank(message = "uri不能为空")
@Length(max = 100, message = "uri长度不能超过100")
@ApiDocField(description = "路由uri")
private String uri;
/**
* uri后面跟的path
*/
@ApiDocField(description = "路由path")
@Length(max = 100, message = "path长度不能超过100")
private String path;
/**
* 是否忽略验证,业务参数验证除外
*/
@ApiDocField(description = "是否忽略验证业务参数验证除外1忽略0不忽略")
private Integer ignoreValidate;
/**
* 合并结果,统一格式输出
*/
@ApiDocField(description = "合并结果统一格式输出1合并2不合并")
private Integer mergeResult;
/**
* 状态
*/
@NotNull
@ApiDocField(description = "状态0审核1启用2禁用")
private Integer status;
@ApiDocField(description = "是否是自定义路由10")
private Integer custom;
}

View File

@@ -1,22 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Getter
@Setter
public class RouteDeleteParam {
@ApiDocField(description = "服务名serviceId")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@ApiDocField(description = "路由id")
@NotBlank(message = "id不能为空")
private String id;
}

View File

@@ -1,11 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class RouteParam {
private String routeId;
}

View File

@@ -1,22 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import java.util.List;
/**
* @author 六如
*/
@Getter
@Setter
public class RoutePermissionParam {
@ApiDocField(description = "routeId", required = true)
@NotBlank(message = "routeId不能为空")
private String routeId;
@ApiDocField(description = "角色")
private List<String> roleCode;
}

View File

@@ -1,33 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Getter
@Setter
public class RouteSearchParam extends PageParam {
@ApiDocField(description = "服务名serviceId")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@ApiDocField(description = "路由id")
@Condition(column = "id", operator = Operator.like)
private String id;
@ApiDocField(description = "是否授权接口1")
@Condition(ignoreValue = "0")
private Integer permission;
@ApiDocField(description = "是否需要token接口1")
@Condition(ignoreValue = "0")
private Integer needToken;
}

View File

@@ -1,32 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Getter
@Setter
public class RouteStatusUpdateParam {
/**
* 路由的Id
*/
@NotBlank(message = "id不能为空")
@ApiDocField(description = "路由id")
private String id;
/**
* 状态
*/
@NotNull
@ApiDocField(description = "状态0审核1启用2禁用")
private Byte status;
}

View File

@@ -1,67 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
@Getter
@Setter
public class RouteUpdateParam {
/**
* 路由的Id
*/
@NotBlank(message = "id不能为空")
@ApiDocField(description = "路由id")
private String id;
@NotBlank(message = "serviceId不能为空")
@Length(max = 100, message = "serviceId长度不能超过100")
@ApiDocField(description = "serviceId")
private String serviceId;
/**
* 路由规则转发的目标uri
*/
@NotBlank(message = "uri不能为空")
@Length(max = 100, message = "uri长度不能超过100")
@ApiDocField(description = "路由uri")
private String uri;
/**
* uri后面跟的path
*/
@ApiDocField(description = "路由path")
@Length(max = 100, message = "path长度不能超过100")
private String path;
/**
* 是否忽略验证,业务参数验证除外
*/
@ApiDocField(description = "是否忽略验证业务参数验证除外1忽略0不忽略")
private Integer ignoreValidate;
/**
* 合并结果,统一格式输出
*/
@ApiDocField(description = "合并结果统一格式输出1合并2不合并")
private Integer mergeResult;
/**
* 状态
*/
@NotNull
@ApiDocField(description = "状态0审核1启用2禁用")
private Integer status;
@ApiDocField(description = "是否是自定义路由10")
private Integer custom;
}

View File

@@ -1,21 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Getter
@Setter
public class ServiceAddParam {
@ApiDocField(description = "服务名serviceId")
@Length(max = 100, message = "长度不能超过100")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
}

View File

@@ -1,25 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class ServiceGrayConfigParam {
@ApiDocField(description = "服务名serviceId")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@ApiDocField(description = "灰度发布用户,多个用英文逗号隔开")
@NotBlank(message = "灰度发布用户不能为空")
private String userKeyContent;
@ApiDocField(description = "灰度发布接口名版本号如order.get1.0=1.2,多个用英文逗号隔开")
@NotBlank(message = "灰度发布接口名版本号不能为空")
private String nameVersionContent;
}

View File

@@ -1,16 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class ServiceIdParam {
@ApiDocField(description = "serviceId")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
}

View File

@@ -1,26 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ServiceInstanceGrayParam extends ServiceInstanceParam {
@ApiDocField(description = "灰度发布用户,多个用英文逗号隔开")
@NotBlank(message = "灰度发布用户不能为空")
private String userKeyContent;
@ApiDocField(description = "灰度发布接口名版本号如order.get1.0=1.2,多个用英文逗号隔开")
@NotBlank(message = "灰度发布接口名版本号不能为空")
private String nameVersionContent;
@ApiDocField(description = "是否仅更新灰度用户")
private Boolean onlyUpdateGrayUserkey;
}

View File

@@ -1,47 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.sop.adminserver.bean.ServiceInstance;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author 六如
*/
@Data
public class ServiceInstanceParam {
@ApiDocField(description = "serviceId")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@ApiDocField(description = "instanceId")
@NotBlank(message = "instanceId不能为空")
private String instanceId;
/**
* ip
*/
@ApiDocField(description = "ip")
private String ip;
/**
* port
*/
@ApiDocField(description = "port")
private int port;
/**
* 服务状态UP已上线OUT_OF_SERVICE已下线
*/
@ApiDocField(description = "status")
private String status;
public ServiceInstance buildServiceInstance() {
ServiceInstance serviceInstance = new ServiceInstance();
CopyUtil.copyPropertiesIgnoreNull(this, serviceInstance);
return serviceInstance;
}
}

View File

@@ -1,19 +0,0 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
/**
* @author 六如
*/
@Getter
@Setter
public class ServiceSearchParam {
@ApiDocField(description = "服务名serviceId")
private String serviceId;
@ApiDocField(description = "instanceId")
private String instanceId;
}

View File

@@ -1,30 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
import java.util.Date;
/**
* 表名config_ip_black
* 备注IP黑名单
*
* @author 六如
*/
@Data
public class ConfigIpBlacklistVO {
/** 数据库字段id */
private Long id;
/** ip, 数据库字段ip */
private String ip;
/** 备注, 数据库字段remark */
private String remark;
/** 数据库字段gmt_create */
private Date gmtCreate;
/** 数据库字段gmt_modified */
private Date gmtModified;
}

View File

@@ -1,82 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* 限流
*
* @author 六如
*/
@Data
public class LimitNewVO {
/** 路由id, 数据库字段route_id */
@ApiDocField(description = "路由id")
private String routeId;
/** 数据库字段app_key */
@ApiDocField(description = "appKey")
private String appKey;
/** 限流ip多个用英文逗号隔开, 数据库字段limit_ip */
@ApiDocField(description = "限流ip多个用英文逗号隔开")
private String limitIp;
@ApiDocField(description = "限流key")
private String limitKey;
/**
* 接口名
*/
@ApiDocField(description = "接口名")
private String name;
/**
* 版本号
*/
@ApiDocField(description = "版本号")
private String version;
@ApiDocField(description = "serviceId")
private String serviceId;
/**
* 限流策略1窗口策略2令牌桶策略, 数据库字段limit_type
*/
@ApiDocField(description = "限流策略1窗口策略2令牌桶策略")
private Byte limitType;
/**
* 每秒可处理请求数, 数据库字段exec_count_per_second
*/
@ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond;
/**
* 返回的错误码, 数据库字段limit_code
*/
@ApiDocField(description = "返回的错误码")
private String limitCode;
/**
* 返回的错误信息, 数据库字段limit_msg
*/
@ApiDocField(description = "返回的错误信息")
private String limitMsg;
/**
* 令牌桶容量, 数据库字段token_bucket_count
*/
@ApiDocField(description = "令牌桶容量")
private Integer tokenBucketCount;
/**
* 1:开启0关闭, 数据库字段limit_status
*/
@ApiDocField(description = "1:开启0关闭")
private Byte limitStatus;
/** 顺序,值小的优先执行, 数据库字段order_index */
@ApiDocField(description = "排序字段")
private Integer orderIndex;
}

View File

@@ -1,69 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* 限流
*
* @author 六如
*/
@Data
public class LimitVO {
@ApiDocField(description = "是否存在记录")
private Integer hasRecord;
@ApiDocField(description = "路由id")
private String routeId;
/**
* 接口名
*/
@ApiDocField(description = "接口名")
private String name;
/**
* 版本号
*/
@ApiDocField(description = "版本号")
private String version;
@ApiDocField(description = "serviceId")
private String serviceId;
/**
* 限流策略1窗口策略2令牌桶策略, 数据库字段limit_type
*/
@ApiDocField(description = "限流策略1窗口策略2令牌桶策略")
private Byte limitType;
/**
* 每秒可处理请求数, 数据库字段exec_count_per_second
*/
@ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond;
/**
* 返回的错误码, 数据库字段limit_code
*/
@ApiDocField(description = "返回的错误码")
private String limitCode;
/**
* 返回的错误信息, 数据库字段limit_msg
*/
@ApiDocField(description = "返回的错误信息")
private String limitMsg;
/**
* 令牌桶容量, 数据库字段token_bucket_count
*/
@ApiDocField(description = "令牌桶容量")
private Integer tokenBucketCount;
/**
* 1:开启0关闭, 数据库字段limit_status
*/
@ApiDocField(description = "1:开启0关闭")
private Byte limitStatus;
}

View File

@@ -1,22 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class LogMonitorInstanceVO {
private String id;
private int treeId;
/** 表主键 */
private long rawId;
private String name;
private String version;
private String serviceId;
private String errorMsg;
private long count;
private int parentId;
private String monitorName;
}

View File

@@ -1,26 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
import java.util.Date;
/**
* @author 六如
*/
@Data
public class MonitorInfoErrorMsgResult {
private String routeId;
private String errorId;
/** 错误信息, 数据库字段error_msg */
private String errorMsg;
private Integer errorStatus;
private Integer count;
/** 数据库字段gmt_modified */
private Date gmtModified;
}

View File

@@ -1,83 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
import java.util.List;
/**
* 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。
*
* @author 六如
*/
@Data
public class MonitorInfoVO {
private Integer id;
private String instanceId;
/**
* 接口名
*/
private String name;
/**
* 版本号
*/
private String version;
/**
* serviceId
*/
private String serviceId;
/**
* 请求耗时最长时间
*/
private Long maxTime;
/**
* 请求耗时最小时间
*/
private Long minTime;
/**
* 总时长
*/
private Long totalTime;
/**
* 总调用次数
*/
private Long totalCount;
/**
* 成功次数
*/
private Long successCount;
/**
* 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)
*/
private Long errorCount;
/**
* 错误信息
*/
private List<String> errorMsgList;
/**
* 总请求数据量
*/
private Long totalRequestDataSize;
/**
* 总返回数据量
*/
private Long totalResponseDataSize;
/**
* 实例id
*/
private List<MonitorInfoVO> children;
public String getRouteId() {
return name + version;
}
/**
* 平均时长,总时长/总调用次数
* @return 返回平均时长
*/
public long getAvgTime() {
return totalTime/totalCount;
}
}

View File

@@ -1,13 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
import java.util.List;
/**
* @author 六如
*/
@Data
public class MonitorResult {
private List<MonitorInfoVO> monitorInfoData;
}

View File

@@ -1,26 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class RouteServiceInfo {
@ApiDocField(description = "serviceId")
private String serviceId;
@ApiDocField(description = "创建时间")
private String createTime;
@ApiDocField(description = "修改时间")
private String updateTime;
@ApiDocField(description = "描述")
private String description;
/** 是否是自定义服务10否 */
@ApiDocField(description = "是否是自定义服务10")
private Integer custom;
}

View File

@@ -1,59 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import com.gitee.sop.adminserver.entity.RouteRoleDTO;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* @author 六如
*/
@Getter
@Setter
public class RouteVO {
/** 数据库字段id */
private String id;
/** 数据库字段service_id */
private String serviceId;
/** 接口名, 数据库字段name */
private String name;
/** 版本号, 数据库字段version */
private String version;
/** 路由断言SpringCloudGateway专用, 数据库字段predicates */
private String predicates;
/** 路由过滤器SpringCloudGateway专用, 数据库字段filters */
private String filters;
/** 路由规则转发的目标uri, 数据库字段uri */
private String uri;
/** uri后面跟的path, 数据库字段path */
private String path;
/** 路由执行的顺序, 数据库字段order */
private Integer order;
/** 是否忽略验证,业务参数验证除外, 数据库字段ignore_validate */
private Byte ignoreValidate;
/** 状态0待审核1启用2禁用, 数据库字段status */
private Byte status;
/** 是否合并结果, 数据库字段merge_result */
private Byte mergeResult;
/** 是否需要token, 数据库字段need_token */
private Byte needToken;
/** 是否需要授权才能访问, 数据库字段permission */
private Byte permission;
private List<RouteRoleDTO> roles;
}

View File

@@ -1,56 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import com.alibaba.fastjson.annotation.JSONField;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import java.util.Date;
/**
* @author 六如
*/
@Data
public class ServiceInfoVo {
@ApiDocField(description = "id")
private Integer id;
@ApiDocField(description = "服务名称(serviceId)")
private String name;
@ApiDocField(description = "instanceId")
private String instanceId;
@ApiDocField(description = "ip")
private String ipAddr;
@ApiDocField(description = "端口")
private String serverPort;
@ApiDocField(description = "status服务状态UP已上线OUT_OF_SERVICE已下线")
private String status;
@ApiDocField(description = "statusPageUrl")
private String statusPageUrl;
@ApiDocField(description = "healthCheckUrl")
private String healthCheckUrl;
@ApiDocField(description = "最后更新时间")
private String lastUpdatedTimestamp;
@ApiDocField(description = "parentId")
private Integer parentId;
public String getServiceId() {
return this.name;
}
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
public Date getUpdateTime() {
if (StringUtils.isBlank(lastUpdatedTimestamp)) {
return null;
}
return new Date(Long.valueOf(lastUpdatedTimestamp));
}
}

View File

@@ -1,47 +0,0 @@
package com.gitee.sop.adminserver.api.service.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import java.util.Map;
/**
* @author 六如
*/
@Data
public class ServiceInstanceVO {
@ApiDocField(description = "id")
private Integer id;
@ApiDocField(description = "服务名称(serviceId)")
private String serviceId;
@ApiDocField(description = "instanceId")
private String instanceId;
@ApiDocField(description = "ipPort")
private String ipPort;
@ApiDocField(description = "ip")
private String ip;
@ApiDocField(description = "port")
private int port;
@ApiDocField(description = "status服务状态UP已上线OUT_OF_SERVICE已下线")
private String status;
@ApiDocField(description = "最后更新时间")
private String updateTime;
@ApiDocField(description = "parentId")
private Integer parentId;
@ApiDocField(description = "metadata")
private Map<String, String> metadata;
public String getIpPort() {
return ip != null && port > 0 ? ip + ":" + port : "";
}
}

View File

@@ -1,69 +0,0 @@
package com.gitee.sop.adminserver.api.system;
import com.gitee.easyopen.ApiContext;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.session.SessionManager;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.system.param.LoginForm;
import com.gitee.sop.adminserver.api.system.result.AdminUserInfoVO;
import com.gitee.sop.adminserver.common.AdminErrors;
import com.gitee.sop.adminserver.common.WebContext;
import com.gitee.sop.adminserver.entity.AdminUserInfo;
import com.gitee.sop.adminserver.mapper.AdminUserInfoMapper;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpSession;
/**
* @author 六如
*/
@ApiService
@ApiDoc("系统接口")
public class SystemApi {
public static final int STATUS_FORBIDDEN = 2;
@Autowired
AdminUserInfoMapper adminUserInfoMapper;
@Api(name = "nologin.admin.login")
@ApiDocMethod(description = "用户登录")
String adminLogin(LoginForm param) {
String username = param.getUsername();
String password = param.getPassword();
password = DigestUtils.md5Hex(username + password + username);
Query query = new Query()
.eq("username", username)
.eq("password", password);
AdminUserInfo user = adminUserInfoMapper.getByQuery(query);
if (user == null) {
throw AdminErrors.ERROR_USERNAME_PWD.getException();
} else {
if (user.getStatus() == STATUS_FORBIDDEN) {
throw AdminErrors.USER_FORBIDDEN.getException();
}
SessionManager sessionManager = ApiContext.getSessionManager();
// 生成一个新session
HttpSession session = sessionManager.getSession(null);
WebContext.getInstance().setLoginUser(session, user);
return session.getId();
}
}
@Api(name = "admin.userinfo.get")
@ApiDocMethod(description = "获取用户信息")
AdminUserInfoVO getAdminUserInfo() {
AdminUserInfo loginUser = WebContext.getInstance().getLoginUser();
AdminUserInfoVO adminUserInfoVO = new AdminUserInfoVO();
BeanUtils.copyProperties(loginUser, adminUserInfoVO);
return adminUserInfoVO;
}
}

View File

@@ -1,9 +0,0 @@
package com.gitee.sop.adminserver.api.system.param;
import com.gitee.fastmybatis.core.query.param.PageParam;
/**
* @author 六如
*/
public class IspPageParam extends PageParam {
}

View File

@@ -1,27 +0,0 @@
package com.gitee.sop.adminserver.api.system.param;
import lombok.Data;
/**
* 表名isp_resource
* 备注ISP资源表
*
* @author 六如
*/
@Data
public class IspResourceParam {
private Long id;
/** 资源名称, 数据库字段name */
private String name;
private String version;
/** 资源内容URL, 数据库字段content */
private String content;
private String extContent;
}

View File

@@ -1,17 +0,0 @@
package com.gitee.sop.adminserver.api.system.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class LoginForm {
@NotBlank(message = "用户名不能为空")
@ApiDocField(description = "用户名", required = true)
private String username;
@NotBlank(message = "密码不能为空")
@ApiDocField(description = "密码", required = true)
private String password;
}

View File

@@ -1,20 +0,0 @@
package com.gitee.sop.adminserver.api.system.result;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class AdminUserInfoVO {
/**
* 数据库字段id
*/
private Long id;
/**
* 用户名, 数据库字段username
*/
private String username;
}

View File

@@ -1,27 +0,0 @@
package com.gitee.sop.adminserver.api.system.result;
import lombok.Data;
/**
* 表名isp_resource
* 备注ISP资源表
*
* @author 六如
*/
@Data
public class IspResourceResult {
private Long id;
/** 资源名称, 数据库字段name */
private String name;
private String version;
/** 资源内容URL, 数据库字段content */
private String content;
private String extContent;
}

View File

@@ -1,31 +0,0 @@
package com.gitee.sop.adminserver.bean;
import com.gitee.sop.adminserver.common.ChannelOperation;
import lombok.Data;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.util.Date;
/**
* @author 六如
*/
@Data
public class ChannelMsg {
private static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss:SSS";
public ChannelMsg(ChannelOperation channelOperation, Object data) {
this.operation = channelOperation.getOperation();
this.data = data;
this.timestamp = DateFormatUtils.format(new Date(), TIME_PATTERN);
}
private String operation;
private Object data;
/**
* 加个时间戳格式yyyy-MM-dd HH:mm:ss:SSS确保每次推送内容都不一样
* nacos监听基于MD5值如果每次推送的内容一样则监听不会触发因此必须确保每次推送的MD5不一样
*/
private String timestamp;
}

View File

@@ -1,53 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import java.util.Date;
@Data
public class ConfigLimitDto {
/** 数据库字段id */
private Long id;
/** 路由id, 数据库字段route_id */
private String routeId;
/** 数据库字段app_key */
private String appKey;
/** 限流ip多个用英文逗号隔开, 数据库字段limit_ip */
private String limitIp;
/** 服务id, 数据库字段service_id */
private String serviceId;
/** 限流策略1窗口策略2令牌桶策略, 数据库字段limit_type */
private Byte limitType;
/** 每秒可处理请求数, 数据库字段exec_count_per_second */
private Integer execCountPerSecond;
/** 限流持续时间默认1秒即每durationSeconds秒允许多少请求当limit_type=1时有效, 数据库字段durationSeconds */
private Integer durationSeconds;
/** 返回的错误码, 数据库字段limit_code */
private String limitCode;
/** 返回的错误信息, 数据库字段limit_msg */
private String limitMsg;
/** 令牌桶容量, 数据库字段token_bucket_count */
private Integer tokenBucketCount;
/** 限流开启状态1:开启0关闭, 数据库字段limit_status */
private Byte limitStatus;
/** 顺序,值小的优先执行, 数据库字段order_index */
private Integer orderIndex;
/** 数据库字段gmt_create */
private Date gmtCreate;
/** 数据库字段gmt_modified */
private Date gmtModified;
}

View File

@@ -1,17 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 六如
*/
@Data
public class GatewayFilterDefinition {
/** Filter Name */
private String name;
/** 对应的路由规则 */
private Map<String, String> args = new LinkedHashMap<>();
}

View File

@@ -1,42 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import org.springframework.util.StringUtils;
import javax.validation.ValidationException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 六如
*/
@Data
public class GatewayPredicateDefinition {
public static final String GEN_KEY = "_genkey_";
/** 断言对应的Name */
private String name;
/** 配置的断言规则 */
private Map<String, String> args = new LinkedHashMap<>();
public GatewayPredicateDefinition() {
}
public GatewayPredicateDefinition(String text) {
int eqIdx = text.indexOf(61);
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse GatewayPredicateDefinition text '" + text + "', must be of the form name=value");
} else {
this.setName(text.substring(0, eqIdx));
String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");
for(int i = 0; i < args.length; ++i) {
this.args.put(generateName(i), args[i]);
}
}
}
public static String generateName(int i) {
return GEN_KEY + i;
}
}

View File

@@ -1,22 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class GatewayPushDTO {
private String dataId;
private String groupId;
private ChannelMsg channelMsg;
public GatewayPushDTO() {
}
public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) {
this.dataId = dataId;
this.groupId = groupId;
this.channelMsg = channelMsg;
}
}

View File

@@ -1,437 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* http请求工具基于OKHTTP3
* @author 六如
*/
public class HttpTool {
private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
private Map<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();
private OkHttpClient httpClient;
public HttpTool() {
this(new HttpToolConfig());
}
public HttpTool(HttpToolConfig httpToolConfig) {
this.initHttpClient(httpToolConfig);
}
protected void initHttpClient(HttpToolConfig httpToolConfig) {
httpClient = new OkHttpClient.Builder()
// 设置链接超时时间默认10秒
.connectTimeout(httpToolConfig.connectTimeoutSeconds, TimeUnit.SECONDS)
.readTimeout(httpToolConfig.readTimeoutSeconds, TimeUnit.SECONDS)
.writeTimeout(httpToolConfig.writeTimeoutSeconds, TimeUnit.SECONDS)
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
cookieStore.put(httpUrl.host(), list);
}
@Override
public List<Cookie> loadForRequest(HttpUrl httpUrl) {
List<Cookie> cookies = cookieStore.get(httpUrl.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
}).build();
}
@Data
public static class HttpToolConfig {
/**
* 请求超时时间
*/
private int connectTimeoutSeconds = 10;
/**
* http读取超时时间
*/
private int readTimeoutSeconds = 10;
/**
* http写超时时间
*/
private int writeTimeoutSeconds = 10;
}
/**
* get请求
*
* @param url
* @param header
* @return
* @throws IOException
*/
public String get(String url, Map<String, String> header) throws IOException {
Request.Builder builder = new Request.Builder().url(url).get();
// 添加header
addHeader(builder, header);
Request request = builder.build();
Response response = httpClient.newCall(request).execute();
return response.body().string();
}
/**
* 提交表单
*
* @param url url
* @param form 参数
* @param header header
* @param method 请求方式postget等
* @return
* @throws IOException
*/
public String request(String url, Map<String, ?> form, Map<String, String> header, HTTPMethod method) throws IOException {
Request.Builder requestBuilder = buildRequestBuilder(url, form, method);
// 添加header
addHeader(requestBuilder, header);
Request request = requestBuilder.build();
Response response = httpClient
.newCall(request)
.execute();
try {
return response.body().string();
} finally {
response.close();
}
}
/**
* 请求json数据contentType=application/json
* @param url 请求路径
* @param json json数据
* @param header header
* @return 返回响应结果
* @throws IOException
*/
public String requestJson(String url, String json, Map<String, String> header) throws IOException {
RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, json);
Request.Builder requestBuilder = new Request.Builder()
.url(url)
.post(body);
// 添加header
addHeader(requestBuilder, header);
Request request = requestBuilder.build();
Response response = httpClient
.newCall(request)
.execute();
try {
return response.body().string();
} finally {
response.close();
}
}
public static Request.Builder buildRequestBuilder(String url, Map<String, ?> form, HTTPMethod method) {
switch (method) {
case GET:
return new Request.Builder()
.url(buildHttpUrl(url, form))
.get();
case HEAD:
return new Request.Builder()
.url(buildHttpUrl(url, form))
.head();
case PUT:
return new Request.Builder()
.url(url)
.put(buildFormBody(form));
case DELETE:
return new Request.Builder()
.url(url)
.delete(buildFormBody(form));
default:
return new Request.Builder()
.url(url)
.post(buildFormBody(form));
}
}
public static HttpUrl buildHttpUrl(String url, Map<String, ?> form) {
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
for (Map.Entry<String, ?> entry : form.entrySet()) {
urlBuilder.addQueryParameter(entry.getKey(), String.valueOf(entry.getValue()));
}
return urlBuilder.build();
}
public static FormBody buildFormBody(Map<String, ?> form) {
FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
for (Map.Entry<String, ?> entry : form.entrySet()) {
paramBuilder.add(entry.getKey(), String.valueOf(entry.getValue()));
}
return paramBuilder.build();
}
/**
* 提交表单,并且上传文件
*
* @param url
* @param form
* @param header
* @param files
* @return
* @throws IOException
*/
public String requestFile(String url, Map<String, ?> form, Map<String, String> header, List<UploadFile> files)
throws IOException {
// 创建MultipartBody.Builder用于添加请求的数据
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
bodyBuilder.setType(MultipartBody.FORM);
for (UploadFile uploadFile : files) {
// 请求的名字
bodyBuilder.addFormDataPart(uploadFile.getName(),
// 文件的文字,服务器端用来解析的
uploadFile.getFileName(),
// 创建RequestBody把上传的文件放入
RequestBody.create(null, uploadFile.getFileData())
);
}
for (Map.Entry<String, ?> entry : form.entrySet()) {
bodyBuilder.addFormDataPart(entry.getKey(), String.valueOf(entry.getValue()));
}
RequestBody requestBody = bodyBuilder.build();
Request.Builder builder = new Request.Builder().url(url).post(requestBody);
// 添加header
addHeader(builder, header);
Request request = builder.build();
Response response = httpClient.newCall(request).execute();
try {
return response.body().string();
} finally {
response.close();
}
}
/**
* 请求数据
*
* @param url 请求url
* @param form 请求数据
* @param header header
* @return 返回Response
* @throws IOException
*/
public Response requestForResponse(String url, Map<String, ?> form, Map<String, String> header, HTTPMethod method) throws IOException {
Request.Builder requestBuilder = buildRequestBuilder(url, form, method);
// 添加header
addHeader(requestBuilder, header);
Request request = requestBuilder.build();
return httpClient
.newCall(request)
.execute();
}
/**
* 下载文件
*
* @param url 请求url
* @param form 请求数据
* @param header header
* @return 返回文件流
* @throws IOException
*/
public InputStream downloadFile(String url, Map<String, ?> form, Map<String, String> header) throws IOException {
Request.Builder requestBuilder = buildRequestBuilder(url, form, HTTPMethod.GET);
// 添加header
addHeader(requestBuilder, header);
Request request = requestBuilder.build();
Response response = httpClient
.newCall(request)
.execute();
if (response.isSuccessful()) {
ResponseBody body = response.body();
return body == null ? null : body.byteStream();
}
return null;
}
private void addHeader(Request.Builder builder, Map<String, String> header) {
if (header != null) {
Set<Map.Entry<String, String>> entrySet = header.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
builder.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
}
}
}
public void setCookieStore(Map<String, List<Cookie>> cookieStore) {
this.cookieStore = cookieStore;
}
public void setHttpClient(OkHttpClient httpClient) {
this.httpClient = httpClient;
}
public enum HTTPMethod {
/** http GET */
GET,
/** http POST */
POST,
/** http PUT */
PUT,
/** http HEAD */
HEAD,
/** http DELETE */
DELETE;
private HTTPMethod() {
}
public String value() {
return this.name();
}
public static HTTPMethod fromValue(String v) {
return valueOf(v.toUpperCase());
}
}
/**
* 文件上传类
* @author 六如
*/
@Getter
@Setter
public static class UploadFile implements Serializable {
private static final long serialVersionUID = -1100614660944996398L;
/**
* @param name 表单名称,不能重复
* @param file 文件
* @throws IOException
*/
public UploadFile(String name, File file) throws IOException {
this(name, file.getName(), FileUtil.toBytes(file));
}
/**
* @param name 表单名称,不能重复
* @param fileName 文件名
* @param input 文件流
* @throws IOException
*/
public UploadFile(String name, String fileName, InputStream input) throws IOException {
this(name, fileName, FileUtil.toBytes(input));
}
/**
* @param name 表单名称,不能重复
* @param fileName 文件名
* @param fileData 文件数据
*/
public UploadFile(String name, String fileName, byte[] fileData) {
super();
this.name = name;
this.fileName = fileName;
this.fileData = fileData;
this.md5 = DigestUtils.md5Hex(fileData);
}
private String name;
private String fileName;
private byte[] fileData;
private String md5;
}
public static class FileUtil {
/**
* The default buffer size to use.
*/
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private static final int EOF = -1;
/**
* 将文件流转换成byte[]
* @param input
* @return
* @throws IOException
*/
public static byte[] toBytes(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
int n = 0;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
}
/**
* 将文件转换成数据流
* @param file 文件
* @return 返回数据流
* @throws IOException
*/
public static byte[] toBytes(File file) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (file.canRead() == false) {
throw new IOException("File '" + file + "' cannot be read");
}
} else {
throw new FileNotFoundException("File '" + file + "' does not exist");
}
InputStream input = null;
try {
input = new FileInputStream(file);
return toBytes(input);
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException ioe) {
}
}
}
}
}

View File

@@ -1,17 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import java.util.List;
/**
* isv授权过的路由
* @author 六如
*/
@Data
public class IsvRoutePermission {
private String appKey;
private List<String> routeIdList;
private String routeIdListMd5;
private String listenPath;
}

View File

@@ -1,37 +0,0 @@
package com.gitee.sop.adminserver.bean;
/**
* @author 六如
*/
public enum MetadataEnum {
/**
* 预发布环境
*/
ENV_PRE("env", "pre"),
/**
* 上线环境
*/
ENV_ONLINE("env", ""),
/**
* 灰度环境
*/
ENV_GRAY("env", "gray"),
;
private String key,value;
MetadataEnum(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
}

View File

@@ -1,29 +0,0 @@
package com.gitee.sop.adminserver.bean;
/**
* @author 六如
*/
public class NacosConfigs {
public static final String GROUP_CHANNEL = "sop:channel";
public static final String GROUP_ROUTE = "sop:route";
public static final String DATA_ID_GRAY = "com.gitee.sop.channel.gray";
public static final String DATA_ID_IP_BLACKLIST = "com.gitee.sop.channel.ipblacklist";
public static final String DATA_ID_ISV = "com.gitee.sop.channel.isv";
public static final String DATA_ID_ROUTE_PERMISSION = "com.gitee.sop.channel.routepermission";
public static final String DATA_ID_LIMIT_CONFIG = "com.gitee.sop.channel.limitconfig";
public static final String DATA_ID_ROUTE_CONFIG = "com.gitee.sop.channel.routeconfig";
private static final String DATA_ID_TPL = "com.gitee.sop.route.%s";
public static String getRouteDataId(String serviceId) {
return String.format(DATA_ID_TPL, serviceId);
}
}

View File

@@ -1,22 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class RouteConfigDto {
/**
* 路由id
*/
private String routeId;
/**
* 状态0待审核1启用2禁用
*/
private Integer status;
}

View File

@@ -1,13 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class RouteErrorCount {
private String routeId;
private Integer count;
}

View File

@@ -1,13 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class ServiceGrayDefinition {
private String serviceId;
private String instanceId;
private String data;
}

View File

@@ -1,29 +0,0 @@
package com.gitee.sop.adminserver.bean;
import java.util.List;
/**
* @author 六如
*/
public class ServiceInfo {
/** 服务名称 */
private String serviceId;
/** 实例列表 */
private List<ServiceInstance> instances;
public String getServiceId() {
return serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public List<ServiceInstance> getInstances() {
return instances;
}
public void setInstances(List<ServiceInstance> instances) {
this.instances = instances;
}
}

View File

@@ -1,48 +0,0 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* @author 六如
*/
@Data
public class ServiceInstance {
/**
* 实例id
*/
private String instanceId;
/**
* 服务名称
*/
private String serviceId;
/**
* ip
*/
private String ip;
/**
* port
*/
private int port;
/**
* 服务状态UP已上线OUT_OF_SERVICE已下线
*/
private String status;
/**
* 更新时间
*/
private String updateTime;
/**
* user extended attributes
*/
private Map<String, String> metadata = new HashMap<String, String>();
}

View File

@@ -1,38 +0,0 @@
package com.gitee.sop.adminserver.common;
import com.gitee.easyopen.message.ErrorMeta;
/**
* 定义错误的地方
* @author 六如
*/
public class AdminErrors {
private AdminErrors(){}
/** error_zh_CN2.properties内容前缀 */
static String isvModule = "isv.error_";
public static final ErrorMeta NO_LOGIN = new ErrorMeta(isvModule, "-100", "用户未登录");
public static final ErrorMeta ERROR_USERNAME_PWD = new ErrorMeta(isvModule, "1", "用户名密码错误");
public static final ErrorMeta DUPLICATE_USERNAME = new ErrorMeta(isvModule, "2", "该用户名已被注册");
public static final ErrorMeta NO_RECORD = new ErrorMeta(isvModule, "1000", "无操作记录");
public static final ErrorMeta ERROR_VALIDATE = new ErrorMeta(isvModule, "1001", "验证失败");
public static final ErrorMeta NULL_OBJECT = new ErrorMeta(isvModule, "1002", "null对象");
public static final ErrorMeta ERROR_SERACH = new ErrorMeta(isvModule, "1004", "查询错误");
public static final ErrorMeta ERROR_EXPORT = new ErrorMeta(isvModule, "1005", "导出错误");
public static final ErrorMeta CLASS_NEW_ERROR = new ErrorMeta(isvModule, "1007", "系统错误");
public static final ErrorMeta ERROR_SAVE = new ErrorMeta(isvModule, "10010", "保存失败");
public static final ErrorMeta ERROR_UPDATE = new ErrorMeta(isvModule, "10011", "修改失败");
public static final ErrorMeta DELETE_UPDATE = new ErrorMeta(isvModule, "10012", "删除失败");
public static final ErrorMeta RECORD_EXSIT = new ErrorMeta(isvModule, "10013", "记录已存在");
public static final ErrorMeta ERROR_OPT = new ErrorMeta(isvModule, "10014", "非法操作");
public static final ErrorMeta NO_USER = new ErrorMeta(isvModule, "10015", "用户不存在");
public static final ErrorMeta USER_FORBIDDEN = new ErrorMeta(isvModule, "10016", "用户已禁用");
}

View File

@@ -1,33 +0,0 @@
package com.gitee.sop.adminserver.common;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.message.Error;
/**
* @author 六如
*/
public class BizException extends ApiException {
public BizException(String msg) {
super(msg, "4000");
}
public BizException(Exception e) {
super(e);
}
public BizException(Error<String> error) {
super(error);
}
public BizException(String msg, String code) {
super(msg, code);
}
public BizException(String msg, String code, Object data) {
super(msg, code, data);
}
public BizException(Error<String> error, Object data) {
super(error, data);
}
}

View File

@@ -1,64 +0,0 @@
package com.gitee.sop.adminserver.common;
/**
* @author 六如
*/
public enum ChannelOperation {
/**
* 限流推送路由配置-修改
*/
LIMIT_CONFIG_UPDATE("update"),
/**
* 路由信息更新
*/
ROUTE_CONFIG_UPDATE("update"),
/**
* isv信息修改
*/
ISV_INFO_UPDATE("update"),
/**
* 黑名单消息类型:添加
*/
BLACKLIST_ADD("add"),
/**
* 黑名单消息类型:删除
*/
BLACKLIST_DELETE("delete"),
/**
* 路由权限配置更新
*/
ROUTE_PERMISSION_UPDATE("update"),
/**
* 路由权限加载
*/
ROUTE_PERMISSION_RELOAD("reload"),
/**
* 灰度发布设置
*/
GRAY_USER_KEY_SET("set"),
/**
* 灰度发布-开启
*/
GRAY_USER_KEY_OPEN("open"),
/**
* 灰度发布-关闭
*/
GRAY_USER_KEY_CLOSE("close"),
;
private String operation;
ChannelOperation(String operation) {
this.operation = operation;
}
public String getOperation() {
return operation;
}
}

View File

@@ -1,75 +0,0 @@
package com.gitee.sop.adminserver.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* @author 六如
*/
@Slf4j
public class CopyUtil {
public static void copyProperties(Object from, Object to) {
BeanUtils.copyProperties(from, to);
}
public static <T> T copyBean(Object from, Supplier<T> supplier) {
Objects.requireNonNull(from);
T to = supplier.get();
BeanUtils.copyProperties(from, to);
return to;
}
public static <T> T copyBeanNullable(Object from, Supplier<T> supplier) {
if (from == null) {
return supplier.get();
}
T to = supplier.get();
BeanUtils.copyProperties(from, to);
return to;
}
public static <T> T copyBean(Object from, Supplier<T> supplier, Consumer<T> after) {
if (from == null) {
return null;
}
T to = supplier.get();
BeanUtils.copyProperties(from, to);
after.accept(to);
return to;
}
public static <T> List<T> copyList(List<?> fromList, Supplier<T> toElement) {
if (fromList == null) {
return Collections.emptyList();
}
return fromList.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
return target;
})
.collect(Collectors.toList());
}
public static <T> List<T> copyList(List<?> fromList, Supplier<T> toElement, Consumer<T> after) {
if (fromList == null) {
return Collections.emptyList();
}
return fromList.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
after.accept(target);
return target;
})
.collect(Collectors.toList());
}
}

View File

@@ -1,167 +0,0 @@
package com.gitee.sop.adminserver.common;
import java.util.UUID;
public class IdGen {
private static long workId = 0;
private static SnowflakeIdWorker worker = new SnowflakeIdWorker(workId++, 0);
/**
* 生成唯一id
* @return
*/
public static String nextId() {
return String.valueOf(worker.nextId());
}
public static String uuid() {
return UUID.randomUUID().toString().replace("-", "");
}
/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0<br>
* 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)
* 得到的值这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性。41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId<br>
* 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位为一个Long型。<br>
* SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)并且效率较高经测试SnowFlake每秒能够产生26万ID左右。
*/
public static class SnowflakeIdWorker {
// ==============================Fields===========================================
/** 开始时间截 (2015-01-01) */
private final long twepoch = 1420041100000L;
/** 机器id所占的位数 */
private final long workerIdBits = 5L;
/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;
/** 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 支持的最大数据标识id结果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/** 序列在id中占的位数 */
private final long sequenceBits = 12L;
/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 数据标识id向左移17位(12+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;
/** 时间截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/** 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095) */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 工作机器ID(0~31) */
private long workerId;
/** 数据中心ID(0~31) */
private long datacenterId;
/** 毫秒内序列(0~4095) */
private long sequence = 0L;
/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;
//==============================Constructors=====================================
/**
* 构造函数
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
// ==============================Methods==========================================
/**
* 获得下一个ID (该方法是线程安全的)
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
* @return 当前时间(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
}
/*//==============================Test=============================================
*/
/** 测试
public static void main(String[] args) {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 100; i++) {
long id = idWorker.nextId();
System.out.println("id:" + id);
System.out.println("toBinaryString:" + Long.toBinaryString(id));
}
}
*/
}

View File

@@ -1,36 +0,0 @@
package com.gitee.sop.adminserver.common;
/**
* 限流
* @author 六如
*/
public enum LimitEnum {
/**
* 限流策略1窗口策略
*/
TYPE_LEAKY_BUCKET((byte) 1),
/**
* 限流策略2令牌桶策略
*/
TYPE_TOKEN_BUCKET((byte) 2),
/**
* 1:开启
*/
STATUS_OPEN((byte) 1),
/**
* 0关闭
*/
STATUS_CLOSE((byte) 0),
;
LimitEnum(byte val) {
this.val = val;
}
private byte val;
public byte getVal() {
return val;
}
}

View File

@@ -1,46 +0,0 @@
package com.gitee.sop.adminserver.common;
import com.gitee.easyopen.verify.DefaultMd5Verifier;
import com.gitee.sop.adminserver.bean.HttpTool;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* @author 六如
*/
public class QueryUtil {
private static HttpTool httpTool = new HttpTool();
public static String buildQueryString(Map<String, ?> params) throws UnsupportedEncodingException {
if (params == null || params.size() == 0) {
return "";
}
StringBuilder query = new StringBuilder();
int i = 0;
for (Map.Entry<String, ?> entry : params.entrySet()) {
String name = entry.getKey();
String value = String.valueOf(entry.getValue());
if (i++ > 0) {
query.append("&");
}
query.append(name).append("=").append(URLEncoder.encode(value, "UTF-8"));
}
return query.toString();
}
public static String requestServer(String ipPort, String path, String secret) throws Exception {
DefaultMd5Verifier md5Verifier = new DefaultMd5Verifier();
Map<String, Object> params = new HashMap<>(16);
params.put("time", System.currentTimeMillis());
String sign = md5Verifier.buildSign(params, secret);
params.put("sign", sign);
String query = QueryUtil.buildQueryString(params);
path = path.startsWith("/") ? path.substring(1) : path;
String url = "http://" + ipPort + "/" + path + "?" + query;
return httpTool.get(url, null);
}
}

View File

@@ -1,417 +0,0 @@
package com.gitee.sop.adminserver.common;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA加解密工具<br>
*
* @author 六如
*/
public class RSATool {
public static String RSA_ALGORITHM = "RSA";
private KeyFormat keyFormat;
private KeyLength keyLength;
public RSATool(KeyFormat keyFormat, KeyLength keyLength) {
this.keyFormat = keyFormat;
this.keyLength = keyLength;
}
/**
* 创建公钥私钥
*
* @return 返回公私钥对
* @throws Exception
*/
public KeyStore createKeys() throws Exception {
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGeno.initialize(keyLength.getLength());
KeyPair keyPair = keyPairGeno.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
KeyStore keyStore = new KeyStore();
if (this.keyFormat == KeyFormat.PKCS1) {
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
keyStore.setPrivateKey(convertPkcs8ToPkcs1(privateKey.getEncoded()));
} else {
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded()));
}
return keyStore;
}
/**
* 获取公钥对象
*
* @param pubKeyData 公钥
* @return 返回公钥对象
* @throws Exception
*/
public RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
/**
* 获取公钥对象
*
* @param pubKey 公钥
* @return 返回私钥对象
* @throws Exception
*/
public RSAPublicKey getPublicKey(String pubKey) throws Exception {
return getPublicKey(Base64.decodeBase64(pubKey));
}
/**
* 获取私钥对象
*
* @param priKey 私钥
* @return 私钥对象
* @throws Exception
*/
public RSAPrivateKey getPrivateKey(String priKey) throws Exception {
return getPrivateKey(Base64.decodeBase64(priKey));
}
/**
* 通过私钥byte[]将公钥还原适用于RSA算法
*
* @param keyBytes
* @return 返回私钥
* @throws Exception
*/
public RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
/**
* 公钥加密
*
* @param data 待加密内容
* @param publicKey 公钥
* @return 返回密文
* @throws Exception
*/
public String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public String encryptByPrivateKey(String data, String privateKey) throws Exception {
return encryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥加密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 返回密文
* @throws Exception
*/
public String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public String decryptByPrivateKey(String data, String privateKey) throws Exception {
return decryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥解密
*
* @param data 待解密内容
* @param privateKey 私钥
* @return 返回明文
* @throws Exception
*/
public String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* 公钥解密
*
* @param data 待解密内容
* @param rsaPublicKey 公钥
* @return 返回明文
* @throws Exception
*/
public String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
// 模长
int key_len = rsaPublicKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
public static String convertPkcs8ToPkcs1(byte[] privateKeyData) throws Exception{
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privateKeyData);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privateKeyPKCS1 = primitive.getEncoded();
return Base64.encodeBase64String(privateKeyPKCS1);
}
/**
* ASCII码转BCD码
*/
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
byte[] bcd = new byte[asc_len / 2];
int j = 0;
for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i] = asc_to_bcd(ascii[j++]);
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4));
}
return bcd;
}
public static byte asc_to_bcd(byte asc) {
byte bcd;
if ((asc >= '0') && (asc <= '9')) {
bcd = (byte) (asc - '0');
} else if ((asc >= 'A') && (asc <= 'F')) {
bcd = (byte) (asc - 'A' + 10);
} else if ((asc >= 'a') && (asc <= 'f')) {
bcd = (byte) (asc - 'a' + 10);
} else {
bcd = (byte) (asc - 48);
}
return bcd;
}
/**
* BCD转字符串
*/
public String bcd2Str(byte[] bytes) {
char[] temp = new char[bytes.length * 2];
char val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
/**
* 拆分字符串
*/
public String[] splitString(String string, int len) {
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0) {
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i = 0; i < x + z; i++) {
if (i == x + z - 1 && y != 0) {
str = string.substring(i * len, i * len + y);
} else {
str = string.substring(i * len, i * len + len);
}
strings[i] = str;
}
return strings;
}
/**
* 拆分数组
*/
public byte[][] splitArray(byte[] data, int len) {
int x = data.length / len;
int y = data.length % len;
int z = 0;
if (y != 0) {
z = 1;
}
byte[][] arrays = new byte[x + z][];
byte[] arr;
for (int i = 0; i < x + z; i++) {
arr = new byte[len];
if (i == x + z - 1 && y != 0) {
System.arraycopy(data, i * len, arr, 0, y);
} else {
System.arraycopy(data, i * len, arr, 0, len);
}
arrays[i] = arr;
}
return arrays;
}
public enum KeyLength {
/**
* 秘钥长度1024
*/
LENGTH_1024(1024),
/**
* 秘钥长度2048
*/
LENGTH_2048(2048);
private int length;
KeyLength(int length) {
this.length = length;
}
public int getLength() {
return length;
}
}
public static class KeyStore {
private String publicKey;
private String privateKey;
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}
public enum KeyFormat {
PKCS1("RSA/ECB/PKCS1Padding"), PKCS8("RSA");
private String cipherAlgorithm;
KeyFormat(String cipherAlgorithm) {
this.cipherAlgorithm = cipherAlgorithm;
}
public String getCipherAlgorithm() {
return cipherAlgorithm;
}
}
public KeyFormat getKeyFormat() {
return keyFormat;
}
public KeyLength getKeyLength() {
return keyLength;
}
/* ------------ Test ------------
public static void main(String[] args) throws Exception {
RSATool rsa_pkcs8_1024 = new RSATool(KeyFormat.PKCS8, KeyLength.LENGTH_1024);
RSATool rsa_pkcs8_2048 = new RSATool(KeyFormat.PKCS8, KeyLength.LENGTH_2048);
doTest(rsa_pkcs8_1024);
doTest(rsa_pkcs8_2048);
// PKCS在Java环境无法测试可以生成一对到非java平台测试如C#
}
private static void doTest(RSATool rsaTool) throws Exception {
System.out.println("秘钥格式:" + rsaTool.keyFormat.name() + ", 秘钥长度:" + rsaTool.keyLength.getLength());
KeyStore keys = rsaTool.createKeys();
String pubKey = keys.getPublicKey();
System.out.println("pubKey:");
System.out.println(pubKey);
String priKey = keys.getPrivateKey();
System.out.println("priKey:");
System.out.println(priKey);
System.out.println("--------");
String ming = "你好abc123~!@=";
// 用公钥加密
String mi = rsaTool.encryptByPublicKey(ming, rsaTool.getPublicKey(pubKey));
System.out.println("mi : " + mi);
// 用私钥解密
String ming2 = rsaTool.decryptByPrivateKey(mi, rsaTool.getPrivateKey(priKey));
System.out.println("ming : " + ming2 + ", 结果:" + ming2.equals(ming));
// 用私钥加密
String mi2 = rsaTool.encryptByPrivateKey(ming, rsaTool.getPrivateKey(priKey));
System.out.println("mi2 : " + mi2);
// 用公钥解密
String ming3 = rsaTool.decryptByPublicKey(mi2, rsaTool.getPublicKey(pubKey));
System.out.println("ming3 : " + ming3 + ", 结果:" + ming3.equals(ming));
System.out.println("---------------------");
}
*/
}

View File

@@ -1,26 +0,0 @@
package com.gitee.sop.adminserver.common;
import lombok.Getter;
/**
* 通用状态枚举
*
* @author 六如
*/
@Getter
public enum StatusEnum {
/**
* 启用
*/
STATUS_ENABLE((byte)1),
/**
* 禁用
*/
STATUS_DISABLE((byte)0),
;
private byte status;
StatusEnum(byte status) {
this.status = status;
}
}

Some files were not shown because too many files have changed in this diff Show More