mirror of https://github.com/jeecgboot/jeecg-boot
微服务重大升级说明(暂时只升级后台3.4.0)
-升级Spring Cloud Alibaba 2021.0.1.0,使用 spring.config.import 方式引入nacos配置 -拆分jeecg-boot-starter出来,使用独立项目维护pull/3958/head
parent
17d42c8d63
commit
bad23add8c
|
@ -7,7 +7,12 @@
|
|||
JEECG BOOT 低代码开发平台(前后端分离版本)
|
||||
===============
|
||||
|
||||
当前最新版本: 3.3.0(发布日期:2022-07-25)
|
||||
当前最新版本: 3.4.0(发布日期:2022-08-06)
|
||||
|
||||
|
||||
> **重大升级说明**
|
||||
> - 升级[Spring Cloud Alibaba 2021.0.1.0](https://github.com/alibaba/spring-cloud-alibaba/blob/2021.x/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sca-upgrade-guide.adoc),使用 spring.config.import 方式引入nacos配置
|
||||
> - 拆分jeecg-boot-starter出来,使用独立项目维护 [jeecg-boot-starter项目新地址](https://gitee.com/jeecg/jeecg-boot-starter)
|
||||
|
||||
|
||||
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Jeecg-Boot 低代码开发平台
|
||||
===============
|
||||
|
||||
当前最新版本: 3.3.0(发布日期:20220725)
|
||||
当前最新版本: 3.4.0(发布日期:20220808)
|
||||
|
||||
|
||||
## 后端技术架构
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,51 +0,0 @@
|
|||
#### 镜像上传
|
||||
# 仓库私服: 81.70.17.111:5000
|
||||
# 第一步:上传镜像到docker仓库
|
||||
#docker tag jeecg-boot-mysql 81.70.17.111:5000/jeecg-boot-mysql:1.1
|
||||
#docker tag jeecg-boot-system 81.70.17.111:5000/jeecg-boot-system:1.0
|
||||
#docker tag nginxhtml:jeecgboot 81.70.17.111:5000/nginxhtml:1.2
|
||||
|
||||
#docker push 81.70.17.111:5000/jeecg-boot-mysql:1.1
|
||||
#docker push 81.70.17.111:5000/jeecg-boot-system:1.0
|
||||
#docker push 81.70.17.111:5000/nginxhtml:1.2
|
||||
|
||||
# 第二步:将此yml文件上传服务器,执行启动命令 docker-compose -f ./docker-compose-server.yml up
|
||||
version: '2'
|
||||
services:
|
||||
jeecg-boot-mysql:
|
||||
image: 81.70.17.111:5000/jeecg-boot-mysql:1.0
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
restart: always
|
||||
container_name: jeecg-boot-mysql
|
||||
command:
|
||||
--character-set-server=utf8mb4
|
||||
--collation-server=utf8mb4_general_ci
|
||||
--explicit_defaults_for_timestamp=true
|
||||
--lower_case_table_names=1
|
||||
--max_allowed_packet=128M
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
jeecg-boot-redis:
|
||||
image: redis:5.0
|
||||
ports:
|
||||
- 6379:6379
|
||||
restart: always
|
||||
container_name: jeecg-boot-redis
|
||||
|
||||
jeecg-boot-system:
|
||||
image: 81.70.17.111:5000/jeecg-boot-system:1.0
|
||||
restart: always
|
||||
container_name: jeecg-boot-system
|
||||
volumes:
|
||||
- /data/config:/jeecg-boot/config
|
||||
ports:
|
||||
- 8080:8080
|
||||
|
||||
jeecg-boot-nginx:
|
||||
image: 81.70.17.111:5000/nginxhtml
|
||||
restart: always
|
||||
container_name: jeecg-boot-nginx
|
||||
ports:
|
||||
- 80:80
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>jeecg-boot-base-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>jeecg-boot-base-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>jeecg-boot-base</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -4,37 +4,17 @@
|
|||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jeecg-boot-base-core</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun Repository</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jeecg</id>
|
||||
<name>jeecg Repository</name>
|
||||
<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!--jeecg-tools-->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base-tools</artifactId>
|
||||
<artifactId>jeecg-boot-common</artifactId>
|
||||
</dependency>
|
||||
<!--集成springmvc框架并实现自动配置 -->
|
||||
<dependency>
|
||||
|
|
|
@ -304,16 +304,21 @@ public interface CommonConstant {
|
|||
String WPS_TYPE_2="2";
|
||||
|
||||
|
||||
/**===============================================================================================*/
|
||||
/**
|
||||
* ::非常重要::
|
||||
* 注意:这四个常量值如果修改,需要与 jeecg-boot-starter/jeecg-boot-common/org.jeecg.config.FeignConfig 类中的值保持一致。
|
||||
*/
|
||||
String X_ACCESS_TOKEN = "X-Access-Token";
|
||||
String X_SIGN = "X-Sign";
|
||||
String X_TIMESTAMP = "X-TIMESTAMP";
|
||||
/** 租户 请求头*/
|
||||
String TENANT_ID = "tenant-id";
|
||||
/**===============================================================================================*/
|
||||
|
||||
String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!";
|
||||
String X_FORWARDED_SCHEME = "X-Forwarded-Scheme";
|
||||
|
||||
/**
|
||||
* 多租户 请求头
|
||||
*/
|
||||
String TENANT_ID = "tenant-id";
|
||||
|
||||
/**
|
||||
* 微服务读取配置文件属性 服务地址
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
/**
|
||||
* 使用Spring自身提供的地址匹配工具匹配URL
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
public class PathMatcherUtil {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String url = "/sys/dict/loadDictOrderByValue/tree,s2,2";
|
||||
String p = "/sys/dict/loadDictOrderByValue/*";
|
||||
|
||||
System.out.println(PathMatcherUtil.match(p,url));
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际验证路径匹配权限
|
||||
*
|
||||
* @param matchPath 权限url
|
||||
* @param path 访问路径
|
||||
* @return 是否拥有权限
|
||||
*/
|
||||
public static boolean match(String matchPath, String path) {
|
||||
SpringAntMatcher springAntMatcher = new SpringAntMatcher(matchPath, true);
|
||||
return springAntMatcher.matches(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际验证路径匹配权限
|
||||
*
|
||||
* @param list 权限url
|
||||
* @param path 访问路径
|
||||
* @return 是否拥有权限
|
||||
*/
|
||||
public static boolean matches(Collection<String> list, String path) {
|
||||
for (String s : list) {
|
||||
SpringAntMatcher springAntMatcher = new SpringAntMatcher(s, true);
|
||||
if (springAntMatcher.matches(path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址表达式匹配工具
|
||||
*/
|
||||
private static class SpringAntMatcher implements Matcher {
|
||||
private final AntPathMatcher antMatcher;
|
||||
private final String pattern;
|
||||
|
||||
private SpringAntMatcher(String pattern, boolean caseSensitive) {
|
||||
this.pattern = pattern;
|
||||
this.antMatcher = createMatcher(caseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String path) {
|
||||
return this.antMatcher.match(this.pattern, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> extractUriTemplateVariables(String path) {
|
||||
return this.antMatcher.extractUriTemplateVariables(this.pattern, path);
|
||||
}
|
||||
|
||||
private static AntPathMatcher createMatcher(boolean caseSensitive) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
matcher.setTrimTokens(false);
|
||||
matcher.setCaseSensitive(caseSensitive);
|
||||
return matcher;
|
||||
}
|
||||
}
|
||||
|
||||
private interface Matcher {
|
||||
|
||||
/**
|
||||
* 实际验证路径匹配权限
|
||||
* @param var1
|
||||
* @return
|
||||
*/
|
||||
boolean matches(String var1);
|
||||
|
||||
/**
|
||||
* 提取path中匹配到的部分
|
||||
* @param var1
|
||||
* @return
|
||||
*/
|
||||
Map<String, String> extractUriTemplateVariables(String var1);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,10 @@ public class JeecgBaseConfig {
|
|||
* @TODO 降低使用成本加的默认值,实际以 yml配置 为准
|
||||
*/
|
||||
private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
|
||||
/**
|
||||
* 需要加强校验的接口清单
|
||||
*/
|
||||
private String signUrls;
|
||||
|
||||
/**
|
||||
* 是否启用安全模式
|
||||
|
@ -79,4 +83,12 @@ public class JeecgBaseConfig {
|
|||
public void setDomainUrl(DomainUrl domainUrl) {
|
||||
this.domainUrl = domainUrl;
|
||||
}
|
||||
|
||||
public String getSignUrls() {
|
||||
return signUrls;
|
||||
}
|
||||
|
||||
public void setSignUrls(String signUrls) {
|
||||
this.signUrls = signUrls;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,11 +45,9 @@ import java.util.List;
|
|||
@Configuration
|
||||
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Value("${jeecg.path.upload}")
|
||||
private String upLoadPath;
|
||||
@Value("${jeecg.path.webapp}")
|
||||
private String webAppPath;
|
||||
@Value("${spring.resource.static-locations}")
|
||||
@Autowired
|
||||
JeecgBaseConfig jeecgBaseConfig;
|
||||
@Value("${spring.resource.static-locations:}")
|
||||
private String staticLocations;
|
||||
|
||||
@Autowired(required = false)
|
||||
|
@ -62,8 +60,8 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
|
|||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/**")
|
||||
//update-begin-author:taoyan date:20211116 for: jeecg.path.webapp配置无效 #3126
|
||||
.addResourceLocations("file:" + upLoadPath + "//")
|
||||
.addResourceLocations("file:" + webAppPath + "//")
|
||||
.addResourceLocations("file:" + jeecgBaseConfig.getPath().getUpload() + "//")
|
||||
.addResourceLocations("file:" + jeecgBaseConfig.getPath().getWebapp() + "//")
|
||||
//update-end-author:taoyan date:20211116 for: jeecg.path.webapp配置无效 #3126
|
||||
.addResourceLocations(staticLocations.split(","));
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
package org.jeecg.config.mybatis;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 多租户 tenant_id存储器
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantContext {
|
||||
private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
|
||||
|
||||
public static void setTenant(String tenant) {
|
||||
log.debug(" setting tenant to " + tenant);
|
||||
currentTenant.set(tenant);
|
||||
}
|
||||
|
||||
public static String getTenant() {
|
||||
return currentTenant.get();
|
||||
}
|
||||
|
||||
public static void clear(){
|
||||
currentTenant.remove();
|
||||
}
|
||||
}
|
||||
//package org.jeecg.config.mybatis;
|
||||
//
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//
|
||||
///**
|
||||
// * 多租户 tenant_id存储器
|
||||
// * @author: jeecg-boot
|
||||
// */
|
||||
//@Slf4j
|
||||
//public class TenantContext {
|
||||
// private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
|
||||
//
|
||||
// public static void setTenant(String tenant) {
|
||||
// log.debug(" setting tenant to " + tenant);
|
||||
// currentTenant.set(tenant);
|
||||
// }
|
||||
//
|
||||
// public static String getTenant() {
|
||||
// return currentTenant.get();
|
||||
// }
|
||||
//
|
||||
// public static void clear(){
|
||||
// currentTenant.remove();
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
|||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
|
@ -17,7 +18,6 @@ import org.jeecg.common.util.RedisUtil;
|
|||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.mybatis.TenantContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ package org.jeecg.config.shiro.filters;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.mybatis.TenantContext;
|
||||
import org.jeecg.config.shiro.JwtToken;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package org.jeecg.config.sign.interceptor;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.util.PathMatcherUtil;
|
||||
import org.jeecg.common.util.SpringContextHolder;
|
||||
import org.jeecg.config.JeecgBaseConfig;
|
||||
import org.jeecg.config.filter.RequestBodyReserveFilter;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -7,16 +11,19 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 签名 拦截器配置
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Configuration
|
||||
public class SignAuthConfiguration implements WebMvcConfigurer {
|
||||
public static String[] SIGN_URL_LIST = new String[]{"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
|
||||
"/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
|
||||
"/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
|
||||
"/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
|
||||
@Resource
|
||||
JeecgBaseConfig jeecgBaseConfig;
|
||||
|
||||
@Bean
|
||||
public SignAuthInterceptor signAuthInterceptor() {
|
||||
return new SignAuthInterceptor();
|
||||
|
@ -24,7 +31,17 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
|
|||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(SIGN_URL_LIST);
|
||||
//------------------------------------------------------------
|
||||
//查询需要进行签名拦截的接口 signUrls
|
||||
String signUrls = jeecgBaseConfig.getSignUrls();
|
||||
String[] signUrlsArray = null;
|
||||
if (StringUtils.isNotBlank(signUrls)) {
|
||||
signUrlsArray = signUrls.split(",");
|
||||
} else {
|
||||
signUrlsArray = PathMatcherUtil.SIGN_URL_LIST;
|
||||
}
|
||||
//------------------------------------------------------------
|
||||
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(signUrlsArray);
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
|
||||
|
@ -38,8 +55,18 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
|
|||
FilterRegistrationBean registration = new FilterRegistrationBean();
|
||||
registration.setFilter(requestBodyReserveFilter());
|
||||
registration.setName("requestBodyReserveFilter");
|
||||
//------------------------------------------------------------
|
||||
//查询需要进行签名拦截的接口 signUrls
|
||||
String signUrls = jeecgBaseConfig.getSignUrls();
|
||||
String[] signUrlsArray = null;
|
||||
if (StringUtils.isNotBlank(signUrls)) {
|
||||
signUrlsArray = signUrls.split(",");
|
||||
} else {
|
||||
signUrlsArray = PathMatcherUtil.SIGN_URL_LIST;
|
||||
}
|
||||
//------------------------------------------------------------
|
||||
// 建议此处只添加post请求地址而不是所有的都需要走过滤器
|
||||
registration.addUrlPatterns(SIGN_URL_LIST);
|
||||
registration.addUrlPatterns(signUrlsArray);
|
||||
return registration;
|
||||
}
|
||||
//update-end-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
<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>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base</artifactId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<description>公共模块</description>
|
||||
<artifactId>jeecg-boot-base-tools</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!--集成springmvc框架 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<!--加载hutool-->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
<!--加载beanutils-->
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,23 +0,0 @@
|
|||
package org.jeecg.common.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @Author:zyf
|
||||
* @Date:2019-07-31 10:43
|
||||
* @Description: 消息队列初始化注解
|
||||
**/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
public @interface RabbitComponent {
|
||||
@AliasFor(
|
||||
annotation = Component.class
|
||||
)
|
||||
String value();
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package org.jeecg.common.base;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import org.apache.commons.beanutils.ConvertUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* BaseMap
|
||||
*
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:17
|
||||
*/
|
||||
public class BaseMap extends HashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
public BaseMap() {
|
||||
|
||||
}
|
||||
|
||||
public BaseMap(Map<String, Object> map) {
|
||||
this.putAll(map);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BaseMap put(String key, Object value) {
|
||||
super.put(key, Optional.ofNullable(value).orElse(""));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseMap add(String key, Object value) {
|
||||
super.put(key, Optional.ofNullable(value).orElse(""));
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key) {
|
||||
Object obj = super.get(key);
|
||||
if (ObjectUtil.isNotEmpty(obj)) {
|
||||
return (T) obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Boolean getBoolean(String key) {
|
||||
Object obj = super.get(key);
|
||||
if (ObjectUtil.isNotEmpty(obj)) {
|
||||
return Boolean.valueOf(obj.toString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Long getLong(String key) {
|
||||
Object v = get(key);
|
||||
if (ObjectUtil.isNotEmpty(v)) {
|
||||
return new Long(v.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long[] getLongs(String key) {
|
||||
Object v = get(key);
|
||||
if (ObjectUtil.isNotEmpty(v)) {
|
||||
return (Long[]) v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Long> getListLong(String key) {
|
||||
List<String> list = get(key);
|
||||
if (ObjectUtil.isNotEmpty(list)) {
|
||||
return list.stream().map(e -> new Long(e)).collect(Collectors.toList());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Long[] getLongIds(String key) {
|
||||
Object ids = get(key);
|
||||
if (ObjectUtil.isNotEmpty(ids)) {
|
||||
return (Long[]) ConvertUtils.convert(ids.toString().split(","), Long.class);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Integer getInt(String key, Integer def) {
|
||||
Object v = get(key);
|
||||
if (ObjectUtil.isNotEmpty(v)) {
|
||||
return Integer.parseInt(v.toString());
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getInt(String key) {
|
||||
Object v = get(key);
|
||||
if (ObjectUtil.isNotEmpty(v)) {
|
||||
return Integer.parseInt(v.toString());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getBigDecimal(String key) {
|
||||
Object v = get(key);
|
||||
if (ObjectUtil.isNotEmpty(v)) {
|
||||
return new BigDecimal(v.toString());
|
||||
}
|
||||
return new BigDecimal("0");
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key, T def) {
|
||||
Object obj = super.get(key);
|
||||
if (ObjectUtil.isEmpty(obj)) {
|
||||
return def;
|
||||
}
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
public static BaseMap toBaseMap(Map<String, Object> obj) {
|
||||
BaseMap map = new BaseMap();
|
||||
map.putAll(obj);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package org.jeecg.common.config;
|
||||
|
||||
import org.jeecg.common.util.SpringContextHolder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
/**
|
||||
* SpringContextHolder注册用
|
||||
*
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:00
|
||||
*/
|
||||
@Configuration
|
||||
public class CommonConfig {
|
||||
|
||||
/**
|
||||
* Spring上下文工具配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SpringContextHolder.class)
|
||||
public SpringContextHolder springContextHolder() {
|
||||
SpringContextHolder holder = new SpringContextHolder();
|
||||
return holder;
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package org.jeecg.common.config.mqtoken;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 存放临时令牌Token到线程上下文
|
||||
*
|
||||
* 供队列、定时任务 feign调用使用(解决无会话Token问题)
|
||||
* @author zyf
|
||||
*/
|
||||
public class TransmitUserTokenFilter implements Filter {
|
||||
|
||||
private static String X_ACCESS_TOKEN="X-Access-Token";
|
||||
|
||||
public TransmitUserTokenFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
this.initUserInfo((HttpServletRequest) request);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void initUserInfo(HttpServletRequest request) {
|
||||
String token = request.getHeader(X_ACCESS_TOKEN);
|
||||
if (token!=null) {
|
||||
try {
|
||||
//将Token放入当前线程上下文中
|
||||
UserTokenContext.setToken(token);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package org.jeecg.common.config.mqtoken;
|
||||
|
||||
|
||||
/**
|
||||
* 用户Token线程上下文
|
||||
*
|
||||
* 供队列、定时任务 feign调用使用(解决无会话Token问题)
|
||||
* @author zyf
|
||||
*/
|
||||
public class UserTokenContext {
|
||||
|
||||
/**
|
||||
* 当前线程的TOKEN副本
|
||||
*/
|
||||
private static ThreadLocal<String> userToken = new ThreadLocal<String>();
|
||||
|
||||
public UserTokenContext() {
|
||||
}
|
||||
|
||||
public static String getToken(){
|
||||
return userToken.get();
|
||||
}
|
||||
|
||||
public static void setToken(String token){
|
||||
userToken.set(token);
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
userToken.remove();
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package org.jeecg.common.constant;
|
||||
|
||||
/**
|
||||
* @author: huangxutao
|
||||
* @date: 2019-06-14
|
||||
* @description: 缓存常量
|
||||
*/
|
||||
public interface CacheConstant {
|
||||
|
||||
/**
|
||||
* 字典信息缓存(含禁用的字典项)
|
||||
*/
|
||||
public static final String SYS_DICT_CACHE = "sys:cache:dict";
|
||||
|
||||
/**
|
||||
* 字典信息缓存 status为有效的
|
||||
*/
|
||||
public static final String SYS_ENABLE_DICT_CACHE = "sys:cache:dictEnable";
|
||||
/**
|
||||
* 表字典信息缓存
|
||||
*/
|
||||
public static final String SYS_DICT_TABLE_CACHE = "sys:cache:dictTable";
|
||||
public static final String SYS_DICT_TABLE_BY_KEYS_CACHE = SYS_DICT_TABLE_CACHE + "ByKeys";
|
||||
|
||||
/**
|
||||
* 数据权限配置缓存
|
||||
*/
|
||||
public static final String SYS_DATA_PERMISSIONS_CACHE = "sys:cache:permission:datarules";
|
||||
|
||||
/**
|
||||
* 缓存用户信息【加密】
|
||||
*/
|
||||
public static final String SYS_USERS_CACHE = "sys:cache:encrypt:user";
|
||||
|
||||
/**
|
||||
* 全部部门信息缓存
|
||||
*/
|
||||
public static final String SYS_DEPARTS_CACHE = "sys:cache:depart:alldata";
|
||||
|
||||
|
||||
/**
|
||||
* 全部部门ids缓存
|
||||
*/
|
||||
public static final String SYS_DEPART_IDS_CACHE = "sys:cache:depart:allids";
|
||||
|
||||
|
||||
/**
|
||||
* 测试缓存key
|
||||
*/
|
||||
public static final String TEST_DEMO_CACHE = "test:demo";
|
||||
|
||||
/**
|
||||
* 字典信息缓存
|
||||
*/
|
||||
public static final String SYS_DYNAMICDB_CACHE = "sys:cache:dbconnect:dynamic:";
|
||||
|
||||
/**
|
||||
* gateway路由缓存
|
||||
*/
|
||||
public static final String GATEWAY_ROUTES = "sys:cache:cloud:gateway_routes";
|
||||
|
||||
|
||||
/**
|
||||
* gateway路由 reload key
|
||||
*/
|
||||
public static final String ROUTE_JVM_RELOAD_TOPIC = "gateway_jvm_route_reload_topic";
|
||||
|
||||
/**
|
||||
* TODO 冗余代码 待删除
|
||||
*插件商城排行榜
|
||||
*/
|
||||
public static final String PLUGIN_MALL_RANKING = "pluginMall::rankingList";
|
||||
/**
|
||||
* TODO 冗余代码 待删除
|
||||
*插件商城排行榜
|
||||
*/
|
||||
public static final String PLUGIN_MALL_PAGE_LIST = "pluginMall::queryPageList";
|
||||
|
||||
|
||||
/**
|
||||
* online列表页配置信息缓存key
|
||||
*/
|
||||
public static final String ONLINE_LIST = "sys:cache:online:list";
|
||||
|
||||
/**
|
||||
* online表单页配置信息缓存key
|
||||
*/
|
||||
public static final String ONLINE_FORM = "sys:cache:online:form";
|
||||
|
||||
/**
|
||||
* online报表
|
||||
*/
|
||||
public static final String ONLINE_RP = "sys:cache:online:rp";
|
||||
|
||||
/**
|
||||
* online图表
|
||||
*/
|
||||
public static final String ONLINE_GRAPH = "sys:cache:online:graph";
|
||||
/**
|
||||
* 拖拽页面信息缓存
|
||||
*/
|
||||
public static final String DRAG_PAGE_CACHE = "drag:cache:page";
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package org.jeecg.common.constant;
|
||||
|
||||
/**
|
||||
* @Description: GlobalConstants
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:01
|
||||
*/
|
||||
public class GlobalConstants {
|
||||
|
||||
/**
|
||||
* 业务处理器beanName传递参数
|
||||
*/
|
||||
public static final String HANDLER_NAME = "handlerName";
|
||||
/**
|
||||
* 路由刷新触发器
|
||||
*/
|
||||
public static final String LODER_ROUDER_HANDLER = "loderRouderHandler";
|
||||
|
||||
/**
|
||||
* redis消息通道名称
|
||||
*/
|
||||
public static final String REDIS_TOPIC_NAME="jeecg_redis_topic";
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package org.jeecg.common.enums;
|
||||
|
||||
/**
|
||||
* @Description: 异常错误信息定义
|
||||
* @author: zyf
|
||||
* @date: 2022/4/14 10:05
|
||||
*/
|
||||
public enum SentinelErrorInfoEnum {
|
||||
|
||||
/**
|
||||
* 流控异常
|
||||
*/
|
||||
FlowException("访问频繁,请稍候再试"),
|
||||
|
||||
/**
|
||||
* 热点参数异常
|
||||
*/
|
||||
ParamFlowException("热点参数限流"),
|
||||
|
||||
/**
|
||||
* 系统规则限流或降级
|
||||
*/
|
||||
SystemBlockException("系统规则限流或降级"),
|
||||
|
||||
/**
|
||||
* 授权规则不通过
|
||||
*/
|
||||
AuthorityException("授权规则不通过"),
|
||||
|
||||
/**
|
||||
* 授权规则不通过
|
||||
*/
|
||||
UnknownError("未知异常"),
|
||||
|
||||
/**
|
||||
* 服务降级
|
||||
*/
|
||||
DegradeException("服务降级");
|
||||
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
String error;
|
||||
|
||||
/**
|
||||
* 错误代码
|
||||
*/
|
||||
Integer code;
|
||||
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造器
|
||||
*
|
||||
* @param error 错误信息
|
||||
* @param code 错误代码
|
||||
*/
|
||||
SentinelErrorInfoEnum(String error, Integer code) {
|
||||
this.error = error;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造器
|
||||
*
|
||||
* @param error 错误信息
|
||||
*/
|
||||
SentinelErrorInfoEnum(String error) {
|
||||
this.error = error;
|
||||
this.code = 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据异常名称匹配
|
||||
*
|
||||
* @param throwable 异常
|
||||
* @return String 错误信息
|
||||
*/
|
||||
public static SentinelErrorInfoEnum getErrorByException(Throwable throwable) {
|
||||
if(throwable==null){
|
||||
return null;
|
||||
}
|
||||
|
||||
String exceptionClass = throwable.getClass().getSimpleName();
|
||||
for (SentinelErrorInfoEnum e : SentinelErrorInfoEnum.values()) {
|
||||
if (exceptionClass.equals(e.name())) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.jeecg.common.exception;
|
||||
|
||||
/**
|
||||
* @Description: jeecg-cloud自定义异常
|
||||
* @Author: zyf
|
||||
* @Date: 2022-05-30
|
||||
*/
|
||||
public class JeecgCloudException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JeecgCloudException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package org.jeecg.common.modules.redis.client;
|
||||
|
||||
import org.jeecg.common.base.BaseMap;
|
||||
import org.jeecg.common.constant.GlobalConstants;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Description: redis客户端
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:01
|
||||
*/
|
||||
@Configuration
|
||||
public class JeecgRedisClient {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param handlerName
|
||||
* @param params
|
||||
*/
|
||||
public void sendMessage(String handlerName, BaseMap params) {
|
||||
params.put(GlobalConstants.HANDLER_NAME, handlerName);
|
||||
redisTemplate.convertAndSend(GlobalConstants.REDIS_TOPIC_NAME, params);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
package org.jeecg.common.modules.redis.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.common.constant.GlobalConstants;
|
||||
|
||||
import org.jeecg.common.modules.redis.receiver.RedisReceiver;
|
||||
import org.jeecg.common.modules.redis.writer.JeecgRedisCacheWriter;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||
import org.springframework.data.redis.serializer.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
||||
/**
|
||||
* 开启缓存支持
|
||||
* @author zyf
|
||||
* @Return:
|
||||
*/
|
||||
@Slf4j
|
||||
@EnableCaching
|
||||
@Configuration
|
||||
public class RedisConfig extends CachingConfigurerSupport {
|
||||
|
||||
@Resource
|
||||
private LettuceConnectionFactory lettuceConnectionFactory;
|
||||
|
||||
/**
|
||||
* RedisTemplate配置
|
||||
* @param lettuceConnectionFactory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
|
||||
log.info(" --- redis config init --- ");
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
|
||||
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
|
||||
|
||||
// key序列化
|
||||
redisTemplate.setKeySerializer(stringSerializer);
|
||||
// value序列化
|
||||
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
// Hash key序列化
|
||||
redisTemplate.setHashKeySerializer(stringSerializer);
|
||||
// Hash value序列化
|
||||
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存配置管理器
|
||||
*
|
||||
* @param factory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager(LettuceConnectionFactory factory) {
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();
|
||||
// 配置序列化(解决乱码的问题),并且配置缓存默认有效期 6小时
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
|
||||
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
|
||||
//.disableCachingNullValues();
|
||||
|
||||
// 以锁写入的方式创建RedisCacheWriter对象
|
||||
//update-begin-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*
|
||||
RedisCacheWriter writer = new JeecgRedisCacheWriter(factory, Duration.ofMillis(50L));
|
||||
//RedisCacheWriter.lockingRedisCacheWriter(factory);
|
||||
// 创建默认缓存配置对象
|
||||
/* 默认配置,设置缓存有效期 1小时*/
|
||||
//RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
|
||||
// 自定义配置test:demo 的超时时间为 5分钟
|
||||
RedisCacheManager cacheManager = RedisCacheManager.builder(writer).cacheDefaults(redisCacheConfiguration)
|
||||
.withInitialCacheConfigurations(singletonMap(CacheConstant.SYS_DICT_TABLE_CACHE,
|
||||
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).disableCachingNullValues()
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))))
|
||||
.withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
|
||||
.withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_RANKING, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))
|
||||
.withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_PAGE_LIST, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))
|
||||
.transactionAware().build();
|
||||
//update-end-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* redis 监听配置
|
||||
*
|
||||
* @param redisConnectionFactory redis 配置
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory, RedisReceiver redisReceiver, MessageListenerAdapter commonListenerAdapter) {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(redisConnectionFactory);
|
||||
container.addMessageListener(commonListenerAdapter, new ChannelTopic(GlobalConstants.REDIS_TOPIC_NAME));
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
MessageListenerAdapter commonListenerAdapter(RedisReceiver redisReceiver) {
|
||||
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(redisReceiver, "onMessage");
|
||||
messageListenerAdapter.setSerializer(jacksonSerializer());
|
||||
return messageListenerAdapter;
|
||||
}
|
||||
|
||||
private Jackson2JsonRedisSerializer jacksonSerializer() {
|
||||
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
|
||||
return jackson2JsonRedisSerializer;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package org.jeecg.common.modules.redis.listener;
|
||||
|
||||
import org.jeecg.common.base.BaseMap;
|
||||
|
||||
/**
|
||||
* @Description: 自定义消息监听
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:02
|
||||
*/
|
||||
public interface JeecgRedisListener {
|
||||
/**
|
||||
* 接受消息
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
void onMessage(BaseMap message);
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package org.jeecg.common.modules.redis.receiver;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.Data;
|
||||
import org.jeecg.common.base.BaseMap;
|
||||
import org.jeecg.common.constant.GlobalConstants;
|
||||
import org.jeecg.common.modules.redis.listener.JeecgRedisListener;
|
||||
import org.jeecg.common.util.SpringContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author zyf
|
||||
*/
|
||||
@Component
|
||||
@Data
|
||||
public class RedisReceiver {
|
||||
|
||||
|
||||
/**
|
||||
* 接受消息并调用业务逻辑处理器
|
||||
*
|
||||
* @param params
|
||||
*/
|
||||
public void onMessage(BaseMap params) {
|
||||
Object handlerName = params.get(GlobalConstants.HANDLER_NAME);
|
||||
JeecgRedisListener messageListener = SpringContextHolder.getHandler(handlerName.toString(), JeecgRedisListener.class);
|
||||
if (ObjectUtil.isNotEmpty(messageListener)) {
|
||||
messageListener.onMessage(params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,250 +0,0 @@
|
|||
package org.jeecg.common.modules.redis.writer;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.dao.PessimisticLockingFailureException;
|
||||
import org.springframework.data.redis.cache.CacheStatistics;
|
||||
import org.springframework.data.redis.cache.CacheStatisticsCollector;
|
||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* 该类参照 DefaultRedisCacheWriter 重写了 remove 方法实现通配符*删除
|
||||
*
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:18
|
||||
*/
|
||||
@Slf4j
|
||||
public class JeecgRedisCacheWriter implements RedisCacheWriter {
|
||||
|
||||
private final RedisConnectionFactory connectionFactory;
|
||||
private final Duration sleepTime;
|
||||
|
||||
public JeecgRedisCacheWriter(RedisConnectionFactory connectionFactory) {
|
||||
this(connectionFactory, Duration.ZERO);
|
||||
}
|
||||
|
||||
public JeecgRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime) {
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
|
||||
Assert.notNull(sleepTime, "SleepTime must not be null!");
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.sleepTime = sleepTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
this.execute(name, (connection) -> {
|
||||
if (shouldExpireWithin(ttl)) {
|
||||
connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());
|
||||
} else {
|
||||
connection.set(key, value);
|
||||
}
|
||||
|
||||
return "OK";
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] get(String name, byte[] key) {
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
return (byte[])this.execute(name, (connection) -> {
|
||||
return connection.get(key);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return (byte[])this.execute(name, (connection) -> {
|
||||
if (this.isLockingCacheWriter()) {
|
||||
this.doLock(name, connection);
|
||||
}
|
||||
|
||||
Object var7;
|
||||
try {
|
||||
boolean put;
|
||||
if (shouldExpireWithin(ttl)) {
|
||||
put = connection.set(key, value, Expiration.from(ttl), SetOption.ifAbsent());
|
||||
} else {
|
||||
put = connection.setNX(key, value);
|
||||
}
|
||||
|
||||
if (!put) {
|
||||
byte[] var11 = connection.get(key);
|
||||
return var11;
|
||||
}
|
||||
|
||||
var7 = null;
|
||||
} finally {
|
||||
if (this.isLockingCacheWriter()) {
|
||||
this.doUnlock(name, connection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (byte[])var7;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String name, byte[] key) {
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
String keyString = new String(key);
|
||||
log.info("redis remove key:" + keyString);
|
||||
String keyIsAll = "*";
|
||||
if(keyString!=null && keyString.endsWith(keyIsAll)){
|
||||
execute(name, connection -> {
|
||||
// 获取某个前缀所拥有的所有的键,某个前缀开头,后面肯定是*
|
||||
Set<byte[]> keys = connection.keys(key);
|
||||
int delNum = 0;
|
||||
for (byte[] keyByte : keys) {
|
||||
delNum += connection.del(keyByte);
|
||||
}
|
||||
return delNum;
|
||||
});
|
||||
}else{
|
||||
this.execute(name, (connection) -> {
|
||||
return connection.del(new byte[][]{key});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean(String name, byte[] pattern) {
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
Assert.notNull(pattern, "Pattern must not be null!");
|
||||
this.execute(name, (connection) -> {
|
||||
boolean wasLocked = false;
|
||||
|
||||
try {
|
||||
if (this.isLockingCacheWriter()) {
|
||||
this.doLock(name, connection);
|
||||
wasLocked = true;
|
||||
}
|
||||
|
||||
byte[][] keys = (byte[][])((Set)Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())).toArray(new byte[0][]);
|
||||
if (keys.length > 0) {
|
||||
connection.del(keys);
|
||||
}
|
||||
} finally {
|
||||
if (wasLocked && this.isLockingCacheWriter()) {
|
||||
this.doUnlock(name, connection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return "OK";
|
||||
});
|
||||
}
|
||||
|
||||
void lock(String name) {
|
||||
this.execute(name, (connection) -> {
|
||||
return this.doLock(name, connection);
|
||||
});
|
||||
}
|
||||
|
||||
void unlock(String name) {
|
||||
this.executeLockFree((connection) -> {
|
||||
this.doUnlock(name, connection);
|
||||
});
|
||||
}
|
||||
|
||||
private Boolean doLock(String name, RedisConnection connection) {
|
||||
return connection.setNX(createCacheLockKey(name), new byte[0]);
|
||||
}
|
||||
|
||||
private Long doUnlock(String name, RedisConnection connection) {
|
||||
return connection.del(new byte[][]{createCacheLockKey(name)});
|
||||
}
|
||||
|
||||
boolean doCheckLock(String name, RedisConnection connection) {
|
||||
return connection.exists(createCacheLockKey(name));
|
||||
}
|
||||
|
||||
private boolean isLockingCacheWriter() {
|
||||
return !this.sleepTime.isZero() && !this.sleepTime.isNegative();
|
||||
}
|
||||
|
||||
private <T> T execute(String name, Function<RedisConnection, T> callback) {
|
||||
RedisConnection connection = this.connectionFactory.getConnection();
|
||||
|
||||
try {
|
||||
this.checkAndPotentiallyWaitUntilUnlocked(name, connection);
|
||||
return callback.apply(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void executeLockFree(Consumer<RedisConnection> callback) {
|
||||
RedisConnection connection = this.connectionFactory.getConnection();
|
||||
|
||||
try {
|
||||
callback.accept(connection);
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
|
||||
if (this.isLockingCacheWriter()) {
|
||||
try {
|
||||
while(this.doCheckLock(name, connection)) {
|
||||
Thread.sleep(this.sleepTime.toMillis());
|
||||
}
|
||||
|
||||
} catch (InterruptedException var4) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name), var4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldExpireWithin(@Nullable Duration ttl) {
|
||||
return ttl != null && !ttl.isZero() && !ttl.isNegative();
|
||||
}
|
||||
|
||||
private static byte[] createCacheLockKey(String name) {
|
||||
return (name + "~lock").getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
//update-begin-author:zyf date:20220216 for:升级springboot版本到2.4.0+以后需要实现的方法*
|
||||
private final CacheStatisticsCollector statistics = CacheStatisticsCollector.create();
|
||||
@Override
|
||||
public CacheStatistics getCacheStatistics(String cacheName) {
|
||||
return statistics.getCacheStatistics(cacheName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearStatistics(String name) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisCacheWriter withStatisticsCollector(CacheStatisticsCollector cacheStatisticsCollector) {
|
||||
return null;
|
||||
}
|
||||
//update-begin-author:zyf date:20220216 for:升级springboot版本到2.4.0+以后需要实现的方法*
|
||||
}
|
|
@ -1,613 +0,0 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* redis 工具类
|
||||
* @Author Scott
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class RedisUtil {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* 指定缓存失效时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean expire(String key, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key 获取过期时间
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @return 时间(秒) 返回0代表为永久有效
|
||||
*/
|
||||
public long getExpire(String key) {
|
||||
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断key是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
try {
|
||||
return redisTemplate.hasKey(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
*
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void del(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
//springboot2.4后用法
|
||||
redisTemplate.delete(Arrays.asList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================String=============================
|
||||
/**
|
||||
* 普通缓存获取
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return key == null ? null : redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean set(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||
* @return true成功 false 失败
|
||||
*/
|
||||
public boolean set(String key, Object value, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
} else {
|
||||
set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增
|
||||
*
|
||||
* @param key 键
|
||||
* @param by 要增加几(大于0)
|
||||
* @return
|
||||
*/
|
||||
public long incr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递增因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递减
|
||||
*
|
||||
* @param key 键
|
||||
* @param by 要减少几(小于0)
|
||||
* @return
|
||||
*/
|
||||
public long decr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递减因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, -delta);
|
||||
}
|
||||
|
||||
// ================================Map=================================
|
||||
/**
|
||||
* HashGet
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return 值
|
||||
*/
|
||||
public Object hget(String key, String item) {
|
||||
return redisTemplate.opsForHash().get(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取hashKey对应的所有键值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 对应的多个键值
|
||||
*/
|
||||
public Map<Object, Object> hmget(String key) {
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @return true 成功 false 失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet 并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @param time 时间(秒)
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除hash表中的值
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 可以使多个 不能为null
|
||||
*/
|
||||
public void hdel(String key, Object... item) {
|
||||
redisTemplate.opsForHash().delete(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断hash表中是否有该项的值
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hHasKey(String key, String item) {
|
||||
return redisTemplate.opsForHash().hasKey(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要增加几(大于0)
|
||||
* @return
|
||||
*/
|
||||
public double hincr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, by);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递减
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要减少记(小于0)
|
||||
* @return
|
||||
*/
|
||||
public double hdecr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, -by);
|
||||
}
|
||||
|
||||
// ============================set=============================
|
||||
/**
|
||||
* 根据key获取Set中的所有值
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public Set<Object> sGet(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据value从一个set中查询,是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean sHasKey(String key, Object value) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().isMember(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据放入set缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSet(String key, Object... values) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().add(key, values);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将set数据放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSetAndTime(String key, long time, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().add(key, values);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取set缓存的长度
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long sGetSetSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除值为value的
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long setRemove(String key, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().remove(key, values);
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// ===============================list=================================
|
||||
|
||||
/**
|
||||
* 获取list缓存的内容
|
||||
*
|
||||
* @param key 键
|
||||
* @param start 开始
|
||||
* @param end 结束 0 到 -1代表所有值
|
||||
* @return
|
||||
*/
|
||||
public List<Object> lGet(String key, long start, long end) {
|
||||
try {
|
||||
return redisTemplate.opsForList().range(key, start, end);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取list缓存的长度
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long lGetListSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过索引 获取list中的值
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
|
||||
* @return
|
||||
*/
|
||||
public Object lGetIndex(String key, long index) {
|
||||
try {
|
||||
return redisTemplate.opsForList().index(key, index);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引修改list中的某条数据
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lUpdateIndex(String key, long index, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().set(key, index, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除N个值为value
|
||||
*
|
||||
* @param key 键
|
||||
* @param count 移除多少个
|
||||
* @param value 值
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long lRemove(String key, long count, Object value) {
|
||||
try {
|
||||
Long remove = redisTemplate.opsForList().remove(key, count, value);
|
||||
return remove;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定前缀的一系列key
|
||||
* 使用scan命令代替keys, Redis是单线程处理,keys命令在KEY数量较多时,
|
||||
* 操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求
|
||||
* @param keyPrefix
|
||||
* @return
|
||||
*/
|
||||
private Set<String> keys(String keyPrefix) {
|
||||
String realKey = keyPrefix + "*";
|
||||
|
||||
try {
|
||||
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
|
||||
Set<String> binaryKeys = new HashSet<>();
|
||||
//springboot2.4后用法
|
||||
Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(realKey).count(Integer.MAX_VALUE).build());
|
||||
while (cursor.hasNext()) {
|
||||
binaryKeys.add(new String(cursor.next()));
|
||||
}
|
||||
|
||||
return binaryKeys;
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定前缀的一系列key
|
||||
* @param keyPrefix
|
||||
*/
|
||||
public void removeAll(String keyPrefix) {
|
||||
try {
|
||||
Set<String> keys = keys(keyPrefix);
|
||||
redisTemplate.delete(keys);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext.
|
||||
*
|
||||
* @author zyf
|
||||
*/
|
||||
@Slf4j
|
||||
public class SpringContextHolder implements ApplicationContextAware {
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
// NOSONAR
|
||||
SpringContextHolder.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得存储在静态变量中的ApplicationContext.
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
checkApplicationContext();
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
public static <T> T getBean(String name) {
|
||||
checkApplicationContext();
|
||||
return (T) applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
public static <T> T getHandler(String name, Class<T> cls) {
|
||||
T t = null;
|
||||
if (ObjectUtil.isNotEmpty(name)) {
|
||||
checkApplicationContext();
|
||||
try {
|
||||
t = applicationContext.getBean(name, cls);
|
||||
} catch (Exception e) {
|
||||
log.warn("Customize redis listener handle [ " + name + " ], does not exist!");
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
checkApplicationContext();
|
||||
return applicationContext.getBean(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除applicationContext静态变量.
|
||||
*/
|
||||
public static void cleanApplicationContext() {
|
||||
applicationContext = null;
|
||||
}
|
||||
|
||||
private static void checkApplicationContext() {
|
||||
if (applicationContext == null) {
|
||||
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>jeecg-boot-parent</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
|||
<modules>
|
||||
<module>jeecg-boot-base-api</module>
|
||||
<module>jeecg-boot-base-core</module>
|
||||
<module>jeecg-boot-base-tools</module>
|
||||
</modules>
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>jeecg-boot-parent</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -18,30 +18,4 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>SpringCloud</id>
|
||||
<!-- 引入springboot独立启动-->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<!-- 引入微服务Starter依赖 -->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-starter-cloud</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
|
@ -1,35 +0,0 @@
|
|||
//package org.jeecg;
|
||||
//
|
||||
//import org.jeecg.common.base.BaseMap;
|
||||
//import org.jeecg.common.constant.GlobalConstants;
|
||||
//import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.boot.CommandLineRunner;
|
||||
//import org.springframework.boot.SpringApplication;
|
||||
//import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
//import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
//import org.springframework.data.redis.core.RedisTemplate;
|
||||
//
|
||||
//@SpringBootApplication
|
||||
//@EnableFeignClients
|
||||
//public class JeecgDemoCloudApplication implements CommandLineRunner {
|
||||
// @Autowired
|
||||
// private RedisTemplate<String, Object> redisTemplate;
|
||||
//
|
||||
// public static void main(String[] args) {
|
||||
// SpringApplication.run(JeecgDemoCloudApplication.class, args);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 启动的时候,触发下gateway网关刷新
|
||||
// *
|
||||
// * 解决: 先启动gateway后启动服务,Swagger接口文档访问不通的问题
|
||||
// * @param args
|
||||
// */
|
||||
// @Override
|
||||
// public void run(String... args) {
|
||||
// BaseMap params = new BaseMap();
|
||||
// params.put(GlobalConstants.HANDLER_NAME, GlobalConstants.LODER_ROUDER_HANDLER);
|
||||
// //刷新网关
|
||||
// redisTemplate.convertAndSend(GlobalConstants.REDIS_TOPIC_NAME, params);
|
||||
// }
|
||||
//}
|
|
@ -1,5 +0,0 @@
|
|||
server:
|
||||
port: 7002
|
||||
spring:
|
||||
application:
|
||||
name: jeecg-demo
|
|
@ -11,6 +11,6 @@ WORKDIR /jeecg-boot
|
|||
EXPOSE 8080
|
||||
|
||||
ADD ./src/main/resources/jeecg ./config/jeecg
|
||||
ADD ./target/jeecg-boot-module-system-3.3.0.jar ./
|
||||
ADD ./target/jeecg-boot-module-system-3.4.0.jar ./
|
||||
|
||||
CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-3.3.0.jar
|
||||
CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-3.4.0.jar
|
|
@ -4,31 +4,12 @@
|
|||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-parent</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jeecg-boot-module-system</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun Repository</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jeecg</id>
|
||||
<name>jeecg Repository</name>
|
||||
<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
|
@ -74,32 +55,4 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<!-- SpringCloud运行环境 -->
|
||||
<profile>
|
||||
<id>SpringCloud</id>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<!-- SpringCloud运行环境的配置,排除system模块jar里的yaml -->
|
||||
<excludes>
|
||||
<exclude>application.yml</exclude>
|
||||
<exclude>application-*.yml</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.xml</include>
|
||||
<include>**/*.json</include>
|
||||
<include>**/*.ftl</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -15,14 +15,12 @@ import java.net.InetAddress;
|
|||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* 单体启动类(采用此类启动为单体模式)
|
||||
* 特别提醒:
|
||||
* 1.需要集成mongodb请删除 exclude={MongoAutoConfiguration.class}
|
||||
* 2.切换微服务 勾选profile的SpringCloud,这个类就无法启动,启动会报错
|
||||
* 单体启动类
|
||||
* 报错提醒: 未集成mongo报错,可以打开启动类上面的注释 exclude={MongoAutoConfiguration.class}
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
|
||||
//@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
|
||||
public class JeecgSystemApplication extends SpringBootServletInitializer {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -48,6 +48,8 @@ public class LoginController {
|
|||
@Autowired
|
||||
private ISysUserService sysUserService;
|
||||
@Autowired
|
||||
private ISysPermissionService sysPermissionService;
|
||||
@Autowired
|
||||
private SysBaseApiImpl sysBaseApi;
|
||||
@Autowired
|
||||
private ISysLogService logService;
|
||||
|
@ -492,6 +494,17 @@ public class LoginController {
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 切换菜单表为vue3的表
|
||||
*/
|
||||
@GetMapping(value = "/switchVue3Menu")
|
||||
public Result<String> switchVue3Menu(HttpServletResponse response) {
|
||||
Result<String> res = new Result<String>();
|
||||
sysPermissionService.switchVue3Menu();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* app登录
|
||||
|
|
|
@ -25,6 +25,14 @@ public interface SysPermissionMapper extends BaseMapper<SysPermission> {
|
|||
* @return
|
||||
*/
|
||||
public List<TreeModel> queryListByParentId(@Param("parentId") String parentId);
|
||||
|
||||
/**
|
||||
* 切换vue3菜单
|
||||
*/
|
||||
@Update("alter table sys_permission rename to sys_permission_v2")
|
||||
public void backupVue2Menu();
|
||||
@Update("alter table sys_permission_v3 rename to sys_permission")
|
||||
public void changeVue3Menu();
|
||||
|
||||
/**
|
||||
* 根据用户查询用户权限
|
||||
|
|
|
@ -18,7 +18,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||
* @since 2018-12-21
|
||||
*/
|
||||
public interface ISysPermissionService extends IService<SysPermission> {
|
||||
|
||||
/**
|
||||
* 切换vue3菜单
|
||||
*/
|
||||
public void switchVue3Menu();
|
||||
|
||||
/**
|
||||
* 通过父id查询菜单
|
||||
* @param parentId 父id
|
||||
|
|
|
@ -60,6 +60,12 @@ public class SysPermissionServiceImpl extends ServiceImpl<SysPermissionMapper, S
|
|||
return sysPermissionMapper.queryListByParentId(parentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchVue3Menu() {
|
||||
sysPermissionMapper.backupVue2Menu();
|
||||
sysPermissionMapper.changeVue3Menu();
|
||||
}
|
||||
|
||||
/**
|
||||
* 真实删除
|
||||
*/
|
||||
|
|
|
@ -183,6 +183,8 @@ jeecg:
|
|||
safeMode: false
|
||||
# 签名密钥串(前后端要一致,正式发布请自行修改)
|
||||
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
|
||||
# 签名拦截接口
|
||||
signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys
|
||||
# 本地:local\Minio:minio\阿里云:alioss
|
||||
uploadType: local
|
||||
# 前端访问地址
|
||||
|
|
|
@ -183,6 +183,8 @@ jeecg:
|
|||
safeMode: false
|
||||
# 签名密钥串(前后端要一致,正式发布请自行修改)
|
||||
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
|
||||
# 签名拦截接口
|
||||
signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys
|
||||
# 本地:local\Minio:minio\阿里云:alioss
|
||||
uploadType: alioss
|
||||
# 前端访问地址
|
||||
|
|
|
@ -183,6 +183,8 @@ jeecg:
|
|||
safeMode: false
|
||||
# 签名密钥串(前后端要一致,正式发布请自行修改)
|
||||
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
|
||||
# 签名拦截接口
|
||||
signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys
|
||||
# 本地:local\Minio:minio\阿里云:alioss
|
||||
uploadType: local
|
||||
# 前端访问地址
|
||||
|
|
|
@ -9,6 +9,6 @@ ${AnsiColor.BRIGHT_BLUE}
|
|||
|
||||
|
||||
${AnsiColor.BRIGHT_GREEN}
|
||||
Jeecg Boot Version: 3.3.0
|
||||
Jeecg Boot Version: 3.4.0
|
||||
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
|
||||
${AnsiColor.BLACK}
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
spring:
|
||||
datasource:
|
||||
druid:
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
loginUsername: admin
|
||||
loginPassword: 123456
|
||||
allow:
|
||||
web-stat-filter:
|
||||
enabled: true
|
||||
dynamic:
|
||||
druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
|
||||
# 连接池的配置信息
|
||||
# 初始化大小,最小,最大
|
||||
initial-size: 5
|
||||
min-idle: 5
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
# 打开PSCache,并且指定每个连接上PSCache的大小
|
||||
poolPreparedStatements: true
|
||||
maxPoolPreparedStatementPerConnectionSize: 20
|
||||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
|
||||
filters: stat,wall,slf4j
|
||||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
|
||||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
|
||||
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://${MYSQL-HOST:jeecg-boot-mysql}:${MYSQL-PORT:3306}/${MYSQL-DB:jeecg-boot}?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
username: ${MYSQL-USER:root}
|
||||
password: ${MYSQL-PWD:root}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 多数据源配置
|
||||
#multi-datasource1:
|
||||
#url: jdbc:mysql://localhost:3306/jeecg-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
#username: root
|
||||
#password: root
|
||||
#driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
#redis 配置
|
||||
redis:
|
||||
database: 0
|
||||
host: jeecg-boot-redis
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
|
||||
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
|
||||
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
|
||||
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
|
||||
shutdown-timeout: 100ms
|
||||
password:
|
||||
port: 6379
|
||||
#rabbitmq配置
|
||||
rabbitmq:
|
||||
host: jeecg-boot-rabbitmq
|
||||
username: guest
|
||||
password: guest
|
||||
port: 5672
|
||||
publisher-confirms: true
|
||||
publisher-returns: true
|
||||
virtual-host: /
|
||||
listener:
|
||||
simple:
|
||||
acknowledge-mode: manual
|
||||
#消费者的最小数量
|
||||
concurrency: 1
|
||||
#消费者的最大数量
|
||||
max-concurrency: 1
|
||||
#是否支持重试
|
||||
retry:
|
||||
enabled: true
|
||||
#minidao
|
||||
minidao :
|
||||
base-package: org.jeecg.modules.jmreport.*
|
||||
#jeecg专用配置
|
||||
jeecg :
|
||||
# 本地:local\Minio:minio\阿里云:alioss
|
||||
uploadType: local
|
||||
path :
|
||||
#文件上传根目录 设置
|
||||
upload: D://opt//upFiles
|
||||
#webapp文件路径
|
||||
webapp: D://opt//webapp
|
||||
shiro:
|
||||
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/map/**,/jmreport/bigscreen2/**
|
||||
#阿里云oss存储配置
|
||||
oss:
|
||||
endpoint: oss-cn-beijing.aliyuncs.com
|
||||
accessKey: ??
|
||||
secretKey: ??
|
||||
bucketName: jeecgdev
|
||||
staticDomain: ??
|
||||
# ElasticSearch 6设置
|
||||
elasticsearch:
|
||||
cluster-name: jeecg-ES
|
||||
cluster-nodes: 127.0.0.1:9200
|
||||
check-enabled: false
|
||||
# 表单设计器配置
|
||||
desform:
|
||||
# 主题颜色(仅支持 16进制颜色代码)
|
||||
theme-color: "#1890ff"
|
||||
# 文件、图片上传方式,可选项:qiniu(七牛云)、system(跟随系统配置)
|
||||
upload-type: system
|
||||
map:
|
||||
# 配置百度地图的AK,申请地址:https://lbs.baidu.com/apiconsole/key?application=key#/home
|
||||
baidu: ??
|
||||
# 在线预览文件服务器地址配置
|
||||
file-view-domain: 127.0.0.1:8012
|
||||
# minio文件上传
|
||||
minio:
|
||||
minio_url: http://minio.jeecg.com
|
||||
minio_name: ??
|
||||
minio_pass: ??
|
||||
bucketName: otatest
|
||||
#大屏报表参数设置
|
||||
jmreport:
|
||||
mode: dev
|
||||
#是否需要校验token
|
||||
is_verify_token: false
|
||||
#必须校验方法
|
||||
verify_methods: remove,delete,save,add,update
|
||||
#Wps在线文档
|
||||
wps:
|
||||
domain: https://wwo.wps.cn/office/
|
||||
appid: ??
|
||||
appsecret: ??
|
||||
#xxl-job配置
|
||||
xxljob:
|
||||
enabled: false
|
||||
adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin
|
||||
appname: ${spring.application.name}
|
||||
accessToken: ''
|
||||
logPath: logs/jeecg/job/jobhandler/
|
||||
logRetentionDays: 30
|
||||
#自定义路由配置 yml nacos database
|
||||
route:
|
||||
config:
|
||||
data-id: jeecg-gateway-router
|
||||
group: DEFAULT_GROUP
|
||||
data-type: yml
|
||||
#分布式锁配置
|
||||
redisson:
|
||||
address: jeecg-boot-redis:6379
|
||||
password:
|
||||
type: STANDALONE
|
||||
enabled: true
|
||||
#Mybatis输出sql日志
|
||||
logging:
|
||||
level:
|
||||
org.jeecg.modules.system.mapper : info
|
||||
#cas单点登录
|
||||
cas:
|
||||
prefixUrl: http://localhost:8888/cas
|
||||
#swagger
|
||||
knife4j:
|
||||
production: false
|
||||
basic:
|
||||
enable: true
|
||||
username: jeecg
|
||||
password: jeecg1314
|
||||
|
||||
#第三方登录
|
||||
justauth:
|
||||
enabled: true
|
||||
type:
|
||||
GITHUB:
|
||||
client-id: ??
|
||||
client-secret: ??
|
||||
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback
|
||||
WECHAT_ENTERPRISE:
|
||||
client-id: ??
|
||||
client-secret: ??
|
||||
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback
|
||||
agent-id: 1000002
|
||||
DINGTALK:
|
||||
client-id: ??
|
||||
client-secret: ??
|
||||
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback
|
||||
cache:
|
||||
type: default
|
||||
prefix: 'demo::'
|
||||
timeout: 1h
|
||||
#第三方APP对接
|
||||
third-app:
|
||||
enabled: false
|
||||
type:
|
||||
#企业微信
|
||||
WECHAT_ENTERPRISE:
|
||||
enabled: false
|
||||
#CORP_ID
|
||||
client-id: ??
|
||||
#SECRET
|
||||
client-secret: ??
|
||||
agent-id: ??
|
||||
#自建应用秘钥(新版企微需要配置)
|
||||
# agent-app-secret: ??
|
||||
#钉钉
|
||||
DINGTALK:
|
||||
enabled: false
|
||||
# appKey
|
||||
client-id: ??
|
||||
# appSecret
|
||||
client-secret: ??
|
||||
agent-id: ??
|
|
@ -1,59 +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>
|
||||
<artifactId>jeecg-boot-starter</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jeecg-boot-starter-cloud</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-system-cloud-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Nacos注册中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<!-- Nacos配置中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<!-- feign -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<!-- sentinel限流熔断降级 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
<!--sentinel持久化 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- springboot up 2.6.6 -->
|
||||
<!-- Alibaba Nacos 配置 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<!-- SpringCloud 负载均衡 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<!-- springboot up 2.6.6 -->
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,195 +0,0 @@
|
|||
package org.jeecg.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.DateUtils;
|
||||
import org.jeecg.common.util.PathMatcherUtil;
|
||||
import org.jeecg.common.config.mqtoken.UserTokenContext;
|
||||
import org.jeecg.config.mybatis.TenantContext;
|
||||
import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
|
||||
import org.jeecg.config.sign.util.HttpUtils;
|
||||
import org.jeecg.config.sign.util.SignUtil;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.support.SpringDecoder;
|
||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.form.spring.SpringFormEncoder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @Description: FeignConfig
|
||||
* @author: JeecgBoot
|
||||
*/
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class FeignConfig {
|
||||
|
||||
/**
|
||||
* 设置feign header参数
|
||||
* 【X_ACCESS_TOKEN】【X_SIGN】【X_TIMESTAMP】
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public RequestInterceptor requestInterceptor() {
|
||||
return requestTemplate -> {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (null != attributes) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
log.debug("Feign request: {}", request.getRequestURI());
|
||||
// 将token信息放入header中
|
||||
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
|
||||
if(token==null || "".equals(token)){
|
||||
token = request.getParameter("token");
|
||||
}
|
||||
log.info("Feign Login Request token: {}", token);
|
||||
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
|
||||
|
||||
//update-begin-author:taoyan date:2022-6-23 for: issues/I5AO20 多租户微服务之间调用找不到tenant-id(自定义页面)
|
||||
// 将tenantId信息放入header中
|
||||
String tenantId = request.getHeader(CommonConstant.TENANT_ID);
|
||||
if(tenantId==null || "".equals(tenantId)){
|
||||
tenantId = request.getParameter(CommonConstant.TENANT_ID);
|
||||
}
|
||||
log.info("Feign Login Request tenantId: {}", tenantId);
|
||||
requestTemplate.header(CommonConstant.TENANT_ID, tenantId);
|
||||
//update-end-author:taoyan date:2022-6-23 for: issues/I5AO20 多租户微服务之间调用找不到tenant-id(自定义页面)
|
||||
|
||||
}else{
|
||||
String token = UserTokenContext.getToken();
|
||||
log.info("Feign no Login token: {}", token);
|
||||
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
|
||||
|
||||
//update-begin-author:taoyan date:2022-6-23 for: issues/I5AO20 多租户微服务之间调用找不到tenant-id(自定义页面)
|
||||
String tenantId = TenantContext.getTenant();
|
||||
log.info("Feign no Login tenantId: {}", tenantId);
|
||||
requestTemplate.header(CommonConstant.TENANT_ID, tenantId);
|
||||
//update-end-author:taoyan date:2022-6-23 for: issues/I5AO20 多租户微服务之间调用找不到tenant-id(自定义页面)
|
||||
}
|
||||
|
||||
//================================================================================================================
|
||||
//针对特殊接口,进行加签验证 ——根据URL地址过滤请求 【字典表参数签名验证】
|
||||
if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.SIGN_URL_LIST),requestTemplate.path())) {
|
||||
try {
|
||||
log.info("============================ [begin] fegin starter url ============================");
|
||||
log.info(requestTemplate.path());
|
||||
log.info(requestTemplate.method());
|
||||
String queryLine = requestTemplate.queryLine();
|
||||
if(queryLine!=null && queryLine.startsWith("?")){
|
||||
queryLine = queryLine.substring(1);
|
||||
}
|
||||
log.info(queryLine);
|
||||
if(requestTemplate.body()!=null){
|
||||
log.info(new String(requestTemplate.body()));
|
||||
}
|
||||
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
|
||||
String sign = SignUtil.getParamsSign(allParams);
|
||||
log.info(" Feign request params sign: {}",sign);
|
||||
log.info("============================ [end] fegin starter url ============================");
|
||||
requestTemplate.header(CommonConstant.X_SIGN, sign);
|
||||
//update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
|
||||
requestTemplate.header(CommonConstant.X_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
|
||||
//update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
//================================================================================================================
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Feign 客户端的日志记录,默认级别为NONE
|
||||
* Logger.Level 的具体级别如下:
|
||||
* NONE:不记录任何信息
|
||||
* BASIC:仅记录请求方法、URL以及响应状态码和执行时间
|
||||
* HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
|
||||
* FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
|
||||
*/
|
||||
@Bean
|
||||
Logger.Level feignLoggerLevel() {
|
||||
return Logger.Level.FULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feign支持文件上传
|
||||
* @param messageConverters
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
@Scope("prototype")
|
||||
public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||
return new SpringFormEncoder(new SpringEncoder(messageConverters));
|
||||
}
|
||||
|
||||
// update-begin--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
|
||||
@Bean
|
||||
public Encoder feignEncoder() {
|
||||
return new SpringEncoder(feignHttpMessageConverter());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Decoder feignDecoder() {
|
||||
return new SpringDecoder(feignHttpMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置解码器为fastjson
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
|
||||
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
|
||||
return () -> httpMessageConverters;
|
||||
}
|
||||
|
||||
private FastJsonHttpMessageConverter getFastJsonConverter() {
|
||||
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
||||
|
||||
List<MediaType> supportedMediaTypes = new ArrayList<>();
|
||||
MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
|
||||
supportedMediaTypes.add(mediaTypeJson);
|
||||
converter.setSupportedMediaTypes(supportedMediaTypes);
|
||||
FastJsonConfig config = new FastJsonConfig();
|
||||
config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
|
||||
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
|
||||
converter.setFastJsonConfig(config);
|
||||
|
||||
return converter;
|
||||
}
|
||||
// update-end--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package org.jeecg.starter.cloud.config;
|
||||
|
||||
/**
|
||||
* @Description: 跨域设置 (升级SpringBoot2.6.6)
|
||||
* @author: zyf
|
||||
* @date: 2022/02/21
|
||||
* @version: V1.0
|
||||
*/
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
@Configuration
|
||||
public class GwCorsFilter {
|
||||
|
||||
@Bean
|
||||
public CorsWebFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.setAllowCredentials(true);
|
||||
config.addAllowedOriginPattern("*");
|
||||
config.addAllowedHeader("*");
|
||||
config.addAllowedMethod("*");
|
||||
config.setMaxAge(18000L);
|
||||
org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source =
|
||||
new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(new PathPatternParser());
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsWebFilter(source);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
//package org.jeecg.starter.cloud.config;
|
||||
//
|
||||
//import feign.Client;
|
||||
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
//import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
//import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
//import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//
|
||||
//@Configuration
|
||||
//public class PersonBeanConfiguration {
|
||||
//
|
||||
// /**
|
||||
// * 创建FeignClient
|
||||
// */
|
||||
// @Bean
|
||||
// @ConditionalOnMissingBean
|
||||
// public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||
// SpringClientFactory clientFactory) {
|
||||
// return new LoadBalancerFeignClient(new Client.Default(null, null),
|
||||
// cachingFactory, clientFactory);
|
||||
// }
|
||||
//}
|
|
@ -1,6 +0,0 @@
|
|||
//package org.jeecg.starter.cloud.feign;
|
||||
//
|
||||
//public interface IJeecgFeignService {
|
||||
//
|
||||
// <T> T newInstance(Class<T> apiType, String name);
|
||||
//}
|
|
@ -1,60 +0,0 @@
|
|||
//package org.jeecg.starter.cloud.feign.impl;
|
||||
//
|
||||
//import feign.*;
|
||||
//import feign.codec.Decoder;
|
||||
//import feign.codec.Encoder;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.jeecg.common.constant.CommonConstant;
|
||||
//import org.jeecg.starter.cloud.feign.IJeecgFeignService;
|
||||
//import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
//import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
//import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||
//import org.springframework.context.annotation.Import;
|
||||
//import org.springframework.stereotype.Service;
|
||||
//import org.springframework.web.context.request.RequestContextHolder;
|
||||
//import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
//
|
||||
//import javax.servlet.http.HttpServletRequest;
|
||||
//
|
||||
//@Service
|
||||
//@Slf4j
|
||||
//@ConditionalOnClass(Feign.class)
|
||||
//@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
//@Import(FeignClientsConfiguration.class)
|
||||
//public class JeecgFeignService implements IJeecgFeignService {
|
||||
//
|
||||
//
|
||||
// //Feign 原生构造器
|
||||
// Feign.Builder builder;
|
||||
//
|
||||
// //创建构造器
|
||||
// public JeecgFeignService(Decoder decoder, Encoder encoder, Client client, Contract contract) {
|
||||
// this.builder = Feign.builder()
|
||||
// .client(client)
|
||||
// .encoder(encoder)
|
||||
// .decoder(decoder)
|
||||
// .contract(contract);
|
||||
//
|
||||
// builder.requestInterceptor(requestTemplate -> {
|
||||
// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
// if (null != attributes) {
|
||||
// HttpServletRequest request = attributes.getRequest();
|
||||
// log.info("Feign request: {}", request.getRequestURI());
|
||||
// // 将token信息放入header中
|
||||
// String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
|
||||
// if(token==null){
|
||||
// token = request.getParameter("token");
|
||||
// }
|
||||
// log.info("Feign request token: {}", token);
|
||||
// requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public <T> T newInstance(Class<T> clientClass, String serviceName) {
|
||||
// return builder.target(clientClass, String.format("http://%s/", serviceName));
|
||||
// }
|
||||
//}
|
|
@ -1,55 +0,0 @@
|
|||
package org.jeecg.starter.cloud.handler;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @Description: 全局Sentinel自定义信息处理(需要启动Sentinel客户端)
|
||||
* @author: zyf
|
||||
* @date: 2022/02/18
|
||||
* @version: V1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class CustomSentinelExceptionHandler implements BlockExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
|
||||
|
||||
String msg = null;
|
||||
|
||||
if (ex instanceof FlowException) {
|
||||
msg = "访问频繁,请稍候再试";
|
||||
|
||||
} else if (ex instanceof DegradeException) {
|
||||
msg = "系统降级";
|
||||
|
||||
} else if (ex instanceof ParamFlowException) {
|
||||
msg = "热点参数限流";
|
||||
|
||||
} else if (ex instanceof SystemBlockException) {
|
||||
msg = "系统规则限流或降级";
|
||||
|
||||
} else if (ex instanceof AuthorityException) {
|
||||
msg = "授权规则不通过";
|
||||
|
||||
} else {
|
||||
msg = "未知限流降级";
|
||||
}
|
||||
// http状态码
|
||||
response.setStatus(200);
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setHeader("Content-Type", "application/json;charset=utf-8");
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.getWriter().write("{\"code\":500,\"message\":"+msg+"}");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package org.jeecg.starter.cloud.interceptor;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.util.IpUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 【示例】sentinel ip和参数授权规则拦截器(黑名单白名单)
|
||||
* 1. 有参数origin的时候走参数拦截规则
|
||||
* 2. 当参数为空时走ip拦截模式
|
||||
*
|
||||
* @author zyf
|
||||
*/
|
||||
@Component
|
||||
public class DefaultRequestOriginParser implements RequestOriginParser {
|
||||
@Override
|
||||
public String parseOrigin(HttpServletRequest request) {
|
||||
//基于请求参数,origin对应授权规则中的流控应用名称,也可通过getHeader传参
|
||||
String origin = request.getParameter("origin");
|
||||
if (StringUtils.isNotEmpty(origin)) {
|
||||
return origin;
|
||||
} else {
|
||||
//当参数为空使用ip拦截模式
|
||||
String ip = IpUtils.getIpAddr(request);
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
spring:
|
||||
#升级SpringBoot2.6.6,允许循环依赖
|
||||
main:
|
||||
allow-circular-references: true
|
||||
profiles:
|
||||
# 当前激活环境
|
||||
active: '@profile.name@'
|
||||
cloud:
|
||||
#配置Bus id(远程推送事件)
|
||||
bus:
|
||||
id: ${spring.application.name}:${server.port}
|
||||
nacos:
|
||||
config:
|
||||
# Nacos 认证用户
|
||||
username: @config.username@
|
||||
# Nacos 认证密码
|
||||
password: @config.password@
|
||||
# 命名空间 常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等
|
||||
namespace: @config.namespace@
|
||||
# 配置中心地址
|
||||
server-addr: @config.server-addr@
|
||||
# 配置对应的分组
|
||||
group: @config.group@
|
||||
# 配置文件后缀
|
||||
file-extension: yaml
|
||||
prefix: @prefix.name@
|
||||
# 支持多个共享 Data Id 的配置,优先级小于extension-configs,自定义 Data Id 配置 属性是个集合,内部由 Config POJO 组成。Config 有 3 个属性,分别是 dataId, group 以及 refresh
|
||||
# shared-configs[0]:
|
||||
# data-id: @prefix.name@-sharding.yaml #分库分表配置
|
||||
# group: @config.group@
|
||||
# refresh: false
|
||||
# shared-configs[1]:
|
||||
# data-id: @prefix.name@-common.yaml # 配置文件名-Data Id
|
||||
# group: @config.group@ # 默认为DEFAULT_GROUP
|
||||
# refresh: false # 是否动态刷新,默认为false
|
||||
discovery:
|
||||
namespace: @config.namespace@
|
||||
server-addr: @config.server-addr@
|
||||
watch:
|
||||
enabled: false
|
||||
# feign启用sentinel
|
||||
feign:
|
||||
sentinel:
|
||||
enabled: true
|
||||
okhttp:
|
||||
enabled: true
|
||||
httpclient:
|
||||
enabled: false
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
#不设置connectTimeout会导致readTimeout设置不生效
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
compression:
|
||||
request:
|
||||
enabled: true
|
||||
response:
|
||||
enabled: true
|
|
@ -1,21 +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>
|
||||
<artifactId>jeecg-boot-starter</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jeecg-boot-starter-job</artifactId>
|
||||
<description>jeecg-boot-starter-定时任务</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>${xxl-job-core.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,228 +0,0 @@
|
|||
package com.xxl.job.core.executor;
|
||||
|
||||
import com.xxl.job.core.biz.AdminBiz;
|
||||
import com.xxl.job.core.biz.client.AdminBizClient;
|
||||
import com.xxl.job.core.handler.IJobHandler;
|
||||
import com.xxl.job.core.log.XxlJobFileAppender;
|
||||
import com.xxl.job.core.server.EmbedServer;
|
||||
import com.xxl.job.core.thread.JobLogFileCleanThread;
|
||||
import com.xxl.job.core.thread.JobThread;
|
||||
import com.xxl.job.core.thread.TriggerCallbackThread;
|
||||
import com.xxl.job.core.util.IpUtil;
|
||||
import com.xxl.job.core.util.NetUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 重写目的修改默认端口9999为10000避免和网关冲突
|
||||
*/
|
||||
public class XxlJobExecutor {
|
||||
private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class);
|
||||
|
||||
// ---------------------- param ----------------------
|
||||
private String adminAddresses;
|
||||
private String accessToken;
|
||||
private String appname;
|
||||
private String address;
|
||||
private String ip;
|
||||
private int port;
|
||||
private String logPath;
|
||||
private int logRetentionDays;
|
||||
|
||||
public void setAdminAddresses(String adminAddresses) {
|
||||
this.adminAddresses = adminAddresses;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public void setAppname(String appname) {
|
||||
this.appname = appname;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setLogPath(String logPath) {
|
||||
this.logPath = logPath;
|
||||
}
|
||||
|
||||
public void setLogRetentionDays(int logRetentionDays) {
|
||||
this.logRetentionDays = logRetentionDays;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- start + stop ----------------------
|
||||
public void start() throws Exception {
|
||||
|
||||
// init logpath
|
||||
XxlJobFileAppender.initLogPath(logPath);
|
||||
|
||||
// init invoker, admin-client
|
||||
initAdminBizList(adminAddresses, accessToken);
|
||||
|
||||
|
||||
// init JobLogFileCleanThread
|
||||
JobLogFileCleanThread.getInstance().start(logRetentionDays);
|
||||
|
||||
// init TriggerCallbackThread
|
||||
TriggerCallbackThread.getInstance().start();
|
||||
|
||||
// init executor-server
|
||||
initEmbedServer(address, ip, port, appname, accessToken);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
// destory executor-server
|
||||
stopEmbedServer();
|
||||
|
||||
// destory jobThreadRepository
|
||||
if (jobThreadRepository.size() > 0) {
|
||||
for (Map.Entry<Integer, JobThread> item : jobThreadRepository.entrySet()) {
|
||||
JobThread oldJobThread = removeJobThread(item.getKey(), "web container destroy and kill the job.");
|
||||
// wait for job thread push result to callback queue
|
||||
if (oldJobThread != null) {
|
||||
try {
|
||||
oldJobThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, JobThread destroy(join) error, jobId:{}", item.getKey(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
jobThreadRepository.clear();
|
||||
}
|
||||
jobHandlerRepository.clear();
|
||||
|
||||
|
||||
// destory JobLogFileCleanThread
|
||||
JobLogFileCleanThread.getInstance().toStop();
|
||||
|
||||
// destory TriggerCallbackThread
|
||||
TriggerCallbackThread.getInstance().toStop();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- admin-client (rpc invoker) ----------------------
|
||||
private static List<AdminBiz> adminBizList;
|
||||
|
||||
private void initAdminBizList(String adminAddresses, String accessToken) throws Exception {
|
||||
if (adminAddresses != null && adminAddresses.trim().length() > 0) {
|
||||
for (String address : adminAddresses.trim().split(",")) {
|
||||
if (address != null && address.trim().length() > 0) {
|
||||
|
||||
AdminBiz adminBiz = new AdminBizClient(address.trim(), accessToken);
|
||||
|
||||
if (adminBizList == null) {
|
||||
adminBizList = new ArrayList<AdminBiz>();
|
||||
}
|
||||
adminBizList.add(adminBiz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<AdminBiz> getAdminBizList() {
|
||||
return adminBizList;
|
||||
}
|
||||
|
||||
// ---------------------- executor-server (rpc provider) ----------------------
|
||||
private EmbedServer embedServer = null;
|
||||
|
||||
private void initEmbedServer(String address, String ip, int port, String appname, String accessToken) throws Exception {
|
||||
|
||||
// fill ip port 修改默认端口
|
||||
port = port > 0 ? port : NetUtil.findAvailablePort(10000);
|
||||
ip = (ip != null && ip.trim().length() > 0) ? ip : IpUtil.getIp();
|
||||
|
||||
// generate address
|
||||
if (address == null || address.trim().length() == 0) {
|
||||
String ip_port_address = IpUtil.getIpPort(ip, port); // registry-address:default use address to registry , otherwise use ip:port if address is null
|
||||
address = "http://{ip_port}/".replace("{ip_port}", ip_port_address);
|
||||
}
|
||||
|
||||
// accessToken
|
||||
if (accessToken == null || accessToken.trim().length() == 0) {
|
||||
logger.warn(">>>>>>>>>>> xxl-job accessToken is empty. To ensure system security, please set the accessToken.");
|
||||
}
|
||||
|
||||
// start
|
||||
embedServer = new EmbedServer();
|
||||
embedServer.start(address, port, appname, accessToken);
|
||||
}
|
||||
|
||||
private void stopEmbedServer() {
|
||||
// stop provider factory
|
||||
if (embedServer != null) {
|
||||
try {
|
||||
embedServer.stop();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- job handler repository ----------------------
|
||||
private static ConcurrentMap<String, IJobHandler> jobHandlerRepository = new ConcurrentHashMap<String, IJobHandler>();
|
||||
|
||||
public static IJobHandler loadJobHandler(String name) {
|
||||
return jobHandlerRepository.get(name);
|
||||
}
|
||||
|
||||
public static IJobHandler registJobHandler(String name, IJobHandler jobHandler) {
|
||||
logger.info(">>>>>>>>>>> xxl-job register jobhandler success, name:{}, jobHandler:{}", name, jobHandler);
|
||||
return jobHandlerRepository.put(name, jobHandler);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- job thread repository ----------------------
|
||||
private static ConcurrentMap<Integer, JobThread> jobThreadRepository = new ConcurrentHashMap<Integer, JobThread>();
|
||||
|
||||
public static JobThread registJobThread(int jobId, IJobHandler handler, String removeOldReason) {
|
||||
JobThread newJobThread = new JobThread(jobId, handler);
|
||||
newJobThread.start();
|
||||
logger.info(">>>>>>>>>>> xxl-job regist JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler});
|
||||
|
||||
JobThread oldJobThread = jobThreadRepository.put(jobId, newJobThread); // putIfAbsent | oh my god, map's put method return the old value!!!
|
||||
if (oldJobThread != null) {
|
||||
oldJobThread.toStop(removeOldReason);
|
||||
oldJobThread.interrupt();
|
||||
}
|
||||
|
||||
return newJobThread;
|
||||
}
|
||||
|
||||
public static JobThread removeJobThread(int jobId, String removeOldReason) {
|
||||
JobThread oldJobThread = jobThreadRepository.remove(jobId);
|
||||
if (oldJobThread != null) {
|
||||
oldJobThread.toStop(removeOldReason);
|
||||
oldJobThread.interrupt();
|
||||
|
||||
return oldJobThread;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JobThread loadJobThread(int jobId) {
|
||||
JobThread jobThread = jobThreadRepository.get(jobId);
|
||||
return jobThread;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package org.jeecg.boot.starter.job.annotation;
|
||||
|
||||
import org.jeecg.boot.starter.job.config.XxlJobConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author zyf
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({ XxlJobConfiguration.class })
|
||||
public @interface EnableXxlJob {
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package org.jeecg.boot.starter.job.config;
|
||||
|
||||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.boot.starter.job.prop.XxlJobProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 定时任务配置
|
||||
*
|
||||
* @author jeecg
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(value = XxlJobProperties.class)
|
||||
@ConditionalOnProperty(value = "jeecg.xxljob.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class XxlJobConfiguration {
|
||||
|
||||
|
||||
@Autowired
|
||||
private XxlJobProperties xxlJobProperties;
|
||||
|
||||
//@Bean(initMethod = "start", destroyMethod = "destroy")
|
||||
@Bean
|
||||
@ConditionalOnClass()
|
||||
public XxlJobSpringExecutor xxlJobExecutor() {
|
||||
log.info(">>>>>>>>>>> xxl-job config init.");
|
||||
//log.info(">>>> ip="+xxlJobProperties.getIp()+",Port="+xxlJobProperties.getPort()+",address="+xxlJobProperties.getAdminAddresses());
|
||||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
|
||||
xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
|
||||
xxlJobSpringExecutor.setAppname(xxlJobProperties.getAppname());
|
||||
//update-begin--Author:scott -- Date:20210305 -- for:system服务和demo服务有办法同时使用xxl-job吗 #2313---
|
||||
//xxlJobSpringExecutor.setIp(xxlJobProperties.getIp());
|
||||
//xxlJobSpringExecutor.setPort(xxlJobProperties.getPort());
|
||||
//update-end--Author:scott -- Date:20210305 -- for:system服务和demo服务有办法同时使用xxl-job吗 #2313---
|
||||
xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
|
||||
xxlJobSpringExecutor.setLogPath(xxlJobProperties.getLogPath());
|
||||
xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getLogRetentionDays());
|
||||
return xxlJobSpringExecutor;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package org.jeecg.boot.starter.job.prop;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "jeecg.xxljob")
|
||||
public class XxlJobProperties {
|
||||
|
||||
|
||||
private String adminAddresses;
|
||||
|
||||
|
||||
private String appname;
|
||||
|
||||
|
||||
private String ip;
|
||||
|
||||
|
||||
private int port;
|
||||
|
||||
|
||||
private String accessToken;
|
||||
|
||||
|
||||
private String logPath;
|
||||
|
||||
|
||||
private int logRetentionDays;
|
||||
|
||||
/**
|
||||
* 是否开启xxljob
|
||||
*/
|
||||
private Boolean enable = true;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.jeecg.boot.starter.job.config.XxlJobConfiguration
|
|
@ -1,41 +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>
|
||||
<artifactId>jeecg-boot-starter</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jeecg-boot-starter-lock</artifactId>
|
||||
<description>jeecg-boot-starter-分布式锁</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base-tools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,57 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.annotation;
|
||||
|
||||
import org.jeecg.boot.starter.lock.enums.LockModel;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Redisson分布式锁注解
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface JLock {
|
||||
|
||||
/**
|
||||
* 锁的模式:如果不设置,自动模式,当参数只有一个.使用 REENTRANT 参数多个 MULTIPLE
|
||||
*/
|
||||
LockModel lockModel() default LockModel.AUTO;
|
||||
|
||||
/**
|
||||
* 如果keys有多个,如果不设置,则使用 联锁
|
||||
* @return
|
||||
*/
|
||||
String[] lockKey() default {};
|
||||
|
||||
/**
|
||||
* key的静态常量:当key的spel的值是LIST,数组时使用+号连接将会被spel认为这个变量是个字符串
|
||||
* @return
|
||||
*/
|
||||
String keyConstant() default "";
|
||||
|
||||
|
||||
/**
|
||||
* 锁超时时间,默认30000毫秒
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
long expireSeconds() default 30000L;
|
||||
|
||||
/**
|
||||
* 等待加锁超时时间,默认10000毫秒 -1 则表示一直等待
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
long waitTime() default 10000L;
|
||||
|
||||
/**
|
||||
* 未取到锁时提示信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String failMsg() default "获取锁失败,请稍后重试";
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.annotation;
|
||||
|
||||
/**
|
||||
* @author zyf
|
||||
*/
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 防止重复提交的注解
|
||||
*
|
||||
* @author 2019年6月18日
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@Documented
|
||||
public @interface JRepeat {
|
||||
|
||||
/**
|
||||
* 超时时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int lockTime();
|
||||
|
||||
|
||||
/**
|
||||
* redis 锁key的
|
||||
*
|
||||
* @return redis 锁key
|
||||
*/
|
||||
String lockKey() default "";
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.annotation;
|
||||
|
||||
/**
|
||||
* @author zyf
|
||||
* @date 2019/10/26 18:26
|
||||
*/
|
||||
|
||||
/**
|
||||
* 分布式锁枚举类
|
||||
* @author zyf
|
||||
*/
|
||||
public enum LockConstant {
|
||||
/**
|
||||
* 通用锁常量
|
||||
*/
|
||||
COMMON("commonLock:", 1, 500, "请勿重复点击");
|
||||
/**
|
||||
* 分布式锁前缀
|
||||
*/
|
||||
private String keyPrefix;
|
||||
/**
|
||||
* 等到最大时间,强制获取锁
|
||||
*/
|
||||
private int waitTime;
|
||||
/**
|
||||
* 锁失效时间
|
||||
*/
|
||||
private int leaseTime;
|
||||
/**
|
||||
* 加锁提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
LockConstant(String keyPrefix, int waitTime, int leaseTime, String message) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
this.waitTime = waitTime;
|
||||
this.leaseTime = leaseTime;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getKeyPrefix() {
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
public void setKeyPrefix(String keyPrefix) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
public int getWaitTime() {
|
||||
return waitTime;
|
||||
}
|
||||
|
||||
public void setWaitTime(int waitTime) {
|
||||
this.waitTime = waitTime;
|
||||
}
|
||||
|
||||
public int getLeaseTime() {
|
||||
return leaseTime;
|
||||
}
|
||||
|
||||
public void setLeaseTime(int leaseTime) {
|
||||
this.leaseTime = leaseTime;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.aspect;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zyf
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseAspect {
|
||||
|
||||
/**
|
||||
* 通过spring SpEL 获取参数
|
||||
*
|
||||
* @param key 定义的key值 以#开头 例如:#user
|
||||
* @param parameterNames 形参
|
||||
* @param values 形参值
|
||||
* @param keyConstant key的常亮
|
||||
* @return
|
||||
*/
|
||||
public List<String> getValueBySpEL(String key, String[] parameterNames, Object[] values, String keyConstant) {
|
||||
List<String> keys = new ArrayList<>();
|
||||
if (!key.contains("#")) {
|
||||
String s = "redis:lock:" + key + keyConstant;
|
||||
log.debug("lockKey:" + s);
|
||||
keys.add(s);
|
||||
return keys;
|
||||
}
|
||||
//spel解析器
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
//spel上下文
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
for (int i = 0; i < parameterNames.length; i++) {
|
||||
context.setVariable(parameterNames[i], values[i]);
|
||||
}
|
||||
Expression expression = parser.parseExpression(key);
|
||||
Object value = expression.getValue(context);
|
||||
if (value != null) {
|
||||
if (value instanceof List) {
|
||||
List value1 = (List) value;
|
||||
for (Object o : value1) {
|
||||
addKeys(keys, o, keyConstant);
|
||||
}
|
||||
} else if (value.getClass().isArray()) {
|
||||
Object[] obj = (Object[]) value;
|
||||
for (Object o : obj) {
|
||||
addKeys(keys, o, keyConstant);
|
||||
}
|
||||
} else {
|
||||
addKeys(keys, value, keyConstant);
|
||||
}
|
||||
}
|
||||
log.info("表达式key={},value={}", key, keys);
|
||||
return keys;
|
||||
}
|
||||
|
||||
private void addKeys(List<String> keys, Object o, String keyConstant) {
|
||||
keys.add("redis:lock:" + o.toString() + keyConstant);
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.aspect;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.jeecg.boot.starter.lock.annotation.JLock;
|
||||
import org.jeecg.boot.starter.lock.enums.LockModel;
|
||||
import org.redisson.RedissonMultiLock;
|
||||
import org.redisson.RedissonRedLock;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* 分布式锁解析器
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class DistributedLockHandler extends BaseAspect{
|
||||
|
||||
|
||||
@Autowired(required = false)
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 切面环绕通知
|
||||
*
|
||||
* @param joinPoint
|
||||
* @param jLock
|
||||
* @return Object
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Around("@annotation(jLock)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, JLock jLock) {
|
||||
Object obj = null;
|
||||
log.info("进入RedisLock环绕通知...");
|
||||
RLock rLock = getLock(joinPoint, jLock);
|
||||
boolean res = false;
|
||||
//获取超时时间
|
||||
long expireSeconds = jLock.expireSeconds();
|
||||
//等待多久,n秒内获取不到锁,则直接返回
|
||||
long waitTime = jLock.waitTime();
|
||||
//执行aop
|
||||
if (rLock != null) {
|
||||
try {
|
||||
if (waitTime == -1) {
|
||||
res = true;
|
||||
//一直等待加锁
|
||||
rLock.lock(expireSeconds, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
res = rLock.tryLock(waitTime, expireSeconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (res) {
|
||||
obj = joinPoint.proceed();
|
||||
} else {
|
||||
log.error("获取锁异常");
|
||||
}
|
||||
} finally {
|
||||
if (res) {
|
||||
rLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("结束RedisLock环绕通知...");
|
||||
return obj;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private RLock getLock(ProceedingJoinPoint joinPoint, JLock jLock) {
|
||||
String[] keys = jLock.lockKey();
|
||||
if (keys.length == 0) {
|
||||
throw new RuntimeException("keys不能为空");
|
||||
}
|
||||
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
LockModel lockModel = jLock.lockModel();
|
||||
RLock rLock = null;
|
||||
String keyConstant = jLock.keyConstant();
|
||||
if (lockModel.equals(LockModel.AUTO)) {
|
||||
if (keys.length > 1) {
|
||||
lockModel = LockModel.REDLOCK;
|
||||
} else {
|
||||
lockModel = LockModel.REENTRANT;
|
||||
}
|
||||
}
|
||||
if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {
|
||||
throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
|
||||
}
|
||||
switch (lockModel) {
|
||||
case FAIR:
|
||||
rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));
|
||||
break;
|
||||
case REDLOCK:
|
||||
List<RLock> rLocks = new ArrayList<>();
|
||||
for (String key : keys) {
|
||||
List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
|
||||
for (String s : valueBySpEL) {
|
||||
rLocks.add(redissonClient.getLock(s));
|
||||
}
|
||||
}
|
||||
RLock[] locks = new RLock[rLocks.size()];
|
||||
int index = 0;
|
||||
for (RLock r : rLocks) {
|
||||
locks[index++] = r;
|
||||
}
|
||||
rLock = new RedissonRedLock(locks);
|
||||
break;
|
||||
case MULTIPLE:
|
||||
rLocks = new ArrayList<>();
|
||||
|
||||
for (String key : keys) {
|
||||
List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
|
||||
for (String s : valueBySpEL) {
|
||||
rLocks.add(redissonClient.getLock(s));
|
||||
}
|
||||
}
|
||||
locks = new RLock[rLocks.size()];
|
||||
index = 0;
|
||||
for (RLock r : rLocks) {
|
||||
locks[index++] = r;
|
||||
}
|
||||
rLock = new RedissonMultiLock(locks);
|
||||
break;
|
||||
case REENTRANT:
|
||||
List<String> valueBySpEL = getValueBySpEL(keys[0], parameterNames, args, keyConstant);
|
||||
//如果spel表达式是数组或者LIST 则使用红锁
|
||||
if (valueBySpEL.size() == 1) {
|
||||
rLock = redissonClient.getLock(valueBySpEL.get(0));
|
||||
} else {
|
||||
locks = new RLock[valueBySpEL.size()];
|
||||
index = 0;
|
||||
for (String s : valueBySpEL) {
|
||||
locks[index++] = redissonClient.getLock(s);
|
||||
}
|
||||
rLock = new RedissonRedLock(locks);
|
||||
}
|
||||
break;
|
||||
case READ:
|
||||
rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).readLock();
|
||||
break;
|
||||
case WRITE:
|
||||
rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).writeLock();
|
||||
break;
|
||||
}
|
||||
return rLock;
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.aspect;
|
||||
|
||||
/**
|
||||
* @author zyf
|
||||
*/
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.jeecg.boot.starter.lock.annotation.JRepeat;
|
||||
import org.jeecg.boot.starter.lock.client.RedissonLockClient;
|
||||
import org.jeecg.common.exception.JeecgCloudException;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 防止重复提交分布式锁拦截器
|
||||
*
|
||||
* @author 2019年6月18日
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class RepeatSubmitAspect extends BaseAspect{
|
||||
|
||||
@Resource
|
||||
private RedissonLockClient redissonLockClient;
|
||||
|
||||
/***
|
||||
* 定义controller切入点拦截规则,拦截JRepeat注解的业务方法
|
||||
*/
|
||||
@Pointcut("@annotation(jRepeat)")
|
||||
public void pointCut(JRepeat jRepeat) {
|
||||
}
|
||||
|
||||
/**
|
||||
* AOP分布式锁拦截
|
||||
*
|
||||
* @param joinPoint
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Around("pointCut(jRepeat)")
|
||||
public Object repeatSubmit(ProceedingJoinPoint joinPoint,JRepeat jRepeat) throws Throwable {
|
||||
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
|
||||
if (Objects.nonNull(jRepeat)) {
|
||||
// 获取参数
|
||||
Object[] args = joinPoint.getArgs();
|
||||
// 进行一些参数的处理,比如获取订单号,操作人id等
|
||||
StringBuffer lockKeyBuffer = new StringBuffer();
|
||||
String key =getValueBySpEL(jRepeat.lockKey(), parameterNames, args,"RepeatSubmit").get(0);
|
||||
// 公平加锁,lockTime后锁自动释放
|
||||
boolean isLocked = false;
|
||||
try {
|
||||
isLocked = redissonLockClient.fairLock(key, TimeUnit.SECONDS, jRepeat.lockTime());
|
||||
// 如果成功获取到锁就继续执行
|
||||
if (isLocked) {
|
||||
// 执行进程
|
||||
return joinPoint.proceed();
|
||||
} else {
|
||||
// 未获取到锁
|
||||
throw new JeecgCloudException("请勿重复提交");
|
||||
}
|
||||
} finally {
|
||||
// 如果锁还存在,在方法执行完成后,释放锁
|
||||
if (isLocked) {
|
||||
redissonLockClient.unlock(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.client;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式锁实现基于Redisson
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RedissonLockClient {
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* 获取锁
|
||||
*/
|
||||
public RLock getLock(String lockKey) {
|
||||
return redissonClient.getLock(lockKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加锁操作
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean tryLock(String lockName, long expireSeconds) {
|
||||
return tryLock(lockName, 0, expireSeconds);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加锁操作
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean tryLock(String lockName, long waitTime, long expireSeconds) {
|
||||
RLock rLock = getLock(lockName);
|
||||
boolean getLock = false;
|
||||
try {
|
||||
getLock = rLock.tryLock(waitTime, expireSeconds, TimeUnit.SECONDS);
|
||||
if (getLock) {
|
||||
log.info("获取锁成功,lockName={}", lockName);
|
||||
} else {
|
||||
log.info("获取锁失败,lockName={}", lockName);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.error("获取式锁异常,lockName=" + lockName, e);
|
||||
getLock = false;
|
||||
}
|
||||
return getLock;
|
||||
}
|
||||
|
||||
|
||||
public boolean fairLock(String lockKey, TimeUnit unit, int leaseTime) {
|
||||
RLock fairLock = redissonClient.getFairLock(lockKey);
|
||||
try {
|
||||
boolean existKey = existKey(lockKey);
|
||||
// 已经存在了,就直接返回
|
||||
if (existKey) {
|
||||
return false;
|
||||
}
|
||||
return fairLock.tryLock(3, leaseTime, unit);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean existKey(String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
/**
|
||||
* 锁lockKey
|
||||
*
|
||||
* @param lockKey
|
||||
* @return
|
||||
*/
|
||||
public RLock lock(String lockKey) {
|
||||
RLock lock = getLock(lockKey);
|
||||
lock.lock();
|
||||
return lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁lockKey
|
||||
*
|
||||
* @param lockKey
|
||||
* @param leaseTime
|
||||
* @return
|
||||
*/
|
||||
public RLock lock(String lockKey, long leaseTime) {
|
||||
RLock lock = getLock(lockKey);
|
||||
lock.lock(leaseTime, TimeUnit.SECONDS);
|
||||
return lock;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
*/
|
||||
public void unlock(String lockName) {
|
||||
try {
|
||||
redissonClient.getLock(lockName).unlock();
|
||||
} catch (Exception e) {
|
||||
log.error("解锁异常,lockName=" + lockName, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.boot.starter.lock.core.RedissonManager;
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Redisson自动化配置
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConditionalOnClass(RedissonProperties.class)
|
||||
@EnableConfigurationProperties(RedissonProperties.class)
|
||||
public class RedissonConfiguration {
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RedissonClient.class)
|
||||
public RedissonClient redissonClient(RedissonProperties redissonProperties) {
|
||||
RedissonManager redissonManager = new RedissonManager(redissonProperties);
|
||||
log.info("RedissonManager初始化完成,当前连接方式:" + redissonProperties.getType() + ",连接地址:" + redissonProperties.getAddress());
|
||||
return redissonManager.getRedisson();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.core;
|
||||
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.impl.ClusterRedissonConfigStrategyImpl;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.impl.MasterslaveRedissonConfigStrategyImpl;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.impl.SentinelRedissonConfigStrategyImpl;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.impl.StandaloneRedissonConfigStrategyImpl;
|
||||
import org.jeecg.boot.starter.lock.enums.RedisConnectionType;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
|
||||
/**
|
||||
* Redisson配置管理器,用于初始化的redisson实例
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-12
|
||||
*/
|
||||
@Slf4j
|
||||
public class RedissonManager {
|
||||
|
||||
private Config config = new Config();
|
||||
|
||||
private Redisson redisson = null;
|
||||
|
||||
public RedissonManager() {
|
||||
}
|
||||
|
||||
public RedissonManager(RedissonProperties redissonProperties) {
|
||||
//装配开关
|
||||
Boolean enabled = redissonProperties.getEnabled();
|
||||
if (enabled) {
|
||||
try {
|
||||
config = RedissonConfigFactory.getInstance().createConfig(redissonProperties);
|
||||
redisson = (Redisson) Redisson.create(config);
|
||||
} catch (Exception e) {
|
||||
log.error("Redisson初始化错误", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Redisson getRedisson() {
|
||||
return redisson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redisson连接方式配置工厂
|
||||
* 双重检查锁
|
||||
*/
|
||||
static class RedissonConfigFactory {
|
||||
|
||||
private RedissonConfigFactory() {
|
||||
}
|
||||
|
||||
private static volatile RedissonConfigFactory factory = null;
|
||||
|
||||
public static RedissonConfigFactory getInstance() {
|
||||
if (factory == null) {
|
||||
synchronized (Object.class) {
|
||||
if (factory == null) {
|
||||
factory = new RedissonConfigFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据连接类型創建连接方式的配置
|
||||
*
|
||||
* @param redissonProperties
|
||||
* @return Config
|
||||
*/
|
||||
Config createConfig(RedissonProperties redissonProperties) {
|
||||
Preconditions.checkNotNull(redissonProperties);
|
||||
Preconditions.checkNotNull(redissonProperties.getAddress(), "redis地址未配置");
|
||||
RedisConnectionType connectionType = redissonProperties.getType();
|
||||
// 声明连接方式
|
||||
RedissonConfigStrategy redissonConfigStrategy;
|
||||
if (connectionType.equals(RedisConnectionType.SENTINEL)) {
|
||||
redissonConfigStrategy = new SentinelRedissonConfigStrategyImpl();
|
||||
} else if (connectionType.equals(RedisConnectionType.CLUSTER)) {
|
||||
redissonConfigStrategy = new ClusterRedissonConfigStrategyImpl();
|
||||
} else if (connectionType.equals(RedisConnectionType.MASTERSLAVE)) {
|
||||
redissonConfigStrategy = new MasterslaveRedissonConfigStrategyImpl();
|
||||
} else {
|
||||
redissonConfigStrategy = new StandaloneRedissonConfigStrategyImpl();
|
||||
}
|
||||
Preconditions.checkNotNull(redissonConfigStrategy, "连接方式创建异常");
|
||||
|
||||
return redissonConfigStrategy.createRedissonConfig(redissonProperties);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.core.strategy;
|
||||
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
/**
|
||||
* Redisson配置构建接口
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
public interface RedissonConfigStrategy {
|
||||
|
||||
/**
|
||||
* 根据不同的Redis配置策略创建对应的Config
|
||||
*
|
||||
* @param redissonProperties
|
||||
* @return Config
|
||||
*/
|
||||
Config createRedissonConfig(RedissonProperties redissonProperties);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.core.strategy.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.jeecg.boot.starter.lock.enums.GlobalConstant;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
|
||||
/**
|
||||
* 集群方式Redisson配置
|
||||
* cluster方式至少6个节点(3主3从)
|
||||
* 配置方式:127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class ClusterRedissonConfigStrategyImpl implements RedissonConfigStrategy {
|
||||
|
||||
@Override
|
||||
public Config createRedissonConfig(RedissonProperties redissonProperties) {
|
||||
Config config = new Config();
|
||||
try {
|
||||
String address = redissonProperties.getAddress();
|
||||
String password = redissonProperties.getPassword();
|
||||
String[] addrTokens = address.split(",");
|
||||
// 设置集群(cluster)节点的服务IP和端口
|
||||
for (int i = 0; i < addrTokens.length; i++) {
|
||||
config.useClusterServers().addNodeAddress(GlobalConstant.REDIS_CONNECTION_PREFIX + addrTokens[i]);
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
config.useClusterServers().setPassword(password);
|
||||
}
|
||||
}
|
||||
log.info("初始化集群方式Config,连接地址:" + address);
|
||||
} catch (Exception e) {
|
||||
log.error("集群Redisson初始化错误", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.core.strategy.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.jeecg.boot.starter.lock.enums.GlobalConstant;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 主从方式Redisson配置
|
||||
* <p>配置方式: 127.0.0.1:6379(主),127.0.0.1:6380(子),127.0.0.1:6381(子)</p>
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class MasterslaveRedissonConfigStrategyImpl implements RedissonConfigStrategy {
|
||||
|
||||
@Override
|
||||
public Config createRedissonConfig(RedissonProperties redissonProperties) {
|
||||
Config config = new Config();
|
||||
try {
|
||||
String address = redissonProperties.getAddress();
|
||||
String password = redissonProperties.getPassword();
|
||||
int database = redissonProperties.getDatabase();
|
||||
String[] addrTokens = address.split(",");
|
||||
String masterNodeAddr = addrTokens[0];
|
||||
// 设置主节点ip
|
||||
config.useMasterSlaveServers().setMasterAddress(masterNodeAddr);
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
config.useMasterSlaveServers().setPassword(password);
|
||||
}
|
||||
config.useMasterSlaveServers().setDatabase(database);
|
||||
// 设置从节点,移除第一个节点,默认第一个为主节点
|
||||
List<String> slaveList = new ArrayList<>();
|
||||
for (String addrToken : addrTokens) {
|
||||
slaveList.add(GlobalConstant.REDIS_CONNECTION_PREFIX + addrToken);
|
||||
}
|
||||
slaveList.remove(0);
|
||||
|
||||
config.useMasterSlaveServers().addSlaveAddress((String[]) slaveList.toArray());
|
||||
log.info("初始化主从方式Config,redisAddress:" + address);
|
||||
} catch (Exception e) {
|
||||
log.error("主从Redisson初始化错误", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.core.strategy.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.jeecg.boot.starter.lock.enums.GlobalConstant;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
|
||||
/**
|
||||
* 哨兵方式Redis连接配置
|
||||
* 比如sentinel.conf里配置为sentinel monitor my-sentinel-name 127.0.0.1 6379 2,那么这里就配置my-sentinel-name
|
||||
* 配置方式:my-sentinel-name,127.0.0.1:26379,127.0.0.1:26389,127.0.0.1:26399
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class SentinelRedissonConfigStrategyImpl implements RedissonConfigStrategy {
|
||||
|
||||
@Override
|
||||
public Config createRedissonConfig(RedissonProperties redissonProperties) {
|
||||
Config config = new Config();
|
||||
try {
|
||||
String address = redissonProperties.getAddress();
|
||||
String password = redissonProperties.getPassword();
|
||||
int database = redissonProperties.getDatabase();
|
||||
String[] addrTokens = address.split(",");
|
||||
String sentinelAliasName = addrTokens[0];
|
||||
// 设置redis配置文件sentinel.conf配置的sentinel别名
|
||||
config.useSentinelServers().setMasterName(sentinelAliasName);
|
||||
config.useSentinelServers().setDatabase(database);
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
config.useSentinelServers().setPassword(password);
|
||||
}
|
||||
// 设置哨兵节点的服务IP和端口
|
||||
for (int i = 1; i < addrTokens.length; i++) {
|
||||
config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX+ addrTokens[i]);
|
||||
}
|
||||
log.info("初始化哨兵方式Config,redisAddress:" + address);
|
||||
} catch (Exception e) {
|
||||
log.error("哨兵Redisson初始化错误", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.core.strategy.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
|
||||
import org.jeecg.boot.starter.lock.prop.RedissonProperties;
|
||||
import org.jeecg.boot.starter.lock.enums.GlobalConstant;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
|
||||
/**
|
||||
* 单机方式Redisson配置
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Slf4j
|
||||
public class StandaloneRedissonConfigStrategyImpl implements RedissonConfigStrategy {
|
||||
|
||||
@Override
|
||||
public Config createRedissonConfig(RedissonProperties redissonProperties) {
|
||||
Config config = new Config();
|
||||
try {
|
||||
String address = redissonProperties.getAddress();
|
||||
String password = redissonProperties.getPassword();
|
||||
int database = redissonProperties.getDatabase();
|
||||
String redisAddr = GlobalConstant.REDIS_CONNECTION_PREFIX + address;
|
||||
config.useSingleServer().setAddress(redisAddr);
|
||||
config.useSingleServer().setDatabase(database);
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
config.useSingleServer().setPassword(password);
|
||||
}
|
||||
log.info("初始化Redisson单机配置,连接地址:" + address);
|
||||
} catch (Exception e) {
|
||||
log.error("单机Redisson初始化错误", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.enums;
|
||||
|
||||
/**
|
||||
* 全局常量枚举
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
|
||||
public interface GlobalConstant {
|
||||
|
||||
/**
|
||||
* Redis地址连接前缀
|
||||
*/
|
||||
String REDIS_CONNECTION_PREFIX = "redis://";
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.enums;
|
||||
|
||||
/**
|
||||
* 锁的模式
|
||||
* @author jeecg
|
||||
*/
|
||||
public enum LockModel {
|
||||
//可重入锁
|
||||
REENTRANT,
|
||||
//公平锁
|
||||
FAIR,
|
||||
//联锁(可以把一组锁当作一个锁来加锁和释放)
|
||||
MULTIPLE,
|
||||
//红锁
|
||||
REDLOCK,
|
||||
//读锁
|
||||
READ,
|
||||
//写锁
|
||||
WRITE,
|
||||
//自动模式,当参数只有一个.使用 REENTRANT 参数多个 REDLOCK
|
||||
AUTO
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Redis连接方式
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RedisConnectionType {
|
||||
/**
|
||||
* 单机部署方式(默认)
|
||||
*/
|
||||
STANDALONE("standalone", "单机部署方式"),
|
||||
/**
|
||||
* 哨兵部署方式
|
||||
*/
|
||||
SENTINEL("sentinel", "哨兵部署方式"),
|
||||
/**
|
||||
* 集群部署方式
|
||||
*/
|
||||
CLUSTER("cluster", "集群方式"),
|
||||
/**
|
||||
* 主从部署方式
|
||||
*/
|
||||
MASTERSLAVE("masterslave", "主从部署方式");
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private final String code;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.prop;
|
||||
|
||||
import lombok.Data;
|
||||
import org.jeecg.boot.starter.lock.enums.RedisConnectionType;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Redisson配置映射类
|
||||
*
|
||||
* @author zyf
|
||||
* @date 2020-11-11
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "jeecg.redisson")
|
||||
public class RedissonProperties {
|
||||
|
||||
/**
|
||||
* redis主机地址,ip:port,多个用逗号(,)分隔
|
||||
*/
|
||||
private String address;
|
||||
/**
|
||||
* 连接类型
|
||||
*/
|
||||
private RedisConnectionType type;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 数据库(默认0)
|
||||
*/
|
||||
private int database;
|
||||
|
||||
/**
|
||||
* 是否装配redisson配置
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.jeecg.boot.starter.lock.config.RedissonConfiguration
|
||||
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.test;
|
||||
|
||||
import org.jeecg.boot.starter.lock.annotation.JLock;
|
||||
import org.jeecg.boot.starter.lock.annotation.JRepeat;
|
||||
import org.jeecg.boot.starter.lock.annotation.LockConstant;
|
||||
import org.jeecg.boot.starter.lock.client.RedissonLockClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class LockService {
|
||||
|
||||
@Resource
|
||||
private RedissonLockClient redissonLockClient;
|
||||
|
||||
int n = 10;
|
||||
|
||||
/**
|
||||
* 模拟秒杀(注解方式)
|
||||
*/
|
||||
@JLock(lockKey = "#productId", expireSeconds = 5000)
|
||||
public void seckill(String productId) {
|
||||
if (n <= 0) {
|
||||
System.out.println("活动已结束,请下次再来");
|
||||
return;
|
||||
}
|
||||
System.out.println(Thread.currentThread().getName() + ":秒杀到了商品");
|
||||
System.out.println(--n);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟秒杀(编程方式)
|
||||
*/
|
||||
public void seckill2(String productId) {
|
||||
redissonLockClient.tryLock(productId, 5000);
|
||||
if (n <= 0) {
|
||||
System.out.println("活动已结束,请下次再来");
|
||||
return;
|
||||
}
|
||||
System.out.println(Thread.currentThread().getName() + ":秒杀到了商品");
|
||||
System.out.println(--n);
|
||||
redissonLockClient.unlock(productId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试重复提交
|
||||
*/
|
||||
@JRepeat(lockKey = "#name", lockTime = 5)
|
||||
public void reSubmit(String name) {
|
||||
try {
|
||||
Thread.sleep(1500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("提交成功" + name);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.test;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = LockTestApplication.class)
|
||||
public class LockTest {
|
||||
@Autowired
|
||||
LockService lockService;
|
||||
|
||||
/**
|
||||
* 测试分布式锁(模拟秒杀)
|
||||
*/
|
||||
@Test
|
||||
public void test1() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(6);
|
||||
IntStream.range(0, 30).forEach(i -> executorService.submit(() -> {
|
||||
try {
|
||||
lockService.seckill("20120508784");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
executorService.awaitTermination(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试分布式锁(模拟秒杀)
|
||||
*/
|
||||
@Test
|
||||
public void test2() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(6);
|
||||
IntStream.range(0, 30).forEach(i -> executorService.submit(() -> {
|
||||
try {
|
||||
lockService.seckill2("20120508784");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
executorService.awaitTermination(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试分布式锁(模拟重复提交)
|
||||
*/
|
||||
@Test
|
||||
public void test3() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(6);
|
||||
IntStream.range(0, 20).forEach(i -> executorService.submit(() -> {
|
||||
try {
|
||||
lockService.reSubmit("test");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
executorService.awaitTermination(30, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = "org.jeecg")
|
||||
@EnableAspectJAutoProxy
|
||||
public class LockTestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LockTestApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package org.jeecg.boot.starter.lock.test;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TestUser {
|
||||
private String userId;
|
||||
private String userName;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
spring:
|
||||
redis:
|
||||
database: 0
|
||||
host: 127.0.0.1
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
|
||||
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
|
||||
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
|
||||
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
|
||||
shutdown-timeout: 100ms
|
||||
password: jeecg
|
||||
port: 6379
|
||||
jeecg :
|
||||
redisson:
|
||||
address: 127.0.0.1:6379
|
||||
password: jeecg
|
||||
type: STANDALONE
|
||||
enabled: true
|
|
@ -1,21 +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>
|
||||
<artifactId>jeecg-boot-starter</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jeecg-boot-starter-rabbitmq</artifactId>
|
||||
<description>jeecg-boot-starter-消息队列</description>
|
||||
<dependencies>
|
||||
<!-- 消息总线 rabbitmq -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,359 +0,0 @@
|
|||
package org.jeecg.boot.starter.rabbitmq.client;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.boot.starter.rabbitmq.event.EventObj;
|
||||
import org.jeecg.boot.starter.rabbitmq.event.JeecgRemoteApplicationEvent;
|
||||
import org.jeecg.boot.starter.rabbitmq.exchange.DelayExchangeBuilder;
|
||||
import org.jeecg.common.annotation.RabbitComponent;
|
||||
import org.jeecg.common.base.BaseMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.bus.BusProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 消息队列客户端
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class RabbitMqClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RabbitMqClient.class);
|
||||
|
||||
private final RabbitAdmin rabbitAdmin;
|
||||
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
|
||||
|
||||
@Resource
|
||||
private SimpleMessageListenerContainer messageListenerContainer;
|
||||
|
||||
@Resource
|
||||
BusProperties busProperties;
|
||||
@Resource
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
@Bean
|
||||
public void initQueue() {
|
||||
Map<String, Object> beansWithRqbbitComponentMap = this.applicationContext.getBeansWithAnnotation(RabbitComponent.class);
|
||||
Class<? extends Object> clazz = null;
|
||||
for (Map.Entry<String, Object> entry : beansWithRqbbitComponentMap.entrySet()) {
|
||||
log.info("初始化队列............");
|
||||
//获取到实例对象的class信息
|
||||
clazz = entry.getValue().getClass();
|
||||
Method[] methods = clazz.getMethods();
|
||||
RabbitListener rabbitListener = clazz.getAnnotation(RabbitListener.class);
|
||||
if (ObjectUtil.isNotEmpty(rabbitListener)) {
|
||||
createQueue(rabbitListener);
|
||||
}
|
||||
for (Method method : methods) {
|
||||
RabbitListener methodRabbitListener = method.getAnnotation(RabbitListener.class);
|
||||
if (ObjectUtil.isNotEmpty(methodRabbitListener)) {
|
||||
createQueue(methodRabbitListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化队列
|
||||
*
|
||||
* @param rabbitListener
|
||||
*/
|
||||
private void createQueue(RabbitListener rabbitListener) {
|
||||
String[] queues = rabbitListener.queues();
|
||||
DirectExchange directExchange = createExchange(DelayExchangeBuilder.DELAY_EXCHANGE);
|
||||
//创建交换机
|
||||
rabbitAdmin.declareExchange(directExchange);
|
||||
if (ObjectUtil.isNotEmpty(queues)) {
|
||||
for (String queueName : queues) {
|
||||
Properties result = rabbitAdmin.getQueueProperties(queueName);
|
||||
if (ObjectUtil.isEmpty(result)) {
|
||||
Queue queue = new Queue(queueName);
|
||||
addQueue(queue);
|
||||
Binding binding = BindingBuilder.bind(queue).to(directExchange).with(queueName);
|
||||
rabbitAdmin.declareBinding(binding);
|
||||
log.info("创建队列:" + queueName);
|
||||
}else{
|
||||
log.info("已有队列:" + queueName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Map sentObj = new HashMap<>();
|
||||
|
||||
|
||||
@Autowired
|
||||
public RabbitMqClient(RabbitAdmin rabbitAdmin, RabbitTemplate rabbitTemplate) {
|
||||
this.rabbitAdmin = rabbitAdmin;
|
||||
this.rabbitTemplate = rabbitTemplate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送远程事件
|
||||
*
|
||||
* @param handlerName
|
||||
* @param baseMap
|
||||
*/
|
||||
public void publishEvent(String handlerName, BaseMap baseMap) {
|
||||
EventObj eventObj = new EventObj();
|
||||
eventObj.setHandlerName(handlerName);
|
||||
eventObj.setBaseMap(baseMap);
|
||||
publisher.publishEvent(new JeecgRemoteApplicationEvent(eventObj, busProperties.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换Message对象
|
||||
*
|
||||
* @param messageType 返回消息类型 MessageProperties类中常量
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public Message getMessage(String messageType, Object msg) {
|
||||
MessageProperties messageProperties = new MessageProperties();
|
||||
messageProperties.setContentType(messageType);
|
||||
Message message = new Message(msg.toString().getBytes(), messageProperties);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 有绑定Key的Exchange发送
|
||||
*
|
||||
* @param routingKey
|
||||
* @param msg
|
||||
*/
|
||||
public void sendMessageToExchange(TopicExchange topicExchange, String routingKey, Object msg) {
|
||||
Message message = getMessage(MessageProperties.CONTENT_TYPE_JSON, msg);
|
||||
rabbitTemplate.send(topicExchange.getName(), routingKey, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 没有绑定KEY的Exchange发送
|
||||
*
|
||||
* @param exchange
|
||||
* @param msg
|
||||
*/
|
||||
public void sendMessageToExchange(TopicExchange topicExchange, AbstractExchange exchange, String msg) {
|
||||
addExchange(exchange);
|
||||
logger.info("RabbitMQ send " + exchange.getName() + "->" + msg);
|
||||
rabbitTemplate.convertAndSend(topicExchange.getName(), msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param queueName 队列名称
|
||||
* @param params 消息内容map
|
||||
*/
|
||||
public void sendMessage(String queueName, Object params) {
|
||||
log.info("发送消息到mq");
|
||||
try {
|
||||
rabbitTemplate.convertAndSend(DelayExchangeBuilder.DELAY_EXCHANGE, queueName, params, message -> {
|
||||
return message;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param queueName 队列名称
|
||||
*/
|
||||
public void sendMessage(String queueName) {
|
||||
this.send(queueName, this.sentObj, 0);
|
||||
this.sentObj.clear();
|
||||
}
|
||||
|
||||
|
||||
public RabbitMqClient put(String key, Object value) {
|
||||
this.sentObj.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟发送消息
|
||||
*
|
||||
* @param queueName 队列名称
|
||||
* @param params 消息内容params
|
||||
* @param expiration 延迟时间 单位毫秒
|
||||
*/
|
||||
public void sendMessage(String queueName, Object params, Integer expiration) {
|
||||
this.send(queueName, params, expiration);
|
||||
}
|
||||
|
||||
private void send(String queueName, Object params, Integer expiration) {
|
||||
Queue queue = new Queue(queueName);
|
||||
addQueue(queue);
|
||||
CustomExchange customExchange = DelayExchangeBuilder.buildExchange();
|
||||
rabbitAdmin.declareExchange(customExchange);
|
||||
Binding binding = BindingBuilder.bind(queue).to(customExchange).with(queueName).noargs();
|
||||
rabbitAdmin.declareBinding(binding);
|
||||
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
log.debug("发送时间:" + sf.format(new Date()));
|
||||
rabbitTemplate.convertAndSend(DelayExchangeBuilder.DEFAULT_DELAY_EXCHANGE, queueName, params, message -> {
|
||||
if (expiration != null && expiration > 0) {
|
||||
message.getMessageProperties().setHeader("x-delay", expiration);
|
||||
}
|
||||
return message;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 给queue发送消息
|
||||
*
|
||||
* @param queueName
|
||||
*/
|
||||
public String receiveFromQueue(String queueName) {
|
||||
return receiveFromQueue(DirectExchange.DEFAULT, queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给direct交换机指定queue发送消息
|
||||
*
|
||||
* @param directExchange
|
||||
* @param queueName
|
||||
*/
|
||||
public String receiveFromQueue(DirectExchange directExchange, String queueName) {
|
||||
Queue queue = new Queue(queueName);
|
||||
addQueue(queue);
|
||||
Binding binding = BindingBuilder.bind(queue).to(directExchange).withQueueName();
|
||||
rabbitAdmin.declareBinding(binding);
|
||||
String messages = (String) rabbitTemplate.receiveAndConvert(queueName);
|
||||
System.out.println("Receive:" + messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Exchange
|
||||
*
|
||||
* @param exchange
|
||||
*/
|
||||
public void addExchange(AbstractExchange exchange) {
|
||||
rabbitAdmin.declareExchange(exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个Exchange
|
||||
*
|
||||
* @param exchangeName
|
||||
*/
|
||||
public boolean deleteExchange(String exchangeName) {
|
||||
return rabbitAdmin.deleteExchange(exchangeName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 声明其名称自动命名的队列。它是用exclusive=true、autoDelete=true和 durable = false
|
||||
*
|
||||
* @return Queue
|
||||
*/
|
||||
public Queue addQueue() {
|
||||
return rabbitAdmin.declareQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个指定的Queue
|
||||
*
|
||||
* @param queue
|
||||
* @return queueName
|
||||
*/
|
||||
public String addQueue(Queue queue) {
|
||||
return rabbitAdmin.declareQueue(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个队列
|
||||
*
|
||||
* @param queueName the name of the queue.
|
||||
* @param unused true if the queue should be deleted only if not in use.
|
||||
* @param empty true if the queue should be deleted only if empty.
|
||||
*/
|
||||
public void deleteQueue(String queueName, boolean unused, boolean empty) {
|
||||
rabbitAdmin.deleteQueue(queueName, unused, empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个队列
|
||||
*
|
||||
* @param queueName
|
||||
* @return true if the queue existed and was deleted.
|
||||
*/
|
||||
public boolean deleteQueue(String queueName) {
|
||||
return rabbitAdmin.deleteQueue(queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定一个队列到一个匹配型交换器使用一个routingKey
|
||||
*
|
||||
* @param queue
|
||||
* @param exchange
|
||||
* @param routingKey
|
||||
*/
|
||||
public void addBinding(Queue queue, TopicExchange exchange, String routingKey) {
|
||||
Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey);
|
||||
rabbitAdmin.declareBinding(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定一个Exchange到一个匹配型Exchange 使用一个routingKey
|
||||
*
|
||||
* @param exchange
|
||||
* @param topicExchange
|
||||
* @param routingKey
|
||||
*/
|
||||
public void addBinding(Exchange exchange, TopicExchange topicExchange, String routingKey) {
|
||||
Binding binding = BindingBuilder.bind(exchange).to(topicExchange).with(routingKey);
|
||||
rabbitAdmin.declareBinding(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉一个binding
|
||||
*
|
||||
* @param binding
|
||||
*/
|
||||
public void removeBinding(Binding binding) {
|
||||
rabbitAdmin.removeBinding(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建交换器
|
||||
*
|
||||
* @param exchangeName
|
||||
* @return
|
||||
*/
|
||||
public DirectExchange createExchange(String exchangeName) {
|
||||
return new DirectExchange(exchangeName, true, false);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package org.jeecg.boot.starter.rabbitmq.config;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jeecg.boot.starter.rabbitmq.event.JeecgRemoteApplicationEvent;
|
||||
import org.jeecg.common.config.mqtoken.TransmitUserTokenFilter;
|
||||
import org.springframework.amqp.core.AcknowledgeMode;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.amqp.support.ConsumerTagStrategy;
|
||||
import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 消息队列配置类
|
||||
*
|
||||
* @author zyf
|
||||
*/
|
||||
@Configuration
|
||||
@RemoteApplicationEventScan(basePackageClasses = JeecgRemoteApplicationEvent.class)
|
||||
public class RabbitMqConfig {
|
||||
|
||||
|
||||
@Bean
|
||||
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
|
||||
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
|
||||
//设置忽略声明异常
|
||||
rabbitAdmin.setIgnoreDeclarationExceptions(true);
|
||||
return rabbitAdmin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入获取token过滤器
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public TransmitUserTokenFilter transmitUserInfoFromHttpHeader(){
|
||||
return new TransmitUserTokenFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
|
||||
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
//手动确认
|
||||
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
|
||||
//当前的消费者数量
|
||||
container.setConcurrentConsumers(1);
|
||||
//最大的消费者数量
|
||||
container.setMaxConcurrentConsumers(1);
|
||||
//是否重回队列
|
||||
container.setDefaultRequeueRejected(true);
|
||||
|
||||
//消费端的标签策略
|
||||
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
|
||||
@Override
|
||||
public String createConsumerTag(String queue) {
|
||||
return queue + "_" + UUID.randomUUID().toString();
|
||||
}
|
||||
});
|
||||
return container;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package org.jeecg.boot.starter.rabbitmq.core;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jeecg.boot.starter.rabbitmq.listenter.MqListener;
|
||||
import org.jeecg.common.config.mqtoken.UserTokenContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zyf
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseRabbiMqHandler<T> {
|
||||
|
||||
private String token= UserTokenContext.getToken();
|
||||
|
||||
public void onMessage(T t, Long deliveryTag, Channel channel, MqListener mqListener) {
|
||||
try {
|
||||
UserTokenContext.setToken(token);
|
||||
mqListener.handler(t, channel);
|
||||
channel.basicAck(deliveryTag, false);
|
||||
} catch (Exception e) {
|
||||
log.info("接收消息失败,重新放回队列");
|
||||
try {
|
||||
/**
|
||||
* deliveryTag:该消息的index
|
||||
* multiple:是否批量.true:将一次性拒绝所有小于deliveryTag的消息。
|
||||
* requeue:被拒绝的是否重新入队列
|
||||
*/
|
||||
channel.basicNack(deliveryTag, false, true);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package org.jeecg.boot.starter.rabbitmq.core;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapMessageConverter implements MessageConverter {
|
||||
@Override
|
||||
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
|
||||
return new Message(object.toString().getBytes(), messageProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromMessage(Message message) throws MessageConversionException {
|
||||
String contentType = message.getMessageProperties().getContentType();
|
||||
if (null != contentType && contentType.contains("text")) {
|
||||
return new String(message.getBody());
|
||||
} else {
|
||||
ObjectInputStream objInt = null;
|
||||
try {
|
||||
ByteArrayInputStream byteInt = new ByteArrayInputStream(message.getBody());
|
||||
objInt = new ObjectInputStream(byteInt);
|
||||
//byte[]转map
|
||||
Map map = (HashMap) objInt.readObject();
|
||||
return map;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package org.jeecg.boot.starter.rabbitmq.event;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import org.jeecg.common.util.SpringContextHolder;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 监听远程事件,并分发消息到业务模块消息处理器
|
||||
*/
|
||||
@Component
|
||||
public class BaseApplicationEvent implements ApplicationListener<JeecgRemoteApplicationEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(JeecgRemoteApplicationEvent jeecgRemoteApplicationEvent) {
|
||||
EventObj eventObj = jeecgRemoteApplicationEvent.getEventObj();
|
||||
if (ObjectUtil.isNotEmpty(eventObj)) {
|
||||
//获取业务模块消息处理器
|
||||
JeecgBusEventHandler busEventHandler = SpringContextHolder.getHandler(eventObj.getHandlerName(), JeecgBusEventHandler.class);
|
||||
if (ObjectUtil.isNotEmpty(busEventHandler)) {
|
||||
//通知业务模块
|
||||
busEventHandler.onMessage(eventObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package org.jeecg.boot.starter.rabbitmq.event;
|
||||
|
||||
import lombok.Data;
|
||||
import org.jeecg.common.base.BaseMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 远程事件数据对象
|
||||
*/
|
||||
@Data
|
||||
public class EventObj implements Serializable {
|
||||
/**
|
||||
* 数据对象
|
||||
*/
|
||||
private BaseMap baseMap;
|
||||
/**
|
||||
* 自定义业务模块消息处理器beanName
|
||||
*/
|
||||
private String handlerName;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue