commit
90c71c40e1
295 changed files with 16902 additions and 0 deletions
@ -0,0 +1,8 @@ |
|||
.idea/ |
|||
logs/ |
|||
target/ |
|||
gyoa-java.iml |
|||
*.http |
|||
*.env.json |
|||
*.json |
|||
/src/test/ |
@ -0,0 +1,223 @@ |
|||
<?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"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<groupId>com.qs</groupId> |
|||
<artifactId>questionnaire</artifactId> |
|||
<version>1.0.0RELEASE</version> |
|||
|
|||
<parent> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>2.6.4</version> |
|||
</parent> |
|||
|
|||
<properties> |
|||
<spring-boot.version>2.6.4</spring-boot.version> |
|||
<maven.compiler.source>8</maven.compiler.source> |
|||
<maven.compiler.target>8</maven.compiler.target> |
|||
<xxlJob.version>2.3.0</xxlJob.version> |
|||
<hutool.version>5.7.19</hutool.version> |
|||
<jwt.version>3.10.3</jwt.version> |
|||
<httpclient.version>4.5.7</httpclient.version> |
|||
<commons-httpclient.version>3.1</commons-httpclient.version> |
|||
<fastjson.version>1.2.49</fastjson.version> |
|||
<mybatis-plus.version>3.4.1</mybatis-plus.version> |
|||
<skipTests>true</skipTests> |
|||
<wx.java.version>4.2.0</wx.java.version> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-all</artifactId> |
|||
<version>${hutool.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter</artifactId> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-logging</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-tomcat</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-undertow</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-data-redis</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-security</artifactId> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-logging</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-aop</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-validation</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.auth0</groupId> |
|||
<artifactId>java-jwt</artifactId> |
|||
<version>${jwt.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-log4j2</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>fastjson</artifactId> |
|||
<version>${fastjson.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.pagehelper</groupId> |
|||
<artifactId>pagehelper-spring-boot-starter</artifactId> |
|||
<version>1.4.1</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.mybatis</groupId> |
|||
<artifactId>mybatis-spring</artifactId> |
|||
</exclusion> |
|||
<exclusion> |
|||
<groupId>org.mybatis</groupId> |
|||
<artifactId>mybatis</artifactId> |
|||
</exclusion> |
|||
<exclusion> |
|||
<groupId>com.github.jsqlparser</groupId> |
|||
<artifactId>jsqlparser</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-boot-starter</artifactId> |
|||
<version>${mybatis-plus.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>mysql</groupId> |
|||
<artifactId>mysql-connector-java</artifactId> |
|||
<version>8.0.15</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
<version>1.18.12</version> |
|||
<scope>compile</scope> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.commons</groupId> |
|||
<artifactId>commons-lang3</artifactId> |
|||
<version>3.12.0</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.binarywang</groupId> |
|||
<artifactId>weixin-java-mp</artifactId> |
|||
<version>${wx.java.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>junit</groupId> |
|||
<artifactId>junit</artifactId> |
|||
<version>RELEASE</version> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.shalousun</groupId> |
|||
<artifactId>smart-doc</artifactId> |
|||
<version>2.4.0</version> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
|
|||
<build> |
|||
<finalName>serve</finalName> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
<version>${spring-boot.version}</version> |
|||
</plugin> |
|||
</plugins> |
|||
<!-- <resources>--> |
|||
<!-- <resource>--> |
|||
<!-- <directory>src/main/resources</directory>--> |
|||
<!-- <excludes>--> |
|||
<!-- <exclude>*.yml</exclude>--> |
|||
<!-- </excludes>--> |
|||
<!-- </resource>--> |
|||
<!-- </resources>--> |
|||
</build> |
|||
<repositories> |
|||
<repository> |
|||
<id>public</id> |
|||
<name>aliyun nexus</name> |
|||
<url>https://maven.aliyun.com/repository/public/</url> |
|||
<releases> |
|||
<enabled>true</enabled> |
|||
</releases> |
|||
</repository> |
|||
</repositories> |
|||
|
|||
<pluginRepositories> |
|||
<pluginRepository> |
|||
<id>public</id> |
|||
<name>aliyun nexus</name> |
|||
<url>https://maven.aliyun.com/repository/public/</url> |
|||
<releases> |
|||
<enabled>true</enabled> |
|||
</releases> |
|||
<snapshots> |
|||
<enabled>false</enabled> |
|||
</snapshots> |
|||
</pluginRepository> |
|||
</pluginRepositories> |
|||
</project> |
@ -0,0 +1,51 @@ |
|||
package com.qs.serve; |
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.boot.builder.SpringApplicationBuilder; |
|||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; |
|||
import org.springframework.cache.annotation.EnableCaching; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.scheduling.annotation.EnableAsync; |
|||
import org.springframework.scheduling.annotation.EnableScheduling; |
|||
import org.springframework.transaction.annotation.EnableTransactionManagement; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.servlet.ServletContext; |
|||
import javax.servlet.ServletException; |
|||
import java.util.TimeZone; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@EnableAsync |
|||
@EnableScheduling |
|||
@EnableTransactionManagement |
|||
@EnableCaching |
|||
@SpringBootApplication |
|||
public class Application extends SpringBootServletInitializer { |
|||
|
|||
public static void main(String[] args) { |
|||
DevEnvironmentConfig.openDevEnv(true); |
|||
SpringApplication.run(Application.class,args); |
|||
} |
|||
|
|||
@Override |
|||
public void onStartup(ServletContext servletContext) throws ServletException { |
|||
super.onStartup(servletContext); |
|||
} |
|||
|
|||
@Override |
|||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { |
|||
return builder.sources(Application.class); |
|||
} |
|||
|
|||
@PostConstruct |
|||
void started() { |
|||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import org.springframework.boot.web.servlet.ServletContextInitializer; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
import javax.servlet.ServletContext; |
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.SessionTrackingMode; |
|||
import java.util.Collections; |
|||
|
|||
|
|||
/** |
|||
* 禁用jsessionid |
|||
* @author YenHex |
|||
* @since 2022/3/11 |
|||
*/ |
|||
@Configuration |
|||
public class CustomServletContextInitializer implements ServletContextInitializer { |
|||
|
|||
@Override |
|||
public void onStartup(ServletContext servletContext) throws ServletException { |
|||
servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE)); |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/11 |
|||
*/ |
|||
@UtilityClass |
|||
public class DevEnvironmentConfig { |
|||
|
|||
private static Boolean devEnv = null; |
|||
public static void openDevEnv(Boolean open){ |
|||
if(devEnv==null){devEnv = open;} |
|||
} |
|||
|
|||
|
|||
public static boolean isDev(){ |
|||
if(devEnv==null){ |
|||
return false; |
|||
} |
|||
return devEnv; |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
|
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @Author: YenHex |
|||
* @Date: 2021/3/3 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Getter |
|||
public class JwtConfig { |
|||
|
|||
/** |
|||
* 单位分钟 |
|||
*/ |
|||
private final Integer expire = 60; |
|||
|
|||
private final String iss = "KP_ISS"; |
|||
|
|||
private final String secret = "QiShenAa18n9VUcCxaSeSqLtFvsSCaRoVPKtBLaYxB0"; |
|||
|
|||
} |
@ -0,0 +1,54 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.DbType; |
|||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
|||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; |
|||
import com.qs.serve.common.framework.mybatis.handler.SysMetaHandler; |
|||
import com.qs.serve.common.framework.mybatis.handler.SysTenantHandler; |
|||
import org.mybatis.spring.annotation.MapperScan; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@MapperScan("com.qs.serve.modules.*.mapper") |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class MyBatisConfig { |
|||
|
|||
@Bean |
|||
public MybatisPlusInterceptor mybatisPlusInterceptor() { |
|||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
|||
// 翻页拦截器
|
|||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); |
|||
// 多租户、乐观锁等配置拦截
|
|||
interceptor.addInnerInterceptor(buildTenantHandler()); |
|||
// 添加防止全表更新与删除拦截器
|
|||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); |
|||
return interceptor; |
|||
} |
|||
|
|||
@Bean |
|||
public MetaObjectHandler sysMetaHandler(){ |
|||
return new SysMetaHandler(); |
|||
} |
|||
|
|||
public TenantLineInnerInterceptor buildTenantHandler(){ |
|||
String[] ignoreTable = new String[]{ |
|||
"sys_user", |
|||
"sys_user_tenant", |
|||
"sys_permit", |
|||
"sys_tenant", |
|||
"wx_app", |
|||
"sys_menu", |
|||
"sys_menu_permit" |
|||
}; |
|||
SysTenantHandler sysTenantHandler = new SysTenantHandler(ignoreTable); |
|||
return new TenantLineInnerInterceptor(sysTenantHandler); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,70 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
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.core.RedisTemplate; |
|||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
|||
import org.springframework.data.redis.serializer.*; |
|||
import org.springframework.data.redis.serializer.RedisSerializationContext; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
import java.time.Duration; |
|||
|
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@EnableCaching |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class RedisConfig extends CachingConfigurerSupport{ |
|||
|
|||
private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer(); |
|||
private static final GenericJackson2JsonRedisSerializer JACKSON__SERIALIZER = new GenericJackson2JsonRedisSerializer(); |
|||
|
|||
@Bean |
|||
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { |
|||
//设置缓存过期时间
|
|||
RedisCacheConfiguration redisCacheCfg=RedisCacheConfiguration.defaultCacheConfig() |
|||
.entryTtl(Duration.ofHours(1)) |
|||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(STRING_SERIALIZER)) |
|||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON__SERIALIZER)); |
|||
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) |
|||
.cacheDefaults(redisCacheCfg) |
|||
.build(); |
|||
} |
|||
|
|||
@Bean |
|||
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory){ |
|||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
|||
container.setConnectionFactory(connectionFactory); |
|||
return container; |
|||
} |
|||
|
|||
@Bean |
|||
@ConditionalOnMissingBean(name = "redisTemplate") |
|||
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){ |
|||
// 配置redisTemplate
|
|||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); |
|||
redisTemplate.setConnectionFactory(factory); |
|||
// key序列化
|
|||
redisTemplate.setKeySerializer(STRING_SERIALIZER); |
|||
// value序列化
|
|||
redisTemplate.setValueSerializer(JACKSON__SERIALIZER); |
|||
// Hash key序列化
|
|||
redisTemplate.setHashKeySerializer(STRING_SERIALIZER); |
|||
// Hash value序列化
|
|||
redisTemplate.setHashValueSerializer(JACKSON__SERIALIZER); |
|||
redisTemplate.afterPropertiesSet(); |
|||
return redisTemplate; |
|||
} |
|||
} |
@ -0,0 +1,64 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.config.properties.PermitProperties; |
|||
import com.qs.serve.common.framework.security.filter.SecurityRequestFilter; |
|||
import com.qs.serve.common.framework.security.handler.SecurityAccessDeniedHandler; |
|||
import com.qs.serve.common.framework.security.handler.SecurityLogoutHandler; |
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.http.HttpMethod; |
|||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
|||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
|||
import org.springframework.security.config.http.SessionCreationPolicy; |
|||
import org.springframework.security.core.userdetails.UserDetailsService; |
|||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2021/6/13 |
|||
*/ |
|||
@Configuration |
|||
@AllArgsConstructor |
|||
@EnableGlobalMethodSecurity(prePostEnabled=true) |
|||
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|||
|
|||
private final SecurityRequestFilter securityRequestFilter; |
|||
private final UserDetailsService userService; |
|||
private final PermitProperties permitProperties; |
|||
|
|||
@Override |
|||
protected void configure(HttpSecurity http) throws Exception { |
|||
List<String> permitUrls = new ArrayList<>(); |
|||
if(permitProperties.getPermits()!=null){ permitUrls.addAll(permitProperties.getPermits()); } |
|||
if(permitProperties.getTenants()!=null){ permitUrls.addAll(permitProperties.getTenants()); } |
|||
permitUrls = permitUrls.stream().distinct().collect(Collectors.toList()); |
|||
String[] permit_urls = new String[permitUrls.size()]; |
|||
permitUrls.toArray(permit_urls); |
|||
http.csrf().disable(); |
|||
http.authorizeRequests() |
|||
.antMatchers(HttpMethod.OPTIONS.toString()).permitAll() |
|||
.antMatchers(permit_urls).permitAll() |
|||
//.antMatchers().permitAll() //测试用
|
|||
.anyRequest().authenticated(); |
|||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); |
|||
http.logout() |
|||
.logoutUrl("/portal/logout") |
|||
.addLogoutHandler(new SecurityLogoutHandler()) |
|||
.invalidateHttpSession(true); |
|||
http.headers().cacheControl(); |
|||
http.addFilterBefore(securityRequestFilter, UsernamePasswordAuthenticationFilter.class); |
|||
http.exceptionHandling().accessDeniedHandler(new SecurityAccessDeniedHandler()); |
|||
//http.formLogin().failureHandler(new SecurityAuthenticationFailureHandler());
|
|||
} |
|||
|
|||
@Override |
|||
public UserDetailsService userDetailsService() { |
|||
return userService; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,68 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.framework.interceptor.ApiAuthInterceptor; |
|||
import com.qs.serve.common.framework.interceptor.LimitSubmitInterceptor; |
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.cors.CorsConfiguration; |
|||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
|||
import org.springframework.web.filter.CorsFilter; |
|||
import org.springframework.web.multipart.MultipartResolver; |
|||
import org.springframework.web.multipart.commons.CommonsMultipartResolver; |
|||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@AllArgsConstructor |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class SpringMvcConfig implements WebMvcConfigurer { |
|||
|
|||
private final LimitSubmitInterceptor limitSubmitInterceptor; |
|||
private final ApiAuthInterceptor apiAuthInterceptor; |
|||
|
|||
@Override |
|||
public void addInterceptors(InterceptorRegistry registry) { |
|||
//注册LimitSubmitInterceptor拦截器
|
|||
registry.addInterceptor(apiAuthInterceptor) |
|||
.addPathPatterns("/api/**") |
|||
.excludePathPatterns("/api/wx/login/*","/api/dev/**"); |
|||
registry.addInterceptor(limitSubmitInterceptor) |
|||
.addPathPatterns("/**"); |
|||
} |
|||
|
|||
/*@Override |
|||
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|||
registry.addResourceHandler("/static/**") |
|||
.addResourceLocations("classpath:/static/"); |
|||
}*/ |
|||
|
|||
@Bean |
|||
public CorsFilter corsFilter() { |
|||
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); |
|||
final CorsConfiguration corsConfiguration = new CorsConfiguration(); |
|||
/*是否允许请求带有验证信息*/ |
|||
corsConfiguration.setAllowCredentials(true); |
|||
/*允许访问的客户端域名*/ |
|||
corsConfiguration.addAllowedOrigin("*"); |
|||
/*允许服务端访问的客户端请求头*/ |
|||
corsConfiguration.addAllowedHeader("*"); |
|||
/*允许访问的方法名,GET POST等*/ |
|||
corsConfiguration.addAllowedMethod("*"); |
|||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); |
|||
return new CorsFilter(urlBasedCorsConfigurationSource); |
|||
} |
|||
|
|||
//@Bean
|
|||
public MultipartResolver multipartResolver(){ |
|||
CommonsMultipartResolver resolver = new CommonsMultipartResolver(); |
|||
resolver.setMaxInMemorySize(5120); |
|||
resolver.setMaxInMemorySize(300 * 1024 * 1024); |
|||
resolver.setDefaultEncoding("UTF-8"); |
|||
return resolver; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,56 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.util.ThreadsUtils; |
|||
import org.apache.commons.lang3.concurrent.BasicThreadFactory; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
|||
|
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
|
|||
|
|||
/** |
|||
* 线程池配置 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@Configuration |
|||
public class ThreadPoolConfig { |
|||
|
|||
/** 核心线程池大小 */ |
|||
private final int corePoolSize = 50; |
|||
|
|||
|
|||
@Bean(name = "threadPoolTaskExecutor") |
|||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() { |
|||
// 最大可创建的线程数
|
|||
int maxPoolSize = 200; |
|||
// 队列最大长度
|
|||
int queueCapacity = 1000; |
|||
// 线程池维护线程所允许的空闲时间
|
|||
int keepAliveSeconds = 300; |
|||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
|||
executor.setMaxPoolSize(maxPoolSize); |
|||
executor.setCorePoolSize(corePoolSize); |
|||
executor.setQueueCapacity(queueCapacity); |
|||
executor.setKeepAliveSeconds(keepAliveSeconds); |
|||
// 线程池对拒绝任务(无线程可用)的处理策略
|
|||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
return executor; |
|||
} |
|||
|
|||
/** 执行周期性或定时任务 */ |
|||
@Bean(name = "scheduledExecutorService") |
|||
protected ScheduledExecutorService scheduledExecutorService() { |
|||
return new ScheduledThreadPoolExecutor(corePoolSize, |
|||
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { |
|||
@Override |
|||
protected void afterExecute(Runnable r, Throwable t) { |
|||
super.afterExecute(r, t); |
|||
ThreadsUtils.printException(r, t); |
|||
} |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @Author: YenHex |
|||
* @Date: 2021/3/3 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "project.ignore-url") |
|||
public class PermitProperties { |
|||
|
|||
/** |
|||
* 可通过路径(适用于回调场景、无拦截场景等) |
|||
*/ |
|||
private List<String> permits = new ArrayList<>(); |
|||
|
|||
/** |
|||
* 默认租户拦截(适用于无需登录接口,但有租户拦截) |
|||
*/ |
|||
private List<String> tenants = new ArrayList<>(); |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/7 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "spring.redis") |
|||
public class RedisProperties { |
|||
|
|||
private String host; |
|||
|
|||
private Integer port; |
|||
|
|||
private String password; |
|||
|
|||
private Integer database; |
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/14 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "project.upload") |
|||
public class UploadProperties { |
|||
|
|||
private String tempPath; |
|||
|
|||
private String proxyUrl; |
|||
|
|||
private String logicalPath; |
|||
|
|||
} |
@ -0,0 +1,210 @@ |
|||
package com.qs.serve.common.framework.aop; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.qs.serve.common.framework.manager.AsyncFactory; |
|||
import com.qs.serve.common.framework.manager.AsyncManager; |
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.model.annotation.SysLog; |
|||
import com.qs.serve.common.model.consts.GySysConst; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import com.qs.serve.common.util.ServletUtils; |
|||
import com.qs.serve.modules.sys.entity.SysOperationLog; |
|||
import com.qs.serve.modules.sys.entity.SysUser; |
|||
import com.qs.serve.modules.sys.service.SysUserService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.aspectj.lang.JoinPoint; |
|||
import org.aspectj.lang.Signature; |
|||
import org.aspectj.lang.annotation.*; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpMethod; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.validation.BindingResult; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
import org.springframework.web.servlet.HandlerMapping; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.lang.reflect.Method; |
|||
import java.util.Collection; |
|||
import java.util.Iterator; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 操作日志织入 |
|||
* @author YenHex |
|||
* @since 2022/3/8 |
|||
*/ |
|||
@Slf4j |
|||
@Aspect |
|||
@Component |
|||
public class SysLogAspect { |
|||
|
|||
@Autowired |
|||
private SysUserService sysUserService; |
|||
|
|||
ThreadLocal<Long> startTimeIndex = new ThreadLocal<>(); |
|||
|
|||
@Pointcut("@annotation(com.qs.serve.common.model.annotation.SysLog)") |
|||
public void logPointCut() {} |
|||
|
|||
@Before("@annotation(com.qs.serve.common.model.annotation.SysLog)") |
|||
public void doBefore() { |
|||
startTimeIndex.set(System.currentTimeMillis()); |
|||
} |
|||
|
|||
/** |
|||
* 处理完请求后执行 |
|||
* @param joinPoint 切点 |
|||
*/ |
|||
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult") |
|||
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) { |
|||
handleLog(joinPoint, null, jsonResult); |
|||
} |
|||
|
|||
/** |
|||
* 拦截异常操作 |
|||
* @param joinPoint 切点 |
|||
* @param e 异常 |
|||
*/ |
|||
@AfterThrowing(value = "logPointCut()", throwing = "e") |
|||
public void doAfterThrowing(JoinPoint joinPoint, Exception e) { |
|||
handleLog(joinPoint, e, null); |
|||
} |
|||
|
|||
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) { |
|||
try { |
|||
// 获得注解
|
|||
SysLog controllerLog = getAnnotationLog(joinPoint); |
|||
if (controllerLog == null) { |
|||
return; |
|||
} |
|||
HttpServletRequest request = ServletUtils.getRequest(); |
|||
// 获取当前的用户
|
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
SysOperationLog operLog = new SysOperationLog(); |
|||
operLog.setReqIp(ServletUtils.getIp(request)); |
|||
operLog.setReqMethod(request.getMethod()); |
|||
operLog.setJsonResult(JSON.toJSONString(jsonResult)); |
|||
operLog.setReqUrl(request.getRequestURI()); |
|||
operLog.setUserAgent(request.getHeader(GySysConst.USER_AGENT_PROP)); |
|||
if (loginUser != null) { |
|||
operLog.setUserId(loginUser.getUserId()); |
|||
operLog.setUserType(loginUser.getTypeFlag().getCode()); |
|||
SysUser sysUser = sysUserService.getById(loginUser.getUserId()); |
|||
if(sysUser!=null){ |
|||
operLog.setCreatorName(sysUser.getName()); |
|||
} |
|||
} |
|||
if (e != null) { |
|||
operLog.setErrMsg(StringUtils.substring(e.getMessage(), 0, 2500)); |
|||
} |
|||
//耗时
|
|||
Long startTime = startTimeIndex.get(); |
|||
if(startTime!=null){ |
|||
operLog.setElapsedTime(System.currentTimeMillis()-startTime); |
|||
}else { |
|||
operLog.setElapsedTime(0L); |
|||
} |
|||
String className = joinPoint.getTarget().getClass().getName(); |
|||
String methodName = joinPoint.getSignature().getName(); |
|||
operLog.setCodeMethod(className + "." + methodName + "()"); |
|||
// 处理设置注解上的参数
|
|||
getControllerMethodDescription(joinPoint, controllerLog, operLog); |
|||
operLog.setTenantId(AuthContextUtils.getTenant()); |
|||
// 保存数据库
|
|||
AsyncManager.me().execute(AsyncFactory.saveOperationLog(operLog)); |
|||
} catch (Exception exp) { |
|||
// 记录本地异常日志
|
|||
log.error("==前置通知异常==\n异常信息:{}", exp.getMessage()); |
|||
exp.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取注解中对方法的描述信息 |
|||
* @param sysLog 日志 |
|||
* @param operationLog 操作日志 |
|||
* @throws Exception |
|||
*/ |
|||
public void getControllerMethodDescription(JoinPoint joinPoint, SysLog sysLog, SysOperationLog operationLog) throws Exception { |
|||
operationLog.setBizType(sysLog.biz().getValue()); |
|||
operationLog.setInterType(sysLog.inter().name()); |
|||
operationLog.setTitle(sysLog.title()); |
|||
operationLog.setModule(sysLog.module().getVale()); |
|||
operationLog.setOptDesc(sysLog.desc()); |
|||
if (sysLog.saveReqParam()) { |
|||
setRequestValue(joinPoint, operationLog); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取请求的参数,放到log中 |
|||
* @param operationLog 操作日志 |
|||
* @throws Exception 异常 |
|||
*/ |
|||
private void setRequestValue(JoinPoint joinPoint, SysOperationLog operationLog) throws Exception { |
|||
String requestMethod = operationLog.getReqMethod(); |
|||
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { |
|||
String params = argsArrayToString(joinPoint.getArgs()); |
|||
operationLog.setReqParam(StringUtils.substring(params, 0, 2500)); |
|||
} else { |
|||
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); |
|||
operationLog.setReqParam(StringUtils.substring(paramsMap.toString(), 0, 2500)); |
|||
} |
|||
} |
|||
|
|||
/** 是否存在注解,如果存在就获取 */ |
|||
private SysLog getAnnotationLog(JoinPoint joinPoint){ |
|||
Signature signature = joinPoint.getSignature(); |
|||
MethodSignature methodSignature = (MethodSignature) signature; |
|||
Method method = methodSignature.getMethod(); |
|||
if (method != null) { |
|||
return method.getAnnotation(SysLog.class); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 参数拼装 |
|||
*/ |
|||
private String argsArrayToString(Object[] paramsArray) { |
|||
String params = ""; |
|||
if (paramsArray != null && paramsArray.length > 0) { |
|||
for (int i = 0; i < paramsArray.length; i++) { |
|||
if (paramsArray[i]!=null && !isFilterObject(paramsArray[i])) { |
|||
Object jsonObj = JSON.toJSON(paramsArray[i]); |
|||
params += jsonObj.toString() + " "; |
|||
} |
|||
} |
|||
} |
|||
return params.trim(); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否需要过滤的对象。 |
|||
* @param o 对象信息。 |
|||
* @return 如果是需要过滤的对象,则返回true;否则返回false。 |
|||
*/ |
|||
@SuppressWarnings("rawtypes") |
|||
public boolean isFilterObject(final Object o) { |
|||
Class<?> clazz = o.getClass(); |
|||
if (clazz.isArray()) { |
|||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class); |
|||
} else if (Collection.class.isAssignableFrom(clazz)) { |
|||
Collection collection = (Collection) o; |
|||
for (Iterator iter = collection.iterator(); iter.hasNext(); ) { |
|||
return iter.next() instanceof MultipartFile; |
|||
} |
|||
} else if (Map.class.isAssignableFrom(clazz)) { |
|||
Map map = (Map) o; |
|||
for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) { |
|||
Map.Entry entry = (Map.Entry) iter.next(); |
|||
return entry.getValue() instanceof MultipartFile; |
|||
} |
|||
} |
|||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse |
|||
|| o instanceof BindingResult; |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.qs.serve.common.framework.exception; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @date 2020/6/11 |
|||
* @version 1.0 |
|||
*/ |
|||
public abstract class BaseException extends RuntimeException { |
|||
|
|||
public BaseException(String message){ |
|||
super(message); |
|||
} |
|||
|
|||
/** |
|||
* 错误代码 |
|||
* @return |
|||
*/ |
|||
public abstract int getCode(); |
|||
|
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.framework.exception; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2020/6/11 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class BusinessException extends BaseException implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
private Integer status; |
|||
|
|||
public BusinessException(String message,Integer status) { |
|||
super(message); |
|||
this.status = status; |
|||
} |
|||
|
|||
@Override |
|||
public int getCode() { |
|||
return status; |
|||
} |
|||
} |
@ -0,0 +1,118 @@ |
|||
package com.qs.serve.common.framework.exception; |
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import com.qs.serve.common.model.dto.R; |
|||
import com.qs.serve.common.model.enums.HttpCode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.dao.DuplicateKeyException; |
|||
import org.springframework.http.converter.HttpMessageNotWritableException; |
|||
import org.springframework.security.access.AccessDeniedException; |
|||
import org.springframework.security.authentication.BadCredentialsException; |
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.validation.BindException; |
|||
import org.springframework.web.HttpRequestMethodNotSupportedException; |
|||
import org.springframework.web.bind.MethodArgumentNotValidException; |
|||
import org.springframework.web.bind.annotation.ControllerAdvice; |
|||
import org.springframework.web.bind.annotation.ExceptionHandler; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
|
|||
import java.sql.SQLIntegrityConstraintViolationException; |
|||
|
|||
|
|||
/** |
|||
* @Author JcYen |
|||
* @Date 2019/6/7 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
@ControllerAdvice |
|||
public class UnifiedExceptionHandler { |
|||
|
|||
|
|||
@ExceptionHandler(value = BusinessException.class) |
|||
@ResponseBody |
|||
public R handleBindException(BusinessException e) { |
|||
return new R(e.getCode(),e.getMessage()); |
|||
} |
|||
|
|||
@ExceptionHandler(value = NullPointerException.class) |
|||
@ResponseBody |
|||
public R handleBindException(NullPointerException e) { |
|||
e.printStackTrace(); |
|||
return R.error("数据不存在或被移除"); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
AccessDeniedException.class |
|||
}) |
|||
@ResponseBody |
|||
public R handleAccessException(AccessDeniedException e) { |
|||
HttpCode hc = HttpCode.FORBIDDEN_403; |
|||
return new R(hc.getCode(),hc.getMsg()); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
UsernameNotFoundException.class, |
|||
}) |
|||
@ResponseBody |
|||
public R handlePrintMsgException(UsernameNotFoundException e) { |
|||
return R.error(); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
BadCredentialsException.class, |
|||
}) |
|||
@ResponseBody |
|||
public R handlePrintMsgException(Exception e) { |
|||
log.warn(e.getMessage()); |
|||
HttpCode hc = HttpCode.LOGIN_ERR_1; |
|||
return new R(hc.getCode(),hc.getMsg()); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
BindException.class, |
|||
MethodArgumentNotValidException.class, |
|||
HttpRequestMethodNotSupportedException.class |
|||
}) |
|||
@ResponseBody |
|||
public R handleDevPrintMsgException(Exception e) { |
|||
log.warn("参数校验异常:{}",e.getMessage()); |
|||
if(DevEnvironmentConfig.isDev()){return R.error(e.getMessage());} |
|||
return R.error(); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
DuplicateKeyException.class, |
|||
SQLIntegrityConstraintViolationException.class, |
|||
}) |
|||
@ResponseBody |
|||
public R handleSqlIntegrityException(Exception e) { |
|||
String msg = e.getMessage(); |
|||
e.printStackTrace(); |
|||
String[] tmps = msg.split("\\n"); |
|||
String msg2 = tmps.length>1?tmps[1]:null; |
|||
return new R(500,"数据库拦截"+(msg2==null?"":" : "+msg2)); |
|||
} |
|||
|
|||
/** |
|||
* 强制运行写入成功 |
|||
* @param e |
|||
* @return |
|||
*/ |
|||
@ExceptionHandler(value = HttpMessageNotWritableException.class) |
|||
@ResponseBody |
|||
public R handleMsgException(HttpMessageNotWritableException e) { |
|||
return R.ok(); |
|||
} |
|||
|
|||
@ExceptionHandler(value = Exception.class) |
|||
@ResponseBody |
|||
public R handleException(Exception e) { |
|||
log.error("Servlet异常\n异常类型:{}\n异常信息:{}\n异常体:",e.getClass().getSimpleName(),e.getMessage(),e); |
|||
if(DevEnvironmentConfig.isDev()){return R.error(e.getMessage());} |
|||
return R.error(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.qs.serve.common.framework.interceptor; |
|||
|
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.framework.security.model.LoginUserDTO; |
|||
import com.qs.serve.common.model.consts.GySysConst; |
|||
import com.qs.serve.common.model.consts.RedisCacheKeys; |
|||
import com.qs.serve.common.util.JsonUtil; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
|||
import org.springframework.security.core.context.SecurityContextHolder; |
|||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import javax.annotation.Resource; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* API接口身份拦截器 |
|||
* @author YenHex |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class ApiAuthInterceptor implements HandlerInterceptor { |
|||
|
|||
|
|||
@Resource private RedisService redisService; |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
|||
String token = request.getHeader(GySysConst.APP_TOKEN_PROP); |
|||
String loginUserJson = redisService.getString(RedisCacheKeys.LOGIN_KEY_WX+token); |
|||
if(loginUserJson!=null){ |
|||
LoginUser loginUser = Objects.requireNonNull(JsonUtil.jsonToPojo(loginUserJson, LoginUserDTO.class)).loginUser(); |
|||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); |
|||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |
|||
SecurityContextHolder.getContext().setAuthentication(authentication); |
|||
return true; |
|||
} |
|||
try { |
|||
response.setHeader("Access-Control-Allow-Origin", "*"); |
|||
response.setHeader("Access-Control-Allow-Credentials", "true"); |
|||
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"); |
|||
response.setHeader("Access-Control-Max-Age", "86400"); |
|||
response.setHeader("Access-Control-Allow-Headers", "*"); |
|||
response.setStatus(200); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setContentType("application/json;charset=UTF-8"); |
|||
response.getWriter().print("{\"status\":401,\"msg\":\"用户信息无效或过期了\"}"); |
|||
response.getWriter().flush(); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,66 @@ |
|||
package com.qs.serve.common.framework.interceptor; |
|||
|
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.model.annotation.LimitSubmit; |
|||
import com.qs.serve.common.util.StringUtils; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.method.HandlerMethod; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import javax.annotation.Resource; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.lang.reflect.Method; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* 防止重复提交拦截器 |
|||
* @author YenHex |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class LimitSubmitInterceptor implements HandlerInterceptor { |
|||
|
|||
public final String REPEAT_Limit_KEY = "repeatLimitKey"; |
|||
|
|||
@Resource |
|||
public RedisService redisService; |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|||
if (handler instanceof HandlerMethod) { |
|||
HandlerMethod handlerMethod = (HandlerMethod) handler; |
|||
Method method = handlerMethod.getMethod(); |
|||
LimitSubmit annotation = method.getAnnotation(LimitSubmit.class); |
|||
if (annotation != null) { |
|||
if (this.LimitSubmit(request, annotation)) { |
|||
if(StringUtils.isNotEmpty(annotation.message())){ |
|||
HttpResponsePrintUtil.print(response,406,annotation.message()); |
|||
}else { |
|||
HttpResponsePrintUtil.print(response,406,"系统处理中,请勿频繁操作"); |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证是否重复提交由子类实现具体的防重复提交的规则 |
|||
* @param request 请求对象 |
|||
* @param annotation 防复注解 |
|||
* @return |
|||
*/ |
|||
public boolean LimitSubmit(HttpServletRequest request, LimitSubmit annotation) throws Exception { |
|||
String key = REPEAT_Limit_KEY+"_"+request.getRequestURI()+"_"+request.getRemoteHost(); |
|||
Object cache = redisService.get(key); |
|||
if(cache!=null){return true;} |
|||
redisService.set(key,"true",annotation.interval(), TimeUnit.MILLISECONDS); |
|||
return false; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,98 @@ |
|||
package com.qs.serve.common.framework.interceptor; |
|||
|
|||
import java.lang.reflect.Method; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import javax.servlet.http.HttpSession; |
|||
|
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.model.annotation.RepeatSubmit; |
|||
import com.qs.serve.common.util.JsonUtil; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.method.HandlerMethod; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
/** |
|||
* 防止重复提交拦截器 |
|||
* @author YenHex |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class RepeatSubmitInterceptor implements HandlerInterceptor { |
|||
|
|||
public final String REPEAT_PARAMS = "repeatParams"; |
|||
public final String REPEAT_TIME = "repeatTime"; |
|||
public final String SESSION_REPEAT_KEY = "repeatData"; |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|||
if (handler instanceof HandlerMethod) { |
|||
HandlerMethod handlerMethod = (HandlerMethod) handler; |
|||
Method method = handlerMethod.getMethod(); |
|||
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); |
|||
if (annotation != null) { |
|||
if (this.isRepeatSubmit(request, annotation)) { |
|||
HttpResponsePrintUtil.print(response,406,"系统正常处理,请勿重复提交"); |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证是否重复提交由子类实现具体的防重复提交的规则 |
|||
* @param request 请求对象 |
|||
* @param annotation 防复注解 |
|||
* @return |
|||
*/ |
|||
public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception { |
|||
// 本次参数及系统时间
|
|||
String nowParams = JsonUtil.objectToJson(request.getParameterMap()); |
|||
Map<String, Object> nowDataMap = new HashMap<String, Object>(); |
|||
nowDataMap.put(REPEAT_PARAMS, nowParams); |
|||
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); |
|||
// 请求地址(作为存放session的key值)
|
|||
String url = request.getRequestURI(); |
|||
HttpSession session = request.getSession(); |
|||
Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY); |
|||
if (sessionObj != null) { |
|||
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj; |
|||
if (sessionMap.containsKey(url)) { |
|||
Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url); |
|||
if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
Map<String, Object> sessionMap = new HashMap<>(); |
|||
sessionMap.put(url, nowDataMap); |
|||
session.setAttribute(SESSION_REPEAT_KEY, sessionMap); |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 判断参数是否相同 |
|||
*/ |
|||
private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) { |
|||
String nowParams = (String) nowMap.get(REPEAT_PARAMS); |
|||
String preParams = (String) preMap.get(REPEAT_PARAMS); |
|||
return nowParams.equals(preParams); |
|||
} |
|||
|
|||
/** |
|||
* 判断两次间隔时间 |
|||
*/ |
|||
private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) { |
|||
long time1 = (Long) nowMap.get(REPEAT_TIME); |
|||
long time2 = (Long) preMap.get(REPEAT_TIME); |
|||
if ((time1 - time2) < interval) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.manager; |
|||
|
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import com.qs.serve.common.util.JsonUtil; |
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.modules.sys.entity.SysOperationLog; |
|||
import com.qs.serve.modules.sys.mapper.SysOperationLogMapper; |
|||
|
|||
import java.util.TimerTask; |
|||
|
|||
/** |
|||
* 异步工厂 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
public class AsyncFactory { |
|||
|
|||
/** |
|||
* 操作日志记录 |
|||
* @param operationLog |
|||
* @return |
|||
*/ |
|||
public static TimerTask saveOperationLog(final SysOperationLog operationLog) { |
|||
return new TimerTask() { |
|||
@Override |
|||
public void run() { |
|||
if(DevEnvironmentConfig.isDev()){ |
|||
System.out.println(JsonUtil.objectToJson(operationLog)); |
|||
} |
|||
SpringUtils.getBean(SysOperationLogMapper.class).saveLog(operationLog); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,52 @@ |
|||
package com.qs.serve.common.framework.manager; |
|||
|
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.common.util.ThreadsUtils; |
|||
|
|||
import java.util.TimerTask; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
|
|||
/** |
|||
* 异步任务管理器 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
public class AsyncManager { |
|||
/** |
|||
* 操作延迟10毫秒 |
|||
*/ |
|||
private final int OPERATE_DELAY_TIME = 10; |
|||
|
|||
/** |
|||
* 异步操作任务调度线程池 |
|||
*/ |
|||
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); |
|||
|
|||
/** |
|||
* 单例模式 |
|||
*/ |
|||
private AsyncManager() { |
|||
} |
|||
|
|||
private static AsyncManager me = new AsyncManager(); |
|||
|
|||
public static AsyncManager me() { |
|||
return me; |
|||
} |
|||
|
|||
/** |
|||
* 执行任务 |
|||
* @param task 任务 |
|||
*/ |
|||
public void execute(TimerTask task) { |
|||
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
/** 停止任务线程池 */ |
|||
public void shutdown() { |
|||
ThreadsUtils.shutdownAndAwaitTermination(executor); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,161 @@ |
|||
package com.qs.serve.common.framework.mvc; |
|||
|
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import com.qs.serve.common.util.DateUtils; |
|||
import com.qs.serve.common.util.ServletUtils; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import javax.servlet.*; |
|||
import javax.servlet.annotation.WebFilter; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletRequestWrapper; |
|||
import java.io.BufferedReader; |
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.IOException; |
|||
import java.io.InputStreamReader; |
|||
import java.nio.charset.StandardCharsets; |
|||
|
|||
/** |
|||
* HttpServletRequest 过滤器 |
|||
* 解决: request.getInputStream()只能读取一次的问题 |
|||
* 目标: 流可重复读 |
|||
* @Author YenHex |
|||
* @Date 2021/4/9 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Slf4j |
|||
@Component |
|||
@WebFilter(filterName = "HttpServletRequestFilter", urlPatterns = "/") |
|||
@Order(10000) |
|||
public class HttpServletRequestFilter implements Filter{ |
|||
|
|||
|
|||
@Override |
|||
public void init(FilterConfig filterConfig) throws ServletException { |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { |
|||
RequestWrapper requestWrapper = null; |
|||
if(servletRequest instanceof HttpServletRequest) { |
|||
//过滤文件URL
|
|||
String url = ((HttpServletRequest) servletRequest).getRequestURI(); |
|||
if(!url.contains("import")&&!url.contains("upload")){ |
|||
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest); |
|||
} |
|||
} |
|||
long startTime = System.currentTimeMillis(); |
|||
if(null == requestWrapper) { |
|||
filterChain.doFilter(servletRequest, servletResponse); |
|||
} else { |
|||
filterChain.doFilter(requestWrapper, servletResponse); |
|||
} |
|||
long endTime = System.currentTimeMillis(); |
|||
double diffTime = DateUtils.diffTime(startTime,endTime); |
|||
assert servletRequest instanceof HttpServletRequest; |
|||
HttpServletRequest request = (HttpServletRequest)servletRequest; |
|||
String tenant = request.getHeader("tenant-id"); |
|||
String url = request.getRequestURL().toString(); |
|||
String method = request.getMethod(); |
|||
String queryStr = request.getQueryString(); |
|||
String reqBody = ""; |
|||
if (ServletUtils.isJsonRequest(request)) { |
|||
reqBody = requestWrapper.getBody(); |
|||
} |
|||
StringBuffer buffer = new StringBuffer("["+method+"] "+url + " IP: "+ServletUtils.getIp(request)); |
|||
buffer.append("\n校区编号: "+tenant); |
|||
try { |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
buffer.append("\n用户类型: "+ loginUser.getTypeFlag().getName()+" 用户ID: "+loginUser.getUserId()); |
|||
} |
|||
} catch (Exception e) {} |
|||
if(!StringUtils.isEmpty(queryStr)){ |
|||
buffer.append("\n参数: "+queryStr); |
|||
} |
|||
if(!StringUtils.isEmpty(reqBody)){ |
|||
buffer.append("\n请求体: "+reqBody); |
|||
} |
|||
buffer.append("\n耗时: "+diffTime+"秒"); |
|||
/*if(diffTime>4){ log.warn("访问速度:{}秒,接口:{},参数:{},请求体:{}",diffTime,"["+method+"] "+url,queryStr,reqBody);}*/ |
|||
System.out.println(buffer); |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
|
|||
} |
|||
|
|||
/*** |
|||
* HttpServletRequest 包装器 |
|||
* 解决: request.getInputStream()只能读取一次的问题 |
|||
* 目标: 流可重复读 |
|||
*/ |
|||
public class RequestWrapper extends HttpServletRequestWrapper { |
|||
|
|||
/** |
|||
* 请求体 |
|||
*/ |
|||
private String mBody; |
|||
|
|||
public RequestWrapper(HttpServletRequest request) { |
|||
super(request); |
|||
mBody = getBody(request); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求体 |
|||
* @param request 请求 |
|||
* @return 请求体 |
|||
*/ |
|||
private String getBody(HttpServletRequest request) { |
|||
return ServletUtils.getBodyString(request); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求体 |
|||
* @return 请求体 |
|||
*/ |
|||
public String getBody() { |
|||
return mBody; |
|||
} |
|||
|
|||
@Override |
|||
public BufferedReader getReader() throws IOException { |
|||
return new BufferedReader(new InputStreamReader(getInputStream())); |
|||
} |
|||
|
|||
@Override |
|||
public ServletInputStream getInputStream() throws IOException { |
|||
// 创建字节数组输入流
|
|||
final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(StandardCharsets.UTF_8)); |
|||
|
|||
return new ServletInputStream() { |
|||
@Override |
|||
public boolean isFinished() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isReady() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public void setReadListener(ReadListener readListener) { |
|||
} |
|||
|
|||
@Override |
|||
public int read() throws IOException { |
|||
return bais.read(); |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler; |
|||
|
|||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; |
|||
import com.qs.serve.common.util.NanoIdUtils; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/4/11 |
|||
*/ |
|||
public class IdGeneratorHandler extends DefaultIdentifierGenerator { |
|||
|
|||
@Override |
|||
public String nextUUID(Object entity) { |
|||
return NanoIdUtils.randomNanoId(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler; |
|||
|
|||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.ibatis.reflection.MetaObject; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
/** |
|||
* 自动注入列数据 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Slf4j |
|||
public class SysMetaHandler implements MetaObjectHandler { |
|||
|
|||
@Override |
|||
public void insertFill(MetaObject metaObject) { |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
this.strictInsertFill(metaObject, "createBy", () -> loginUser.getUserId(), String.class); |
|||
} |
|||
this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); |
|||
} |
|||
|
|||
@Override |
|||
public void updateFill(MetaObject metaObject) { |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
this.strictUpdateFill(metaObject, "updateBy", () -> loginUser.getUserId(), String.class); |
|||
} |
|||
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler; |
|||
|
|||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; |
|||
import com.qs.serve.common.model.consts.GySysConst; |
|||
import com.qs.serve.common.model.enums.HttpCode; |
|||
import com.qs.serve.common.util.Assert; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import net.sf.jsqlparser.expression.Expression; |
|||
import net.sf.jsqlparser.expression.StringValue; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* @Author YenHex |
|||
* @Date 2021/5/7 |
|||
* @Version: 1.0 |
|||
**/ |
|||
public class SysTenantHandler implements TenantLineHandler { |
|||
|
|||
public SysTenantHandler(String[] ignoreTables){ |
|||
this.IGNORE_TABLES = Arrays.asList(ignoreTables); |
|||
} |
|||
|
|||
|
|||
final private java.util.List<String> IGNORE_TABLES; |
|||
|
|||
@Override |
|||
public Expression getTenantId() { |
|||
String tenant = AuthContextUtils.getTenant(); |
|||
if(tenant==null){ |
|||
Assert.throwEx(HttpCode.LOGIN_ERR_4001); |
|||
} |
|||
return new StringValue(tenant); |
|||
} |
|||
|
|||
@Override |
|||
public String getTenantIdColumn() { |
|||
return GySysConst.TENANT_COLUMN; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否忽略该表,默认全忽略,匹配到的不忽略 |
|||
* @param tableName |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public boolean ignoreTable(String tableName) { |
|||
for (String tb : this.IGNORE_TABLES) { |
|||
if(tableName.equalsIgnoreCase(tb)){ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -0,0 +1,50 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler.meta; |
|||
|
|||
import cn.hutool.json.JSONUtil; |
|||
import org.apache.ibatis.type.BaseTypeHandler; |
|||
import org.apache.ibatis.type.JdbcType; |
|||
import org.apache.ibatis.type.MappedJdbcTypes; |
|||
import org.apache.ibatis.type.MappedTypes; |
|||
|
|||
import java.sql.CallableStatement; |
|||
import java.sql.PreparedStatement; |
|||
import java.sql.ResultSet; |
|||
import java.sql.SQLException; |
|||
|
|||
/** |
|||
* 存储到数据库, 将String数组转换成字符串; |
|||
* 从数据库获取数据, 将字符串转为LONG数组. |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@MappedTypes({String[].class}) |
|||
@MappedJdbcTypes({JdbcType.VARCHAR}) |
|||
public class JsonStringTypeHandler extends BaseTypeHandler<String[]> { |
|||
|
|||
private static String[] l = new String[]{}; |
|||
|
|||
@Override |
|||
public void setNonNullParameter(PreparedStatement ps, int i, |
|||
String[] parameter, JdbcType jdbcType) throws SQLException { |
|||
ps.setString(i, JSONUtil.toJsonStr(parameter)); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, String columnName) |
|||
throws SQLException { |
|||
return JSONUtil.parseArray(rs.getString(columnName)).toArray(l); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, int columnIndex) |
|||
throws SQLException { |
|||
return JSONUtil.parseArray(rs.getString(columnIndex)).toArray(l); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(CallableStatement cs, int columnIndex) |
|||
throws SQLException { |
|||
return JSONUtil.parseArray(cs.getString(columnIndex)).toArray(l); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,74 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler.meta; |
|||
|
|||
import com.qs.serve.common.util.StringUtils; |
|||
import org.apache.ibatis.type.BaseTypeHandler; |
|||
import org.apache.ibatis.type.JdbcType; |
|||
import org.apache.ibatis.type.MappedJdbcTypes; |
|||
import org.apache.ibatis.type.MappedTypes; |
|||
|
|||
import java.sql.CallableStatement; |
|||
import java.sql.PreparedStatement; |
|||
import java.sql.ResultSet; |
|||
import java.sql.SQLException; |
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 存储到数据库, 将String数组转换成字符串; |
|||
* 从数据库获取数据, 将字符串转为LONG数组. |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@MappedTypes({String[].class}) |
|||
@MappedJdbcTypes({JdbcType.VARCHAR}) |
|||
public class SplitStringTypeHandler extends BaseTypeHandler<String[]> { |
|||
|
|||
private static String[] l = new String[]{}; |
|||
|
|||
@Override |
|||
public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException { |
|||
if(parameter!=null&¶meter.length>1){ |
|||
StringBuffer buffer = new StringBuffer(); |
|||
for (int i1 = 0; i1 < parameter.length; i1++) { |
|||
buffer.append(parameter[i1]); |
|||
if(i1+1<parameter.length){ |
|||
buffer.append(","); |
|||
} |
|||
} |
|||
ps.setString(i, buffer.toString()); |
|||
}else if (parameter!=null&¶meter.length==1){ |
|||
ps.setString(i, parameter[0]); |
|||
}else { |
|||
ps.setString(i, ""); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException { |
|||
String valueString = rs.getString(columnName); |
|||
return initValues(valueString); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { |
|||
String valueString = rs.getString(columnIndex); |
|||
return initValues(valueString); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { |
|||
String valueString = cs.getString(columnIndex); |
|||
return initValues(valueString); |
|||
} |
|||
|
|||
private String[] initValues(String valueString){ |
|||
if(StringUtils.isNotEmpty(valueString)){ |
|||
String[] vals = valueString.split(","); |
|||
return Arrays.stream(vals) |
|||
.filter(val -> val != null && val.trim().length() > 0) |
|||
.toArray(String[]::new); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,115 @@ |
|||
package com.qs.serve.common.framework.mybatis.join; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.qs.serve.common.framework.mybatis.join.annotations.BindEntity; |
|||
import com.qs.serve.common.framework.mybatis.join.enums.JoinType; |
|||
import com.qs.serve.common.framework.mybatis.join.model.JoinFieldData; |
|||
import com.qs.serve.common.util.CollectionUtil; |
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.common.util.WordUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.lang.annotation.Annotation; |
|||
import java.lang.reflect.Field; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
@Slf4j |
|||
public class JoinUtil { |
|||
|
|||
private static Map<String, List<JoinFieldData>> CACHE_MAP = new ConcurrentHashMap<>(); |
|||
|
|||
private static List<JoinFieldData> getJoinFields(List<?> list) { |
|||
if(CollectionUtil.isEmpty(list)){ |
|||
return new ArrayList<>(); |
|||
} |
|||
String className = list.get(0).getClass().getName(); |
|||
List<JoinFieldData> joinFieldDataList = new ArrayList<>(); |
|||
Class<?> clazz = null; |
|||
try { |
|||
clazz = Class.forName(className); |
|||
} catch (ClassNotFoundException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
if(clazz==null){return null;} |
|||
Field[] fields = clazz.getDeclaredFields(); |
|||
Field fieldJoinVal = null; |
|||
Field fieldSetVal = null; |
|||
BindEntity bindEntity = null; |
|||
for (Field field : fields) { |
|||
for (Annotation annotation : field.getAnnotations()) { |
|||
if(annotation instanceof BindEntity){ |
|||
fieldSetVal = field; |
|||
bindEntity = (BindEntity) annotation; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if(fieldSetVal!=null){ |
|||
for (Field field :fields){ |
|||
if(field.getName().equals(bindEntity.joinField())){ |
|||
fieldJoinVal = field; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if(bindEntity!=null&&fieldJoinVal!=null){ |
|||
JoinFieldData fieldData = new JoinFieldData(); |
|||
fieldData.setFieldJoinVal(fieldJoinVal); |
|||
fieldData.setBindEntity(bindEntity); |
|||
fieldData.setFieldSetVal(fieldSetVal); |
|||
joinFieldDataList.add(fieldData); |
|||
} |
|||
CACHE_MAP.put(className,joinFieldDataList); |
|||
return joinFieldDataList; |
|||
} |
|||
|
|||
public static void relate(List<?> list) { |
|||
List<JoinFieldData> joinFieldData = getJoinFields(list); |
|||
HashMap<String,Object> tempCache = new HashMap<>(list.size()+1); |
|||
for (JoinFieldData fieldData : joinFieldData) { |
|||
for (Object object : list) { |
|||
try { |
|||
Field fieldJoinVal = fieldData.getFieldJoinVal(); |
|||
Field fieldSetVal = fieldData.getFieldSetVal(); |
|||
BindEntity bindEntity = fieldData.getBindEntity(); |
|||
fieldSetVal.setAccessible(true); |
|||
fieldJoinVal.setAccessible(true); |
|||
IService<?> iService = SpringUtils.getBean(bindEntity.service()); |
|||
Object identity = fieldJoinVal.get(object); |
|||
if(identity==null){ |
|||
continue; |
|||
} |
|||
Object result = tempCache.get(identity.toString()); |
|||
if(result!=null){ |
|||
fieldSetVal.set(object,result); |
|||
continue; |
|||
} |
|||
if(bindEntity.joinType()== JoinType.List){ |
|||
QueryWrapper wrapper = new QueryWrapper<>(); |
|||
wrapper.eq(WordUtil.toLine(bindEntity.joinField()),identity); |
|||
result = iService.list(wrapper); |
|||
}else { |
|||
QueryWrapper wrapper = new QueryWrapper(); |
|||
wrapper.eq(WordUtil.toLine(bindEntity.selfField()),identity); |
|||
result = iService.getOne(wrapper,false); |
|||
} |
|||
fieldSetVal.set(object,result); |
|||
tempCache.put(identity.toString(),result); |
|||
} catch (IllegalAccessException e) { |
|||
log.warn("join 工具失败:",e); |
|||
} |
|||
} |
|||
} |
|||
tempCache = null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.mybatis.join.annotations; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.qs.serve.common.framework.mybatis.join.enums.JoinType; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* 实现left join |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
@Target({ElementType.FIELD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
public @interface BindEntity { |
|||
|
|||
Class<? extends IService> service(); |
|||
|
|||
/** |
|||
* 关联的表 |
|||
* @return |
|||
*/ |
|||
String joinField() default ""; |
|||
|
|||
/** |
|||
* 主表 |
|||
* @return |
|||
*/ |
|||
String selfField() default "id"; |
|||
|
|||
JoinType joinType() default JoinType.Object; |
|||
|
|||
} |
@ -0,0 +1,14 @@ |
|||
package com.qs.serve.common.framework.mybatis.join.enums; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
public enum JoinType { |
|||
|
|||
/** |
|||
* 返回类型 |
|||
*/ |
|||
Object,List |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.serve.common.framework.mybatis.join.model; |
|||
|
|||
import com.qs.serve.common.framework.mybatis.join.annotations.BindEntity; |
|||
import lombok.Data; |
|||
|
|||
import java.lang.reflect.Field; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
@Data |
|||
public class JoinFieldData { |
|||
Field fieldJoinVal; |
|||
Field fieldSetVal; |
|||
BindEntity bindEntity; |
|||
} |
@ -0,0 +1,159 @@ |
|||
package com.qs.serve.common.framework.mybatis.query; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryFieldData; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryFieldDataValue; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryType; |
|||
import com.qs.serve.common.framework.mybatis.query.annotations.QueryWrap; |
|||
import com.qs.serve.common.util.WordUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.util.CollectionUtils; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.lang.annotation.Annotation; |
|||
import java.lang.reflect.Field; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2017/10/28 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
public class AnnotationQueryStorage { |
|||
|
|||
private static final Map<String, List<QueryFieldData>> BEAN_QUERY_FIELD = new ConcurrentHashMap<>(); |
|||
|
|||
protected static List<QueryFieldData> getQueryFieldList(String className){ |
|||
List<QueryFieldData> queryFieldList = BEAN_QUERY_FIELD.get(className); |
|||
if(queryFieldList==null){ |
|||
Class<?> clazz = null; |
|||
try { |
|||
clazz = Class.forName(className); |
|||
} catch (ClassNotFoundException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
queryFieldList = new ArrayList<>(); |
|||
if(clazz==null){return queryFieldList;} |
|||
Field[] fields = clazz.getDeclaredFields(); |
|||
for (Field field : fields) { |
|||
Annotation[] annotations = field.getAnnotations(); |
|||
boolean isExist = false; |
|||
boolean isIgnore = false; |
|||
for (Annotation annotation : annotations) { |
|||
if(annotation instanceof QueryWrap){ |
|||
QueryWrap queryWrap = (QueryWrap)annotation; |
|||
QueryFieldData queryField= handleQueryField(queryWrap.type(),field); |
|||
if(queryField!=null){ |
|||
isExist = true; |
|||
queryFieldList.add(queryField); |
|||
break; |
|||
} |
|||
} |
|||
if(annotation instanceof TableField){ |
|||
TableField tableField = (TableField) annotation; |
|||
isIgnore = tableField.exist(); |
|||
} |
|||
} |
|||
//不存在,添加默认
|
|||
if( !isExist && !isIgnore ){ |
|||
QueryFieldData queryField = new QueryFieldData(); |
|||
//系统默认使用格式。示例: userInfo -> user_info
|
|||
queryField.setColumnName(WordUtil.toLine(field.getName())); |
|||
queryField.setField(field); |
|||
queryField.setFieldName(field.getName()); |
|||
queryField.setType(QueryType.NONE); |
|||
queryFieldList.add(queryField); |
|||
} |
|||
} |
|||
BEAN_QUERY_FIELD.put(className,queryFieldList); |
|||
if(queryFieldList.size()==0){ |
|||
log.warn("无匹配的注解字段,类名:"+className); |
|||
} |
|||
} |
|||
return queryFieldList; |
|||
} |
|||
|
|||
/** |
|||
* 处理查询列 |
|||
* @param queryType |
|||
* @param field |
|||
* @return |
|||
*/ |
|||
private static QueryFieldData handleQueryField(QueryType queryType, Field field){ |
|||
String columnName = null; |
|||
if(queryType!=null){ |
|||
QueryFieldData queryField = new QueryFieldData(); |
|||
if(StringUtils.isEmpty(columnName)){ |
|||
columnName = WordUtil.toLine(field.getName()); |
|||
} |
|||
queryField.setFieldName(field.getName()); |
|||
queryField.setColumnName(columnName); |
|||
queryField.setField(field); |
|||
queryField.setType(queryType); |
|||
return queryField; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 加载查询对象生成配置值 |
|||
* @param query |
|||
* @return |
|||
*/ |
|||
protected static List<QueryFieldDataValue> loadQueryFieldDataValues(Object query){ |
|||
String className = query.getClass().getName(); |
|||
List<QueryFieldData> dataList = AnnotationQueryStorage.getQueryFieldList(className); |
|||
if(!CollectionUtils.isEmpty(dataList)){ |
|||
List<QueryFieldDataValue> valueList = new ArrayList<>(); |
|||
for (QueryFieldData fieldData : dataList) { |
|||
Field field = fieldData.getField(); |
|||
field.setAccessible(true); |
|||
Object value = null; |
|||
try { |
|||
value = field.get(query); |
|||
} catch (IllegalAccessException e) { |
|||
log.warn("查询类字段参数异常。查询类:{},字段:{}",className,field.getName()); |
|||
} |
|||
if(value!=null){ |
|||
QueryFieldDataValue dataValue = new QueryFieldDataValue(fieldData); |
|||
//通过列条件,重新赋值(赋值查询条件和格式)
|
|||
dataValue.setValue(value); |
|||
valueList.add(dataValue); |
|||
} |
|||
} |
|||
return valueList; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 初始化封装包参数 |
|||
* @param fieldValues |
|||
* @param queryWrapper |
|||
* @param <T> |
|||
* @return 判断是否排序 |
|||
*/ |
|||
protected static <T> void initWrap(List<QueryFieldDataValue> fieldValues, QueryWrapper<T> queryWrapper){ |
|||
for (QueryFieldDataValue fieldValue : fieldValues) { |
|||
QueryType queryType = fieldValue.getType(); |
|||
Object value = fieldValue.getValue(); |
|||
String column = fieldValue.getColumnName(); |
|||
if(queryType.equals(QueryType.EQ)){ queryWrapper.eq(column,value); } |
|||
if(queryType.equals(QueryType.GE)){ queryWrapper.ge(column,value); } |
|||
if(queryType.equals(QueryType.GT)){ queryWrapper.gt(column,value); } |
|||
if(queryType.equals(QueryType.In)){ queryWrapper.in(column,value); } |
|||
if(queryType.equals(QueryType.LE)){ queryWrapper.le(column,value); } |
|||
if(queryType.equals(QueryType.Like)){ queryWrapper.like(column,value); } |
|||
if(queryType.equals(QueryType.LT)){ queryWrapper.lt(column,value); } |
|||
if(queryType.equals(QueryType.NE)){ queryWrapper.ne(column,value); } |
|||
if(queryType.equals(QueryType.RightLike)){ queryWrapper.likeRight(column,value); } |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.annotations; |
|||
|
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryType; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @date 2020/5/26 |
|||
* SQL In条件注解 |
|||
*/ |
|||
@Target({ElementType.FIELD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
public @interface QueryWrap { |
|||
|
|||
/** |
|||
* 列名(默认为空字符串) |
|||
* 空字符串,采用字段转列策略 示例: userInfo -> user_info |
|||
* 非空时,采用列的值 |
|||
*/ |
|||
String value() default ""; |
|||
|
|||
/** |
|||
* 查询类型(默认采用值相对) |
|||
*/ |
|||
QueryType type() default QueryType.EQ; |
|||
|
|||
} |
|||
|
@ -0,0 +1,23 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.model; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.lang.reflect.Field; |
|||
|
|||
/** |
|||
* 存储DTO |
|||
* @author JcYen |
|||
* @Date 2017/10/28 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class QueryFieldData { |
|||
|
|||
private Field field; |
|||
private QueryType type; |
|||
private String columnName; |
|||
private String fieldName; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.model; |
|||
|
|||
import com.qs.serve.common.util.CopierUtil; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* 存储DTO |
|||
* @author JcYen |
|||
* @Date 2017/10/28 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class QueryFieldDataValue{ |
|||
|
|||
public QueryFieldDataValue(QueryFieldData data){ |
|||
CopierUtil.copy(data,this); |
|||
} |
|||
|
|||
private QueryType type; |
|||
private String columnName; |
|||
private String fieldName; |
|||
private Object value; |
|||
} |
@ -0,0 +1,40 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.model; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
*/ |
|||
public enum QueryType { |
|||
|
|||
/** |
|||
* 无 |
|||
*/ |
|||
NONE, |
|||
|
|||
/** |
|||
* 通用的equals(Integer,Long,String) |
|||
*/ |
|||
EQ, |
|||
NE, |
|||
|
|||
/** |
|||
* 比较修饰符,修饰日期,数值类型(Integer,Long,Double,BigDecimal) |
|||
*/ |
|||
LT, |
|||
LE, |
|||
GT, |
|||
GE, |
|||
|
|||
/** |
|||
* 修饰List<Long> |
|||
*/ |
|||
In, |
|||
NotIn, |
|||
|
|||
/** |
|||
* 修饰字符串 |
|||
*/ |
|||
Like, |
|||
RightLike, |
|||
NotLike, |
|||
|
|||
} |
@ -0,0 +1,309 @@ |
|||
package com.qs.serve.common.framework.redis; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.data.redis.core.*; |
|||
import org.springframework.stereotype.Service; |
|||
import java.io.Serializable; |
|||
import java.util.*; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @since 2022/3/4 |
|||
*/ |
|||
@Service |
|||
@AllArgsConstructor |
|||
public class RedisService { |
|||
|
|||
private RedisTemplate redisTemplate; |
|||
|
|||
private StringRedisTemplate stringRedisTemplate; |
|||
|
|||
|
|||
/** |
|||
* 写入缓存 |
|||
* |
|||
* @param key |
|||
* @param value |
|||
* @return |
|||
*/ |
|||
public boolean set(final String key, Object value) { |
|||
boolean result = false; |
|||
try { |
|||
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); |
|||
operations.set(key, value); |
|||
result = true; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 写入缓存设置时效时间 |
|||
* |
|||
* @param key |
|||
* @param value |
|||
* @return |
|||
*/ |
|||
public boolean set(final String key, Object value, int expireTime) { |
|||
return set(key, value, expireTime,TimeUnit.SECONDS); |
|||
} |
|||
|
|||
/** |
|||
* 写入缓存设置时效时间 |
|||
* |
|||
* @param key |
|||
* @param value |
|||
* @return |
|||
*/ |
|||
public boolean set(final String key, Object value, int expireTime,TimeUnit unit) { |
|||
boolean result = false; |
|||
try { |
|||
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); |
|||
operations.set(key, value); |
|||
redisTemplate.expire(key, expireTime, unit); |
|||
result = true; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 批量删除对应的value |
|||
* |
|||
* @param keys |
|||
*/ |
|||
public void remove(final String... keys) { |
|||
for (String key : keys) { |
|||
remove(key); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 批量删除key |
|||
* |
|||
* @param pattern |
|||
*/ |
|||
public void removePattern(final String pattern) { |
|||
Set<Serializable> keys = redisTemplate.keys(pattern); |
|||
if (keys.size() > 0) |
|||
redisTemplate.delete(keys); |
|||
} |
|||
|
|||
/** |
|||
* 删除对应的value |
|||
* |
|||
* @param key |
|||
*/ |
|||
public void remove(final String key) { |
|||
if (exists(key)) { |
|||
redisTemplate.delete(key); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断缓存中是否有对应的value |
|||
* |
|||
* @param text |
|||
* @return |
|||
*/ |
|||
public Set<String> list(final String text) { |
|||
Set<String> keySet = redisTemplate.keys("*" + text + "*"); |
|||
for (String key : keySet) { |
|||
System.out.println(key); |
|||
} |
|||
return keySet; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 获取所有数据 |
|||
* |
|||
* @return |
|||
*/ |
|||
public Set<String> list() { |
|||
Set<String> keySet = redisTemplate.keys("*" + "#" +"*"); |
|||
for (String key : keySet) { |
|||
System.out.println(key); |
|||
} |
|||
return keySet; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 判断缓存中是否有对应的value |
|||
* |
|||
* @param text |
|||
* @return |
|||
*/ |
|||
public void removeForLike(final String text) { |
|||
Set<String> keySet = redisTemplate.keys("*" + text + "*"); |
|||
Iterator<String> it = keySet.iterator(); |
|||
while(it.hasNext()){ |
|||
redisTemplate.delete(it.next()); |
|||
} |
|||
} |
|||
|
|||
|
|||
public Map<String, Object> cleanRedis() { |
|||
Map<String, Object> map = new HashMap<>(); |
|||
try { |
|||
// 获取所有key
|
|||
Set<String> keys = redisTemplate.keys("*"); |
|||
assert keys != null; |
|||
// 迭代
|
|||
Iterator<String> it1 = keys.iterator(); |
|||
while (it1.hasNext()) { |
|||
// 循环删除
|
|||
stringRedisTemplate.delete(it1.next()); |
|||
} |
|||
map.put("code", 1); |
|||
map.put("msg", "清理全局缓存成功"); |
|||
return map; |
|||
} catch (Exception e) { |
|||
map.put("code", -1); |
|||
map.put("msg", "清理全局缓存失败"); |
|||
return map; |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 判断缓存中是否有对应的value |
|||
* |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public boolean exists(final String key) { |
|||
return redisTemplate.hasKey(key); |
|||
} |
|||
|
|||
/** |
|||
* 读取缓存 |
|||
* |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public Object get(final String key) { |
|||
Object result = null; |
|||
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); |
|||
result = operations.get(key); |
|||
return result; |
|||
} |
|||
|
|||
public String getString(final String key) { |
|||
Object result = null; |
|||
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); |
|||
result = operations.get(key); |
|||
if(result!=null){ |
|||
return result.toString(); |
|||
|
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public Integer getInteger(final String key) { |
|||
String result = getString(key); |
|||
if(result!=null){ |
|||
return Integer.parseInt(result); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 哈希 添加 |
|||
* |
|||
* @param key |
|||
* @param hashKey |
|||
* @param value |
|||
*/ |
|||
public void hmSet(String key, Object hashKey, Object value) { |
|||
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); |
|||
hash.put(key, hashKey, value); |
|||
} |
|||
|
|||
/** |
|||
* 哈希获取数据 |
|||
* |
|||
* @param key |
|||
* @param hashKey |
|||
* @return |
|||
*/ |
|||
public Object hmGet(String key, Object hashKey) { |
|||
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); |
|||
return hash.get(key, hashKey); |
|||
} |
|||
|
|||
/** |
|||
* 列表添加 |
|||
* |
|||
* @param k |
|||
* @param v |
|||
*/ |
|||
public void lPush(String k, Object v) { |
|||
ListOperations<String, Object> list = redisTemplate.opsForList(); |
|||
list.rightPush(k, v); |
|||
} |
|||
|
|||
/** |
|||
* 列表获取 |
|||
* |
|||
* @param k |
|||
* @param l |
|||
* @param l1 |
|||
* @return |
|||
*/ |
|||
public List<Object> lRange(String k, long l, long l1) { |
|||
ListOperations<String, Object> list = redisTemplate.opsForList(); |
|||
return list.range(k, l, l1); |
|||
} |
|||
|
|||
/** |
|||
* 集合添加 |
|||
* |
|||
* @param key |
|||
* @param value |
|||
*/ |
|||
public void add(String key, Object value) { |
|||
SetOperations<String, Object> set = redisTemplate.opsForSet(); |
|||
set.add(key, value); |
|||
} |
|||
|
|||
/** |
|||
* 集合获取 |
|||
* |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public Set<Object> setMembers(String key) { |
|||
SetOperations<String, Object> set = redisTemplate.opsForSet(); |
|||
return set.members(key); |
|||
} |
|||
|
|||
/** |
|||
* 有序集合添加 |
|||
* |
|||
* @param key |
|||
* @param value |
|||
* @param scoure |
|||
*/ |
|||
public void zAdd(String key, Object value, double scoure) { |
|||
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); |
|||
zset.add(key, value, scoure); |
|||
} |
|||
|
|||
/** |
|||
* 有序集合获取 |
|||
* |
|||
* @param key |
|||
* @param scoure |
|||
* @param scoure1 |
|||
* @return |
|||
*/ |
|||
public Set<Object> rangeByScore(String key, double scoure, double scoure1) { |
|||
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); |
|||
return zset.rangeByScore(key, scoure, scoure1); |
|||
} |
|||
} |
@ -0,0 +1,192 @@ |
|||
package com.qs.serve.common.framework.security.filter; |
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import com.qs.serve.common.config.properties.PermitProperties; |
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
import com.qs.serve.common.framework.security.model.LoginUserType; |
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.model.consts.GySysConst; |
|||
import com.qs.serve.common.model.consts.RedisCacheKeys; |
|||
import com.qs.serve.common.util.JwtUtils; |
|||
import com.qs.serve.common.framework.security.util.SecurityPermitUtil; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import com.qs.serve.modules.sys.service.SysUserService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
|||
import org.springframework.security.core.context.SecurityContextHolder; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.filter.OncePerRequestFilter; |
|||
|
|||
import javax.servlet.FilterChain; |
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* 判断token情况 |
|||
* @Author JcYen |
|||
* @Date 2022/03/01 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
public class SecurityRequestFilter extends OncePerRequestFilter { |
|||
|
|||
@Value("${server.servlet.context-path}") |
|||
public String contextPath; |
|||
|
|||
@Autowired |
|||
private SysUserService sysUserService; |
|||
|
|||
@Autowired |
|||
private PermitProperties permitProperties; |
|||
|
|||
@Autowired |
|||
private RedisService redisService; |
|||
|
|||
@Override |
|||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { |
|||
//移除前缀
|
|||
String url = request.getRequestURI(); |
|||
if(!StringUtils.isEmpty(contextPath)&&!contextPath.equals("/")){ |
|||
url = url.replace(contextPath,""); |
|||
} |
|||
//分析拦截类型
|
|||
boolean ignorePermit = false; |
|||
for(String reg : permitProperties.getPermits()){ |
|||
if(SecurityPermitUtil.verify(reg,url)){ |
|||
ignorePermit=true; |
|||
break; |
|||
} |
|||
} |
|||
//分析拦截类型
|
|||
boolean ignoreTenant = false; |
|||
for(String reg : permitProperties.getTenants()){ |
|||
if(SecurityPermitUtil.verify(reg,url)){ |
|||
ignoreTenant = true; |
|||
break; |
|||
} |
|||
} |
|||
response.setHeader("Access-Control-Allow-Origin", "*"); |
|||
//登录拦截
|
|||
if ("OPTIONS".equals(request.getMethod())) { |
|||
HttpResponsePrintUtil.print(response,200,"success"); |
|||
return; |
|||
} |
|||
if(!ignoreTenant){ |
|||
boolean hasTenant = handleTenant(request,response); |
|||
if(!hasTenant){ |
|||
return; |
|||
} |
|||
} |
|||
if(ignorePermit){ |
|||
//try to doSecurityAuthor
|
|||
tryDoSecurityAuthor(request, response, chain); |
|||
chain.doFilter(request,response); |
|||
}else { |
|||
doSecurityAuthor(request, response, chain); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 尝试执行登录 |
|||
* @param request |
|||
* @param response |
|||
* @param chain |
|||
* @throws IOException |
|||
* @throws ServletException |
|||
*/ |
|||
private void tryDoSecurityAuthor(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { |
|||
String tenant = request.getHeader(GySysConst.TENANT_PROP); |
|||
String token = request.getHeader(GySysConst.AUTHORIZATION_PROP); |
|||
if(tenant!=null&&token!=null&&JwtUtils.verify(token)){ |
|||
String userId = JwtUtils.getUserId(token); |
|||
String userType = JwtUtils.getUserType(token); |
|||
AuthContextUtils.setTenant(tenant); |
|||
if(SecurityContextHolder.getContext().getAuthentication()==null){ |
|||
UserDetails userDetails = null; |
|||
if(userType.equals(LoginUserType.SYS_USER.getCode()) |
|||
||userType.equals(LoginUserType.SYS_SUP_USER.getCode())){ |
|||
//后台管理员登录
|
|||
userDetails = sysUserService.getLoginUserById(userId); |
|||
} |
|||
if(userDetails!=null){ |
|||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); |
|||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |
|||
SecurityContextHolder.getContext().setAuthentication(authentication); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 执行登录 |
|||
* @param request |
|||
* @param response |
|||
* @param chain |
|||
* @throws IOException |
|||
* @throws ServletException |
|||
*/ |
|||
private void doSecurityAuthor(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { |
|||
String token = request.getHeader(GySysConst.AUTHORIZATION_PROP); |
|||
if(!JwtUtils.verify(token)){ |
|||
HttpResponsePrintUtil.print2(response,401,"身份凭证无效或过期"); |
|||
}else{ |
|||
String userId = JwtUtils.getUserId(token); |
|||
String userType = JwtUtils.getUserType(token); |
|||
String client = JwtUtils.getClient(token); |
|||
String redisKey = com.qs.serve.common.util.StringUtils.format(RedisCacheKeys.LOGIN_KEY_APP,client,userId); |
|||
String cacheToken = redisService.getString(redisKey); |
|||
if(!DevEnvironmentConfig.isDev()&&!token.equals(cacheToken)){ |
|||
redisService.remove(redisKey); |
|||
HttpResponsePrintUtil.print2(response,401,"异地登录,已被系统登出"); |
|||
//return解决:security.access.AccessDeniedException
|
|||
return; |
|||
} |
|||
if(SecurityContextHolder.getContext().getAuthentication()==null){ |
|||
UserDetails userDetails = null; |
|||
if(userType.equals(LoginUserType.SYS_USER.getCode()) |
|||
||userType.equals(LoginUserType.SYS_SUP_USER.getCode())){ |
|||
//后台管理员登录
|
|||
userDetails = sysUserService.getLoginUserById(userId); |
|||
} |
|||
if(userDetails==null){ |
|||
HttpResponsePrintUtil.print2(response,401,"登录信息无效,请重新登录"); |
|||
//return解决:security.access.AccessDeniedException
|
|||
return; |
|||
}else { |
|||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); |
|||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |
|||
SecurityContextHolder.getContext().setAuthentication(authentication); |
|||
} |
|||
} |
|||
chain.doFilter(request, response); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 多租户模式下使用 |
|||
* @param request |
|||
* @param response |
|||
* @throws IOException |
|||
* @throws ServletException |
|||
*/ |
|||
private boolean handleTenant(HttpServletRequest request, HttpServletResponse response) throws IOException{ |
|||
String tenant = request.getHeader(GySysConst.TENANT_PROP); |
|||
if (tenant != null) { |
|||
AuthContextUtils.setTenant(tenant); |
|||
return true; |
|||
}else{ |
|||
HttpResponsePrintUtil.print2(response,4001,"校区信息错误"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.qs.serve.common.framework.security.handler; |
|||
|
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import org.springframework.security.access.AccessDeniedException; |
|||
import org.springframework.security.web.access.AccessDeniedHandler; |
|||
|
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/3 |
|||
*/ |
|||
public class SecurityAccessDeniedHandler implements AccessDeniedHandler { |
|||
|
|||
@Override |
|||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { |
|||
HttpResponsePrintUtil.print(response,403,"权限不足"); |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.qs.serve.common.framework.security.handler; |
|||
|
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import org.springframework.security.core.AuthenticationException; |
|||
import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
|||
|
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/3 |
|||
*/ |
|||
public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler { |
|||
|
|||
@Override |
|||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException { |
|||
HttpResponsePrintUtil.print(response,401,"登录信息已过期或失效"); |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.security.handler; |
|||
|
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.modules.sys.mapper.SysUserMapper; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.security.web.authentication.logout.LogoutHandler; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2021/12/22 |
|||
*/ |
|||
public class SecurityLogoutHandler implements LogoutHandler { |
|||
|
|||
|
|||
|
|||
@Override |
|||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { |
|||
SysUserMapper sysUserMapper = SpringUtils.getBean(SysUserMapper.class); |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
sysUserMapper.updateYmTokenInt(loginUser.getUserId(),"#","#"); |
|||
} |
|||
try { |
|||
HttpResponsePrintUtil.print(response,200,"退出登录成功"); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,97 @@ |
|||
package com.qs.serve.common.framework.security.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.springframework.security.core.GrantedAuthority; |
|||
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 用户登录信息 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor |
|||
public class LoginUser implements UserDetails { |
|||
|
|||
private String userId; |
|||
|
|||
private String nick; |
|||
|
|||
private String password; |
|||
|
|||
private String loginIp; |
|||
|
|||
/** 用户类型 */ |
|||
private LoginUserType typeFlag; |
|||
|
|||
private List<String> authorList; |
|||
private List<String> authorIds; |
|||
|
|||
private String tenant; |
|||
|
|||
@Override |
|||
public Collection<? extends GrantedAuthority> getAuthorities() { |
|||
if(authorList!=null&&authorList.size()>0){ |
|||
return authorList |
|||
.stream() |
|||
.filter(n->n.length()>0) |
|||
.map(auth->new SimpleGrantedAuthority("ROLE_"+auth)) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
return Collections.singletonList(new SimpleGrantedAuthority("NOT_ROLE")); |
|||
} |
|||
|
|||
@Override |
|||
public String getPassword() { |
|||
return password; |
|||
} |
|||
|
|||
@Override |
|||
public String getUsername() { |
|||
return userId; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isAccountNonExpired() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isAccountNonLocked() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isCredentialsNonExpired() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isEnabled() { |
|||
return true; |
|||
} |
|||
|
|||
public LoginUserDTO loginUserDTO(){ |
|||
return new LoginUserDTO(userId,nick,password,loginIp,typeFlag.getCode(),authorList,authorIds,tenant); |
|||
} |
|||
|
|||
public boolean verifyRole(String permitCode){ |
|||
return authorList.contains(permitCode); |
|||
} |
|||
|
|||
/** |
|||
* 是否拥有招生超管权限 |
|||
* @return |
|||
*/ |
|||
public boolean hasRegSuperRole(){ |
|||
return verifyRole("edu:reg:admin"); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.qs.serve.common.framework.security.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 用户登录信息 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class LoginUserDTO { |
|||
|
|||
private String userId; |
|||
|
|||
private String nick; |
|||
|
|||
private String password; |
|||
|
|||
private String loginIp; |
|||
|
|||
private String typeFlagCode; |
|||
|
|||
private List<String> authorList; |
|||
private List<String> authorIds; |
|||
private String tenant; |
|||
|
|||
public LoginUser loginUser(){ |
|||
return new LoginUser(userId,nick,password,loginIp,LoginUserType.getByCode(typeFlagCode),authorList,authorIds,tenant); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.qs.serve.common.framework.security.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/7 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum LoginUserType { |
|||
|
|||
/** |
|||
* 人员分类 |
|||
*/ |
|||
APP_USER("00","微信用户"), |
|||
SYS_USER("01","系统人员"), |
|||
SYS_SUP_USER("02","超级管理员") |
|||
; |
|||
|
|||
private String code; |
|||
private String name; |
|||
|
|||
public static LoginUserType getByCode(String code){ |
|||
for (LoginUserType value : LoginUserType.values()) { |
|||
if(value.getCode().equals(code)){ |
|||
return value; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.qs.serve.common.framework.security.service; |
|||
|
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.modules.sys.entity.SysUser; |
|||
import com.qs.serve.modules.sys.service.SysUserService; |
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
import org.springframework.security.core.userdetails.UserDetailsService; |
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Service |
|||
@AllArgsConstructor |
|||
public class SysUserDetailsServiceImpl implements UserDetailsService { |
|||
|
|||
private final SysUserService sysUserService; |
|||
|
|||
@Override |
|||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { |
|||
return sysUserService.getLoginUserByAccount(username); |
|||
} |
|||
|
|||
public LoginUser buildLoginUser(SysUser sysUser){ |
|||
return sysUserService.buildLoginUser(sysUser); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.qs.serve.common.framework.security.util; |
|||
|
|||
/** |
|||
* @Author JcYen |
|||
* @Date 2021/9/24 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class SecurityPermitUtil { |
|||
|
|||
public static boolean verify(String reg, String input){ |
|||
if ("/*".equals(reg)) return true; |
|||
String[] reg_split = reg.split("\\*"); |
|||
int index = 0, reg_len = reg_split.length; |
|||
boolean b = reg.charAt(reg.length() - 1) == '*' ? true : false; |
|||
while (input.length() > 0) { |
|||
if (index == reg_len) { |
|||
if (b) return true; |
|||
else return false; |
|||
} |
|||
String r = reg_split[index++]; |
|||
int indexOf = input.indexOf(r); |
|||
if (indexOf != 0) return false; |
|||
input = input.substring(indexOf + r.length()); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.qs.serve.common.model; |
|||
|
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/30 |
|||
*/ |
|||
public class HttpResponsePrintUtil { |
|||
|
|||
public static void print(HttpServletResponse response,Integer code ,String msg) throws IOException,SecurityException{ |
|||
response.setHeader("Access-Control-Allow-Origin", "*"); |
|||
response.setHeader("Access-Control-Allow-Credentials", "true"); |
|||
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"); |
|||
response.setHeader("Access-Control-Max-Age", "86400"); |
|||
response.setHeader("Access-Control-Allow-Headers", "*"); |
|||
response.setStatus(200); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setContentType("application/json;charset=UTF-8"); |
|||
response.getWriter().print("{\"status\":"+code+",\"msg\":\""+msg+"\"}"); |
|||
response.getWriter().flush(); |
|||
} |
|||
|
|||
public static void print2(HttpServletResponse response,Integer code ,String msg)throws IOException,SecurityException{ |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setContentType("application/json;charset=UTF-8"); |
|||
response.getWriter().print("{\"status\":"+code+",\"msg\":\""+msg+"\"}"); |
|||
response.getWriter().flush(); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* 防止表单重复提交 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
@Target({ElementType.METHOD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface LimitSubmit { |
|||
|
|||
/** |
|||
* 间隔时间(ms),在世界间隔内超过执行次数,视为被限制 |
|||
*/ |
|||
int interval() default 1000; |
|||
|
|||
/** |
|||
* 提示消息 |
|||
*/ |
|||
String message() default ""; |
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
/** |
|||
* 防止表单重复提交 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
public @interface RepeatSubmit { |
|||
|
|||
/** |
|||
* 间隔时间(ms),小于此时间视为重复提交 |
|||
*/ |
|||
int interval() default 5000; |
|||
|
|||
/** |
|||
* 提示消息 |
|||
*/ |
|||
String message() default "不允许重复提交,请稍后再试"; |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
import com.qs.serve.common.model.enums.BizType; |
|||
import com.qs.serve.common.model.enums.InterType; |
|||
import com.qs.serve.common.model.enums.SystemModule; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* 记录用户登录操作 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
@Target({ElementType.METHOD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface SysLog { |
|||
|
|||
SystemModule module() default SystemModule.SYSTEM; |
|||
|
|||
/** 业务标题 */ |
|||
String title() default ""; |
|||
|
|||
/** 操作描述 */ |
|||
String desc() default ""; |
|||
|
|||
/** 操作类型 */ |
|||
BizType biz() default BizType.OTHER; |
|||
|
|||
/** 接口类型 */ |
|||
InterType inter() default InterType.ADMIN; |
|||
|
|||
boolean saveReqParam() default true; |
|||
|
|||
} |
@ -0,0 +1,10 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
public class DateConst { |
|||
|
|||
|
|||
} |
@ -0,0 +1,40 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* 系统字段 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
public class GySysConst { |
|||
|
|||
/** 后台身份验证 */ |
|||
public static final String AUTHORIZATION_PROP = "Authorization"; |
|||
|
|||
/** API身份验证 */ |
|||
public static final String APP_TOKEN_PROP = "token"; |
|||
|
|||
/** 请求头参数 */ |
|||
public static final String USER_AGENT_PROP = "User-Agent"; |
|||
|
|||
public static final String TENANT_PROP = "tenant-id"; |
|||
|
|||
/** 系统租户数据库列 */ |
|||
public static final String TENANT_COLUMN = "tenant_id"; |
|||
|
|||
/** 第三方应用id。如:公众号 */ |
|||
public static final String APP_ID_PROP = "appid"; |
|||
|
|||
/** 通用参数 */ |
|||
public static final int STATUS_YES_1 = 1; |
|||
public static final int STATUS_NO_1 = 0; |
|||
|
|||
public static final String LANG_ZH_CN = "zh_CN"; |
|||
|
|||
|
|||
public static final String ROLE_ADMIN_OPT_LOG = "sys:operationLog:admin"; |
|||
public static final String ROLE_ADMIN_REG = "edu:reg:admin"; |
|||
|
|||
public static final String DEFAULT_PASSWORD = "12345678"; |
|||
|
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/10 |
|||
*/ |
|||
public class OssConst { |
|||
|
|||
/** |
|||
* 访问类型 |
|||
*/ |
|||
public static final String ACCESS_OWNER = "own"; |
|||
public static final String ACCESS_DEPT = "dept"; |
|||
|
|||
/** |
|||
* 文件夹前缀 |
|||
*/ |
|||
public static final String OWNER_DIR = "owner/"; |
|||
public static final String DEPT_DIR = "dept/"; |
|||
public static final String SPLIT = "/"; |
|||
|
|||
/** |
|||
* Tag |
|||
*/ |
|||
public static final String TAG_CREATOR = "creator"; |
|||
public static final String TAG_CREATE_BY = "create_by"; |
|||
|
|||
public static final String FILE_TYPE_DIR = "文件夹"; |
|||
|
|||
public static final String DIR_TYPE_UNKNOWN = "未知"; |
|||
|
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
public class RedisCacheKeys { |
|||
|
|||
/** 权限列表 */ |
|||
public final static String PERMIT_ALL_LIST = "expire_permit_list"; |
|||
|
|||
/** 租户列表 */ |
|||
public final static String Tenant_ALL_LIST = "expire_tenant_list"; |
|||
|
|||
/** 管理员登录key */ |
|||
public final static String LOGIN_KEY_ADMIN = "login_admin_:"; |
|||
|
|||
/** 家长登录key */ |
|||
public final static String LOGIN_KEY_WX = "login_wx_:"; |
|||
|
|||
/** 用户登录APP {}客户端 {}用户ID */ |
|||
public final static String LOGIN_KEY_APP = "login_app_{}:{}:"; |
|||
|
|||
/** 手机号验证吗 */ |
|||
public final static String PHONE_KEY = "phone_code_:"; |
|||
|
|||
/** 工号标识 */ |
|||
public final static String SYS_USER_CODE_KEY = "sys_user_code_:"; |
|||
|
|||
} |
@ -0,0 +1,12 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
public class RedisListenerKeys { |
|||
|
|||
public final static String EXPIRED_TYPE = "__keyevent@*__:expired"; |
|||
|
|||
|
|||
} |
@ -0,0 +1,46 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import com.qs.serve.common.util.PageUtil; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Getter |
|||
@Setter |
|||
public class PageVo<T>{ |
|||
|
|||
/** |
|||
* 页幅 |
|||
*/ |
|||
private Integer pageSize; |
|||
|
|||
/** |
|||
* 页眉 |
|||
*/ |
|||
private Integer pageNum; |
|||
|
|||
/** |
|||
* 总数据数 |
|||
*/ |
|||
private Long total; |
|||
|
|||
/** |
|||
* 总页数 |
|||
*/ |
|||
private Long totalPage; |
|||
|
|||
/** |
|||
* 数据列表 |
|||
*/ |
|||
private List<T> list; |
|||
|
|||
public void initPageByTotal(long total){ |
|||
this.total = total; |
|||
this.pageNum = PageUtil.getPageNum(); |
|||
this.pageSize = PageUtil.getPageSize(); |
|||
if(pageSize==null||pageSize==0){ return; } |
|||
this.totalPage = total%pageSize==0?total/pageSize:(total/pageSize+1); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,173 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import com.baomidou.mybatisplus.core.metadata.IPage; |
|||
import com.github.pagehelper.PageInfo; |
|||
import com.qs.serve.common.util.PageUtil; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 统一返回封装 |
|||
* 1.替代旧版的ViewResult |
|||
* 2.支持SmartDoc |
|||
* @author JcYen |
|||
* @date 2021/4/22 |
|||
* @version 2.0 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class R<T> implements Serializable { |
|||
|
|||
public R(int status, String msg) { |
|||
this.status = status; |
|||
this.msg = msg; |
|||
} |
|||
|
|||
public R(int status, String msg, T data) { |
|||
this.status = status; |
|||
this.msg = msg; |
|||
this.data = data; |
|||
} |
|||
|
|||
/** |
|||
* 状态码 |
|||
*/ |
|||
private Integer status; |
|||
|
|||
/** |
|||
* 信息 |
|||
*/ |
|||
private String msg; |
|||
|
|||
/** |
|||
* 数据 |
|||
*/ |
|||
private T data; |
|||
|
|||
public static final int SUCCESS_STATUS = 200; |
|||
public static final int LOGOUT_STATUS = 401; |
|||
public static final int FORBIDDEN_STATUS = 403; |
|||
public static final int NOT_FOUND_STATUS = 434; |
|||
public static final int FAILED_STATUS = 500; |
|||
|
|||
public static final String SUCCESS_TIPS = "操作成功"; |
|||
public static final String FAILED_TIPS = "操作失败"; |
|||
public static final String FAILED_TIPS_2 = "业务操作失败"; |
|||
public static final String LOGOUT_TIPS = "登录无效"; |
|||
public static final String FORBIDDEN_TIPS = "无权限访问"; |
|||
public static final String NOT_FOUND_TIPS = "数据不存在或被移除"; |
|||
|
|||
public static R ok() { |
|||
return new R(SUCCESS_STATUS,SUCCESS_TIPS,null); |
|||
} |
|||
|
|||
public static R ok(String msg) { |
|||
return new R<>(SUCCESS_STATUS,msg,null); |
|||
} |
|||
|
|||
public static <TYPE> R<TYPE> ok(TYPE data) { |
|||
return new R<>(SUCCESS_STATUS,SUCCESS_TIPS,data); |
|||
} |
|||
|
|||
public static <TYPE> R<TYPE> ok(TYPE data,String message) { |
|||
return new R<>(SUCCESS_STATUS,message,data); |
|||
} |
|||
|
|||
public static R isTrue(Boolean bool){ |
|||
if(bool==null|| !bool){ |
|||
return error(); |
|||
} |
|||
return ok(); |
|||
} |
|||
|
|||
public static R isTrue(Boolean bool, String message){ |
|||
if(bool==null|| !bool){ |
|||
return error(message); |
|||
} |
|||
return ok(null,message); |
|||
} |
|||
|
|||
|
|||
public static <TYPE> R<TYPE> isNotNull(TYPE object){ |
|||
if(object==null){ |
|||
return error(); |
|||
} |
|||
return ok(object,SUCCESS_TIPS); |
|||
} |
|||
|
|||
public static R<?> isOk(Boolean rs){ |
|||
if(rs==null){ |
|||
return error(); |
|||
} |
|||
return ok(); |
|||
} |
|||
|
|||
public static R<?> isOk(Boolean rs,String errMsg){ |
|||
if(rs==null){ |
|||
return error(errMsg); |
|||
} |
|||
return ok(); |
|||
} |
|||
|
|||
public static R error(String message) { |
|||
return new R<>(FAILED_STATUS,message,null); |
|||
} |
|||
|
|||
public static R error() { |
|||
return new R<>(FAILED_STATUS,FAILED_TIPS,null); |
|||
} |
|||
|
|||
public static R error2() { |
|||
return new R<>(FAILED_STATUS,FAILED_TIPS_2,null); |
|||
} |
|||
|
|||
public static <TYPE> R<PageVo<TYPE>> byMbpList(IPage<TYPE> page){ |
|||
PageVo<TYPE> pageVo = new PageVo<>(); |
|||
pageVo.initPageByTotal(page.getTotal()); |
|||
pageVo.setList(page.getRecords()); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
public static <TYPE> R<PageVo<TYPE>> byMbpList(IPage<?> page,List<TYPE> data){ |
|||
PageVo<TYPE> pageVo = new PageVo<>(); |
|||
pageVo.initPageByTotal(page.getTotal()); |
|||
pageVo.setList(data); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
public static <TYPE> R<PageVo<TYPE>> byPageHelperList(List<TYPE> data){ |
|||
PageVo<TYPE> pageVo = new PageVo<>(); |
|||
PageInfo<TYPE> pageInfo = new PageInfo<>(data); |
|||
pageVo.setPageSize(pageInfo.getPageSize()); |
|||
pageVo.setPageNum(pageInfo.getPageNum()); |
|||
pageVo.setTotal(pageInfo.getTotal()); |
|||
pageVo.setList(data); |
|||
pageVo.setTotalPage(new Long(pageInfo.getPages())); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
public static <TYPE> R<PageVo<TYPE>> byEmptyList(){ |
|||
PageVo<TYPE> pageVo = new PageVo<>(); |
|||
pageVo.setPageSize(PageUtil.getPageSize()); |
|||
pageVo.setPageNum(PageUtil.getPageNum()); |
|||
pageVo.setTotal(0L); |
|||
pageVo.setList(null); |
|||
pageVo.setTotalPage(0L); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
public static <TYPE,R_TYPE> R<PageVo<R_TYPE>> byPageHelperList(List<TYPE> sources,List<R_TYPE> details){ |
|||
PageVo<R_TYPE> pageVo = new PageVo<>(); |
|||
PageInfo<TYPE> pageInfo = new PageInfo<>(sources); |
|||
pageVo.setPageSize(pageInfo.getPageSize()); |
|||
pageVo.setPageNum(pageInfo.getPageNum()); |
|||
pageVo.setTotal(pageInfo.getTotal()); |
|||
pageVo.setList(details); |
|||
pageVo.setTotalPage(new Long(pageInfo.getPages())); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,23 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/7 |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor |
|||
public class SimpleKeyValue<T> { |
|||
|
|||
/** 键 */ |
|||
private String label; |
|||
|
|||
/** 值 */ |
|||
private T value; |
|||
|
|||
/** 说明 */ |
|||
private String remark; |
|||
|
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 树节点 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Data |
|||
public class TreeNode { |
|||
protected String id; |
|||
protected String parentId; |
|||
private Integer sort; |
|||
protected List<TreeNode> children = new ArrayList<>(); |
|||
|
|||
public void addChildren(TreeNode treeNode) { |
|||
children.add(treeNode); |
|||
} |
|||
|
|||
public List<TreeNode> getChildren() { |
|||
if(children.size()<=0){ |
|||
return null; |
|||
} |
|||
return children; |
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/8 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum BizType { |
|||
|
|||
/** 其它 */ |
|||
OTHER("其它"), |
|||
|
|||
/** 新增 */ |
|||
INSERT("新增"), |
|||
|
|||
/** 修改 */ |
|||
UPDATE("修改"), |
|||
|
|||
/** 删除 */ |
|||
DELETE("删除"), |
|||
|
|||
/** 授权 */ |
|||
GRANT("授权"), |
|||
|
|||
/** 强退 */ |
|||
FORCE("强退"), |
|||
|
|||
/** 清空数据 */ |
|||
CLEAN("清空"), |
|||
|
|||
/** 登录 */ |
|||
LOGIN("登录"), |
|||
|
|||
LOGOUT("登出"), |
|||
|
|||
UPLOAD("上传"), |
|||
|
|||
DOWNLOAD("下载"), |
|||
|
|||
QUERY("查询"), |
|||
|
|||
RESET("重置"); |
|||
|
|||
String value; |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/10 |
|||
*/ |
|||
@AllArgsConstructor |
|||
public enum ExcelFilePath { |
|||
|
|||
/** 地址 */ |
|||
EDU_REG_ARCHIVE_IN("1000",""); |
|||
|
|||
String code; |
|||
|
|||
String path; |
|||
|
|||
} |
@ -0,0 +1,44 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* 请求状态码 |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum HttpCode { |
|||
|
|||
/** 基本参数 */ |
|||
SUCCESS(200,"操作成功"), |
|||
ERROR(500,"操作失败"), |
|||
DEV_ERR(500,"部分功能开发中..."), |
|||
WX_ERR(500,"微信服务调用异常"), |
|||
|
|||
LOGIN_ERR(401,"登录信息已过期或失效"), |
|||
LOGIN_ERR_2(500,"该账号已被停用,请联系系统管理员"), |
|||
LOGIN_ERR_1(500,"账号或登录密码不正确"), |
|||
LOGIN_ERR_4001(4001,"校区信息错误"), |
|||
LOGIN_ERR_4002(4002,"应用信息错误"), |
|||
|
|||
|
|||
/** 资源权限相关 */ |
|||
FORBIDDEN_403(403,"权限不足,无法访问"), |
|||
FORBIDDEN_403_1(403,"账套未注册,无权访问"), |
|||
FORBIDDEN_403_2(403,"客户端未注册,无权访问"), |
|||
FORBIDDEN_403_3(403,"IP地址未注册,无权访问"), |
|||
FORBIDDEN_403_4(403,"无招生超管权限,无权访问"), |
|||
FORBIDDEN_404(404,"资源不存在或被移除"), |
|||
FORBIDDEN_406(406,"系统正常处理,请勿重复提交"), |
|||
|
|||
|
|||
ERROR_EDU_SCHOOL_YEAR(5031,"当前学年信息错误,请重新设置当前学年"), |
|||
; |
|||
|
|||
Integer code; |
|||
String msg; |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/8 |
|||
*/ |
|||
public enum InterType { |
|||
|
|||
/** |
|||
* 前台接口 |
|||
*/ |
|||
API, |
|||
|
|||
|
|||
|
|||
/** |
|||
* 后台接口 |
|||
*/ |
|||
ADMIN |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* minio策略配置 |
|||
* @author YenHex |
|||
* @since 2022/5/10 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum PolicyType { |
|||
|
|||
/** |
|||
* 只读 |
|||
*/ |
|||
READ("read-only"), |
|||
|
|||
/** |
|||
* 只写 |
|||
*/ |
|||
WRITE("write-only"), |
|||
|
|||
/** |
|||
* 读写 |
|||
*/ |
|||
READ_WRITE("read-write"); |
|||
|
|||
/** |
|||
* 类型 |
|||
*/ |
|||
private final String type; |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/7/25 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum SystemModule { |
|||
|
|||
QUESTION("qms","问卷"), |
|||
SYSTEM("sys","系统"), |
|||
UMS("ums","手机用户"); |
|||
|
|||
String code; |
|||
|
|||
String vale; |
|||
|
|||
} |
@ -0,0 +1,8 @@ |
|||
package com.qs.serve.common.model.group; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/16 |
|||
*/ |
|||
public interface EditGroup { |
|||
} |
@ -0,0 +1,8 @@ |
|||
package com.qs.serve.common.model.group; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/16 |
|||
*/ |
|||
public interface SaveGroup { |
|||
} |
@ -0,0 +1,8 @@ |
|||
package com.qs.serve.common.model.group; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/16 |
|||
*/ |
|||
public interface SearchGroup { |
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.qs.serve.common.service; |
|||
|
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/7/4 |
|||
*/ |
|||
public abstract class RedisBaseListenerImpl implements RedisServiceListener{ |
|||
|
|||
@Override |
|||
public String getKey(String pk) { |
|||
return getPrefix()+pk+"_"+ AuthContextUtils.getTenant(); |
|||
} |
|||
|
|||
public String getPk(String key){ |
|||
return key.replace(getPrefix(),"") |
|||
.replace("_"+AuthContextUtils.getTenant(),""); |
|||
} |
|||
|
|||
@Override |
|||
public void doJob(String key) { |
|||
String[] vals = key.split("_"); |
|||
AuthContextUtils.setTenant(vals[vals.length-1]); |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
package com.qs.serve.common.service; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/7/4 |
|||
*/ |
|||
public interface RedisServiceListener { |
|||
|
|||
/** |
|||
* 业务前缀 |
|||
* @return |
|||
*/ |
|||
String getPrefix(); |
|||
|
|||
/** |
|||
* 获取完整的key |
|||
* @param pk |
|||
* @return |
|||
*/ |
|||
String getKey(String pk); |
|||
|
|||
/** |
|||
* 同步最近时间段任务,添加redis实现监听 |
|||
*/ |
|||
void syncTask(); |
|||
|
|||
/** |
|||
* 监听过期得key |
|||
* @param key |
|||
*/ |
|||
void doJob(String key); |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
|
|||
import com.qs.serve.common.framework.exception.BusinessException; |
|||
import com.qs.serve.common.model.enums.HttpCode; |
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
/** |
|||
* 抛出异常工具类 |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
@UtilityClass |
|||
public class Assert { |
|||
|
|||
public static void throwEx(Integer code,String msg){ |
|||
throw new BusinessException(msg, code); |
|||
} |
|||
|
|||
public static void throwEx(HttpCode httpCode){ |
|||
throw new BusinessException(httpCode.getMsg(),httpCode.getCode()); |
|||
} |
|||
|
|||
public static void throwEx(String msg){ |
|||
throw new BusinessException(msg,500); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,109 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.framework.security.model.LoginUserType; |
|||
import com.qs.serve.common.model.consts.GySysConst; |
|||
import com.qs.serve.common.model.enums.HttpCode; |
|||
import lombok.experimental.UtilityClass; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.security.core.context.SecurityContext; |
|||
import org.springframework.security.core.context.SecurityContextHolder; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@UtilityClass |
|||
public class AuthContextUtils { |
|||
|
|||
private final static ThreadLocal<String> THREAD_TENANT = new ThreadLocal<>(); |
|||
|
|||
public static String getAppId(){ |
|||
return getAppId(true); |
|||
} |
|||
|
|||
public static String getAppId(boolean throwEx){ |
|||
String appId = ServletUtils.getHeader(GySysConst.APP_ID_PROP); |
|||
if(throwEx&&StringUtils.isEmpty(appId)){ |
|||
Assert.throwEx(HttpCode.LOGIN_ERR_4002); |
|||
} |
|||
return appId; |
|||
} |
|||
|
|||
/** |
|||
* 获取用户登录信息 |
|||
* @return |
|||
*/ |
|||
public static LoginUser getLoginUser(){ |
|||
return getLoginUser(true); |
|||
} |
|||
|
|||
/** |
|||
* 获取微信用户ID |
|||
* @return |
|||
*/ |
|||
public static String getAppUserId(){ |
|||
LoginUser loginUser = getLoginUser(); |
|||
if(loginUser.getTypeFlag().equals(LoginUserType.APP_USER)){ |
|||
return loginUser.getUserId(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 系统用户ID |
|||
* @return |
|||
*/ |
|||
public static String getSysUserId(){ |
|||
LoginUser loginUser = getLoginUser(); |
|||
if(loginUser.getTypeFlag().equals(LoginUserType.SYS_SUP_USER) |
|||
||loginUser.getTypeFlag().equals(LoginUserType.SYS_USER)){ |
|||
return loginUser.getUserId(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否超级管理员 |
|||
* @return |
|||
*/ |
|||
public static boolean isSupAdmin(){ |
|||
return Objects.requireNonNull(getLoginUser(false)) |
|||
.getTypeFlag().equals(LoginUserType.SYS_SUP_USER); |
|||
} |
|||
|
|||
public static void setTenant(String value){THREAD_TENANT.set(value);} |
|||
|
|||
public static String getTenant(){return THREAD_TENANT.get();} |
|||
|
|||
public static void removeTenant(){THREAD_TENANT.remove();} |
|||
|
|||
public static LoginUser getLoginUser(boolean throwEx){ |
|||
UserDetails details = getUserDetail(throwEx); |
|||
if(details instanceof LoginUser){ |
|||
return (LoginUser) details; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private static UserDetails getUserDetail(boolean throwEx){ |
|||
SecurityContext ctx = SecurityContextHolder.getContext(); |
|||
Authentication auth = ctx.getAuthentication(); |
|||
if (auth != null) { |
|||
Object authPri = auth.getPrincipal(); |
|||
if (authPri instanceof UserDetails) { |
|||
UserDetails details = (UserDetails) authPri; |
|||
return details; |
|||
} |
|||
} |
|||
if(throwEx){ |
|||
throw new UsernameNotFoundException("未登录或被登出"); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,90 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/25 |
|||
*/ |
|||
@UtilityClass |
|||
public class CollectionUtil extends cn.hutool.core.collection.CollectionUtil{ |
|||
|
|||
/** |
|||
* 去除list中的重复数据 |
|||
*/ |
|||
public static <T> List<T> removeRepeat(List<T> list) { |
|||
if (isEmpty(list)) { |
|||
return list; |
|||
} |
|||
List<T> result = new ArrayList<T>(); |
|||
for (T e : list) { |
|||
if (!result.contains(e)) { |
|||
result.add(e); |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
public static boolean hasNull(Object... objects){ |
|||
for (int i = 0; i < objects.length; i++) { |
|||
if(objects[i]==null){return true;} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public static boolean hasNotNull(Object... objects){ |
|||
for (int i = 0; i < objects.length; i++) { |
|||
if(objects[i]!=null){return true;} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 将集合转换为String数组 |
|||
*/ |
|||
public static <T> String[] toArray(List<T> list) { |
|||
if (isEmpty(list)) { |
|||
return null; |
|||
} |
|||
String[] result = new String[list.size()]; |
|||
for (int i = 0; i < list.size(); i++) { |
|||
result[i] = String.valueOf(list.get(i)); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 将list拆分成多给指定的大小的list |
|||
*/ |
|||
public static <T> List<List<T>> createList(List<T> target, int size) { |
|||
List<List<T>> listArr = new ArrayList<>(); |
|||
//获取被拆分的数组个数
|
|||
int arrSize = target.size()%size==0?target.size()/size:target.size()/size+1; |
|||
for(int i=0;i<arrSize;i++) { |
|||
List<T> sub = new ArrayList<T>(); |
|||
//把指定索引数据放入到list中
|
|||
for(int j=i*size;j<=size*(i+1)-1;j++) { |
|||
if(j<=target.size()-1) { |
|||
sub.add(target.get(j)); |
|||
} |
|||
} |
|||
listArr.add(sub); |
|||
} |
|||
return listArr; |
|||
} |
|||
|
|||
public static <T> boolean isNotEmpty(T[] arr){ |
|||
return arr!=null && arr.length>0; |
|||
} |
|||
|
|||
public static <T> T selectFirst(List<T> list){ |
|||
if(isNotEmpty(list)){ |
|||
return list.get(0); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,95 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
import org.springframework.cglib.beans.BeanCopier; |
|||
|
|||
import java.io.*; |
|||
import java.util.Collection; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* 取代BeanUtils.CP |
|||
* @Author JcYen |
|||
* @Date 2019/6/3 |
|||
* @Version 1.0 |
|||
*/ |
|||
@UtilityClass |
|||
public class CopierUtil { |
|||
|
|||
private static final Map<String, BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<String, BeanCopier>(); |
|||
|
|||
private static String genKey(Class<?> source, Class<?> target) { |
|||
return source.getName() + target.getName(); |
|||
} |
|||
|
|||
/** |
|||
* target字段值,会被source字段值覆盖(包括null) |
|||
* @param source |
|||
* @param target |
|||
*/ |
|||
public static <T> T copy(Object source, T target) { |
|||
String key = genKey(source.getClass(), target.getClass()); |
|||
BeanCopier copier; |
|||
if (!BEAN_COPIERS.containsKey(key)) { |
|||
copier = BeanCopier.create(source.getClass(), target.getClass(), false); |
|||
BEAN_COPIERS.put(key, copier); |
|||
} else { |
|||
copier = BEAN_COPIERS.get(key); |
|||
} |
|||
copier.copy(source, target, null); |
|||
return target; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 采用对象的序列化完成对象的深克隆 |
|||
* @param obj 待克隆的对象 |
|||
* @return |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static <T extends Serializable> T cloneObject(T obj) { |
|||
T cloneObj = null; |
|||
try { |
|||
// 写入字节流
|
|||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
|||
ObjectOutputStream obs = new ObjectOutputStream(out); |
|||
obs.writeObject(obj); |
|||
obs.close(); |
|||
|
|||
// 分配内存,写入原始对象,生成新对象
|
|||
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); |
|||
ObjectInputStream ois = new ObjectInputStream(ios); |
|||
// 返回生成的新对象
|
|||
cloneObj = (T) ois.readObject(); |
|||
ois.close(); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return cloneObj; |
|||
} |
|||
|
|||
/** |
|||
* 利用序列化完成集合的深克隆 |
|||
* |
|||
* @param collection 待克隆的集合 |
|||
* @return |
|||
* @throws ClassNotFoundException |
|||
* @throws IOException |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static <T> Collection<T> cloneCollection(Collection<T> collection) throws ClassNotFoundException, IOException{ |
|||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); |
|||
ObjectOutputStream out = new ObjectOutputStream(byteOut); |
|||
out.writeObject(collection); |
|||
out.close(); |
|||
|
|||
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); |
|||
ObjectInputStream in = new ObjectInputStream(byteIn); |
|||
Collection<T> dest = (Collection<T>) in.readObject(); |
|||
in.close(); |
|||
|
|||
return dest; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,98 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.qs.serve.common.util.model.DateFormatString; |
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
import java.text.ParseException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.time.LocalDate; |
|||
import java.time.LocalDateTime; |
|||
import java.time.ZoneId; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.time.temporal.ChronoUnit; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@UtilityClass |
|||
public class DateUtils { |
|||
|
|||
public static String getString(DateFormatString format){ |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format.getValue()); |
|||
return LocalDateTime.now().format(formatter); |
|||
} |
|||
|
|||
/** |
|||
* 获取设置后得时间 |
|||
* @param time |
|||
* @param unit |
|||
* @param num |
|||
* @return |
|||
*/ |
|||
public static LocalDateTime beSetTime(LocalDateTime time, ChronoUnit unit,int num){ |
|||
if(num>0){ |
|||
return time.plus(num,unit); |
|||
}else { |
|||
return time.minus(-num,unit); |
|||
} |
|||
} |
|||
|
|||
public static LocalDate beSetDate(LocalDate time,int days){ |
|||
return time.plusDays(days); |
|||
} |
|||
|
|||
public static Date toDate(String str,String format){ |
|||
SimpleDateFormat sdf = new SimpleDateFormat(format); |
|||
try { |
|||
return sdf.parse(str); |
|||
} catch (ParseException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static Date toDate(LocalDateTime time){ |
|||
return Date.from( time.atZone( ZoneId.systemDefault()).toInstant()); |
|||
} |
|||
|
|||
public static double diffTime(long beganTime, long endTime) { |
|||
return (double)(endTime - beganTime) / 1000.0D; |
|||
} |
|||
|
|||
public static String format(LocalDateTime localDateTime, DateFormatString format){ |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format.getValue()); |
|||
return localDateTime.format(formatter); |
|||
} |
|||
|
|||
public static String datePath(){ |
|||
return format(LocalDate.now(),DateFormatString.DATE); |
|||
} |
|||
|
|||
public static String format(LocalDate localDate, DateFormatString format){ |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format.getValue()); |
|||
return localDate.format(formatter); |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
System.out.println(DateUtils.beSetTime(LocalDateTime.now(), ChronoUnit.DAYS,-2)); |
|||
// Get the LocalDateTime instance
|
|||
LocalDateTime ldt |
|||
= LocalDateTime |
|||
.parse("2019-12-31T19:15:30"); |
|||
|
|||
// Get the String representation of this LocalDateTime
|
|||
System.out.println("Original LocalDateTime: " |
|||
+ ldt.toString()); |
|||
|
|||
// subtract 200 DAYS to LocalDateTime
|
|||
LocalDateTime value |
|||
= ldt.minus(200, ChronoUnit.DAYS); |
|||
|
|||
// print result
|
|||
System.out.println("LocalDateTime after subtracting DAYS: " |
|||
+ value); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,51 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import cn.hutool.core.lang.Snowflake; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.Random; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/3 |
|||
*/ |
|||
public class IdUtil extends cn.hutool.core.util.IdUtil { |
|||
|
|||
private static final long BASE_DIFF_MILLIS = 1648013774104L; |
|||
|
|||
private static final Snowflake snowflake = getSnowflake(1,1); |
|||
|
|||
public static String timeStampId() { |
|||
long millis = System.currentTimeMillis(); |
|||
String prefix = String.format("%013d", millis-BASE_DIFF_MILLIS); |
|||
Random random = new Random(); |
|||
int end2 = random.nextInt(999); |
|||
return prefix + String.format("%03d", end2); |
|||
} |
|||
|
|||
public static Long timeStampLong() { |
|||
return System.currentTimeMillis()-BASE_DIFF_MILLIS; |
|||
} |
|||
|
|||
public static String dateTimeStampId() { |
|||
LocalDateTime localDateTime = LocalDateTime.now(); |
|||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); |
|||
String localTime = df.format(localDateTime); |
|||
Random random = new Random(); |
|||
int end2 = random.nextInt(999); |
|||
return localTime + String.format("%03d", end2); |
|||
} |
|||
|
|||
public static String genCode(int len) { |
|||
int max = new Double(Math.pow(10, len)).intValue() - 1; |
|||
Random random = new Random(); |
|||
int end2 = random.nextInt(max); |
|||
return String.format("%0"+len+"d", end2); |
|||
} |
|||
|
|||
public static long getSnowFlakeId(){ |
|||
return snowflake.nextId(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,99 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.fasterxml.jackson.annotation.JsonInclude; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.fasterxml.jackson.databind.JavaType; |
|||
import com.fasterxml.jackson.databind.MapperFeature; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
|
|||
/** |
|||
* Jackson json序列化和反序列化工具类 |
|||
* @Author JcYen |
|||
* @Date 2019/6/3 |
|||
* @Version 1.0 |
|||
*/ |
|||
@UtilityClass |
|||
public class JsonUtil { |
|||
|
|||
/** |
|||
* jackson对象 |
|||
*/ |
|||
static final ObjectMapper MAPPER = new ObjectMapper(); |
|||
|
|||
static { |
|||
MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); |
|||
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); |
|||
} |
|||
|
|||
/** |
|||
* 将对象转换成json字符串。 |
|||
*/ |
|||
public static String objectToJson(Object data) { |
|||
try { |
|||
String string = MAPPER.writeValueAsString(data); |
|||
return string; |
|||
} catch (JsonProcessingException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 将json结果集转化为对象 |
|||
* |
|||
* @param jsonData json数据 |
|||
* @param beanType 对象中的object类型 |
|||
*/ |
|||
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) { |
|||
try { |
|||
T t = MAPPER.readValue(jsonData, beanType); |
|||
return t; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 将json数据转换成pojo对象list |
|||
*/ |
|||
public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) { |
|||
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); |
|||
try { |
|||
List<T> list = MAPPER.readValue(jsonData, javaType); |
|||
return list; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static Map<String, Object> jsonToMap(String jsonStr){ |
|||
Map<String, Object> json = JSONObject.parseObject(jsonStr, Map.class); |
|||
return json; |
|||
} |
|||
|
|||
public static Map<String,Object> objectToMap(Object object){ |
|||
Map<String,Object> map = JSONObject.parseObject(JSON.toJSONString(object)); |
|||
return map; |
|||
} |
|||
|
|||
public static <T> List<Map<String,Object>> listToMap(List<T> objects){ |
|||
List<Map<String,Object>> result = new ArrayList<>(); |
|||
for (T object : objects) { |
|||
Map<String,Object> map = objectToMap(object); |
|||
result.add(map); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,82 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.auth0.jwt.JWT; |
|||
import com.auth0.jwt.JWTVerifier; |
|||
import com.auth0.jwt.algorithms.Algorithm; |
|||
import com.qs.serve.common.config.JwtConfig; |
|||
import com.qs.serve.common.framework.security.model.LoginUserType; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.security.authentication.BadCredentialsException; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.temporal.ChronoUnit; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Slf4j |
|||
public class JwtUtils { |
|||
|
|||
private final static String USER_ID_CLAIM = "UID"; |
|||
private final static String USER_TYPE_CLAIM = "TYPE"; |
|||
private final static String USER_CLIENT_CLAIM = "CLIENT"; |
|||
|
|||
private final static JwtConfig jwtConfig = new JwtConfig(); |
|||
|
|||
public static String generateToken(String userId, LoginUserType userType,String client) { |
|||
LocalDateTime dateTime = LocalDateTime.now(); |
|||
LocalDateTime exTime = DateUtils.beSetTime(dateTime, ChronoUnit.DAYS,jwtConfig.getExpire()); |
|||
Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecret()); |
|||
return JWT.create() |
|||
.withIssuer(jwtConfig.getIss()) |
|||
.withIssuedAt(DateUtils.toDate(dateTime)) |
|||
.withExpiresAt(DateUtils.toDate(exTime)) |
|||
.withClaim(USER_TYPE_CLAIM,userType.getCode()) |
|||
.withClaim(USER_ID_CLAIM,userId) |
|||
.withClaim(USER_CLIENT_CLAIM,client) |
|||
.sign(algorithm); |
|||
} |
|||
|
|||
public static boolean verify(String token) { |
|||
try{ |
|||
Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecret()); |
|||
JWTVerifier verifier = JWT.require(algorithm) |
|||
.withIssuer(jwtConfig.getIss()) |
|||
.build(); |
|||
verifier.verify(token); |
|||
return true; |
|||
} catch (Exception ex){ |
|||
log.debug("令牌验证异常:{}",ex.getMessage()); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public static String getUserId(String token) { |
|||
try{ |
|||
return JWT.decode(token).getClaim(USER_ID_CLAIM).asString(); |
|||
}catch (Exception ex){ |
|||
log.debug("方法:getUserId() 令牌验证异常:{}",ex.getMessage()); |
|||
throw new BadCredentialsException("调用凭证无效"); |
|||
} |
|||
} |
|||
|
|||
public static String getUserType(String token) { |
|||
try{ |
|||
return JWT.decode(token).getClaim(USER_TYPE_CLAIM).asString(); |
|||
}catch (Exception ex){ |
|||
log.debug("方法:getUserId() 令牌验证异常:{}",ex.getMessage()); |
|||
throw new BadCredentialsException("调用凭证无效"); |
|||
} |
|||
} |
|||
|
|||
public static String getClient(String token) { |
|||
try{ |
|||
return JWT.decode(token).getClaim(USER_CLIENT_CLAIM).asString(); |
|||
}catch (Exception ex){ |
|||
log.debug("方法:getUserId() 令牌验证异常:{}",ex.getMessage()); |
|||
throw new BadCredentialsException("调用凭证无效"); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,84 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.qs.serve.common.model.enums.PolicyType; |
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/10 |
|||
*/ |
|||
@UtilityClass |
|||
public class MinioUtil { |
|||
|
|||
public static String getPolicy(String bucketName, PolicyType policyType) { |
|||
StringBuilder builder = new StringBuilder(); |
|||
builder.append("{\n"); |
|||
builder.append(" \"Statement\": [\n"); |
|||
builder.append(" {\n"); |
|||
builder.append(" \"Action\": [\n"); |
|||
if (policyType == PolicyType.WRITE) { |
|||
builder.append(" \"s3:GetBucketLocation\",\n"); |
|||
builder.append(" \"s3:ListBucketMultipartUploads\"\n"); |
|||
} else if (policyType == PolicyType.READ_WRITE) { |
|||
builder.append(" \"s3:GetBucketLocation\",\n"); |
|||
builder.append(" \"s3:ListBucket\",\n"); |
|||
builder.append(" \"s3:ListBucketMultipartUploads\"\n"); |
|||
} else { |
|||
builder.append(" \"s3:GetBucketLocation\"\n"); |
|||
} |
|||
builder.append(" ],\n"); |
|||
builder.append(" \"Effect\": \"Allow\",\n"); |
|||
builder.append(" \"Principal\": \"*\",\n"); |
|||
builder.append(" \"Resource\": \"arn:aws:s3:::"); |
|||
builder.append(bucketName); |
|||
builder.append("\"\n"); |
|||
builder.append(" },\n"); |
|||
if (PolicyType.READ.equals(policyType)) { |
|||
builder.append(" {\n"); |
|||
builder.append(" \"Action\": [\n"); |
|||
builder.append(" \"s3:ListBucket\"\n"); |
|||
builder.append(" ],\n"); |
|||
builder.append(" \"Effect\": \"Deny\",\n"); |
|||
builder.append(" \"Principal\": \"*\",\n"); |
|||
builder.append(" \"Resource\": \"arn:aws:s3:::"); |
|||
builder.append(bucketName); |
|||
builder.append("\"\n"); |
|||
builder.append(" },\n"); |
|||
} |
|||
builder.append(" {\n"); |
|||
builder.append(" \"Action\": "); |
|||
switch (policyType) { |
|||
case WRITE: |
|||
builder.append("[\n"); |
|||
builder.append(" \"s3:AbortMultipartUpload\",\n"); |
|||
builder.append(" \"s3:DeleteObject\",\n"); |
|||
builder.append(" \"s3:ListMultipartUploadParts\",\n"); |
|||
builder.append(" \"s3:PutObject\"\n"); |
|||
builder.append(" ],\n"); |
|||
break; |
|||
case READ_WRITE: |
|||
builder.append("[\n"); |
|||
builder.append(" \"s3:AbortMultipartUpload\",\n"); |
|||
builder.append(" \"s3:DeleteObject\",\n"); |
|||
builder.append(" \"s3:GetObject\",\n"); |
|||
builder.append(" \"s3:ListMultipartUploadParts\",\n"); |
|||
builder.append(" \"s3:PutObject\"\n"); |
|||
builder.append(" ],\n"); |
|||
break; |
|||
default: |
|||
builder.append("\"s3:GetObject\",\n"); |
|||
break; |
|||
} |
|||
builder.append(" \"Effect\": \"Allow\",\n"); |
|||
builder.append(" \"Principal\": \"*\",\n"); |
|||
builder.append(" \"Resource\": \"arn:aws:s3:::"); |
|||
builder.append(bucketName); |
|||
builder.append("/*\"\n"); |
|||
builder.append(" }\n"); |
|||
builder.append(" ],\n"); |
|||
builder.append(" \"Version\": \"2012-10-17\"\n"); |
|||
builder.append("}\n"); |
|||
return builder.toString(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import java.security.SecureRandom; |
|||
import java.util.Random; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/4/11 |
|||
*/ |
|||
public final class NanoIdUtils { |
|||
public static final SecureRandom DEFAULT_NUMBER_GENERATOR = new SecureRandom(); |
|||
public static final char[] DEFAULT_ALPHABET = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); |
|||
public static final int DEFAULT_SIZE = 21; |
|||
|
|||
private NanoIdUtils() { |
|||
} |
|||
|
|||
public static String randomNanoId() { |
|||
return randomNanoId(DEFAULT_NUMBER_GENERATOR, DEFAULT_ALPHABET, 21); |
|||
} |
|||
|
|||
public static String randomNanoId(Random random, char[] alphabet, int size) { |
|||
if (random == null) { |
|||
throw new IllegalArgumentException("random cannot be null."); |
|||
} else if (alphabet == null) { |
|||
throw new IllegalArgumentException("alphabet cannot be null."); |
|||
} else if (alphabet.length != 0 && alphabet.length < 256) { |
|||
if (size <= 0) { |
|||
throw new IllegalArgumentException("size must be greater than zero."); |
|||
} else { |
|||
int mask = (2 << (int)Math.floor(Math.log((double)(alphabet.length - 1)) / Math.log(2.0D))) - 1; |
|||
int step = (int)Math.ceil(1.6D * (double)mask * (double)size / (double)alphabet.length); |
|||
StringBuilder idBuilder = new StringBuilder(); |
|||
|
|||
while(true) { |
|||
byte[] bytes = new byte[step]; |
|||
random.nextBytes(bytes); |
|||
|
|||
for(int i = 0; i < step; ++i) { |
|||
int alphabetIndex = bytes[i] & mask; |
|||
if (alphabetIndex < alphabet.length) { |
|||
idBuilder.append(alphabet[alphabetIndex]); |
|||
if (idBuilder.length() == size) { |
|||
return idBuilder.toString(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
throw new IllegalArgumentException("alphabet must contain between 1 and 255 symbols."); |
|||
} |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
for (int i = 0; i < 1000; i++) { |
|||
System.out.println(randomNanoId()); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,169 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.baomidou.mybatisplus.core.metadata.OrderItem; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.github.pagehelper.PageHelper; |
|||
import com.qs.serve.common.framework.exception.BusinessException; |
|||
import lombok.experimental.UtilityClass; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 翻页工具类 |
|||
* @Author YenHex |
|||
* @Date 2021/6/9 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@UtilityClass |
|||
public class PageUtil { |
|||
|
|||
private static final String PAGE_NUM = "pageNum"; |
|||
private static final String PAGE_SIZE = "pageSize"; |
|||
|
|||
private static final String ORDER_TYPE = "orderType"; |
|||
private static final String ORDER_PROP = "orderProp"; |
|||
|
|||
private static final String ORDER_TYPE_ASC = "ASC"; |
|||
private static final String ORDER_TYPE_DESC = "DESC"; |
|||
|
|||
private static final int MAX_PAGE_SIZE = 66; |
|||
|
|||
public static <T> Page<T> getMbpPage(){ |
|||
Integer pageNum = getPageNum(); |
|||
Integer pageSize = getPageSize(); |
|||
String orderProp = getOrderProp(); |
|||
String orderType = getOrderType(); |
|||
if ( pageNum ==null || pageNum<1 || pageSize==null || pageSize<1) { |
|||
throw new BusinessException("翻页参数异常",500); |
|||
} |
|||
Page<T> page = new Page<>(); |
|||
page.setCurrent(pageNum); |
|||
page.setSize(pageSize); |
|||
if(StringUtils.hasText(orderProp)){ |
|||
OrderItem orderItem = new OrderItem(); |
|||
orderItem.setColumn(orderProp); |
|||
orderItem.setAsc(orderType.equals(ORDER_TYPE_ASC)); |
|||
List<OrderItem> orderItems = new ArrayList<>(); |
|||
orderItems.add(orderItem); |
|||
page.setOrders(orderItems); |
|||
} |
|||
return page; |
|||
} |
|||
|
|||
public static Integer getPageNum(){ |
|||
Integer pageNum=null; |
|||
String pageNumStr = ServletUtils.getParameter(PAGE_NUM); |
|||
if(StringUtils.hasText(pageNumStr)){ |
|||
pageNum = Integer.parseInt(pageNumStr); |
|||
} |
|||
return pageNum; |
|||
} |
|||
|
|||
public static Integer getPageSize(){ |
|||
String pageSizeStr = ServletUtils.getParameter(PAGE_SIZE); |
|||
Integer pageSize = null; |
|||
if(StringUtils.hasText(pageSizeStr)){ |
|||
pageSize = Integer.parseInt(pageSizeStr); |
|||
} |
|||
if(pageSize!=null && pageSize > MAX_PAGE_SIZE){ |
|||
throw new BusinessException("超出数据额度",500); |
|||
} |
|||
return pageSize; |
|||
} |
|||
|
|||
public static String getOrderProp(){ |
|||
String orderProp = ServletUtils.getParameter(ORDER_PROP); |
|||
if(StringUtils.hasText(orderProp)){ |
|||
orderProp = orderProp.trim(); |
|||
if(orderProp.contains(".")){ |
|||
String[] strings = orderProp.split("\\."); |
|||
return strings[0]+"."+strings[1]; |
|||
} |
|||
return WordUtil.toLine(orderProp); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static String getOrderType(){ |
|||
String orderType = ServletUtils.getParameter(ORDER_TYPE); |
|||
if(StringUtils.hasText(orderType)){ |
|||
if(orderType.equalsIgnoreCase(ORDER_TYPE_DESC)){ |
|||
return ORDER_TYPE_DESC; |
|||
} |
|||
} |
|||
return ORDER_TYPE_ASC; |
|||
} |
|||
|
|||
public static Integer getStartRow(){ |
|||
Integer pageNum = getPageNum(); |
|||
Integer pageSize = getPageSize(); |
|||
if (pageNum==null || pageSize==null) { |
|||
throw new BusinessException("翻页参数异常",500); |
|||
} |
|||
return (pageNum-1)*pageSize; |
|||
} |
|||
|
|||
/** |
|||
* 启动翻页(翻页失败,会抛出异常) |
|||
* @return 是否使用翻页 |
|||
*/ |
|||
public static boolean startPage(){ |
|||
return startPage(true); |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* @param throwEx 是否抛异常 |
|||
* @return 是否使用翻页 |
|||
*/ |
|||
public static boolean startPage(boolean throwEx){ |
|||
return startPage(null,null,throwEx); |
|||
} |
|||
|
|||
/** |
|||
* 执行翻页 |
|||
* @param object POJO |
|||
* @return 是否使用翻页 |
|||
*/ |
|||
public static <T> boolean startPage(T object){ |
|||
return startPage(object,null,true); |
|||
} |
|||
|
|||
public static <T> boolean startPage(String prefix){ |
|||
return startPage(null,prefix,true); |
|||
} |
|||
/** |
|||
* |
|||
* @param throwEx 是否抛异常 |
|||
* @return 是否使用翻页 |
|||
*/ |
|||
public static <T> boolean startPage(T object,String prefix,boolean throwEx){ |
|||
Integer pageNum = getPageNum(); |
|||
Integer pageSize = getPageSize(); |
|||
String orderProp = getOrderProp(); |
|||
String orderType = getOrderType(); |
|||
if (pageNum!=null && pageNum>0 && pageSize!=null && pageSize>0) { |
|||
if(!StringUtils.isEmpty(orderProp)){ |
|||
if(prefix==null && object!=null){ |
|||
prefix = WordUtil.toLine(object.getClass().getSimpleName()); |
|||
} |
|||
if(prefix!=null){ |
|||
PageHelper.startPage(pageNum, pageSize).setOrderBy(prefix+"."+orderProp+" "+orderType); |
|||
}else { |
|||
PageHelper.startPage(pageNum, pageSize).setOrderBy(orderProp+" "+orderType); |
|||
} |
|||
return true; |
|||
} |
|||
PageHelper.startPage(pageNum, pageSize); |
|||
return true; |
|||
}else{ |
|||
if(throwEx){ |
|||
throw new BusinessException("翻页参数异常",500); |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,129 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.web.context.request.RequestAttributes; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
import javax.servlet.ServletRequest; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpSession; |
|||
import java.io.BufferedReader; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.io.InputStreamReader; |
|||
import java.nio.charset.StandardCharsets; |
|||
|
|||
/** |
|||
* Servlet容器工具类 |
|||
* @author YenHex |
|||
* @date 2021/6/9 |
|||
**/ |
|||
@UtilityClass |
|||
public class ServletUtils { |
|||
/** |
|||
* 获取String参数 |
|||
*/ |
|||
public static String getParameter(String name) { |
|||
return getRequest().getParameter(name); |
|||
} |
|||
|
|||
/** |
|||
* 获取String参数 |
|||
*/ |
|||
public static String getHeader(String name) { |
|||
return getRequest().getHeader(name); |
|||
} |
|||
|
|||
/** |
|||
* 获取IP |
|||
* @return |
|||
*/ |
|||
public static String getIp(HttpServletRequest request) { |
|||
if(request==null){ |
|||
request = getRequest(); |
|||
} |
|||
String ip = request.getHeader("x-forwarded-for"); |
|||
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("Proxy-Client-IP"); |
|||
} |
|||
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("WL-Proxy-Client-IP"); |
|||
} |
|||
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getRemoteAddr(); |
|||
} |
|||
return ip; |
|||
} |
|||
|
|||
/** |
|||
* 判断本次请求的数据类型是否为json |
|||
* @param request request |
|||
* @return true: 是 JSON 数据; false: 非 json 数据 |
|||
*/ |
|||
public static boolean isJsonRequest(HttpServletRequest request) { |
|||
if (request.getContentType() != null) { |
|||
return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || |
|||
request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 获取请求Body |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
public static String getBodyString(ServletRequest request) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
InputStream inputStream = null; |
|||
BufferedReader reader = null; |
|||
try { |
|||
inputStream = request.getInputStream(); |
|||
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); |
|||
String line = ""; |
|||
while ((line = reader.readLine()) != null) { |
|||
sb.append(line); |
|||
} |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
if (inputStream != null) { |
|||
try { |
|||
inputStream.close(); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
if (reader != null) { |
|||
try { |
|||
reader.close(); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 获取request |
|||
*/ |
|||
public static HttpServletRequest getRequest() { |
|||
return getRequestAttributes().getRequest(); |
|||
} |
|||
|
|||
/** |
|||
* 获取session |
|||
*/ |
|||
public static HttpSession getSession() { |
|||
return getRequest().getSession(); |
|||
} |
|||
|
|||
private static ServletRequestAttributes getRequestAttributes() { |
|||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); |
|||
return (ServletRequestAttributes) attributes; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,50 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.qs.serve.modules.wx.common.model.WxSmsProp; |
|||
import com.qs.serve.modules.wx.entity.dto.SmsBaseDto; |
|||
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; |
|||
|
|||
import java.lang.annotation.Annotation; |
|||
import java.lang.reflect.Field; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/16 |
|||
*/ |
|||
public class SmsReflectUtil { |
|||
|
|||
/** |
|||
* 反射获取WX_SMS配置 |
|||
* @param baseDto |
|||
* @param <T> |
|||
* @return |
|||
*/ |
|||
public static<T extends SmsBaseDto> List<WxMpTemplateData> getTemplateDataList(T baseDto){ |
|||
List<WxMpTemplateData> dataList = new ArrayList<>(); |
|||
for (Field field : baseDto.getClass().getDeclaredFields()) { |
|||
WxSmsProp wxSmsProp = null; |
|||
for (Annotation annotation : field.getAnnotations()) { |
|||
if(annotation instanceof WxSmsProp){ |
|||
wxSmsProp = (WxSmsProp)annotation; |
|||
break; |
|||
} |
|||
} |
|||
try { |
|||
field.setAccessible(true); |
|||
Object fieldObject = field.get(baseDto); |
|||
String value = ""; |
|||
if(fieldObject!=null){ |
|||
value = fieldObject.toString(); |
|||
} |
|||
WxMpTemplateData templateData = new WxMpTemplateData(wxSmsProp.keyword(),value); |
|||
dataList.add(templateData); |
|||
} catch (IllegalAccessException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
return dataList; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,148 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import org.springframework.aop.framework.AopContext; |
|||
import org.springframework.beans.BeansException; |
|||
import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
|||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; |
|||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
|||
import org.springframework.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* spring工具类 |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
@Component |
|||
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware |
|||
{ |
|||
/** Spring应用上下文环境 */ |
|||
private static ConfigurableListableBeanFactory beanFactory; |
|||
|
|||
private static ApplicationContext applicationContext; |
|||
|
|||
@Override |
|||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException |
|||
{ |
|||
SpringUtils.beanFactory = beanFactory; |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException |
|||
{ |
|||
SpringUtils.applicationContext = applicationContext; |
|||
} |
|||
|
|||
/** |
|||
* 获取对象 |
|||
* |
|||
* @param name |
|||
* @return Object 一个以所给名字注册的bean的实例 |
|||
* @throws BeansException |
|||
* |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static <T> T getBean(String name) throws BeansException |
|||
{ |
|||
return (T) beanFactory.getBean(name); |
|||
} |
|||
|
|||
/** |
|||
* 获取类型为requiredType的对象 |
|||
* |
|||
* @param clz |
|||
* @return |
|||
* @throws BeansException |
|||
* |
|||
*/ |
|||
public static <T> T getBean(Class<T> clz) throws BeansException |
|||
{ |
|||
T result = (T) beanFactory.getBean(clz); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true |
|||
* |
|||
* @param name |
|||
* @return boolean |
|||
*/ |
|||
public static boolean containsBean(String name) |
|||
{ |
|||
return beanFactory.containsBean(name); |
|||
} |
|||
|
|||
/** |
|||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) |
|||
* |
|||
* @param name |
|||
* @return boolean |
|||
* @throws NoSuchBeanDefinitionException |
|||
* |
|||
*/ |
|||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException |
|||
{ |
|||
return beanFactory.isSingleton(name); |
|||
} |
|||
|
|||
/** |
|||
* @param name |
|||
* @return Class 注册对象的类型 |
|||
* @throws NoSuchBeanDefinitionException |
|||
* |
|||
*/ |
|||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException |
|||
{ |
|||
return beanFactory.getType(name); |
|||
} |
|||
|
|||
/** |
|||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名 |
|||
* |
|||
* @param name |
|||
* @return |
|||
* @throws NoSuchBeanDefinitionException |
|||
* |
|||
*/ |
|||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException |
|||
{ |
|||
return beanFactory.getAliases(name); |
|||
} |
|||
|
|||
/** |
|||
* 获取aop代理对象 |
|||
* |
|||
* @param invoker |
|||
* @return |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static <T> T getAopProxy(T invoker) |
|||
{ |
|||
return (T) AopContext.currentProxy(); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前的环境配置,无配置返回null |
|||
* |
|||
* @return 当前的环境配置 |
|||
*/ |
|||
public static String[] getActiveProfiles() |
|||
{ |
|||
return applicationContext.getEnvironment().getActiveProfiles(); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前的环境配置,当有多个环境配置时,只获取第一个 |
|||
* |
|||
* @return 当前的环境配置 |
|||
*/ |
|||
public static String getActiveProfile() |
|||
{ |
|||
final String[] activeProfiles = getActiveProfiles(); |
|||
return CollectionUtil.isNotEmpty(Arrays.asList(activeProfiles)) ? activeProfiles[0] : null; |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
@UtilityClass |
|||
public class StringUtils extends org.springframework.util.StringUtils { |
|||
|
|||
/** 非空 */ |
|||
public static boolean isNotEmpty(String str){ |
|||
return !isEmpty(str); |
|||
} |
|||
|
|||
/** 含有空对象 */ |
|||
public static boolean hasEmpty(Object... strings){ |
|||
for (Object str : strings) { |
|||
if(isEmpty(str)){return true;} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 格式化 |
|||
* @param exp 含{}的表达式 |
|||
* @param params 参数(注意跳过空值) |
|||
* @return |
|||
*/ |
|||
public static String format(String exp,String... params){ |
|||
StringBuilder builder = new StringBuilder(exp); |
|||
int idx_p = 0; |
|||
while (hasText(builder,"{}")){ |
|||
int idx = builder.indexOf("{}"); |
|||
if(idx_p<params.length&¶ms[idx_p]!=null){ |
|||
builder.replace(idx,idx+2,params[idx_p]); |
|||
idx_p++; |
|||
}else { |
|||
break; |
|||
} |
|||
} |
|||
return builder.toString(); |
|||
} |
|||
|
|||
private static boolean hasText(StringBuilder a,String text){ |
|||
return a.toString().contains(text); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,75 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.concurrent.*; |
|||
|
|||
|
|||
/** |
|||
* 线程相关 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
@Slf4j |
|||
@UtilityClass |
|||
public class ThreadsUtils { |
|||
|
|||
/** |
|||
* sleep等待,单位为毫秒 |
|||
*/ |
|||
public static void sleep(long milliseconds) { |
|||
try { |
|||
Thread.sleep(milliseconds); |
|||
} catch (InterruptedException e) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 停止线程池 |
|||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. |
|||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. |
|||
* 如果仍人超時,則強制退出. |
|||
* 另对在shutdown时线程本身被调用中断做了处理. |
|||
*/ |
|||
public static void shutdownAndAwaitTermination(ExecutorService pool) { |
|||
if (pool != null && !pool.isShutdown()) { |
|||
pool.shutdown(); |
|||
try { |
|||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { |
|||
pool.shutdownNow(); |
|||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { |
|||
log.info("Pool did not terminate"); |
|||
} |
|||
} |
|||
} catch (InterruptedException ie) { |
|||
pool.shutdownNow(); |
|||
Thread.currentThread().interrupt(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 打印线程异常信息 |
|||
*/ |
|||
public static void printException(Runnable r, Throwable t) { |
|||
if (t == null && r instanceof Future<?>) { |
|||
try { |
|||
Future<?> future = (Future<?>) r; |
|||
if (future.isDone()) { |
|||
future.get(); |
|||
} |
|||
} catch (CancellationException ce) { |
|||
t = ce; |
|||
} catch (ExecutionException ee) { |
|||
t = ee.getCause(); |
|||
} catch (InterruptedException ie) { |
|||
Thread.currentThread().interrupt(); |
|||
} |
|||
} |
|||
if (t != null) { |
|||
log.error(t.getMessage(), t); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,46 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
|
|||
import com.qs.serve.common.util.model.DesUtils; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/25 |
|||
*/ |
|||
public class TokenUtil { |
|||
|
|||
private static final String secretKey = "N2aD7vLTJjeo71vCHwbqYsSF9rZPUh"; |
|||
|
|||
/** |
|||
* 加密 |
|||
* @param userId |
|||
* @return |
|||
*/ |
|||
public static String desEncrypt(String userId){ |
|||
String formatStr = userId+"-N2aD7vLTJjeo71vCHwbqYsSF9rZPUh"; |
|||
DesUtils des = new DesUtils(secretKey); |
|||
try { |
|||
return des.encrypt(formatStr); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 解密 |
|||
* @param encryptText |
|||
* @return clientId |
|||
*/ |
|||
public static String desDecrypt(String encryptText){ |
|||
DesUtils des = new DesUtils(secretKey); |
|||
try { |
|||
String formatStr = des.decrypt(encryptText); |
|||
return formatStr.split("-")[0]; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,74 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.qs.serve.common.model.dto.TreeNode; |
|||
import lombok.experimental.UtilityClass; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@UtilityClass |
|||
public class TreeUtil { |
|||
|
|||
public static final String DEFAULT_PID_STRING = "0"; |
|||
|
|||
/** |
|||
* 两层循环实现建树 |
|||
* @param treeNodes 传入的树节点列表 |
|||
* @param rootPid |
|||
* @return |
|||
* sorted(Comparator.comparingInt(DemoEntity::getSort)) |
|||
*/ |
|||
public <T extends TreeNode> List<T> build(List<T> treeNodes, String rootPid) { |
|||
List<T> trees = new ArrayList<>(); |
|||
for (T treeNode : treeNodes) { |
|||
if (rootPid.equals(treeNode.getParentId())) { |
|||
trees.add(treeNode); |
|||
} |
|||
for (T it : treeNodes) { |
|||
if (it.getParentId().equals(treeNode.getId())) { |
|||
treeNode.addChildren(it); |
|||
} |
|||
} |
|||
} |
|||
return trees; |
|||
} |
|||
|
|||
/** |
|||
* 使用递归方法建树 |
|||
* |
|||
* @param treeNodes |
|||
* @return |
|||
*/ |
|||
public <T extends TreeNode> List<T> buildByRecursive(List<T> treeNodes, Object root) { |
|||
List<T> trees = new ArrayList<T>(); |
|||
for (T treeNode : treeNodes) { |
|||
if (root.equals(treeNode.getParentId())) { |
|||
trees.add(findChildren(treeNode, treeNodes)); |
|||
} |
|||
} |
|||
return trees; |
|||
} |
|||
|
|||
/** |
|||
* 递归查找子节点 |
|||
* |
|||
* @param treeNodes |
|||
* @return |
|||
*/ |
|||
public <T extends TreeNode> T findChildren(T treeNode, List<T> treeNodes) { |
|||
for (T it : treeNodes) { |
|||
if (treeNode.getId().equals(it.getParentId())) { |
|||
if (treeNode.getChildren() == null) { |
|||
treeNode.setChildren(new ArrayList<>()); |
|||
} |
|||
treeNode.addChildren(findChildren(it, treeNodes)); |
|||
} |
|||
} |
|||
return treeNode; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,53 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import cn.hutool.core.io.FileUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
import org.springframework.web.multipart.MultipartHttpServletRequest; |
|||
import org.springframework.web.util.WebUtils; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.time.LocalDate; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/14 |
|||
*/ |
|||
@Slf4j |
|||
public class UploadUtil { |
|||
|
|||
/** |
|||
* 返回相对地址 |
|||
* @param uploadPath 物理路径 |
|||
* @param fileName2 文件名 |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
public static String put(String uploadPath,final String fileName2,HttpServletRequest request){ |
|||
String contentType = request.getContentType(); |
|||
MultipartFile file = null; |
|||
if (contentType != null && contentType.toLowerCase().startsWith("multipart")) { |
|||
MultipartHttpServletRequest multipartHttpServletRequest = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class); |
|||
file = multipartHttpServletRequest.getFile("file"); |
|||
} |
|||
if (file==null||file.isEmpty()) { |
|||
Assert.throwEx("请选择文件"); |
|||
} |
|||
String fileOrgName = file.getOriginalFilename(); |
|||
String mimeType = fileOrgName.substring(fileOrgName.lastIndexOf(".")); |
|||
String fileName = fileName2 + mimeType ; |
|||
String prefix = LocalDate.now().toString().replace("-","")+"/"; |
|||
try { |
|||
FileUtil.createTempFile(new File(uploadPath+prefix)); |
|||
File dest = new File(uploadPath + prefix + fileName); |
|||
file.transferTo(dest); |
|||
return prefix + fileName; |
|||
} catch (IOException e) { |
|||
log.error(e.toString(), e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,62 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.qs.serve.common.framework.mybatis.query.AnnotationQueryStorage; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryFieldDataValue; |
|||
import lombok.experimental.UtilityClass; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 生成SQL条件包装对象 |
|||
* @author YenHex |
|||
* @date 2022/2/28 |
|||
**/ |
|||
@UtilityClass |
|||
@Slf4j |
|||
public class WarpUtil extends AnnotationQueryStorage { |
|||
|
|||
private static final String ORDER_PROP_NAME = "prop"; |
|||
private static final String ORDER_TYPE_NAME = "order"; |
|||
private static final String ORDER_DESC = "desc"; |
|||
private static final String CREATE_TIME = "create_time"; |
|||
|
|||
private static final String START_TIME = "startTime"; |
|||
private static final String END_TIME = "endTime"; |
|||
|
|||
public static <T> QueryWrapper<T> build(T query){ |
|||
return build(query,false); |
|||
} |
|||
|
|||
public static <T> QueryWrapper<T> build(T query,Boolean orderAble){ |
|||
QueryWrapper<T> queryWrapper = new QueryWrapper<>(); |
|||
List<QueryFieldDataValue> dataValues = loadQueryFieldDataValues(query); |
|||
assert dataValues != null; |
|||
initWrap(dataValues,queryWrapper); |
|||
initBetweenTime(queryWrapper); |
|||
if(orderAble){ |
|||
String columnsString = ServletUtils.getParameter(ORDER_PROP_NAME); |
|||
String orderType = ServletUtils.getParameter(ORDER_TYPE_NAME); |
|||
if(StringUtils.isNotEmpty(columnsString)){ |
|||
String[] columns = columnsString.split(","); |
|||
if(orderType.equals(ORDER_DESC)){ |
|||
queryWrapper.orderByDesc(columns); |
|||
}else { |
|||
queryWrapper.orderByAsc(columns); |
|||
} |
|||
} |
|||
} |
|||
return queryWrapper; |
|||
} |
|||
|
|||
public static void initBetweenTime(QueryWrapper<?> queryWrapper){ |
|||
String startTime = ServletUtils.getParameter(START_TIME); |
|||
String endTime = ServletUtils.getParameter(END_TIME); |
|||
if(!StringUtils.hasEmpty(startTime,endTime)){ |
|||
queryWrapper.ge(CREATE_TIME,startTime); |
|||
queryWrapper.le(CREATE_TIME,endTime); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,109 @@ |
|||
package com.qs.serve.common.util; |
|||
|
|||
import lombok.experimental.UtilityClass; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
/** |
|||
* 单词间转换工具类 |
|||
* @author JcYen |
|||
* @Date 2020/5/27 |
|||
* @Version 1.0 |
|||
*/ |
|||
@UtilityClass |
|||
public class WordUtil { |
|||
|
|||
public static String lowerFirst(String className){ |
|||
return lowerFirst(className,null); |
|||
} |
|||
|
|||
/** |
|||
* 类名首字母小写 |
|||
* @param className 类名 |
|||
* @param rmPrefix 移除的前缀,不区分大小写 |
|||
* @return |
|||
*/ |
|||
public static String lowerFirst(String className,String rmPrefix){ |
|||
if(className==null){throw new RuntimeException("className is null");} |
|||
if(rmPrefix!=null){ |
|||
String prefix = className.substring(0,rmPrefix.length()); |
|||
if(prefix.toLowerCase().equals(rmPrefix.toLowerCase())){ |
|||
className = className.substring(rmPrefix.length()); |
|||
} |
|||
} |
|||
char[]chars = className.toCharArray(); |
|||
chars[0] += 32; |
|||
return String.valueOf(chars); |
|||
} |
|||
|
|||
/** |
|||
* 数据库表名 转 类名 : ums_user_log ->> UmsUserLog |
|||
* 1.首字母转小写 |
|||
* 2.下横线后的字母大写,去掉"_"下横线, |
|||
* @param tableName |
|||
* @return |
|||
*/ |
|||
public static String tableToClass(String tableName) { |
|||
if(StringUtils.isEmpty(tableName)){return null;} |
|||
char[] chars = tableName.toCharArray(); |
|||
for (int i = 0; i < chars.length; i++) { |
|||
if(Character.isLowerCase(chars[i])&&i==0){ |
|||
chars[i] = Character.toUpperCase(chars[i]); |
|||
} |
|||
if(chars[i] == '_' && i+1<chars.length){ |
|||
chars[i+1] = Character.toUpperCase(chars[i+1]); |
|||
} |
|||
} |
|||
return String.valueOf(chars).replace("_",""); |
|||
} |
|||
|
|||
/** |
|||
* 数据库列 转 字段名 : ums_user_log ->> umsUserLog |
|||
* 1.首字母转小写 |
|||
* 2.下横线后的字母大写,去掉"_"下横线, |
|||
* @param columnName |
|||
* @return |
|||
*/ |
|||
public static String columnToField(String columnName) { |
|||
if(StringUtils.isEmpty(columnName)){return null;} |
|||
char[] chars = columnName.toCharArray(); |
|||
for (int i = 0; i < chars.length; i++) { |
|||
if(chars[i] == '_' && i+1<chars.length){ |
|||
chars[i+1] = Character.toUpperCase(chars[i+1]); |
|||
} |
|||
} |
|||
return String.valueOf(chars).replace("_",""); |
|||
} |
|||
|
|||
/** |
|||
* 字段名 转 数据库列 : umsUserLog / UmsUserLog ->> ums_user_log |
|||
* 1.首字母转小写 |
|||
* 2.字符非首个字母,添加"_"下横线,并大写字母转小写 |
|||
* @param fieldName |
|||
* @return |
|||
*/ |
|||
public static String toLine(String fieldName) { |
|||
if(StringUtils.isEmpty(fieldName)){return null;} |
|||
columnToField(fieldName); |
|||
char[] chars = fieldName.toCharArray(); |
|||
int countUpper = 0; |
|||
for (int i = 0; i < chars.length; i++) { |
|||
if(Character.isUpperCase(chars[i])&&i!=0){ |
|||
countUpper ++ ; |
|||
} |
|||
} |
|||
char[] newChars = new char[chars.length+countUpper]; |
|||
//偏移量
|
|||
int offset = 0; |
|||
for (int i = 0; i < chars.length; i++) { |
|||
if(Character.isUpperCase(chars[i])){ |
|||
if(i!=0){ |
|||
newChars[i+offset] = '_'; |
|||
offset++; |
|||
} |
|||
} |
|||
newChars[i+offset] = Character.toLowerCase(chars[i]); |
|||
} |
|||
return String.valueOf(newChars); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.common.util.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/17 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum DateFormatString { |
|||
|
|||
DATE_TIME("yyyy-MM-dd HH:mm:ss"), |
|||
DATE_TIME_NUM("yyyyMMddHHmmss"), |
|||
DATE_TIME_CN("yyyy年MM月dd日 HH时mm分ss秒"), |
|||
|
|||
DATE("yyyy-MM-dd"), |
|||
DATE_NUM("yyyyMMdd"), |
|||
DATE_CN("yyyy年MM月dd日"); |
|||
|
|||
private String value; |
|||
|
|||
} |
@ -0,0 +1,125 @@ |
|||
package com.qs.serve.common.util.model; |
|||
|
|||
import javax.crypto.Cipher; |
|||
import java.security.Key; |
|||
|
|||
/** |
|||
* 使用DES算法对字符串进行加密解密 (加密解密的操作步骤正好相反, 参考 {@link #encrypt(String)}, {@link #decrypt(String)}) |
|||
*/ |
|||
public class DesUtils { |
|||
private static String defaultSecretKey = "default_secret_key"; //默认密钥
|
|||
private Cipher encryptCipher = null; //加密器
|
|||
private Cipher decryptCipher = null; //解密器
|
|||
|
|||
public DesUtils() throws Exception { |
|||
this(defaultSecretKey); |
|||
} |
|||
|
|||
/** |
|||
* @param secretKey 加密解密使用的密钥 |
|||
*/ |
|||
public DesUtils(String secretKey) { |
|||
Key key; |
|||
try { |
|||
key = getKey(secretKey.getBytes()); |
|||
encryptCipher = Cipher.getInstance("DES"); |
|||
encryptCipher.init(Cipher.ENCRYPT_MODE, key); |
|||
decryptCipher = Cipher.getInstance("DES"); |
|||
decryptCipher.init(Cipher.DECRYPT_MODE, key); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加密 (逻辑: 1. 将要加密的字符串转换为字节数组(byte array)<br/> |
|||
* 2. 将第一步的字节数组作为输入使用加密器(Cipher)的doFinal方法进行加密, 返回字节数组<br/> |
|||
* 3. 把加密后的字节数组转换成十六进制的字符串)<br/> |
|||
* @param strIn 要加密的字符串 |
|||
* @return 返回加密后的十六进制字符串 |
|||
* @throws Exception |
|||
*/ |
|||
public String encrypt(String strIn) throws Exception { |
|||
return byteArr2HexStr(encrypt(strIn.getBytes())); |
|||
} |
|||
|
|||
public byte[] encrypt(byte[] arrB) throws Exception { |
|||
return encryptCipher.doFinal(arrB); |
|||
} |
|||
|
|||
/** |
|||
* 解密 (逻辑: 1. 把加密后的十六进制字符串转换成字节数组(byte array)<br/> |
|||
* 2. 将第一步的字节数组作为输入使用加密器(Cipher)的doFinal方法进行解密, 返回字节数组(byte array)<br/> |
|||
* 3. 把解密后的字节数组转换成字符串)<br/> |
|||
* @param strIn |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
public String decrypt(String strIn) throws Exception { |
|||
return new String(decrypt(hexStr2ByteArr(strIn))); |
|||
} |
|||
|
|||
public byte[] decrypt(byte[] arrB) throws Exception { |
|||
return decryptCipher.doFinal(arrB); |
|||
} |
|||
|
|||
public static String byteArr2HexStr(byte[] arrB) throws Exception { |
|||
int iLen = arrB.length; |
|||
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
|
|||
StringBuffer sb = new StringBuffer(iLen * 2); |
|||
for (int i = 0; i < iLen; i++) { |
|||
int intTmp = arrB[i]; |
|||
// 把负数转换为正数
|
|||
while (intTmp < 0) { |
|||
intTmp = intTmp + 256; |
|||
} |
|||
// 小于0F的数需要在前面补0
|
|||
if (intTmp < 16) { |
|||
sb.append("0"); |
|||
} |
|||
sb.append(Integer.toString(intTmp, 16)); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
public static byte[] hexStr2ByteArr(String strIn) throws Exception { |
|||
byte[] arrB = strIn.getBytes(); |
|||
int iLen = arrB.length; |
|||
// 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
|
|||
byte[] arrOut = new byte[iLen / 2]; |
|||
for (int i = 0; i < iLen; i = i + 2) { |
|||
String strTmp = new String(arrB, i, 2); |
|||
arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16); |
|||
} |
|||
return arrOut; |
|||
} |
|||
|
|||
private Key getKey(byte[] arrBTmp) throws Exception { |
|||
// 创建一个空的8位字节数组(默认值为0)
|
|||
byte[] arrB = new byte[8]; |
|||
// 将原始字节数组转换为8位
|
|||
for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) { |
|||
arrB[i] = arrBTmp[i]; |
|||
} |
|||
// 生成密钥
|
|||
Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES"); |
|||
return key; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 用法实例 |
|||
*/ |
|||
public static void main(String[] args) { |
|||
try { |
|||
String test = "liwc"; |
|||
DesUtils des = new DesUtils("leemenz"); //自定义密钥
|
|||
System.out.println("加密前的字符:" + test); |
|||
System.out.println("加密后的字符:" + des.encrypt(test)); |
|||
System.out.println("解密后的字符:" + des.decrypt(des.encrypt(test))); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,138 @@ |
|||
package com.qs.serve.controller; |
|||
|
|||
import cn.hutool.crypto.SecureUtil; |
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
import com.qs.serve.common.model.annotation.LimitSubmit; |
|||
import com.qs.serve.common.model.annotation.SysLog; |
|||
import com.qs.serve.common.model.consts.RedisCacheKeys; |
|||
import com.qs.serve.common.model.dto.R; |
|||
import com.qs.serve.common.model.enums.BizType; |
|||
import com.qs.serve.common.util.IdUtil; |
|||
import com.qs.serve.modules.sys.entity.SysTenant; |
|||
import com.qs.serve.modules.sys.entity.SysUser; |
|||
import com.qs.serve.modules.sys.entity.dto.SysLoginByPhoneParam; |
|||
import com.qs.serve.modules.sys.entity.dto.SysLoginParam; |
|||
import com.qs.serve.modules.sys.entity.dto.SysResetPwdByPhoneParam; |
|||
import com.qs.serve.modules.sys.mapper.SysNoticeUserMapper; |
|||
import com.qs.serve.modules.sys.service.SysTenantService; |
|||
import com.qs.serve.modules.sys.service.SysUserLoginService; |
|||
import com.qs.serve.modules.sys.service.SysUserService; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import javax.validation.Valid; |
|||
import java.util.List; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* 门户:后台接口 |
|||
* @author YenHex |
|||
* @since 2022/3/2 |
|||
*/ |
|||
@Slf4j |
|||
@AllArgsConstructor |
|||
@RestController |
|||
@RequestMapping("portal") |
|||
public class AdminPortalController { |
|||
|
|||
private SysTenantService sysTenantService; |
|||
private SysUserLoginService sysUserLoginService; |
|||
private RedisService redisService; |
|||
private SysUserService sysUserService; |
|||
|
|||
/** |
|||
* 获取所有租户 |
|||
* @return |
|||
*/ |
|||
@GetMapping("/listTenant") |
|||
public R<List<SysTenant>> getList(){ |
|||
List<SysTenant> list = sysTenantService.list2(); |
|||
return R.ok(list); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 登录接口 |
|||
* @return |
|||
*/ |
|||
@LimitSubmit |
|||
@SysLog(title = "人员",desc = "后台登录",biz = BizType.LOGIN,saveReqParam = false) |
|||
@PostMapping("/login") |
|||
public R<?> login(@RequestBody @Valid SysLoginParam param){ |
|||
return R.ok(sysUserLoginService.login(param)); |
|||
} |
|||
|
|||
/** |
|||
* 手机登陆 |
|||
* @return |
|||
*/ |
|||
@LimitSubmit |
|||
@SysLog(title = "人员",desc = "手机登陆",biz = BizType.LOGIN,saveReqParam = false) |
|||
@PostMapping("/phoneLogin") |
|||
public R<?> phoneLogin(@RequestBody @Valid SysLoginByPhoneParam param){ |
|||
return R.ok(sysUserLoginService.login(param)); |
|||
} |
|||
|
|||
/** |
|||
* 登出接口 |
|||
* @return |
|||
*/ |
|||
@SysLog(title = "人员",desc = "退出登录",biz = BizType.LOGOUT,saveReqParam = false) |
|||
@GetMapping("/logout") |
|||
public R<?> login(){ |
|||
return R.ok(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 获取用户手机验证码 |
|||
*/ |
|||
@LimitSubmit(interval = 10000,message = "请10秒后尝试") |
|||
@SysLog(title = "人员",desc = "获取用户手机验证码",biz = BizType.OTHER) |
|||
@GetMapping("/userPhoneCode/{phone}") |
|||
public R<?> phoneCode(@PathVariable("phone") String phone){ |
|||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); |
|||
wrapper.eq(SysUser::getAccount,phone); |
|||
int count = sysUserService.count(wrapper); |
|||
if(count<1){ |
|||
return R.error("无效手机号,请重新输入"); |
|||
} |
|||
String key = RedisCacheKeys.PHONE_KEY+phone; |
|||
String code = redisService.getString(key); |
|||
if(code==null){ |
|||
code = IdUtil.genCode(6); |
|||
} |
|||
redisService.set(key,code,1, TimeUnit.MINUTES); |
|||
//TODO send msg
|
|||
return R.ok(); |
|||
} |
|||
|
|||
/** |
|||
* 重置手机用户密码 |
|||
*/ |
|||
@LimitSubmit(interval = 10000,message = "请10秒后尝试") |
|||
@SysLog(title = "人员",desc = "重置手机用户密码",biz = BizType.RESET) |
|||
@PostMapping("/resetPwdByPhone") |
|||
public R<?> phoneCode(@RequestBody @Valid SysResetPwdByPhoneParam param){ |
|||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); |
|||
wrapper.eq(SysUser::getAccount,param.getPhone()); |
|||
List<SysUser> userList = sysUserService.list(wrapper); |
|||
if(userList.size()<1){ |
|||
return R.error("无效手机号,请重新输入"); |
|||
} |
|||
String key = RedisCacheKeys.PHONE_KEY+param.getPhone(); |
|||
String code = redisService.getString(key); |
|||
if(!param.getCode().equals(code)){ |
|||
return R.error("验证码无效或过期"); |
|||
} |
|||
SysUser sysUser = new SysUser(); |
|||
sysUser.setId(userList.get(0).getId()); |
|||
sysUser.setPassword(SecureUtil.md5(param.getNewPwd())); |
|||
sysUserService.updateById(sysUser); |
|||
return R.ok(); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.qs.serve.controller; |
|||
|
|||
import com.qs.serve.common.model.dto.R; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PathVariable; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import javax.servlet.http.HttpServletResponse; |
|||
|
|||
/** |
|||
* 门户:API通用接口 |
|||
* @author YenHex |
|||
* @since 2022/3/14 |
|||
*/ |
|||
@Slf4j |
|||
@AllArgsConstructor |
|||
@RestController |
|||
@RequestMapping("/api/common") |
|||
public class CommonApi { |
|||
|
|||
|
|||
|
|||
} |
@ -0,0 +1,103 @@ |
|||
package com.qs.serve.controller; |
|||
|
|||
import com.qs.serve.common.config.properties.UploadProperties; |
|||
import com.qs.serve.common.model.annotation.SysLog; |
|||
import com.qs.serve.common.model.dto.R; |
|||
import com.qs.serve.common.model.enums.BizType; |
|||
import com.qs.serve.common.util.*; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
import org.springframework.web.multipart.MultipartHttpServletRequest; |
|||
import org.springframework.web.util.WebUtils; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.time.LocalDate; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 门户:后台通用接口 |
|||
* @author YenHex |
|||
* @since 2022/3/14 |
|||
*/ |
|||
@Slf4j |
|||
@AllArgsConstructor |
|||
@RestController |
|||
@RequestMapping("/common") |
|||
public class CommonController { |
|||
|
|||
private UploadProperties uploadProperties; |
|||
|
|||
|
|||
|
|||
/** |
|||
* 单图上传 |
|||
* @apiNote file不能为空 |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
@SysLog(title = "文件",biz = BizType.UPLOAD) |
|||
@PostMapping("/upload") |
|||
public R upload(HttpServletRequest request) { |
|||
String relativePath = UploadUtil.put(uploadProperties.getLogicalPath(),IdUtil.timeStampId(),request); |
|||
if(relativePath!=null){ |
|||
return R.ok(uploadProperties.getProxyUrl()+relativePath,"上传成功"); |
|||
} |
|||
return R.error("上传失败"); |
|||
} |
|||
|
|||
/** |
|||
* 验证token |
|||
* @param token |
|||
* @return |
|||
*/ |
|||
@PostMapping("/validToken") |
|||
public R validToken(String token) { |
|||
return R.isOk(JwtUtils.verify(token),"无效token"); |
|||
} |
|||
|
|||
/** |
|||
* 多图上传 |
|||
* @apiNote files不能为空 |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
@SysLog(title = "文件",biz = BizType.UPLOAD) |
|||
@PostMapping( "/uploadMulti") |
|||
public R<?> multiUpload(HttpServletRequest request) { |
|||
String contentType = request.getContentType(); |
|||
List<MultipartFile> files = null; |
|||
if (contentType != null && contentType.toLowerCase().startsWith("multipart")) { |
|||
MultipartHttpServletRequest multipartHttpServletRequest = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class); |
|||
files = multipartHttpServletRequest.getFiles("files"); |
|||
} |
|||
if (files==null||files.isEmpty()) { |
|||
return R.error("请选择文件"); |
|||
} |
|||
List<String> list = new ArrayList<>(); |
|||
for (int i = 0; i < files.size(); i++) { |
|||
MultipartFile file = files.get(i); |
|||
if (file.isEmpty()) { |
|||
return R.error("上传第" + (i++) + "个文件失败"); |
|||
} |
|||
String fileOrgName = file.getOriginalFilename(); |
|||
String mimeType = fileOrgName.substring(fileOrgName.lastIndexOf(".")); |
|||
String filePath = LocalDate.now().toString().replace("-","")+"/"+ IdUtil.timeStampId()+mimeType; |
|||
String uploadPath = uploadProperties.getLogicalPath(); |
|||
File dest = new File(uploadPath + filePath); |
|||
try { |
|||
file.transferTo(dest); |
|||
list.add(uploadProperties.getProxyUrl()+filePath); |
|||
} catch (IOException e) { |
|||
log.error(e.toString(), e); |
|||
return R.error("上传第" + (i++) + "个文件失败"); |
|||
} |
|||
} |
|||
return R.ok(list); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,129 @@ |
|||
package com.qs.serve.controller; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import me.chanjar.weixin.mp.api.WxMpMessageRouter; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PathVariable; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import me.chanjar.weixin.mp.api.WxMpService; |
|||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
|||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
|||
|
|||
/** |
|||
* 门户:微信公众号回调 |
|||
* @author YenHex |
|||
* @since 2022-03-07 |
|||
*/ |
|||
@Slf4j |
|||
@AllArgsConstructor |
|||
//@RestController
|
|||
//@RequestMapping("/api/wx/mp/portal/{appid}")
|
|||
public class WxMpPortalApi { |
|||
|
|||
private final WxMpService wxService; |
|||
private final WxMpMessageRouter messageRouter; |
|||
|
|||
/** |
|||
* 校验身份 |
|||
* @param appid |
|||
* @param signature |
|||
* @param timestamp |
|||
* @param nonce |
|||
* @param echostr |
|||
* @return |
|||
*/ |
|||
@GetMapping(produces = "text/plain;charset=utf-8") |
|||
public String authGet(@PathVariable String appid, |
|||
@RequestParam(name = "signature", required = false) String signature, |
|||
@RequestParam(name = "timestamp", required = false) String timestamp, |
|||
@RequestParam(name = "nonce", required = false) String nonce, |
|||
@RequestParam(name = "echostr", required = false) String echostr) { |
|||
|
|||
log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, |
|||
timestamp, nonce, echostr); |
|||
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { |
|||
throw new IllegalArgumentException("请求参数非法,请核实!"); |
|||
} |
|||
if (!this.wxService.switchover(appid)) { |
|||
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid)); |
|||
} |
|||
if (wxService.checkSignature(timestamp, nonce, signature)) { |
|||
return echostr; |
|||
} |
|||
return "非法请求"; |
|||
} |
|||
|
|||
/** |
|||
* 身份校验2 |
|||
* @param appid |
|||
* @param requestBody |
|||
* @param signature |
|||
* @param timestamp |
|||
* @param nonce |
|||
* @param openid |
|||
* @param encType |
|||
* @param msgSignature |
|||
* @return |
|||
*/ |
|||
@PostMapping(produces = "application/xml; charset=UTF-8") |
|||
public String post(@PathVariable String appid, |
|||
@RequestBody String requestBody, |
|||
@RequestParam("signature") String signature, |
|||
@RequestParam("timestamp") String timestamp, |
|||
@RequestParam("nonce") String nonce, |
|||
@RequestParam("openid") String openid, |
|||
@RequestParam(name = "encrypt_type", required = false) String encType, |
|||
@RequestParam(name = "msg_signature", required = false) String msgSignature) { |
|||
log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}]," |
|||
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", |
|||
openid, signature, encType, msgSignature, timestamp, nonce, requestBody); |
|||
if (!this.wxService.switchover(appid)) { |
|||
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid)); |
|||
} |
|||
|
|||
if (!wxService.checkSignature(timestamp, nonce, signature)) { |
|||
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); |
|||
} |
|||
String out = null; |
|||
if (encType == null) { |
|||
// 明文传输的消息
|
|||
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); |
|||
WxMpXmlOutMessage outMessage = this.route(inMessage); |
|||
if (outMessage == null) { |
|||
return ""; |
|||
} |
|||
out = outMessage.toXml(); |
|||
} else if ("aes".equalsIgnoreCase(encType)) { |
|||
// aes加密的消息
|
|||
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(), |
|||
timestamp, nonce, msgSignature); |
|||
log.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); |
|||
WxMpXmlOutMessage outMessage = this.route(inMessage); |
|||
if (outMessage == null) { |
|||
return ""; |
|||
} |
|||
|
|||
out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage()); |
|||
} |
|||
|
|||
log.debug("\n组装回复信息:{}", out); |
|||
return out; |
|||
} |
|||
|
|||
private WxMpXmlOutMessage route(WxMpXmlMessage message) { |
|||
try { |
|||
return this.messageRouter.route(message); |
|||
} catch (Exception e) { |
|||
log.error("路由消息时出现异常!", e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,97 @@ |
|||
package com.qs.serve.controller; |
|||
|
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.framework.security.model.LoginUserType; |
|||
import com.qs.serve.common.model.annotation.SysLog; |
|||
import com.qs.serve.common.model.consts.RedisCacheKeys; |
|||
import com.qs.serve.common.model.dto.R; |
|||
import com.qs.serve.common.model.enums.BizType; |
|||
import com.qs.serve.common.model.enums.HttpCode; |
|||
import com.qs.serve.common.model.enums.InterType; |
|||
import com.qs.serve.common.util.*; |
|||
import com.qs.serve.modules.ums.entity.UmsUser; |
|||
import com.qs.serve.modules.ums.service.UmsUserService; |
|||
import com.qs.serve.modules.wx.entity.WxUser; |
|||
import com.qs.serve.modules.wx.entity.dto.WxLoginUser; |
|||
import com.qs.serve.modules.wx.service.WxUserService; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.validation.Valid; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* 门户:微信登录 |
|||
* @author YenHex |
|||
* @since 2022/3/7 |
|||
*/ |
|||
@Slf4j |
|||
@AllArgsConstructor |
|||
@RestController |
|||
@RequestMapping("/api/wx/login") |
|||
public class WxSvcLoginApi { |
|||
|
|||
private final WxUserService wxUserService; |
|||
|
|||
private final RedisService redisService; |
|||
|
|||
private final UmsUserService umsUserService; |
|||
|
|||
|
|||
/** |
|||
* 公众号登录 |
|||
* @param wxLoginUser |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@SysLog(title = "公众号登录",biz = BizType.LOGIN,inter = InterType.API) |
|||
@PostMapping("/mp") |
|||
public R<?> login(@RequestBody @Valid WxLoginUser wxLoginUser, HttpServletRequest request){ |
|||
Map<String,Object> objectMap = new HashMap<>(); |
|||
WxUser wxUser = null; |
|||
try { |
|||
wxUser = wxUserService.login(wxLoginUser); |
|||
} catch (Exception e) { |
|||
log.warn(e.getMessage()); |
|||
} |
|||
if(wxUser==null){ |
|||
Assert.throwEx(HttpCode.WX_ERR); |
|||
} |
|||
String token = IdUtil.fastSimpleUUID(); |
|||
String key = RedisCacheKeys.LOGIN_KEY_WX + token; |
|||
String wxUserId = wxUser.getId(); |
|||
UmsUser umsUser = umsUserService.getByWxUserId(wxUserId,wxUser); |
|||
LoginUser loginUser = new LoginUser(umsUser.getId()+"",umsUser.getName(),"", |
|||
ServletUtils.getIp(request), LoginUserType.SYS_USER,new ArrayList<>(),null,AuthContextUtils.getTenant()); |
|||
redisService.set(key, JsonUtil.objectToJson(loginUser.loginUserDTO()),2, TimeUnit.DAYS); |
|||
objectMap.put("token",token); |
|||
return R.ok(objectMap); |
|||
} |
|||
|
|||
/** |
|||
* 公众号测试登录 |
|||
*/ |
|||
@PostMapping("/mptest") |
|||
public R<?> login(HttpServletRequest request){ |
|||
Map<String,Object> objectMap = new HashMap<>(); |
|||
String token = TokenUtil.desEncrypt("1"); |
|||
String key = RedisCacheKeys.LOGIN_KEY_WX + token; |
|||
LoginUser loginUser = new LoginUser("1","微信测试用户","", |
|||
ServletUtils.getIp(request), LoginUserType.APP_USER,new ArrayList<>(),null,AuthContextUtils.getTenant()); |
|||
redisService.set(key, JsonUtil.objectToJson(loginUser.loginUserDTO()),2, TimeUnit.DAYS); |
|||
objectMap.put("token",token); |
|||
return R.ok(objectMap); |
|||
} |
|||
|
|||
|
|||
|
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue