Browse Source

init framework

master
Yen 3 years ago
commit
90c71c40e1
  1. 8
      .gitignore
  2. 223
      pom.xml
  3. 51
      src/main/java/com/qs/serve/Application.java
  4. 24
      src/main/java/com/qs/serve/common/config/CustomServletContextInitializer.java
  5. 24
      src/main/java/com/qs/serve/common/config/DevEnvironmentConfig.java
  6. 23
      src/main/java/com/qs/serve/common/config/JwtConfig.java
  7. 54
      src/main/java/com/qs/serve/common/config/MyBatisConfig.java
  8. 70
      src/main/java/com/qs/serve/common/config/RedisConfig.java
  9. 64
      src/main/java/com/qs/serve/common/config/SecurityConfig.java
  10. 68
      src/main/java/com/qs/serve/common/config/SpringMvcConfig.java
  11. 56
      src/main/java/com/qs/serve/common/config/ThreadPoolConfig.java
  12. 32
      src/main/java/com/qs/serve/common/config/properties/PermitProperties.java
  13. 25
      src/main/java/com/qs/serve/common/config/properties/RedisProperties.java
  14. 24
      src/main/java/com/qs/serve/common/config/properties/UploadProperties.java
  15. 210
      src/main/java/com/qs/serve/common/framework/aop/SysLogAspect.java
  16. 21
      src/main/java/com/qs/serve/common/framework/exception/BaseException.java
  17. 25
      src/main/java/com/qs/serve/common/framework/exception/BusinessException.java
  18. 118
      src/main/java/com/qs/serve/common/framework/exception/UnifiedExceptionHandler.java
  19. 60
      src/main/java/com/qs/serve/common/framework/interceptor/ApiAuthInterceptor.java
  20. 66
      src/main/java/com/qs/serve/common/framework/interceptor/LimitSubmitInterceptor.java
  21. 98
      src/main/java/com/qs/serve/common/framework/interceptor/RepeatSubmitInterceptor.java
  22. 36
      src/main/java/com/qs/serve/common/framework/manager/AsyncFactory.java
  23. 52
      src/main/java/com/qs/serve/common/framework/manager/AsyncManager.java
  24. 161
      src/main/java/com/qs/serve/common/framework/mvc/HttpServletRequestFilter.java
  25. 17
      src/main/java/com/qs/serve/common/framework/mybatis/handler/IdGeneratorHandler.java
  26. 36
      src/main/java/com/qs/serve/common/framework/mybatis/handler/SysMetaHandler.java
  27. 58
      src/main/java/com/qs/serve/common/framework/mybatis/handler/SysTenantHandler.java
  28. 50
      src/main/java/com/qs/serve/common/framework/mybatis/handler/meta/JsonStringTypeHandler.java
  29. 74
      src/main/java/com/qs/serve/common/framework/mybatis/handler/meta/SplitStringTypeHandler.java
  30. 115
      src/main/java/com/qs/serve/common/framework/mybatis/join/JoinUtil.java
  31. 36
      src/main/java/com/qs/serve/common/framework/mybatis/join/annotations/BindEntity.java
  32. 14
      src/main/java/com/qs/serve/common/framework/mybatis/join/enums/JoinType.java
  33. 17
      src/main/java/com/qs/serve/common/framework/mybatis/join/model/JoinFieldData.java
  34. 159
      src/main/java/com/qs/serve/common/framework/mybatis/query/AnnotationQueryStorage.java
  35. 32
      src/main/java/com/qs/serve/common/framework/mybatis/query/annotations/QueryWrap.java
  36. 23
      src/main/java/com/qs/serve/common/framework/mybatis/query/model/QueryFieldData.java
  37. 25
      src/main/java/com/qs/serve/common/framework/mybatis/query/model/QueryFieldDataValue.java
  38. 40
      src/main/java/com/qs/serve/common/framework/mybatis/query/model/QueryType.java
  39. 309
      src/main/java/com/qs/serve/common/framework/redis/RedisService.java
  40. 192
      src/main/java/com/qs/serve/common/framework/security/filter/SecurityRequestFilter.java
  41. 22
      src/main/java/com/qs/serve/common/framework/security/handler/SecurityAccessDeniedHandler.java
  42. 22
      src/main/java/com/qs/serve/common/framework/security/handler/SecurityAuthenticationFailureHandler.java
  43. 36
      src/main/java/com/qs/serve/common/framework/security/handler/SecurityLogoutHandler.java
  44. 97
      src/main/java/com/qs/serve/common/framework/security/model/LoginUser.java
  45. 37
      src/main/java/com/qs/serve/common/framework/security/model/LoginUserDTO.java
  46. 34
      src/main/java/com/qs/serve/common/framework/security/model/LoginUserType.java
  47. 31
      src/main/java/com/qs/serve/common/framework/security/service/SysUserDetailsServiceImpl.java
  48. 28
      src/main/java/com/qs/serve/common/framework/security/util/SecurityPermitUtil.java
  49. 34
      src/main/java/com/qs/serve/common/model/HttpResponsePrintUtil.java
  50. 25
      src/main/java/com/qs/serve/common/model/annotation/LimitSubmit.java
  51. 20
      src/main/java/com/qs/serve/common/model/annotation/RepeatSubmit.java
  52. 35
      src/main/java/com/qs/serve/common/model/annotation/SysLog.java
  53. 10
      src/main/java/com/qs/serve/common/model/consts/DateConst.java
  54. 40
      src/main/java/com/qs/serve/common/model/consts/GySysConst.java
  55. 32
      src/main/java/com/qs/serve/common/model/consts/OssConst.java
  56. 30
      src/main/java/com/qs/serve/common/model/consts/RedisCacheKeys.java
  57. 12
      src/main/java/com/qs/serve/common/model/consts/RedisListenerKeys.java
  58. 46
      src/main/java/com/qs/serve/common/model/dto/PageVo.java
  59. 173
      src/main/java/com/qs/serve/common/model/dto/R.java
  60. 23
      src/main/java/com/qs/serve/common/model/dto/SimpleKeyValue.java
  61. 30
      src/main/java/com/qs/serve/common/model/dto/TreeNode.java
  62. 49
      src/main/java/com/qs/serve/common/model/enums/BizType.java
  63. 19
      src/main/java/com/qs/serve/common/model/enums/ExcelFilePath.java
  64. 44
      src/main/java/com/qs/serve/common/model/enums/HttpCode.java
  65. 21
      src/main/java/com/qs/serve/common/model/enums/InterType.java
  66. 35
      src/main/java/com/qs/serve/common/model/enums/PolicyType.java
  67. 22
      src/main/java/com/qs/serve/common/model/enums/SystemModule.java
  68. 8
      src/main/java/com/qs/serve/common/model/group/EditGroup.java
  69. 8
      src/main/java/com/qs/serve/common/model/group/SaveGroup.java
  70. 8
      src/main/java/com/qs/serve/common/model/group/SearchGroup.java
  71. 26
      src/main/java/com/qs/serve/common/service/RedisBaseListenerImpl.java
  72. 33
      src/main/java/com/qs/serve/common/service/RedisServiceListener.java
  73. 28
      src/main/java/com/qs/serve/common/util/Assert.java
  74. 109
      src/main/java/com/qs/serve/common/util/AuthContextUtils.java
  75. 90
      src/main/java/com/qs/serve/common/util/CollectionUtil.java
  76. 95
      src/main/java/com/qs/serve/common/util/CopierUtil.java
  77. 98
      src/main/java/com/qs/serve/common/util/DateUtils.java
  78. 51
      src/main/java/com/qs/serve/common/util/IdUtil.java
  79. 99
      src/main/java/com/qs/serve/common/util/JsonUtil.java
  80. 82
      src/main/java/com/qs/serve/common/util/JwtUtils.java
  81. 84
      src/main/java/com/qs/serve/common/util/MinioUtil.java
  82. 60
      src/main/java/com/qs/serve/common/util/NanoIdUtils.java
  83. 169
      src/main/java/com/qs/serve/common/util/PageUtil.java
  84. 129
      src/main/java/com/qs/serve/common/util/ServletUtils.java
  85. 50
      src/main/java/com/qs/serve/common/util/SmsReflectUtil.java
  86. 148
      src/main/java/com/qs/serve/common/util/SpringUtils.java
  87. 50
      src/main/java/com/qs/serve/common/util/StringUtils.java
  88. 75
      src/main/java/com/qs/serve/common/util/ThreadsUtils.java
  89. 46
      src/main/java/com/qs/serve/common/util/TokenUtil.java
  90. 74
      src/main/java/com/qs/serve/common/util/TreeUtil.java
  91. 53
      src/main/java/com/qs/serve/common/util/UploadUtil.java
  92. 62
      src/main/java/com/qs/serve/common/util/WarpUtil.java
  93. 109
      src/main/java/com/qs/serve/common/util/WordUtil.java
  94. 24
      src/main/java/com/qs/serve/common/util/model/DateFormatString.java
  95. 125
      src/main/java/com/qs/serve/common/util/model/DesUtils.java
  96. 138
      src/main/java/com/qs/serve/controller/AdminPortalController.java
  97. 26
      src/main/java/com/qs/serve/controller/CommonApi.java
  98. 103
      src/main/java/com/qs/serve/controller/CommonController.java
  99. 129
      src/main/java/com/qs/serve/controller/WxMpPortalApi.java
  100. 97
      src/main/java/com/qs/serve/controller/WxSvcLoginApi.java

8
.gitignore

@ -0,0 +1,8 @@
.idea/
logs/
target/
gyoa-java.iml
*.http
*.env.json
*.json
/src/test/

223
pom.xml

@ -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>

51
src/main/java/com/qs/serve/Application.java

@ -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"));
}
}

24
src/main/java/com/qs/serve/common/config/CustomServletContextInitializer.java

@ -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));
}
}

24
src/main/java/com/qs/serve/common/config/DevEnvironmentConfig.java

@ -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;
}
}

23
src/main/java/com/qs/serve/common/config/JwtConfig.java

@ -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";
}

54
src/main/java/com/qs/serve/common/config/MyBatisConfig.java

@ -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);
}
}

70
src/main/java/com/qs/serve/common/config/RedisConfig.java

@ -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;
}
}

64
src/main/java/com/qs/serve/common/config/SecurityConfig.java

@ -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;
}
}

68
src/main/java/com/qs/serve/common/config/SpringMvcConfig.java

@ -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;
}
}

56
src/main/java/com/qs/serve/common/config/ThreadPoolConfig.java

@ -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);
}
};
}
}

32
src/main/java/com/qs/serve/common/config/properties/PermitProperties.java

@ -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<>();
}

25
src/main/java/com/qs/serve/common/config/properties/RedisProperties.java

@ -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;
}

24
src/main/java/com/qs/serve/common/config/properties/UploadProperties.java

@ -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;
}

210
src/main/java/com/qs/serve/common/framework/aop/SysLogAspect.java

@ -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;
}
}

21
src/main/java/com/qs/serve/common/framework/exception/BaseException.java

@ -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();
}

25
src/main/java/com/qs/serve/common/framework/exception/BusinessException.java

@ -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;
}
}

118
src/main/java/com/qs/serve/common/framework/exception/UnifiedExceptionHandler.java

@ -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();
}
}

60
src/main/java/com/qs/serve/common/framework/interceptor/ApiAuthInterceptor.java

@ -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;
}
}

66
src/main/java/com/qs/serve/common/framework/interceptor/LimitSubmitInterceptor.java

@ -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;
}
}

98
src/main/java/com/qs/serve/common/framework/interceptor/RepeatSubmitInterceptor.java

@ -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;
}
}

36
src/main/java/com/qs/serve/common/framework/manager/AsyncFactory.java

@ -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);
}
};
}
}

52
src/main/java/com/qs/serve/common/framework/manager/AsyncManager.java

@ -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);
}
}

161
src/main/java/com/qs/serve/common/framework/mvc/HttpServletRequestFilter.java

@ -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();
}
};
}
}
}

17
src/main/java/com/qs/serve/common/framework/mybatis/handler/IdGeneratorHandler.java

@ -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();
}
}

36
src/main/java/com/qs/serve/common/framework/mybatis/handler/SysMetaHandler.java

@ -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);
}
}

58
src/main/java/com/qs/serve/common/framework/mybatis/handler/SysTenantHandler.java

@ -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;
}
}

50
src/main/java/com/qs/serve/common/framework/mybatis/handler/meta/JsonStringTypeHandler.java

@ -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);
}
}

74
src/main/java/com/qs/serve/common/framework/mybatis/handler/meta/SplitStringTypeHandler.java

@ -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&&parameter.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&&parameter.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;
}
}

115
src/main/java/com/qs/serve/common/framework/mybatis/join/JoinUtil.java

@ -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;
}
}

36
src/main/java/com/qs/serve/common/framework/mybatis/join/annotations/BindEntity.java

@ -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;
}

14
src/main/java/com/qs/serve/common/framework/mybatis/join/enums/JoinType.java

@ -0,0 +1,14 @@
package com.qs.serve.common.framework.mybatis.join.enums;
/**
* @author YenHex
* @since 2022/5/24
*/
public enum JoinType {
/**
* 返回类型
*/
Object,List
}

17
src/main/java/com/qs/serve/common/framework/mybatis/join/model/JoinFieldData.java

@ -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;
}

159
src/main/java/com/qs/serve/common/framework/mybatis/query/AnnotationQueryStorage.java

@ -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); }
}
}
}

32
src/main/java/com/qs/serve/common/framework/mybatis/query/annotations/QueryWrap.java

@ -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;
}

23
src/main/java/com/qs/serve/common/framework/mybatis/query/model/QueryFieldData.java

@ -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;
}

25
src/main/java/com/qs/serve/common/framework/mybatis/query/model/QueryFieldDataValue.java

@ -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;
}

40
src/main/java/com/qs/serve/common/framework/mybatis/query/model/QueryType.java

@ -0,0 +1,40 @@
package com.qs.serve.common.framework.mybatis.query.model;
/**
* @author JcYen
*/
public enum QueryType {
/**
*
*/
NONE,
/**
* 通用的equalsIntegerLongString
*/
EQ,
NE,
/**
* 比较修饰符修饰日期数值类型Integer,Long,Double,BigDecimal)
*/
LT,
LE,
GT,
GE,
/**
* 修饰List<Long>
*/
In,
NotIn,
/**
* 修饰字符串
*/
Like,
RightLike,
NotLike,
}

309
src/main/java/com/qs/serve/common/framework/redis/RedisService.java

@ -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);
}
}

192
src/main/java/com/qs/serve/common/framework/security/filter/SecurityRequestFilter.java

@ -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;
}
}
}

22
src/main/java/com/qs/serve/common/framework/security/handler/SecurityAccessDeniedHandler.java

@ -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,"权限不足");
}
}

22
src/main/java/com/qs/serve/common/framework/security/handler/SecurityAuthenticationFailureHandler.java

@ -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,"登录信息已过期或失效");
}
}

36
src/main/java/com/qs/serve/common/framework/security/handler/SecurityLogoutHandler.java

@ -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();
}
}
}

97
src/main/java/com/qs/serve/common/framework/security/model/LoginUser.java

@ -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");
}
}

37
src/main/java/com/qs/serve/common/framework/security/model/LoginUserDTO.java

@ -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);
}
}

34
src/main/java/com/qs/serve/common/framework/security/model/LoginUserType.java

@ -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;
}
}

31
src/main/java/com/qs/serve/common/framework/security/service/SysUserDetailsServiceImpl.java

@ -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);
}
}

28
src/main/java/com/qs/serve/common/framework/security/util/SecurityPermitUtil.java

@ -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;
}
}

34
src/main/java/com/qs/serve/common/model/HttpResponsePrintUtil.java

@ -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();
}
}

25
src/main/java/com/qs/serve/common/model/annotation/LimitSubmit.java

@ -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 "";
}

20
src/main/java/com/qs/serve/common/model/annotation/RepeatSubmit.java

@ -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 "不允许重复提交,请稍后再试";
}

35
src/main/java/com/qs/serve/common/model/annotation/SysLog.java

@ -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;
}

10
src/main/java/com/qs/serve/common/model/consts/DateConst.java

@ -0,0 +1,10 @@
package com.qs.serve.common.model.consts;
/**
* @author YenHex
* @since 2022/2/24
*/
public class DateConst {
}

40
src/main/java/com/qs/serve/common/model/consts/GySysConst.java

@ -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";
}

32
src/main/java/com/qs/serve/common/model/consts/OssConst.java

@ -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 = "未知";
}

30
src/main/java/com/qs/serve/common/model/consts/RedisCacheKeys.java

@ -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_:";
}

12
src/main/java/com/qs/serve/common/model/consts/RedisListenerKeys.java

@ -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";
}

46
src/main/java/com/qs/serve/common/model/dto/PageVo.java

@ -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);
}
}

173
src/main/java/com/qs/serve/common/model/dto/R.java

@ -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);
}
}

23
src/main/java/com/qs/serve/common/model/dto/SimpleKeyValue.java

@ -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;
}

30
src/main/java/com/qs/serve/common/model/dto/TreeNode.java

@ -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;
}
}

49
src/main/java/com/qs/serve/common/model/enums/BizType.java

@ -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;
}

19
src/main/java/com/qs/serve/common/model/enums/ExcelFilePath.java

