first commit

This commit is contained in:
2025-06-21 11:06:35 +08:00
commit 4f3d1693b4
721 changed files with 47462 additions and 0 deletions
@@ -0,0 +1,71 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 异步调用线程配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Slf4j
@Configuration
public class AsyncConfig {
/**
* 线程池 配置bean名称
*/
public static final String ASYNC_EXECUTOR_THREAD_NAME = "smart-admin-async-executor";
/**
* 配置线程池
*
* @return
*/
@Bean(name = ASYNC_EXECUTOR_THREAD_NAME)
public AsyncTaskExecutor executor() {
int processors = Runtime.getRuntime().availableProcessors();
int threadCount = Math.max(1, processors - 1);
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数量
taskExecutor.setCorePoolSize(threadCount);
// 最大线程数量
taskExecutor.setMaxPoolSize(threadCount);
taskExecutor.setThreadNamePrefix(ASYNC_EXECUTOR_THREAD_NAME);
taskExecutor.initialize();
return taskExecutor;
}
/**
* spring 异步任务 异常配置
*/
@Configuration
public static class AsyncExceptionConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
/**
* 自定义异常处理
*/
public static class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("异步任务发生异常:{}, 参数:{}, ", method.getDeclaringClass().getSimpleName() + "." + method.getName(), Arrays.toString(objects), throwable);
}
}
}
@@ -0,0 +1,45 @@
package net.lab1024.sa.common.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 跨域配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2021/11/15 20:38
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class CorsFilterConfig {
@Value("${access-control-allow-origin}")
private String accessControlAllowOrigin;
/**
* 跨域配置
*
* @return
*/
@Bean
public CorsFilter corsFilter () {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern(accessControlAllowOrigin);
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 对接口配置跨域设置
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
@@ -0,0 +1,197 @@
package net.lab1024.sa.common.config;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.domain.DataScopePlugin;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据源配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2017-11-28 15:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Slf4j
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.driver-class-name}")
String driver;
@Value("${spring.datasource.url}")
String url;
@Value("${spring.datasource.username}")
String username;
@Value("${spring.datasource.password}")
String password;
@Value("${spring.datasource.initial-size}")
int initialSize;
@Value("${spring.datasource.min-idle}")
int minIdle;
@Value("${spring.datasource.max-active}")
int maxActive;
@Value("${spring.datasource.max-wait}")
long maxWait;
@Value("${spring.datasource.time-between-eviction-runs-millis}")
long timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.min-evictable-idle-time-millis}")
long minEvictableIdleTimeMillis;
@Value("${spring.datasource.filters}")
String filters;
@Value("${spring.datasource.druid.username}")
String druidUserName;
@Value("${spring.datasource.druid.password}")
String druidPassword;
@Value("${spring.datasource.druid.login.enabled}")
boolean druidLoginEnable;
@Value("${spring.datasource.druid.method.pointcut}")
String methodPointcut;
@Autowired
private MybatisPlusInterceptor paginationInterceptor;
@Autowired(required = false)
private DataScopePlugin dataScopePlugin;
@Bean
@Primary
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDbType(DbType.MYSQL.getDb());
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setInitialSize(initialSize);
druidDataSource.setMinIdle(minIdle);
druidDataSource.setMaxActive(maxActive);
druidDataSource.setMaxWait(maxWait);
druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
druidDataSource.setValidationQuery("SELECT 1");
try {
druidDataSource.setFilters(filters);
ArrayList<Filter> arrayList = new ArrayList<>();
StatFilter statFilter = new StatFilter();
statFilter.setMergeSql(true);
statFilter.setSlowSqlMillis(500);
statFilter.setLogSlowSql(true);
arrayList.add(statFilter);
druidDataSource.setProxyFilters(arrayList);
druidDataSource.init();
} catch (SQLException e) {
log.error("初始化数据源出错", e);
}
return druidDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(druidDataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:/mapper/**/*.xml");
factoryBean.setMapperLocations(resources);
// 设置 MyBatis-Plus 分页插件 注意此处myBatisPlugin一定要放在后面
List<Interceptor> pluginsList = new ArrayList<>();
pluginsList.add(paginationInterceptor);
if (dataScopePlugin != null) {
pluginsList.add(dataScopePlugin);
}
factoryBean.setPlugins(pluginsList.toArray(new Interceptor[pluginsList.size()]));
return factoryBean.getObject();
}
/**
* 非正式环境 才加载
*
* @return
*/
@Conditional(SystemEnvironmentConfig.class)
@Bean
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<String, String>();
//不设置用户名密码可以直接通过druid/index.html访问
if (druidLoginEnable) {
initParameters.put("loginUsername", druidUserName);
initParameters.put("loginPassword", druidPassword);
}
initParameters.put("resetEnable", "false");
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<WebStatFilter>();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/*");
return filterRegistrationBean;
}
@Bean
public JdkRegexpMethodPointcut jdkRegexpMethodPointcut() {
JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
jdkRegexpMethodPointcut.setPatterns(methodPointcut);
return jdkRegexpMethodPointcut;
}
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor();
pointcutAdvisor.setPointcut(jdkRegexpMethodPointcut());
pointcutAdvisor.setAdvice(new DruidStatInterceptor());
return pointcutAdvisor;
}
}
@@ -0,0 +1,88 @@
package net.lab1024.sa.common.config;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
/**
* java8 localDate 时间类格式化配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2017-11-28 15:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class DateConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
builder.deserializers(new LocalDateDeserializer(DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter()));
builder.deserializers(new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter()));
builder.serializers(new LocalDateSerializer(DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter()));
builder.serializers(new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter()));
};
}
/**
* string 转为 LocalDateTime 配置类
*
* @author 卓大
*/
@Configuration
public static class StringToLocalDateTime implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
LocalDateTime localDateTime;
try {
localDateTime = LocalDateTimeUtil.parse(str, DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter());
} catch (DateTimeParseException e) {
throw new RuntimeException("请输入正确的日期格式:yyyy-MM-dd HH:mm:ss");
}
return localDateTime;
}
}
/**
* string 转为 LocalDate 配置类
*
* @author 卓大
*/
@Configuration
public static class StringToLocalDate implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
LocalDate localDate;
try {
localDate = LocalDateTimeUtil.parseDate(str, DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter());
} catch (DateTimeParseException e) {
throw new RuntimeException("请输入正确的日期格式:yyyy-MM-dd");
}
return localDate;
}
}
}
@@ -0,0 +1,85 @@
package net.lab1024.sa.common.config;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import lombok.Data;
import net.lab1024.sa.common.module.support.file.service.FileStorageCloudServiceImpl;
import net.lab1024.sa.common.module.support.file.service.FileStorageLocalServiceImpl;
import net.lab1024.sa.common.module.support.file.service.IFileStorageService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 文件上传 配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2019-09-02 23:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Data
@Configuration
public class FileCloudConfig {
@Value("${file.storage.cloud.region}")
private String region;
@Value("${file.storage.cloud.endpoint}")
private String endpoint;
@Value("${file.storage.cloud.bucket-name}")
private String bucketName;
@Value("${file.storage.cloud.access-key}")
private String accessKey;
@Value("${file.storage.cloud.secret-key}")
private String secretKey;
@Value("${file.storage.cloud.url.expire}")
private Long urlExpire;
@Value("${file.storage.cloud.url.public}")
private String publicUrl;
/**
* 初始化 云oss client 配置
*
* @return
*/
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public AmazonS3 initAmazonS3() {
ClientConfiguration clientConfig = new ClientConfiguration();
clientConfig.setProtocol(Protocol.HTTPS);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.withClientConfiguration(clientConfig)
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withPathStyleAccessEnabled(false)
.build();
return s3Client;
}
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public IFileStorageService initCloudFileService() {
return new FileStorageCloudServiceImpl();
}
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "local")
public IFileStorageService initLocalFileService() {
return new FileStorageLocalServiceImpl();
}
}
@@ -0,0 +1,37 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.module.support.heartbeat.core.HeartBeatManager;
import net.lab1024.sa.common.module.support.heartbeat.core.IHeartBeatRecordHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 心跳配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2018/10/9 18:47
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class HeartBeatConfig {
/**
* 间隔时间
*/
@Value("${heart-beat.interval-seconds}")
private Long intervalSeconds;
@Autowired
private IHeartBeatRecordHandler heartBeatRecordHandler;
@Bean
public HeartBeatManager heartBeatManager() {
return new HeartBeatManager(intervalSeconds * 1000L, heartBeatRecordHandler);
}
}
@@ -0,0 +1,50 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.common.interceptor.AbstractInterceptor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* web相关配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired(required = false)
private List<HandlerInterceptor> interceptorList;
@Override
public void addInterceptors (InterceptorRegistry registry) {
if (CollectionUtils.isEmpty(interceptorList)) {
return;
}
interceptorList.forEach(e->{
registry.addInterceptor(e).addPathPatterns("/**");
});
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/preview/**");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/druidMonitor").setViewName("redirect:druid/index.html");
registry.addViewController("/swaggerApi").setViewName("redirect:swagger-ui.html");
}
}
@@ -0,0 +1,33 @@
package net.lab1024.sa.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* mp 插件
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
@@ -0,0 +1,56 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
import java.util.List;
/**
* yaml 读取配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
@Slf4j
public class PostProcessorConfig implements EnvironmentPostProcessor {
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
this.loadProperty(propertySources);
}
private void loadProperty(MutablePropertySources propertySources) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = resolver.getResources("classpath*:sa-*.yaml");
if (resources.length < 1) {
return;
}
for (Resource resource : resources) {
log.info("初始化系统配置:{}", resource.getFilename());
List<PropertySource<?>> load = loader.load(resource.getFilename(), resource);
load.forEach(propertySources::addLast);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,72 @@
package net.lab1024.sa.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021-09-02 20:21:10
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
//@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory factory;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setDefaultSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
@Bean
public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
return redisTemplate.opsForValue();
}
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
@@ -0,0 +1,38 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.module.support.reload.ReloadCommand;
import net.lab1024.sa.common.module.support.reload.core.SmartReloadManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* reload配置
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/9/1 21:40
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Slf4j
@Configuration
public class ReloadConfig {
/**
* 间隔时间
*/
@Value("${reload.interval-seconds}")
private Integer intervalSeconds;
@Autowired
private ReloadCommand reloadCommand;
@Bean
public SmartReloadManager initSmartReloadManager() {
// 创建 Reload Manager 调度器
return new SmartReloadManager(reloadCommand,intervalSeconds);
}
}
@@ -0,0 +1,40 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.common.constant.StringConst;
import net.lab1024.sa.common.common.util.SmartRequestUtil;
import net.lab1024.sa.common.module.support.repeatsubmit.RepeatSubmitAspect;
import net.lab1024.sa.common.module.support.repeatsubmit.ticket.RepeatSubmitCaffeineTicket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 重复提交配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2021/10/9 18:47
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class RepeatSubmitConfig {
@Bean
public RepeatSubmitAspect repeatSubmitAspect() {
RepeatSubmitCaffeineTicket caffeineTicket = new RepeatSubmitCaffeineTicket(this::ticket);
return new RepeatSubmitAspect(caffeineTicket);
}
/**
* 获取指明某个用户的凭证
*
* @return
*/
private String ticket(String servletPath) {
Long userId = SmartRequestUtil.getRequestUserId();
if (null == userId) {
return StringConst.EMPTY;
}
return servletPath + "_" + userId;
}
}
@@ -0,0 +1,130 @@
package net.lab1024.sa.common.config;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* http请求配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class RestTemplateConfig {
@Value("${http.pool.max-total}")
private Integer maxTotal;
@Value("${http.pool.connect-timeout}")
private Integer connectTimeout;
@Value("${http.pool.read-timeout}")
private Integer readTimeout;
@Value("${http.pool.write-timeout}")
private Integer writeTimeout;
@Value("${http.pool.keep-alive}")
private Integer keepAlive;
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(this.clientHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverterList = restTemplate.getMessageConverters();
messageConverterList.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
messageConverterList.addAll(this.converters());
return restTemplate;
}
public List<HttpMessageConverter<?>> converters() {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
fastMediaTypes.add(MediaType.APPLICATION_JSON);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
converters.add(converter);
converters.add(fastConverter);
return converters;
}
public OkHttp3ClientHttpRequestFactory clientHttpRequestFactory() {
return new OkHttp3ClientHttpRequestFactory(httpClientBuilder());
}
public OkHttpClient httpClientBuilder() {
return new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.connectionPool(this.pool())
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
.build();
}
public ConnectionPool pool() {
return new ConnectionPool(maxTotal, keepAlive, TimeUnit.MILLISECONDS);
}
@Bean
public X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory sslSocketFactory() {
try {
//信任任何链接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,47 @@
package net.lab1024.sa.common.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 定时任务调度 配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Slf4j
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
private ScheduledTaskRegistrar taskRegistrar;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
}
public String destroy() {
List<Task> taskList = new ArrayList<>();
taskList.addAll(taskRegistrar.getCronTaskList());
taskList.addAll(taskRegistrar.getTriggerTaskList());
taskList.addAll(taskRegistrar.getFixedDelayTaskList());
taskList.addAll(taskRegistrar.getFixedRateTaskList());
taskRegistrar.destroy();
List<String> taskNameList = taskList.stream().map(Task::toString).collect(Collectors.toList());
return "已关闭 @Scheduled定时任务:" + taskNameList.size() + "个!";
}
}
@@ -0,0 +1,206 @@
package net.lab1024.sa.common.config;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
import net.lab1024.sa.common.common.swagger.SwaggerApiModelPropertyEnumPlugin;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 根据SwaggerTagConst内部类动态生成Swagger group
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2020-03-25 22:54:46
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Slf4j
@EnableSwagger2
@Configuration
@Conditional(SystemEnvironmentConfig.class)
public class SwaggerConfig implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {
/**
* 文档标题
*/
private String title;
/**
* 文档描述
*/
private String description;
/**
* api版本
*/
private String version;
/**
* service url
*/
private String teamUrl;
/**
* host
*/
private String host;
private String tagClass;
@Bean
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public SwaggerApiModelPropertyEnumPlugin swaggerEnum() {
return new SwaggerApiModelPropertyEnumPlugin();
}
@Override
public void setEnvironment(Environment environment) {
this.title = environment.getProperty("swagger.title");
this.description = environment.getProperty("swagger.description");
this.version = environment.getProperty("swagger.version");
this.host = environment.getProperty("swagger.host");
this.tagClass = environment.getProperty("swagger.tag-class");
this.teamUrl = environment.getProperty("swagger.team-url");
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
Map<String, List<String>> groupMap = this.buildGroup();
for (Map.Entry<String, List<String>> entry : groupMap.entrySet()) {
String group = entry.getKey();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Docket.class, () -> this.baseDocket(group, entry.getValue()));
BeanDefinition beanDefinition = builder.getRawBeanDefinition();
registry.registerBeanDefinition(group + "Api", beanDefinition);
}
}
@SneakyThrows
private Map<String, List<String>> buildGroup() {
Class<?> clazz = Class.forName(tagClass);
Class<?>[] innerClazz = clazz.getClasses();
Map<String, List<String>> groupMap = new HashMap<>(16);
for (Class<?> cls : innerClazz) {
String group = cls.getSimpleName();
List<String> apiTags = Lists.newArrayList();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
boolean isFinal = Modifier.isFinal(field.getModifiers());
if (isFinal) {
apiTags.add(field.get(null).toString());
}
}
groupMap.put(group, apiTags);
}
return groupMap;
}
private Docket baseDocket(String groupName, List<String> apiTagList) {
// 配置全局参数
List<Parameter> parameterList = this.generateParameter();
Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName(groupName)
.forCodeGeneration(true)
.select()
// 过滤规则
.apis(this.getControllerPredicate(apiTagList))
// 与 过滤规则 controller 包路径 二选一
// .apis(RequestHandlerSelectors.basePackage(packAge))
.paths(PathSelectors.any())
.build().apiInfo(this.apiInfo())
.globalOperationParameters(parameterList);
if (StringUtils.isNotBlank(host)) {
docket = docket.host(host);
}
return docket;
}
private Predicate<RequestHandler> getControllerPredicate(List<String> apiTagList) {
Predicate<RequestHandler> methodPredicate = (input) -> {
Api api = null;
Optional<Api> apiOptional = input.findControllerAnnotation(Api.class);
if (apiOptional.isPresent()) {
api = apiOptional.get();
}
if (api == null) {
return false;
}
List<String> tags = Arrays.asList(api.tags());
if (apiTagList.containsAll(tags)) {
return true;
}
return false;
};
Predicate controllerPredicate = Predicates.or(RequestHandlerSelectors.withClassAnnotation(RestController.class), RequestHandlerSelectors.withClassAnnotation(Controller.class));
return Predicates.and(controllerPredicate, methodPredicate);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title(title)
.description(description)
.version(version)
.termsOfServiceUrl(teamUrl)
.contact(new Contact("", teamUrl, ""))
.build();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
/**
* 生成共用请求参数
*
* @return
*/
private List<Parameter> generateParameter() {
// 配置全局参数 token
Parameter token = new ParameterBuilder().name(RequestHeaderConst.TOKEN)
.description("token")
.modelRef(new ModelRef("string"))
.parameterType("header").defaultValue("1")
.required(false)
.build();
return Lists.newArrayList(token);
}
}
@@ -0,0 +1,49 @@
package net.lab1024.sa.common.config;
import net.lab1024.sa.common.common.domain.SystemEnvironment;
import net.lab1024.sa.common.common.enumeration.SystemEnvironmentEnum;
import net.lab1024.sa.common.common.util.SmartEnumUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 系统环境
*
* @Author 1024创新实验室-主任: 卓大
* @Date 2021/08/13 18:56
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
public class SystemEnvironmentConfig implements Condition {
@Value("${spring.profiles.active}")
private String systemEnvironment;
@Value("${project.name}")
private String projectName;
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String property = conditionContext.getEnvironment().getProperty("spring.profiles.active");
return StringUtils.isNotBlank(property) && !SystemEnvironmentEnum.PROD.equalsValue(property);
}
@Bean
public SystemEnvironment initEnvironment() {
SystemEnvironmentEnum currentEnvironment = SmartEnumUtil.getEnumByValue(systemEnvironment, SystemEnvironmentEnum.class);
if (currentEnvironment == null) {
throw new ExceptionInInitializerError("无法获取当前环境!请在 application.yaml 配置参数:spring.profiles.active");
}
if (StringUtils.isBlank(projectName)) {
throw new ExceptionInInitializerError("无法获取当前项目名称!请在 application.yaml 配置参数:project.name");
}
return new SystemEnvironment(currentEnvironment == SystemEnvironmentEnum.PROD, projectName, currentEnvironment);
}
}
@@ -0,0 +1,150 @@
package net.lab1024.sa.common.config;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.common.common.annoation.NoNeedLogin;
import net.lab1024.sa.common.common.annoation.SaAuth;
import net.lab1024.sa.common.common.domain.RequestUrlVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* url配置
*
* @Author 1024创新实验室: 罗伊
* @Date 2022-05-30 21:22:12
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright 1024创新实验室 ( https://1024lab.net )
*/
@Configuration
@Slf4j
public class UrlConfig {
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
/**
* 获取每个方法的请求路径
*
* @return
*/
@Bean
public Map<Method, Set<String>> methodUrlMap() {
Map<Method, Set<String>> methodUrlMap = Maps.newHashMap();
//获取url与类和方法的对应信息
Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
RequestMappingInfo requestMappingInfo = entry.getKey();
Set<String> urls = requestMappingInfo.getPatternsCondition().getPatterns();
if (CollectionUtils.isEmpty(urls)) {
continue;
}
HandlerMethod handlerMethod = entry.getValue();
methodUrlMap.put(handlerMethod.getMethod(), urls);
}
return methodUrlMap;
}
/**
* 需要进行url权限校验的方法
*
* @param methodUrlMap
* @return
*/
@Bean
public List<RequestUrlVO> authUrl(Map<Method, Set<String>> methodUrlMap) {
List<RequestUrlVO> authUrlList = Lists.newArrayList();
for (Map.Entry<Method, Set<String>> entry : methodUrlMap.entrySet()) {
Method method = entry.getKey();
SaAuth saAuth = method.getAnnotation(SaAuth.class);
if (null == saAuth) {
continue;
}
List<RequestUrlVO> requestUrlList = this.buildRequestUrl(method, entry.getValue());
authUrlList.addAll(requestUrlList);
}
log.info("需要权限校验的URL:{}", authUrlList.stream().map(e -> e.getUrl()).collect(Collectors.toList()));
return authUrlList;
}
private List<RequestUrlVO> buildRequestUrl(Method method, Set<String> urlSet) {
List<RequestUrlVO> requestUrlList = Lists.newArrayList();
if (CollectionUtils.isEmpty(urlSet)) {
return requestUrlList;
}
//url对应的方法名称
String className = method.getDeclaringClass().getName();
String methodName = method.getName();
List<String> list = StrUtil.split(className, ".");
String controllerName = list.get(list.size() - 1);
String name = controllerName + "." + methodName;
//swagger 说明信息
String methodComment = null;
ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (apiOperation != null) {
methodComment = apiOperation.value();
}
for (String url : urlSet) {
RequestUrlVO requestUrlVO = new RequestUrlVO();
requestUrlVO.setUrl(url);
requestUrlVO.setName(name);
requestUrlVO.setComment(methodComment);
requestUrlList.add(requestUrlVO);
}
return requestUrlList;
}
/**
* 获取无需登录可以匿名访问的url信息
*
* @return
*/
@Bean
public List<String> noNeedLoginUrlList(Map<Method, Set<String>> methodUrlMap) {
List<String> noNeedLoginUrlList = Lists.newArrayList();
for (Map.Entry<Method, Set<String>> entry : methodUrlMap.entrySet()) {
Method method = entry.getKey();
NoNeedLogin noNeedLogin = method.getAnnotation(NoNeedLogin.class);
if (null == noNeedLogin) {
continue;
}
noNeedLoginUrlList.addAll(entry.getValue());
}
log.info("不需要登录的URL:{}", noNeedLoginUrlList);
return noNeedLoginUrlList;
}
/**
* 获取忽略的url信息
*
* @return
*/
@Bean
public List<String> ignoreUrlList() {
List<String> ignoreUrlList = Lists.newArrayList();
ignoreUrlList.add("/swagger-ui.html");
ignoreUrlList.add("/swagger-resources/**");
ignoreUrlList.add("/webjars/**");
ignoreUrlList.add("/druid/**");
ignoreUrlList.add("/*/api-docs");
return ignoreUrlList;
}
}