@ -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;
}

44
src/main/java/com/qs/serve/common/model/enums/HttpCode.java

@ -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;
}

21
src/main/java/com/qs/serve/common/model/enums/InterType.java

@ -0,0 +1,21 @@
package com.qs.serve.common.model.enums;
/**
* @author YenHex
* @since 2022/3/8
*/
public enum InterType {
/**
* 前台接口
*/
API,
/**
* 后台接口
*/
ADMIN
}

35
src/main/java/com/qs/serve/common/model/enums/PolicyType.java

@ -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;
}

22
src/main/java/com/qs/serve/common/model/enums/SystemModule.java

@ -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;
}

8
src/main/java/com/qs/serve/common/model/group/EditGroup.java

@ -0,0 +1,8 @@
package com.qs.serve.common.model.group;
/**
* @author YenHex
* @since 2022/5/16
*/
public interface EditGroup {
}

8
src/main/java/com/qs/serve/common/model/group/SaveGroup.java

@ -0,0 +1,8 @@
package com.qs.serve.common.model.group;
/**
* @author YenHex
* @since 2022/5/16
*/
public interface SaveGroup {
}

8
src/main/java/com/qs/serve/common/model/group/SearchGroup.java

@ -0,0 +1,8 @@
package com.qs.serve.common.model.group;
/**
* @author YenHex
* @since 2022/5/16
*/
public interface SearchGroup {
}

26
src/main/java/com/qs/serve/common/service/RedisBaseListenerImpl.java

@ -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]);
}
}

33
src/main/java/com/qs/serve/common/service/RedisServiceListener.java

@ -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);
}

28
src/main/java/com/qs/serve/common/util/Assert.java

@ -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);
}
}

109
src/main/java/com/qs/serve/common/util/AuthContextUtils.java

@ -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;
}
}

90
src/main/java/com/qs/serve/common/util/CollectionUtil.java

@ -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;
}
}

95
src/main/java/com/qs/serve/common/util/CopierUtil.java

@ -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;
}
}

98
src/main/java/com/qs/serve/common/util/DateUtils.java

@ -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);
}
}

51
src/main/java/com/qs/serve/common/util/IdUtil.java

@ -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();
}
}

99
src/main/java/com/qs/serve/common/util/JsonUtil.java

@ -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;
}
}

82
src/main/java/com/qs/serve/common/util/JwtUtils.java

@ -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("调用凭证无效");
}
}
}

84
src/main/java/com/qs/serve/common/util/MinioUtil.java

@ -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();
}
}

60
src/main/java/com/qs/serve/common/util/NanoIdUtils.java

@ -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());
}
}
}

169
src/main/java/com/qs/serve/common/util/PageUtil.java

@ -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;
}
}
}

129
src/main/java/com/qs/serve/common/util/ServletUtils.java

@ -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;
}
}

50
src/main/java/com/qs/serve/common/util/SmsReflectUtil.java

@ -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;
}
}

148
src/main/java/com/qs/serve/common/util/SpringUtils.java

@ -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;
}
}

50
src/main/java/com/qs/serve/common/util/StringUtils.java

@ -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&&params[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);
}
}

75
src/main/java/com/qs/serve/common/util/ThreadsUtils.java

@ -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);
}
}
}

46
src/main/java/com/qs/serve/common/util/TokenUtil.java

@ -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;
}
}

74
src/main/java/com/qs/serve/common/util/TreeUtil.java

@ -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;
}
}

53
src/main/java/com/qs/serve/common/util/UploadUtil.java

@ -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;
}
}

62
src/main/java/com/qs/serve/common/util/WarpUtil.java

@ -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);
}
}
}

109
src/main/java/com/qs/serve/common/util/WordUtil.java

@ -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);
}
}

24
src/main/java/com/qs/serve/common/util/model/DateFormatString.java

@ -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;
}

125
src/main/java/com/qs/serve/common/util/model/DesUtils.java

@ -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();
}
}
}

138
src/main/java/com/qs/serve/controller/AdminPortalController.java

@ -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();
}
}

26
src/main/java/com/qs/serve/controller/CommonApi.java

@ -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 {
}

103
src/main/java/com/qs/serve/controller/CommonController.java

@ -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);
}
}

129
src/main/java/com/qs/serve/controller/WxMpPortalApi.java

@ -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;
}
}

97
src/main/java/com/qs/serve/controller/WxSvcLoginApi.java

@ -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…
Cancel
Save