commit
e8d399ac19
436 changed files with 31446 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||
.idea/ |
|||
logs/ |
|||
target/ |
|||
gyoa-java.iml |
|||
*.http |
|||
*.env.json |
|||
*.json |
|||
/src/test/ |
|||
questionnaire.iml |
@ -0,0 +1,241 @@ |
|||
<?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>company-svc</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.5.2</mybatis-plus.version> |
|||
<skipTests>true</skipTests> |
|||
<wx.java.version>4.2.0</wx.java.version> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
|
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.datatype</groupId> |
|||
<artifactId>jackson-datatype-jsr310</artifactId> |
|||
<version>2.13.0</version> |
|||
</dependency> |
|||
|
|||
<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.4</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.mybatis</groupId> |
|||
<artifactId>mybatis-spring</artifactId> |
|||
</exclusion> |
|||
<exclusion> |
|||
<groupId>org.mybatis</groupId> |
|||
<artifactId>mybatis</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> |
|||
<version>${mybatis-plus.version}</version> |
|||
</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>com.microsoft.sqlserver</groupId> |
|||
<artifactId>mssql-jdbc</artifactId> |
|||
<version>8.4.1.jre8</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>com.github.binarywang</groupId> |
|||
<artifactId>weixin-java-cp</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.aliyun.oss</groupId> |
|||
<artifactId>aliyun-sdk-oss</artifactId> |
|||
<version>3.16.1</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.cloud</groupId> |
|||
<artifactId>spring-cloud-starter-openfeign</artifactId> |
|||
<version>3.1.5</version> |
|||
</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> |
|||
</build> |
|||
<repositories> |
|||
<repository> |
|||
<id>public</id> |
|||
<name>aliyun nexus</name> |
|||
<url>https://maven.aliyun.com/repository/public/</url> |
|||
<releases> |
|||
<enabled>true</enabled> |
|||
</releases> |
|||
</repository> |
|||
</repositories> |
|||
|
|||
<pluginRepositories> |
|||
<pluginRepository> |
|||
<id>public</id> |
|||
<name>aliyun nexus</name> |
|||
<url>https://maven.aliyun.com/repository/public/</url> |
|||
<releases> |
|||
<enabled>true</enabled> |
|||
</releases> |
|||
<snapshots> |
|||
<enabled>false</enabled> |
|||
</snapshots> |
|||
</pluginRepository> |
|||
</pluginRepositories> |
|||
</project> |
@ -0,0 +1,69 @@ |
|||
package com.qs.serve; |
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
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.core.env.Environment; |
|||
import org.springframework.scheduling.annotation.EnableAsync; |
|||
import org.springframework.scheduling.annotation.EnableScheduling; |
|||
import org.springframework.stereotype.Indexed; |
|||
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 |
|||
*/ |
|||
@Indexed |
|||
@EnableAsync |
|||
@EnableScheduling |
|||
@EnableTransactionManagement |
|||
@EnableCaching |
|||
@SpringBootApplication |
|||
public class Application extends SpringBootServletInitializer { |
|||
|
|||
@Value("${project.dev}") |
|||
String dev; |
|||
|
|||
public static void main(String[] args) { |
|||
long start = System.currentTimeMillis(); |
|||
SpringApplication.run(Application.class,args); |
|||
long end = System.currentTimeMillis(); |
|||
double diff = (end-start)/1000.0; |
|||
System.out.println("启动时间:"+diff |
|||
+"\n ____ _ _ __ _ _ \n" + |
|||
" / ___| | |_ __ _ _ __ | |_ _ _ _ __ ___ _ _ ___ ___ ___ ___ ___ / _| _ _ | || |\n" + |
|||
" \\___ \\ | __|/ _` || '__|| __| | | | || '_ \\ / __|| | | | / __|/ __|/ _ \\/ __|/ __|| |_ | | | || || |\n" + |
|||
" ___) || |_| (_| || | | |_ | |_| || |_) | \\__ \\| |_| || (__| (__| __/\\__ \\\\__ \\| _|| |_| || ||_|\n" + |
|||
" |____/ \\__|\\__,_||_| \\__| \\__,_|| .__/ |___/ \\__,_| \\___|\\___|\\___||___/|___/|_| \\__,_||_|(_)\n" + |
|||
" |_| \n"); |
|||
} |
|||
|
|||
@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")); |
|||
DevEnvironmentConfig.openDevEnv(dev.equals("true")); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import org.springframework.boot.web.servlet.ServletContextInitializer; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
import javax.servlet.ServletContext; |
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.SessionTrackingMode; |
|||
import java.util.Collections; |
|||
|
|||
|
|||
/** |
|||
* 禁用jsessionid |
|||
* @author YenHex |
|||
* @since 2022/3/11 |
|||
*/ |
|||
@Configuration |
|||
public class CustomServletContextInitializer implements ServletContextInitializer { |
|||
|
|||
@Override |
|||
public void onStartup(ServletContext servletContext) throws ServletException { |
|||
servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE)); |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.util.Assert; |
|||
import lombok.experimental.UtilityClass; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/11 |
|||
*/ |
|||
@Slf4j |
|||
@UtilityClass |
|||
public class DevEnvironmentConfig { |
|||
|
|||
private static Boolean devEnv = null; |
|||
public static void openDevEnv(Boolean open){ |
|||
log.warn("环境变量:devEnv=>>> "+open); |
|||
devEnv = open; |
|||
} |
|||
|
|||
public static Boolean isDev(){ |
|||
if(devEnv==null){ |
|||
Assert.throwEx("资源加载中..."); |
|||
} |
|||
return devEnv; |
|||
} |
|||
|
|||
/** |
|||
* 开启账套 |
|||
*/ |
|||
public final static boolean OPEN_TENANT_BOOK = true; |
|||
|
|||
} |
@ -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 = 7; |
|||
|
|||
private final String iss = "KP_ISS"; |
|||
|
|||
private final String secret = "QiShenAa18n9VUcCxaSeSqLtFvsSCaRoVPKtBLaYxB0123456"; |
|||
|
|||
} |
@ -0,0 +1,56 @@ |
|||
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", |
|||
"sys_request_log", |
|||
"bir_budget_target" |
|||
}; |
|||
SysTenantHandler sysTenantHandler = new SysTenantHandler(ignoreTable); |
|||
return new TenantLineInnerInterceptor(sysTenantHandler); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,70 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
import org.springframework.cache.CacheManager; |
|||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
|||
import org.springframework.cache.annotation.EnableCaching; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.cache.RedisCacheConfiguration; |
|||
import org.springframework.data.redis.cache.RedisCacheManager; |
|||
import org.springframework.data.redis.cache.RedisCacheWriter; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
|||
import org.springframework.data.redis.serializer.*; |
|||
import org.springframework.data.redis.serializer.RedisSerializationContext; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
import java.time.Duration; |
|||
|
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@EnableCaching |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class RedisConfig extends CachingConfigurerSupport{ |
|||
|
|||
private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer(); |
|||
private static final GenericJackson2JsonRedisSerializer JACKSON__SERIALIZER = new GenericJackson2JsonRedisSerializer(); |
|||
|
|||
@Bean |
|||
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { |
|||
//设置缓存过期时间
|
|||
RedisCacheConfiguration redisCacheCfg=RedisCacheConfiguration.defaultCacheConfig() |
|||
.entryTtl(Duration.ofHours(1)) |
|||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(STRING_SERIALIZER)) |
|||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON__SERIALIZER)); |
|||
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) |
|||
.cacheDefaults(redisCacheCfg) |
|||
.build(); |
|||
} |
|||
|
|||
@Bean |
|||
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory){ |
|||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
|||
container.setConnectionFactory(connectionFactory); |
|||
return container; |
|||
} |
|||
|
|||
@Bean |
|||
@ConditionalOnMissingBean(name = "redisTemplate") |
|||
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){ |
|||
// 配置redisTemplate
|
|||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); |
|||
redisTemplate.setConnectionFactory(factory); |
|||
// key序列化
|
|||
redisTemplate.setKeySerializer(STRING_SERIALIZER); |
|||
// value序列化
|
|||
redisTemplate.setValueSerializer(JACKSON__SERIALIZER); |
|||
// Hash key序列化
|
|||
redisTemplate.setHashKeySerializer(STRING_SERIALIZER); |
|||
// Hash value序列化
|
|||
redisTemplate.setHashValueSerializer(JACKSON__SERIALIZER); |
|||
redisTemplate.afterPropertiesSet(); |
|||
return redisTemplate; |
|||
} |
|||
} |
@ -0,0 +1,64 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.config.properties.PermitProperties; |
|||
import com.qs.serve.common.framework.security.filter.SecurityRequestFilter; |
|||
import com.qs.serve.common.framework.security.handler.SecurityAccessDeniedHandler; |
|||
import com.qs.serve.common.framework.security.handler.SecurityLogoutHandler; |
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.http.HttpMethod; |
|||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
|||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
|||
import org.springframework.security.config.http.SessionCreationPolicy; |
|||
import org.springframework.security.core.userdetails.UserDetailsService; |
|||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2021/6/13 |
|||
*/ |
|||
@Configuration |
|||
@AllArgsConstructor |
|||
@EnableGlobalMethodSecurity(prePostEnabled=true) |
|||
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|||
|
|||
private final SecurityRequestFilter securityRequestFilter; |
|||
private final UserDetailsService userService; |
|||
private final PermitProperties permitProperties; |
|||
|
|||
@Override |
|||
protected void configure(HttpSecurity http) throws Exception { |
|||
List<String> permitUrls = new ArrayList<>(); |
|||
if(permitProperties.getPermits()!=null){ permitUrls.addAll(permitProperties.getPermits()); } |
|||
if(permitProperties.getTenants()!=null){ permitUrls.addAll(permitProperties.getTenants()); } |
|||
permitUrls = permitUrls.stream().distinct().collect(Collectors.toList()); |
|||
String[] permit_urls = new String[permitUrls.size()]; |
|||
permitUrls.toArray(permit_urls); |
|||
http.csrf().disable(); |
|||
http.authorizeRequests() |
|||
.antMatchers(HttpMethod.OPTIONS.toString()).permitAll() |
|||
.antMatchers(permit_urls).permitAll() |
|||
//.antMatchers().permitAll() //测试用
|
|||
.anyRequest().authenticated(); |
|||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); |
|||
http.logout() |
|||
.logoutUrl("/portal/logout") |
|||
.addLogoutHandler(new SecurityLogoutHandler()) |
|||
.invalidateHttpSession(true); |
|||
http.headers().cacheControl(); |
|||
http.addFilterBefore(securityRequestFilter, UsernamePasswordAuthenticationFilter.class); |
|||
http.exceptionHandling().accessDeniedHandler(new SecurityAccessDeniedHandler()); |
|||
//http.formLogin().failureHandler(new SecurityAuthenticationFailureHandler());
|
|||
} |
|||
|
|||
@Override |
|||
public UserDetailsService userDetailsService() { |
|||
return userService; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,73 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.config.properties.ProjectProperties; |
|||
import com.qs.serve.common.framework.interceptor.LimitSubmitInterceptor; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.cors.CorsConfiguration; |
|||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
|||
import org.springframework.web.filter.CorsFilter; |
|||
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.ResourceHandlerRegistry; |
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@Slf4j |
|||
@AllArgsConstructor |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class SpringMvcConfig implements WebMvcConfigurer { |
|||
|
|||
private final LimitSubmitInterceptor limitSubmitInterceptor; |
|||
//private final ApiAuthInterceptor apiAuthInterceptor;
|
|||
|
|||
private final ProjectProperties projectProperties; |
|||
|
|||
@Override |
|||
public void addInterceptors(InterceptorRegistry registry) { |
|||
registry.addInterceptor(limitSubmitInterceptor) |
|||
.addPathPatterns("/**"); |
|||
} |
|||
|
|||
@Override |
|||
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|||
log.warn("PcWebLocal==>{}",projectProperties.getPcWebLocal()); |
|||
registry.addResourceHandler("/web/**") |
|||
.addResourceLocations("file:"+projectProperties.getPcWebLocal()); |
|||
registry.addResourceHandler("/static/**") |
|||
.addResourceLocations("file:"+projectProperties.getPcWebLocal()+"static/"); |
|||
} |
|||
|
|||
@Bean |
|||
public CorsFilter corsFilter() { |
|||
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); |
|||
final CorsConfiguration corsConfiguration = new CorsConfiguration(); |
|||
/*是否允许请求带有验证信息*/ |
|||
corsConfiguration.setAllowCredentials(true); |
|||
/*允许访问的客户端域名*/ |
|||
corsConfiguration.addAllowedOrigin("*"); |
|||
/*允许服务端访问的客户端请求头*/ |
|||
corsConfiguration.addAllowedHeader("*"); |
|||
/*允许访问的方法名,GET POST等*/ |
|||
corsConfiguration.addAllowedMethod("*"); |
|||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); |
|||
return new CorsFilter(urlBasedCorsConfigurationSource); |
|||
} |
|||
|
|||
//@Bean
|
|||
public MultipartResolver multipartResolver(){ |
|||
CommonsMultipartResolver resolver = new CommonsMultipartResolver(); |
|||
resolver.setMaxInMemorySize(5120); |
|||
resolver.setMaxInMemorySize(300 * 1024 * 1024); |
|||
resolver.setDefaultEncoding("UTF-8"); |
|||
return resolver; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,56 @@ |
|||
package com.qs.serve.common.config; |
|||
|
|||
import com.qs.serve.common.util.ThreadsUtils; |
|||
import org.apache.commons.lang3.concurrent.BasicThreadFactory; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
|||
|
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
|
|||
|
|||
/** |
|||
* 线程池配置 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@Configuration |
|||
public class ThreadPoolConfig { |
|||
|
|||
/** 核心线程池大小 */ |
|||
private final int corePoolSize = 50; |
|||
|
|||
|
|||
@Bean(name = "threadPoolTaskExecutor") |
|||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() { |
|||
// 最大可创建的线程数
|
|||
int maxPoolSize = 200; |
|||
// 队列最大长度
|
|||
int queueCapacity = 1000; |
|||
// 线程池维护线程所允许的空闲时间
|
|||
int keepAliveSeconds = 300; |
|||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
|||
executor.setMaxPoolSize(maxPoolSize); |
|||
executor.setCorePoolSize(corePoolSize); |
|||
executor.setQueueCapacity(queueCapacity); |
|||
executor.setKeepAliveSeconds(keepAliveSeconds); |
|||
// 线程池对拒绝任务(无线程可用)的处理策略
|
|||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
return executor; |
|||
} |
|||
|
|||
/** 执行周期性或定时任务 */ |
|||
@Bean(name = "scheduledExecutorService") |
|||
protected ScheduledExecutorService scheduledExecutorService() { |
|||
return new ScheduledThreadPoolExecutor(corePoolSize, |
|||
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { |
|||
@Override |
|||
protected void afterExecute(Runnable r, Throwable t) { |
|||
super.afterExecute(r, t); |
|||
ThreadsUtils.printException(r, t); |
|||
} |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,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 |
|||
* @Date: 2021/3/3 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "aliyun.oss") |
|||
public class AliYunOssProperties { |
|||
|
|||
private String bucket; |
|||
private String accessKeyId; |
|||
private String accessKeySecret; |
|||
private String endpoint; |
|||
private String prefix; |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2024/3/27 |
|||
*/ |
|||
public class AuthUrlConst { |
|||
|
|||
public final static String[] AUTH_ALLOW_URL = new String[]{ |
|||
"bir/" |
|||
}; |
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @Author: YenHex |
|||
* @Date: 2021/3/3 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "project.ignore-url") |
|||
public class PermitProperties { |
|||
|
|||
/** |
|||
* 可通过路径(适用于回调场景、无拦截场景等) |
|||
*/ |
|||
private List<String> permits = new ArrayList<>(); |
|||
|
|||
/** |
|||
* 默认租户拦截(适用于无需登录接口,但有租户拦截) |
|||
*/ |
|||
private List<String> tenants = new ArrayList<>(); |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
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 2023/9/7 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "project.apis") |
|||
public class ProjectApisProperties { |
|||
|
|||
/** 支付接口 */ |
|||
private String costToPay; |
|||
|
|||
/** 同步核销的支付状态 */ |
|||
private String checkPayStatus; |
|||
|
|||
/** 调度系统-发货单 */ |
|||
private String diaoduPlanOrder; |
|||
|
|||
/** 嘉士利接口,spu转换sku */ |
|||
private String spuToSku; |
|||
|
|||
/** |
|||
* 原是生成销路通apt的中间表,替换伟成的接口直接生成 |
|||
*/ |
|||
private String policySyncCate; |
|||
|
|||
private String policySyncInv; |
|||
} |
@ -0,0 +1,31 @@ |
|||
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 |
|||
* @Date: 2021/3/3 |
|||
* @Version: 1.0 |
|||
**/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "project") |
|||
public class ProjectProperties { |
|||
|
|||
private String closeAuth; |
|||
|
|||
private String birService; |
|||
|
|||
private String birServiceUrl; |
|||
|
|||
private String hostUrl; |
|||
|
|||
private String webUrl; |
|||
|
|||
private String pcWebLocal; |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
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 2024/6/18 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.qisheng") |
|||
public class QiShengDsProperties { |
|||
|
|||
private String url; |
|||
private String username; |
|||
private String password; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/7 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "spring.redis") |
|||
public class RedisProperties { |
|||
|
|||
private String host; |
|||
|
|||
private Integer port; |
|||
|
|||
private String password; |
|||
|
|||
private Integer database; |
|||
} |
@ -0,0 +1,28 @@ |
|||
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.seeyon") |
|||
public class SeeYonProperties { |
|||
|
|||
private Boolean enable; |
|||
|
|||
private String url; |
|||
|
|||
private String extApi; |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.common.config.properties; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/14 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
@Component |
|||
@ConfigurationProperties(prefix = "project.upload") |
|||
public class UploadProperties { |
|||
|
|||
private String tempPath; |
|||
|
|||
private String proxyUrl; |
|||
|
|||
private String logicalPath; |
|||
|
|||
} |
@ -0,0 +1,4 @@ |
|||
package com.qs.serve.common.framework.annotations; |
|||
|
|||
public @interface TagField { |
|||
} |
@ -0,0 +1,215 @@ |
|||
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(); |
|||
//忽略GET
|
|||
if(request.getMethod().equals("GET")){ |
|||
return; |
|||
} |
|||
// 获取当前的用户
|
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
SysOperationLog operLog = new SysOperationLog(); |
|||
operLog.setReqIp(ServletUtils.getIp(request)); |
|||
operLog.setReqMethod(request.getMethod()); |
|||
operLog.setJsonResult(JSON.toJSONString(jsonResult)); |
|||
operLog.setReqUrl(request.getRequestURI()); |
|||
operLog.setUserAgent(request.getHeader(GySysConst.USER_AGENT_PROP)); |
|||
if (loginUser != null) { |
|||
operLog.setUserId(loginUser.getUserId()); |
|||
operLog.setUserType(loginUser.getTypeFlag().getCode()); |
|||
SysUser sysUser = sysUserService.getById(loginUser.getUserId()); |
|||
if(sysUser!=null){ |
|||
operLog.setCreatorName(sysUser.getName()); |
|||
} |
|||
} |
|||
if (e != null) { |
|||
operLog.setErrMsg(StringUtils.substring(e.getMessage(), 0, 2500)); |
|||
} |
|||
//耗时
|
|||
Long startTime = startTimeIndex.get(); |
|||
if(startTime!=null){ |
|||
operLog.setElapsedTime(System.currentTimeMillis()-startTime); |
|||
}else { |
|||
operLog.setElapsedTime(0L); |
|||
} |
|||
String className = joinPoint.getTarget().getClass().getName(); |
|||
String methodName = joinPoint.getSignature().getName(); |
|||
operLog.setCodeMethod(className + "." + methodName + "()"); |
|||
// 处理设置注解上的参数
|
|||
getControllerMethodDescription(joinPoint, controllerLog, operLog); |
|||
operLog.setTenantId(AuthContextUtils.getTenant()); |
|||
// 保存数据库
|
|||
AsyncManager.me().execute(AsyncFactory.saveOperationLog(operLog)); |
|||
} catch (Exception exp) { |
|||
// 记录本地异常日志
|
|||
log.error("==前置通知异常==\n异常信息:{}", exp.getMessage()); |
|||
exp.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取注解中对方法的描述信息 |
|||
* @param sysLog 日志 |
|||
* @param operationLog 操作日志 |
|||
* @throws Exception |
|||
*/ |
|||
public void getControllerMethodDescription(JoinPoint joinPoint, SysLog sysLog, SysOperationLog operationLog) throws Exception { |
|||
operationLog.setBizType(sysLog.biz().getValue()); |
|||
operationLog.setInterType(sysLog.inter().name()); |
|||
operationLog.setTitle(sysLog.title()); |
|||
operationLog.setModule(sysLog.module().getVale()); |
|||
operationLog.setOptDesc(sysLog.desc()); |
|||
if (sysLog.saveReqParam()) { |
|||
setRequestValue(joinPoint, operationLog); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取请求的参数,放到log中 |
|||
* @param operationLog 操作日志 |
|||
* @throws Exception 异常 |
|||
*/ |
|||
private void setRequestValue(JoinPoint joinPoint, SysOperationLog operationLog) throws Exception { |
|||
String requestMethod = operationLog.getReqMethod(); |
|||
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { |
|||
String params = argsArrayToString(joinPoint.getArgs()); |
|||
operationLog.setReqParam(StringUtils.substring(params, 0, 2500)); |
|||
} else { |
|||
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); |
|||
operationLog.setReqParam(StringUtils.substring(paramsMap.toString(), 0, 2500)); |
|||
} |
|||
} |
|||
|
|||
/** 是否存在注解,如果存在就获取 */ |
|||
private SysLog getAnnotationLog(JoinPoint joinPoint){ |
|||
Signature signature = joinPoint.getSignature(); |
|||
MethodSignature methodSignature = (MethodSignature) signature; |
|||
Method method = methodSignature.getMethod(); |
|||
if (method != null) { |
|||
return method.getAnnotation(SysLog.class); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 参数拼装 |
|||
*/ |
|||
private String argsArrayToString(Object[] paramsArray) { |
|||
String params = ""; |
|||
if (paramsArray != null && paramsArray.length > 0) { |
|||
for (int i = 0; i < paramsArray.length; i++) { |
|||
if (paramsArray[i]!=null && !isFilterObject(paramsArray[i])) { |
|||
Object jsonObj = JSON.toJSON(paramsArray[i]); |
|||
params += jsonObj.toString() + " "; |
|||
} |
|||
} |
|||
} |
|||
return params.trim(); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否需要过滤的对象。 |
|||
* @param o 对象信息。 |
|||
* @return 如果是需要过滤的对象,则返回true;否则返回false。 |
|||
*/ |
|||
@SuppressWarnings("rawtypes") |
|||
public boolean isFilterObject(final Object o) { |
|||
Class<?> clazz = o.getClass(); |
|||
if (clazz.isArray()) { |
|||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class); |
|||
} else if (Collection.class.isAssignableFrom(clazz)) { |
|||
Collection collection = (Collection) o; |
|||
for (Iterator iter = collection.iterator(); iter.hasNext(); ) { |
|||
return iter.next() instanceof MultipartFile; |
|||
} |
|||
} else if (Map.class.isAssignableFrom(clazz)) { |
|||
Map map = (Map) o; |
|||
for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) { |
|||
Map.Entry entry = (Map.Entry) iter.next(); |
|||
return entry.getValue() instanceof MultipartFile; |
|||
} |
|||
} |
|||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse |
|||
|| o instanceof BindingResult; |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.qs.serve.common.framework.exception; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @date 2020/6/11 |
|||
* @version 1.0 |
|||
*/ |
|||
public abstract class BaseException extends RuntimeException { |
|||
|
|||
public BaseException(String message){ |
|||
super(message); |
|||
} |
|||
|
|||
/** |
|||
* 错误代码 |
|||
* @return |
|||
*/ |
|||
public abstract int getCode(); |
|||
|
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.framework.exception; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2020/6/11 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class BusinessException extends BaseException implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
private Integer status; |
|||
|
|||
public BusinessException(String message,Integer status) { |
|||
super(message); |
|||
this.status = status; |
|||
} |
|||
|
|||
@Override |
|||
public int getCode() { |
|||
return status; |
|||
} |
|||
} |
@ -0,0 +1,148 @@ |
|||
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.HttpMessageNotReadableException; |
|||
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.BeanPropertyBindingResult; |
|||
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 javax.servlet.http.HttpServletRequest; |
|||
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,HttpServletRequest request) { |
|||
log.warn("自定义异常触发【{},{}】,请求地址:{}",e.getCode(),e.getMessage(),request.getRequestURI()); |
|||
return new R(e.getCode(),e.getMessage()); |
|||
} |
|||
|
|||
@ExceptionHandler(value = NullPointerException.class) |
|||
@ResponseBody |
|||
public R handleBindException(NullPointerException e,HttpServletRequest request) { |
|||
e.printStackTrace(); |
|||
log.error("空指针【{}】,请求地址:{}",e.getMessage(),request.getRequestURI()); |
|||
return R.error("数据不存在或被移除"); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
AccessDeniedException.class |
|||
}) |
|||
@ResponseBody |
|||
public R handleAccessException(AccessDeniedException e,HttpServletRequest request) { |
|||
HttpCode hc = HttpCode.FORBIDDEN_403; |
|||
log.error("访问拦截【{}】,请求地址:{}",e.getMessage(),request.getRequestURI()); |
|||
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, |
|||
HttpRequestMethodNotSupportedException.class |
|||
}) |
|||
@ResponseBody |
|||
public R handleDevPrintMsgException(Exception e,HttpServletRequest request) { |
|||
log.error("参数校验异常:{}",e.getMessage()); |
|||
if(DevEnvironmentConfig.isDev()){ |
|||
return R.error(e.getMessage()); |
|||
} |
|||
return R.error(); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
MethodArgumentNotValidException.class, |
|||
}) |
|||
@ResponseBody |
|||
public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e,HttpServletRequest request) { |
|||
log.error("参数校验异常【{}】,请求地址:{}",e.getMessage(),request.getRequestURI()); |
|||
return R.error(e.getBindingResult().getAllErrors().get(0).getDefaultMessage()); |
|||
} |
|||
|
|||
|
|||
@ExceptionHandler(value = DuplicateKeyException.class) |
|||
@ResponseBody |
|||
public R handleDuplicateKeyException(DuplicateKeyException e,HttpServletRequest request) { |
|||
log.error("限制重复数据【{}】,请求地址:{}",e.getMessage(),request.getRequestURI()); |
|||
e.printStackTrace(); |
|||
return R.error("限制重复数据,请联系管理员"); |
|||
} |
|||
|
|||
@ExceptionHandler(value = HttpMessageNotReadableException.class) |
|||
@ResponseBody |
|||
public R handleHttpMessageNotReadableException(HttpMessageNotReadableException e,HttpServletRequest request) { |
|||
log.error("请求参数无法解析【{}】,请求地址:{}",e.getMessage(),request.getRequestURI()); |
|||
return R.error("请求参数无法解析"); |
|||
} |
|||
|
|||
@ExceptionHandler({ |
|||
SQLIntegrityConstraintViolationException.class, |
|||
}) |
|||
@ResponseBody |
|||
public R handleSqlIntegrityException(Exception e,HttpServletRequest request) { |
|||
log.warn("数据库拦截【{}】,请求地址:{}",e.getMessage(),request.getRequestURI()); |
|||
e.printStackTrace(); |
|||
return new R(500,"数据库拦截,请联系管理员"); |
|||
} |
|||
|
|||
/** |
|||
* 强制运行写入成功 |
|||
* @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,HttpServletRequest request) { |
|||
log.error("Servlet异常\n请求地址:{},异常类型:{}\n异常信息:{}\n异常体:",request.getRequestURI(),e.getClass().getSimpleName(),e.getMessage(),e); |
|||
e.printStackTrace(); |
|||
if(DevEnvironmentConfig.isDev()){return R.error(e.getMessage());} |
|||
return R.error(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,66 @@ |
|||
package com.qs.serve.common.framework.interceptor; |
|||
|
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.model.annotation.LimitSubmit; |
|||
import com.qs.serve.common.util.StringUtils; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.method.HandlerMethod; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import javax.annotation.Resource; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.lang.reflect.Method; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* 防止重复提交拦截器 |
|||
* @author YenHex |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class LimitSubmitInterceptor implements HandlerInterceptor { |
|||
|
|||
public final String REPEAT_Limit_KEY = "repeatLimitKey"; |
|||
|
|||
@Resource |
|||
public RedisService redisService; |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|||
if (handler instanceof HandlerMethod) { |
|||
HandlerMethod handlerMethod = (HandlerMethod) handler; |
|||
Method method = handlerMethod.getMethod(); |
|||
LimitSubmit annotation = method.getAnnotation(LimitSubmit.class); |
|||
if (annotation != null) { |
|||
if (this.LimitSubmit(request, annotation)) { |
|||
if(StringUtils.isNotEmpty(annotation.message())){ |
|||
HttpResponsePrintUtil.print(response,406,annotation.message()); |
|||
}else { |
|||
HttpResponsePrintUtil.print(response,406,"系统处理中,请勿频繁操作"); |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证是否重复提交由子类实现具体的防重复提交的规则 |
|||
* @param request 请求对象 |
|||
* @param annotation 防复注解 |
|||
* @return |
|||
*/ |
|||
public boolean LimitSubmit(HttpServletRequest request, LimitSubmit annotation) throws Exception { |
|||
String key = REPEAT_Limit_KEY+"_"+request.getRequestURI()+"_"+request.getRemoteHost(); |
|||
Object cache = redisService.get(key); |
|||
if(cache!=null){return true;} |
|||
redisService.set(key,"true",annotation.interval(), TimeUnit.MILLISECONDS); |
|||
return false; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.qs.serve.common.framework.interceptor; |
|||
|
|||
import com.alibaba.fastjson.serializer.JSONSerializer; |
|||
import com.alibaba.fastjson.serializer.ObjectSerializer; |
|||
|
|||
import java.io.IOException; |
|||
import java.lang.reflect.Type; |
|||
import java.text.SimpleDateFormat; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2024/7/18 |
|||
*/ |
|||
public class LocalDateTimeFormatSerializer implements ObjectSerializer { |
|||
|
|||
@Override |
|||
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { |
|||
if (object == null) { |
|||
serializer.out.writeNull(); |
|||
return; |
|||
} |
|||
LocalDateTime date = (LocalDateTime) object; |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
|||
String timeString = formatter.format(date); |
|||
serializer.write(timeString); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,98 @@ |
|||
package com.qs.serve.common.framework.interceptor; |
|||
|
|||
import java.lang.reflect.Method; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import javax.servlet.http.HttpSession; |
|||
|
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.model.annotation.RepeatSubmit; |
|||
import com.qs.serve.common.util.JsonUtil; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.method.HandlerMethod; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
/** |
|||
* 防止重复提交拦截器 |
|||
* @author YenHex |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
public class RepeatSubmitInterceptor implements HandlerInterceptor { |
|||
|
|||
public final String REPEAT_PARAMS = "repeatParams"; |
|||
public final String REPEAT_TIME = "repeatTime"; |
|||
public final String SESSION_REPEAT_KEY = "repeatData"; |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|||
if (handler instanceof HandlerMethod) { |
|||
HandlerMethod handlerMethod = (HandlerMethod) handler; |
|||
Method method = handlerMethod.getMethod(); |
|||
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); |
|||
if (annotation != null) { |
|||
if (this.isRepeatSubmit(request, annotation)) { |
|||
HttpResponsePrintUtil.print(response,406,"系统正常处理,请勿重复提交"); |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证是否重复提交由子类实现具体的防重复提交的规则 |
|||
* @param request 请求对象 |
|||
* @param annotation 防复注解 |
|||
* @return |
|||
*/ |
|||
public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception { |
|||
// 本次参数及系统时间
|
|||
String nowParams = JsonUtil.objectToJson(request.getParameterMap()); |
|||
Map<String, Object> nowDataMap = new HashMap<String, Object>(); |
|||
nowDataMap.put(REPEAT_PARAMS, nowParams); |
|||
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); |
|||
// 请求地址(作为存放session的key值)
|
|||
String url = request.getRequestURI(); |
|||
HttpSession session = request.getSession(); |
|||
Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY); |
|||
if (sessionObj != null) { |
|||
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj; |
|||
if (sessionMap.containsKey(url)) { |
|||
Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url); |
|||
if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
Map<String, Object> sessionMap = new HashMap<>(); |
|||
sessionMap.put(url, nowDataMap); |
|||
session.setAttribute(SESSION_REPEAT_KEY, sessionMap); |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 判断参数是否相同 |
|||
*/ |
|||
private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) { |
|||
String nowParams = (String) nowMap.get(REPEAT_PARAMS); |
|||
String preParams = (String) preMap.get(REPEAT_PARAMS); |
|||
return nowParams.equals(preParams); |
|||
} |
|||
|
|||
/** |
|||
* 判断两次间隔时间 |
|||
*/ |
|||
private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) { |
|||
long time1 = (Long) nowMap.get(REPEAT_TIME); |
|||
long time2 = (Long) preMap.get(REPEAT_TIME); |
|||
if ((time1 - time2) < interval) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.qs.serve.common.framework.manager; |
|||
|
|||
|
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.modules.sys.entity.SysOperationLog; |
|||
import com.qs.serve.modules.sys.mapper.SysOperationLogMapper; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.TimerTask; |
|||
|
|||
|
|||
/** |
|||
* 异步工厂 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
@Slf4j |
|||
public class AsyncFactory { |
|||
|
|||
/** |
|||
* 操作日志记录 |
|||
* @param operationLog |
|||
* @return |
|||
*/ |
|||
public static TimerTask saveOperationLog(final SysOperationLog operationLog) { |
|||
return new TimerTask() { |
|||
@Override |
|||
public void run() { |
|||
SpringUtils.getBean(SysOperationLogMapper.class).saveLog(operationLog); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,52 @@ |
|||
package com.qs.serve.common.framework.manager; |
|||
|
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.common.util.ThreadsUtils; |
|||
|
|||
import java.util.TimerTask; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
|
|||
/** |
|||
* 异步任务管理器 |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
public class AsyncManager { |
|||
/** |
|||
* 操作延迟10毫秒 |
|||
*/ |
|||
private final int OPERATE_DELAY_TIME = 10; |
|||
|
|||
/** |
|||
* 异步操作任务调度线程池 |
|||
*/ |
|||
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); |
|||
|
|||
/** |
|||
* 单例模式 |
|||
*/ |
|||
private AsyncManager() { |
|||
} |
|||
|
|||
private static AsyncManager me = new AsyncManager(); |
|||
|
|||
public static AsyncManager me() { |
|||
return me; |
|||
} |
|||
|
|||
/** |
|||
* 执行任务 |
|||
* @param task 任务 |
|||
*/ |
|||
public void execute(TimerTask task) { |
|||
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
/** 停止任务线程池 */ |
|||
public void shutdown() { |
|||
ThreadsUtils.shutdownAndAwaitTermination(executor); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,187 @@ |
|||
package com.qs.serve.common.framework.mvc; |
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import com.qs.serve.common.framework.redis.RedisService; |
|||
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.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import javax.annotation.Resource; |
|||
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; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
|
|||
/** |
|||
* 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{ |
|||
|
|||
@Resource |
|||
RedisService redisService; |
|||
|
|||
@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 supplierId = request.getHeader("supplierId"); |
|||
String url = request.getRequestURL().toString(); |
|||
String method = request.getMethod(); |
|||
String queryStr = request.getQueryString(); |
|||
String reqBody = ""; |
|||
if (ServletUtils.isJsonRequest(request)&&requestWrapper!=null) { |
|||
reqBody = requestWrapper.getBody(); |
|||
} |
|||
StringBuffer buffer = new StringBuffer("\n["+method+"] "+url); |
|||
if(supplierId!=null){ |
|||
buffer.append(" 供应商ID: "+supplierId); |
|||
} |
|||
if(!StringUtils.isEmpty(queryStr)){ |
|||
buffer.append("\n参数: "+queryStr); |
|||
} |
|||
if(DevEnvironmentConfig.isDev() &&!StringUtils.isEmpty(reqBody)){ |
|||
buffer.append("\n请求体: "+reqBody); |
|||
} |
|||
if(diffTime>0.6D){ |
|||
buffer.append("\n耗时: "+diffTime+"秒"); |
|||
} |
|||
try { |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
buffer.append("\n用户: "+loginUser.getNick()); |
|||
buffer.append(" IP:["+ServletUtils.getIp(request)+"]"); |
|||
buffer.append(" 时间:["+ LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+"]"); |
|||
} |
|||
} catch (Exception e) {} |
|||
if(diffTime< 1D){ |
|||
log.info(buffer.toString()); |
|||
}else if (diffTime< 20D){ |
|||
log.warn(buffer.toString()); |
|||
}else { |
|||
log.error(buffer.toString()); |
|||
} |
|||
String themeKey = AuthContextUtils.getLockTheme(); |
|||
if(StringUtils.hasText(themeKey)){ |
|||
log.warn("释放资源key:{}",themeKey); |
|||
redisService.remove(themeKey); |
|||
} |
|||
//释放资源 防止线程池重复利用出现脏数据
|
|||
AuthContextUtils.removeThreadLocal(); |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
|
|||
} |
|||
|
|||
/*** |
|||
* HttpServletRequest 包装器 |
|||
* 解决: request.getInputStream()只能读取一次的问题 |
|||
* 目标: 流可重复读 |
|||
*/ |
|||
public class RequestWrapper extends HttpServletRequestWrapper { |
|||
|
|||
/** |
|||
* 请求体 |
|||
*/ |
|||
private String mBody; |
|||
|
|||
public RequestWrapper(HttpServletRequest request) { |
|||
super(request); |
|||
mBody = getBody(request); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求体 |
|||
* @param request 请求 |
|||
* @return 请求体 |
|||
*/ |
|||
private String getBody(HttpServletRequest request) { |
|||
return ServletUtils.getBodyString(request); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求体 |
|||
* @return 请求体 |
|||
*/ |
|||
public String getBody() { |
|||
return mBody; |
|||
} |
|||
|
|||
@Override |
|||
public BufferedReader getReader() throws IOException { |
|||
return new BufferedReader(new InputStreamReader(getInputStream())); |
|||
} |
|||
|
|||
@Override |
|||
public ServletInputStream getInputStream() throws IOException { |
|||
// 创建字节数组输入流
|
|||
final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(StandardCharsets.UTF_8)); |
|||
|
|||
return new ServletInputStream() { |
|||
@Override |
|||
public boolean isFinished() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isReady() { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public void setReadListener(ReadListener readListener) { |
|||
} |
|||
|
|||
@Override |
|||
public int read() throws IOException { |
|||
return bais.read(); |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler; |
|||
|
|||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; |
|||
import com.qs.serve.common.util.NanoIdUtils; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/4/11 |
|||
*/ |
|||
public class IdGeneratorHandler extends DefaultIdentifierGenerator { |
|||
|
|||
@Override |
|||
public String nextUUID(Object entity) { |
|||
return NanoIdUtils.randomNanoId(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler; |
|||
|
|||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.ibatis.reflection.MetaObject; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
/** |
|||
* 自动注入列数据 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Slf4j |
|||
public class SysMetaHandler implements MetaObjectHandler { |
|||
|
|||
@Override |
|||
public void insertFill(MetaObject metaObject) { |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
this.strictInsertFill(metaObject, "createBy", () -> loginUser.getUserId(), String.class); |
|||
} |
|||
this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); |
|||
} |
|||
|
|||
@Override |
|||
public void updateFill(MetaObject metaObject) { |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
this.strictUpdateFill(metaObject, "updateBy", () -> loginUser.getUserId(), String.class); |
|||
} |
|||
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler; |
|||
|
|||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; |
|||
import com.qs.serve.common.model.consts.GySysConst; |
|||
import com.qs.serve.common.model.enums.HttpCode; |
|||
import com.qs.serve.common.util.Assert; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import net.sf.jsqlparser.expression.Expression; |
|||
import net.sf.jsqlparser.expression.StringValue; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* @Author YenHex |
|||
* @Date 2021/5/7 |
|||
* @Version: 1.0 |
|||
**/ |
|||
public class SysTenantHandler implements TenantLineHandler { |
|||
|
|||
public SysTenantHandler(String[] ignoreTables){ |
|||
this.IGNORE_TABLES = Arrays.asList(ignoreTables); |
|||
} |
|||
|
|||
|
|||
final private java.util.List<String> IGNORE_TABLES; |
|||
|
|||
@Override |
|||
public Expression getTenantId() { |
|||
String tenant = AuthContextUtils.getTenant(); |
|||
if(tenant==null){ |
|||
Assert.throwEx(HttpCode.LOGIN_ERR_4001); |
|||
} |
|||
return new StringValue(tenant); |
|||
} |
|||
|
|||
@Override |
|||
public String getTenantIdColumn() { |
|||
return GySysConst.TENANT_COLUMN; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否忽略该表,默认全忽略,匹配到的不忽略 |
|||
* @param tableName |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public boolean ignoreTable(String tableName) { |
|||
for (String tb : this.IGNORE_TABLES) { |
|||
if(tableName.equalsIgnoreCase(tb)){ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -0,0 +1,50 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler.meta; |
|||
|
|||
import cn.hutool.json.JSONUtil; |
|||
import org.apache.ibatis.type.BaseTypeHandler; |
|||
import org.apache.ibatis.type.JdbcType; |
|||
import org.apache.ibatis.type.MappedJdbcTypes; |
|||
import org.apache.ibatis.type.MappedTypes; |
|||
|
|||
import java.sql.CallableStatement; |
|||
import java.sql.PreparedStatement; |
|||
import java.sql.ResultSet; |
|||
import java.sql.SQLException; |
|||
|
|||
/** |
|||
* 存储到数据库, 将String数组转换成字符串; |
|||
* 从数据库获取数据, 将字符串转为LONG数组. |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@MappedTypes({String[].class}) |
|||
@MappedJdbcTypes({JdbcType.VARCHAR}) |
|||
public class JsonStringTypeHandler extends BaseTypeHandler<String[]> { |
|||
|
|||
private static String[] l = new String[]{}; |
|||
|
|||
@Override |
|||
public void setNonNullParameter(PreparedStatement ps, int i, |
|||
String[] parameter, JdbcType jdbcType) throws SQLException { |
|||
ps.setString(i, JSONUtil.toJsonStr(parameter)); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, String columnName) |
|||
throws SQLException { |
|||
return JSONUtil.parseArray(rs.getString(columnName)).toArray(l); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, int columnIndex) |
|||
throws SQLException { |
|||
return JSONUtil.parseArray(rs.getString(columnIndex)).toArray(l); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(CallableStatement cs, int columnIndex) |
|||
throws SQLException { |
|||
return JSONUtil.parseArray(cs.getString(columnIndex)).toArray(l); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,74 @@ |
|||
package com.qs.serve.common.framework.mybatis.handler.meta; |
|||
|
|||
import com.qs.serve.common.util.StringUtils; |
|||
import org.apache.ibatis.type.BaseTypeHandler; |
|||
import org.apache.ibatis.type.JdbcType; |
|||
import org.apache.ibatis.type.MappedJdbcTypes; |
|||
import org.apache.ibatis.type.MappedTypes; |
|||
|
|||
import java.sql.CallableStatement; |
|||
import java.sql.PreparedStatement; |
|||
import java.sql.ResultSet; |
|||
import java.sql.SQLException; |
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 存储到数据库, 将String数组转换成字符串; |
|||
* 从数据库获取数据, 将字符串转为LONG数组. |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@MappedTypes({String[].class}) |
|||
@MappedJdbcTypes({JdbcType.VARCHAR}) |
|||
public class SplitStringTypeHandler extends BaseTypeHandler<String[]> { |
|||
|
|||
private static String[] l = new String[]{}; |
|||
|
|||
@Override |
|||
public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException { |
|||
if(parameter!=null&¶meter.length>1){ |
|||
StringBuffer buffer = new StringBuffer(); |
|||
for (int i1 = 0; i1 < parameter.length; i1++) { |
|||
buffer.append(parameter[i1]); |
|||
if(i1+1<parameter.length){ |
|||
buffer.append(","); |
|||
} |
|||
} |
|||
ps.setString(i, buffer.toString()); |
|||
}else if (parameter!=null&¶meter.length==1){ |
|||
ps.setString(i, parameter[0]); |
|||
}else { |
|||
ps.setString(i, ""); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException { |
|||
String valueString = rs.getString(columnName); |
|||
return initValues(valueString); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { |
|||
String valueString = rs.getString(columnIndex); |
|||
return initValues(valueString); |
|||
} |
|||
|
|||
@Override |
|||
public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { |
|||
String valueString = cs.getString(columnIndex); |
|||
return initValues(valueString); |
|||
} |
|||
|
|||
private String[] initValues(String valueString){ |
|||
if(StringUtils.isNotEmpty(valueString)){ |
|||
String[] vals = valueString.split(","); |
|||
return Arrays.stream(vals) |
|||
.filter(val -> val != null && val.trim().length() > 0) |
|||
.toArray(String[]::new); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,115 @@ |
|||
package com.qs.serve.common.framework.mybatis.join; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.qs.serve.common.framework.mybatis.join.annotations.BindEntity; |
|||
import com.qs.serve.common.framework.mybatis.join.enums.JoinType; |
|||
import com.qs.serve.common.framework.mybatis.join.model.JoinFieldData; |
|||
import com.qs.serve.common.util.CollectionUtil; |
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.common.util.WordUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.lang.annotation.Annotation; |
|||
import java.lang.reflect.Field; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
@Slf4j |
|||
public class JoinUtil { |
|||
|
|||
private static Map<String, List<JoinFieldData>> CACHE_MAP = new ConcurrentHashMap<>(); |
|||
|
|||
private static List<JoinFieldData> getJoinFields(List<?> list) { |
|||
if(CollectionUtil.isEmpty(list)){ |
|||
return new ArrayList<>(); |
|||
} |
|||
String className = list.get(0).getClass().getName(); |
|||
List<JoinFieldData> joinFieldDataList = new ArrayList<>(); |
|||
Class<?> clazz = null; |
|||
try { |
|||
clazz = Class.forName(className); |
|||
} catch (ClassNotFoundException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
if(clazz==null){return null;} |
|||
Field[] fields = clazz.getDeclaredFields(); |
|||
Field fieldJoinVal = null; |
|||
Field fieldSetVal = null; |
|||
BindEntity bindEntity = null; |
|||
for (Field field : fields) { |
|||
for (Annotation annotation : field.getAnnotations()) { |
|||
if(annotation instanceof BindEntity){ |
|||
fieldSetVal = field; |
|||
bindEntity = (BindEntity) annotation; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if(fieldSetVal!=null){ |
|||
for (Field field :fields){ |
|||
if(field.getName().equals(bindEntity.joinField())){ |
|||
fieldJoinVal = field; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if(bindEntity!=null&&fieldJoinVal!=null){ |
|||
JoinFieldData fieldData = new JoinFieldData(); |
|||
fieldData.setFieldJoinVal(fieldJoinVal); |
|||
fieldData.setBindEntity(bindEntity); |
|||
fieldData.setFieldSetVal(fieldSetVal); |
|||
joinFieldDataList.add(fieldData); |
|||
} |
|||
CACHE_MAP.put(className,joinFieldDataList); |
|||
return joinFieldDataList; |
|||
} |
|||
|
|||
public static void relate(List<?> list) { |
|||
List<JoinFieldData> joinFieldData = getJoinFields(list); |
|||
HashMap<String,Object> tempCache = new HashMap<>(list.size()+1); |
|||
for (JoinFieldData fieldData : joinFieldData) { |
|||
for (Object object : list) { |
|||
try { |
|||
Field fieldJoinVal = fieldData.getFieldJoinVal(); |
|||
Field fieldSetVal = fieldData.getFieldSetVal(); |
|||
BindEntity bindEntity = fieldData.getBindEntity(); |
|||
fieldSetVal.setAccessible(true); |
|||
fieldJoinVal.setAccessible(true); |
|||
IService<?> iService = SpringUtils.getBean(bindEntity.service()); |
|||
Object identity = fieldJoinVal.get(object); |
|||
if(identity==null){ |
|||
continue; |
|||
} |
|||
Object result = tempCache.get(identity.toString()); |
|||
if(result!=null){ |
|||
fieldSetVal.set(object,result); |
|||
continue; |
|||
} |
|||
if(bindEntity.joinType()== JoinType.List){ |
|||
QueryWrapper wrapper = new QueryWrapper<>(); |
|||
wrapper.eq(WordUtil.toLine(bindEntity.joinField()),identity); |
|||
result = iService.list(wrapper); |
|||
}else { |
|||
QueryWrapper wrapper = new QueryWrapper(); |
|||
wrapper.eq(WordUtil.toLine(bindEntity.selfField()),identity); |
|||
result = iService.getOne(wrapper,false); |
|||
} |
|||
fieldSetVal.set(object,result); |
|||
tempCache.put(identity.toString(),result); |
|||
} catch (IllegalAccessException e) { |
|||
log.warn("join 工具失败:",e); |
|||
} |
|||
} |
|||
} |
|||
tempCache = null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.mybatis.join.annotations; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.qs.serve.common.framework.mybatis.join.enums.JoinType; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* 实现left join |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
@Target({ElementType.FIELD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
public @interface BindEntity { |
|||
|
|||
Class<? extends IService> service(); |
|||
|
|||
/** |
|||
* 关联的表 |
|||
* @return |
|||
*/ |
|||
String joinField() default ""; |
|||
|
|||
/** |
|||
* 主表 |
|||
* @return |
|||
*/ |
|||
String selfField() default "id"; |
|||
|
|||
JoinType joinType() default JoinType.Object; |
|||
|
|||
} |
@ -0,0 +1,14 @@ |
|||
package com.qs.serve.common.framework.mybatis.join.enums; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
public enum JoinType { |
|||
|
|||
/** |
|||
* 返回类型 |
|||
*/ |
|||
Object,List |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.serve.common.framework.mybatis.join.model; |
|||
|
|||
import com.qs.serve.common.framework.mybatis.join.annotations.BindEntity; |
|||
import lombok.Data; |
|||
|
|||
import java.lang.reflect.Field; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/24 |
|||
*/ |
|||
@Data |
|||
public class JoinFieldData { |
|||
Field fieldJoinVal; |
|||
Field fieldSetVal; |
|||
BindEntity bindEntity; |
|||
} |
@ -0,0 +1,159 @@ |
|||
package com.qs.serve.common.framework.mybatis.query; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryFieldData; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryFieldDataValue; |
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryType; |
|||
import com.qs.serve.common.framework.mybatis.query.annotations.QueryWrap; |
|||
import com.qs.serve.common.util.WordUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.util.CollectionUtils; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.lang.annotation.Annotation; |
|||
import java.lang.reflect.Field; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @Date 2017/10/28 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
public class AnnotationQueryStorage { |
|||
|
|||
private static final Map<String, List<QueryFieldData>> BEAN_QUERY_FIELD = new ConcurrentHashMap<>(); |
|||
|
|||
protected static List<QueryFieldData> getQueryFieldList(String className){ |
|||
List<QueryFieldData> queryFieldList = BEAN_QUERY_FIELD.get(className); |
|||
if(queryFieldList==null){ |
|||
Class<?> clazz = null; |
|||
try { |
|||
clazz = Class.forName(className); |
|||
} catch (ClassNotFoundException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
queryFieldList = new ArrayList<>(); |
|||
if(clazz==null){return queryFieldList;} |
|||
Field[] fields = clazz.getDeclaredFields(); |
|||
for (Field field : fields) { |
|||
Annotation[] annotations = field.getAnnotations(); |
|||
boolean isExist = false; |
|||
boolean isIgnore = false; |
|||
for (Annotation annotation : annotations) { |
|||
if(annotation instanceof QueryWrap){ |
|||
QueryWrap queryWrap = (QueryWrap)annotation; |
|||
QueryFieldData queryField= handleQueryField(queryWrap.type(),field); |
|||
if(queryField!=null){ |
|||
isExist = true; |
|||
queryFieldList.add(queryField); |
|||
break; |
|||
} |
|||
} |
|||
if(annotation instanceof TableField){ |
|||
TableField tableField = (TableField) annotation; |
|||
isIgnore = tableField.exist(); |
|||
} |
|||
} |
|||
//不存在,添加默认
|
|||
if( !isExist && !isIgnore ){ |
|||
QueryFieldData queryField = new QueryFieldData(); |
|||
//系统默认使用格式。示例: userInfo -> user_info
|
|||
queryField.setColumnName(WordUtil.toLine(field.getName())); |
|||
queryField.setField(field); |
|||
queryField.setFieldName(field.getName()); |
|||
queryField.setType(QueryType.NONE); |
|||
queryFieldList.add(queryField); |
|||
} |
|||
} |
|||
BEAN_QUERY_FIELD.put(className,queryFieldList); |
|||
if(queryFieldList.size()==0){ |
|||
log.warn("无匹配的注解字段,类名:"+className); |
|||
} |
|||
} |
|||
return queryFieldList; |
|||
} |
|||
|
|||
/** |
|||
* 处理查询列 |
|||
* @param queryType |
|||
* @param field |
|||
* @return |
|||
*/ |
|||
private static QueryFieldData handleQueryField(QueryType queryType, Field field){ |
|||
String columnName = null; |
|||
if(queryType!=null){ |
|||
QueryFieldData queryField = new QueryFieldData(); |
|||
if(StringUtils.isEmpty(columnName)){ |
|||
columnName = WordUtil.toLine(field.getName()); |
|||
} |
|||
queryField.setFieldName(field.getName()); |
|||
queryField.setColumnName(columnName); |
|||
queryField.setField(field); |
|||
queryField.setType(queryType); |
|||
return queryField; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 加载查询对象生成配置值 |
|||
* @param query |
|||
* @return |
|||
*/ |
|||
protected static List<QueryFieldDataValue> loadQueryFieldDataValues(Object query){ |
|||
String className = query.getClass().getName(); |
|||
List<QueryFieldData> dataList = AnnotationQueryStorage.getQueryFieldList(className); |
|||
if(!CollectionUtils.isEmpty(dataList)){ |
|||
List<QueryFieldDataValue> valueList = new ArrayList<>(); |
|||
for (QueryFieldData fieldData : dataList) { |
|||
Field field = fieldData.getField(); |
|||
field.setAccessible(true); |
|||
Object value = null; |
|||
try { |
|||
value = field.get(query); |
|||
} catch (IllegalAccessException e) { |
|||
log.warn("查询类字段参数异常。查询类:{},字段:{}",className,field.getName()); |
|||
} |
|||
if(value!=null){ |
|||
QueryFieldDataValue dataValue = new QueryFieldDataValue(fieldData); |
|||
//通过列条件,重新赋值(赋值查询条件和格式)
|
|||
dataValue.setValue(value); |
|||
valueList.add(dataValue); |
|||
} |
|||
} |
|||
return valueList; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 初始化封装包参数 |
|||
* @param fieldValues |
|||
* @param queryWrapper |
|||
* @param <T> |
|||
* @return 判断是否排序 |
|||
*/ |
|||
protected static <T> void initWrap(List<QueryFieldDataValue> fieldValues, QueryWrapper<T> queryWrapper){ |
|||
for (QueryFieldDataValue fieldValue : fieldValues) { |
|||
QueryType queryType = fieldValue.getType(); |
|||
Object value = fieldValue.getValue(); |
|||
String column = fieldValue.getColumnName(); |
|||
if(queryType.equals(QueryType.EQ)){ queryWrapper.eq(column,value); } |
|||
if(queryType.equals(QueryType.GE)){ queryWrapper.ge(column,value); } |
|||
if(queryType.equals(QueryType.GT)){ queryWrapper.gt(column,value); } |
|||
if(queryType.equals(QueryType.In)){ queryWrapper.in(column,value); } |
|||
if(queryType.equals(QueryType.LE)){ queryWrapper.le(column,value); } |
|||
if(queryType.equals(QueryType.Like)){ queryWrapper.like(column,value); } |
|||
if(queryType.equals(QueryType.LT)){ queryWrapper.lt(column,value); } |
|||
if(queryType.equals(QueryType.NE)){ queryWrapper.ne(column,value); } |
|||
if(queryType.equals(QueryType.RightLike)){ queryWrapper.likeRight(column,value); } |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.annotations; |
|||
|
|||
import com.qs.serve.common.framework.mybatis.query.model.QueryType; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
* @date 2020/5/26 |
|||
* SQL In条件注解 |
|||
*/ |
|||
@Target({ElementType.FIELD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
public @interface QueryWrap { |
|||
|
|||
/** |
|||
* 列名(默认为空字符串) |
|||
* 空字符串,采用字段转列策略 示例: userInfo -> user_info |
|||
* 非空时,采用列的值 |
|||
*/ |
|||
String value() default ""; |
|||
|
|||
/** |
|||
* 查询类型(默认采用值相对) |
|||
*/ |
|||
QueryType type() default QueryType.EQ; |
|||
|
|||
} |
|||
|
@ -0,0 +1,23 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.model; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.lang.reflect.Field; |
|||
|
|||
/** |
|||
* 存储DTO |
|||
* @author JcYen |
|||
* @Date 2017/10/28 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class QueryFieldData { |
|||
|
|||
private Field field; |
|||
private QueryType type; |
|||
private String columnName; |
|||
private String fieldName; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.model; |
|||
|
|||
import com.qs.serve.common.util.CopierUtil; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* 存储DTO |
|||
* @author JcYen |
|||
* @Date 2017/10/28 |
|||
* @Version 1.0 |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class QueryFieldDataValue{ |
|||
|
|||
public QueryFieldDataValue(QueryFieldData data){ |
|||
CopierUtil.copy(data,this); |
|||
} |
|||
|
|||
private QueryType type; |
|||
private String columnName; |
|||
private String fieldName; |
|||
private Object value; |
|||
} |
@ -0,0 +1,40 @@ |
|||
package com.qs.serve.common.framework.mybatis.query.model; |
|||
|
|||
/** |
|||
* @author JcYen |
|||
*/ |
|||
public enum QueryType { |
|||
|
|||
/** |
|||
* 无 |
|||
*/ |
|||
NONE, |
|||
|
|||
/** |
|||
* 通用的equals(Integer,Long,String) |
|||
*/ |
|||
EQ, |
|||
NE, |
|||
|
|||
/** |
|||
* 比较修饰符,修饰日期,数值类型(Integer,Long,Double,BigDecimal) |
|||
*/ |
|||
LT, |
|||
LE, |
|||
GT, |
|||
GE, |
|||
|
|||
/** |
|||
* 修饰List<Long> |
|||
*/ |
|||
In, |
|||
NotIn, |
|||
|
|||
/** |
|||
* 修饰字符串 |
|||
*/ |
|||
Like, |
|||
RightLike, |
|||
NotLike, |
|||
|
|||
} |
@ -0,0 +1,395 @@ |
|||
package com.qs.serve.common.framework.redis; |
|||
|
|||
import com.qs.serve.common.util.Assert; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Primary; |
|||
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.core.*; |
|||
import org.springframework.data.redis.serializer.RedisSerializationContext; |
|||
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 { |
|||
|
|||
@Bean |
|||
@Primary |
|||
public RedisCacheManager selfCacheManager(RedisTemplate<String, Object> redisTemplate) { |
|||
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()); |
|||
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() |
|||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())); |
|||
return new TtlRedisCacheManager(redisCacheWriter, redisCacheConfiguration); |
|||
} |
|||
|
|||
private RedisTemplate redisTemplate; |
|||
|
|||
private StringRedisTemplate stringRedisTemplate; |
|||
|
|||
/** |
|||
* 加锁 |
|||
* @param theme |
|||
* @param id |
|||
* @return 是否在锁 |
|||
*/ |
|||
public void throwResLock(String theme,String id){ |
|||
String key = "LOCK_RES:"+theme+":"+id; |
|||
String value = this.getString(key); |
|||
System.out.println("throwResLock:"+value); |
|||
String newVal = "lock"; |
|||
if(newVal.equals(value)){ |
|||
Assert.throwEx("服务正在处理,请稍后再试"); |
|||
} |
|||
this.set(key,newVal,10,TimeUnit.MINUTES); |
|||
String value2 = this.getString(key); |
|||
if(!newVal.equals(value2)){ |
|||
Assert.throwEx("锁失效"); |
|||
} |
|||
AuthContextUtils.setLockTheme(key); |
|||
} |
|||
|
|||
/** |
|||
* 释放锁 |
|||
* @param theme |
|||
* @param id |
|||
* @return 是否在锁 |
|||
*/ |
|||
public void removeResLock(String theme,String id){ |
|||
String key = "LOCK_RES:"+theme+":"+id; |
|||
this.remove(key); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 写入缓存 |
|||
* |
|||
* @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; |
|||
} |
|||
|
|||
public Long getLong(final String key) { |
|||
String result = getString(key); |
|||
if(result!=null){ |
|||
return Long.parseLong(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); |
|||
} |
|||
|
|||
/** |
|||
* 移除业务锁 |
|||
* @param code |
|||
* @param targetId |
|||
*/ |
|||
public void removeLock(String code,String targetId){ |
|||
String key = "lock_commit:"+code+":"+targetId; |
|||
this.remove(key); |
|||
} |
|||
|
|||
/** |
|||
* 锁 |
|||
* @param code |
|||
* @param targetId |
|||
* @return true--新建锁成功,false反之 |
|||
*/ |
|||
public boolean tryToLock(String code,String targetId){ |
|||
String key = "lock_commit:"+code+":"+targetId; |
|||
Integer existState = getInteger(key); |
|||
if(existState==null){ |
|||
this.set(key,1,20,TimeUnit.SECONDS); |
|||
return true; |
|||
}else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.qs.serve.common.framework.redis; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.data.redis.cache.RedisCache; |
|||
import org.springframework.data.redis.cache.RedisCacheConfiguration; |
|||
import org.springframework.data.redis.cache.RedisCacheManager; |
|||
import org.springframework.data.redis.cache.RedisCacheWriter; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.time.Duration; |
|||
|
|||
@Slf4j |
|||
public class TtlRedisCacheManager extends RedisCacheManager { |
|||
public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { |
|||
super(cacheWriter, defaultCacheConfiguration); |
|||
} |
|||
|
|||
@Override |
|||
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { |
|||
log.debug("TtlRedisCacheManager keyName:{}",name); |
|||
String[] cells = StringUtils.delimitedListToStringArray(name, "="); |
|||
name = cells[0]; |
|||
if (cells.length > 1) { |
|||
long ttl = Long.parseLong(cells[1]); |
|||
// 根据传参设置缓存失效时间,默认单位是秒
|
|||
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl)); |
|||
} |
|||
return super.createRedisCache(name, cacheConfig); |
|||
} |
|||
} |
@ -0,0 +1,205 @@ |
|||
package com.qs.serve.common.framework.security.filter; |
|||
|
|||
import com.qs.serve.common.config.DevEnvironmentConfig; |
|||
import com.qs.serve.common.config.properties.AuthUrlConst; |
|||
import com.qs.serve.common.config.properties.PermitProperties; |
|||
import com.qs.serve.common.config.properties.ProjectProperties; |
|||
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.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.JsonUtil; |
|||
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; |
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* 判断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; |
|||
|
|||
@Autowired |
|||
private ProjectProperties projectProperties; |
|||
|
|||
@Override |
|||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { |
|||
|
|||
String supplierId = request.getHeader(GySysConst.SUPPLIER_PROP); |
|||
AuthContextUtils.setSupplierId(supplierId); |
|||
//移除前缀
|
|||
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(projectProperties.getCloseAuth()!=null && projectProperties.getCloseAuth().equals("true")){ |
|||
for (String allowUrl : AuthUrlConst.AUTH_ALLOW_URL) { |
|||
if(url.contains(allowUrl)){ |
|||
doSecurityAuthor(false,request, response, chain); |
|||
} |
|||
} |
|||
}else { |
|||
if(ignorePermit){ |
|||
//try to doSecurityAuthor
|
|||
tryDoSecurityAuthor(request, response, chain); |
|||
chain.doFilter(request,response); |
|||
}else { |
|||
doSecurityAuthor(true,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); |
|||
AuthContextUtils.setTenant(tenant); |
|||
if(SecurityContextHolder.getContext().getAuthentication()==null){ |
|||
UserDetails 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(boolean checkRedis,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); |
|||
if(checkRedis){ |
|||
String redisKey = com.qs.serve.common.util.StringUtils.format(RedisCacheKeys.LOGIN_KEY_APP,client,userId); |
|||
String cacheToken = redisService.getString(redisKey); |
|||
if(cacheToken==null){ |
|||
//redisService.remove(redisKey);
|
|||
HttpResponsePrintUtil.print2(response,401,"已被系统登出"); |
|||
//return解决:security.access.AccessDeniedException
|
|||
return; |
|||
} |
|||
} |
|||
if(SecurityContextHolder.getContext().getAuthentication()==null){ |
|||
UserDetails 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{ |
|||
log.error("商户信息错误,请求地址:{}",request.getRequestURI()); |
|||
HttpResponsePrintUtil.print2(response,4001,"商户信息错误"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.qs.serve.common.framework.security.handler; |
|||
|
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import org.springframework.security.access.AccessDeniedException; |
|||
import org.springframework.security.web.access.AccessDeniedHandler; |
|||
|
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/3 |
|||
*/ |
|||
public class SecurityAccessDeniedHandler implements AccessDeniedHandler { |
|||
|
|||
@Override |
|||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { |
|||
HttpResponsePrintUtil.print(response,403,"权限不足"); |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
package com.qs.serve.common.framework.security.handler; |
|||
|
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import org.springframework.security.core.AuthenticationException; |
|||
import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
|||
|
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/3 |
|||
*/ |
|||
public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler { |
|||
|
|||
@Override |
|||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException { |
|||
HttpResponsePrintUtil.print(response,401,"登录信息已过期或失效"); |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.qs.serve.common.framework.security.handler; |
|||
|
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.common.model.HttpResponsePrintUtil; |
|||
import com.qs.serve.common.util.AuthContextUtils; |
|||
import com.qs.serve.common.util.SpringUtils; |
|||
import com.qs.serve.modules.sys.mapper.SysUserMapper; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.security.web.authentication.logout.LogoutHandler; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2021/12/22 |
|||
*/ |
|||
public class SecurityLogoutHandler implements LogoutHandler { |
|||
|
|||
|
|||
|
|||
@Override |
|||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { |
|||
SysUserMapper sysUserMapper = SpringUtils.getBean(SysUserMapper.class); |
|||
LoginUser loginUser = AuthContextUtils.getLoginUser(false); |
|||
if(loginUser!=null){ |
|||
sysUserMapper.updateYmTokenInt(loginUser.getUserId(),"#","#"); |
|||
} |
|||
try { |
|||
HttpResponsePrintUtil.print(response,200,"退出登录成功"); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,97 @@ |
|||
package com.qs.serve.common.framework.security.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.springframework.security.core.GrantedAuthority; |
|||
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 用户登录信息 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor |
|||
public class LoginUser implements UserDetails { |
|||
|
|||
private String userId; |
|||
|
|||
private String nick; |
|||
|
|||
private String password; |
|||
|
|||
private String loginIp; |
|||
|
|||
/** 用户类型 */ |
|||
private LoginUserType typeFlag; |
|||
|
|||
private List<String> authorList; |
|||
private List<String> authorIds; |
|||
|
|||
private String tenant; |
|||
|
|||
@Override |
|||
public Collection<? extends GrantedAuthority> getAuthorities() { |
|||
if(authorList!=null&&authorList.size()>0){ |
|||
return authorList |
|||
.stream() |
|||
.filter(n->n.length()>0) |
|||
.map(auth->new SimpleGrantedAuthority("ROLE_"+auth)) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
return Collections.singletonList(new SimpleGrantedAuthority("NOT_ROLE")); |
|||
} |
|||
|
|||
@Override |
|||
public String getPassword() { |
|||
return password; |
|||
} |
|||
|
|||
@Override |
|||
public String getUsername() { |
|||
return userId; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isAccountNonExpired() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isAccountNonLocked() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isCredentialsNonExpired() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean isEnabled() { |
|||
return true; |
|||
} |
|||
|
|||
public LoginUserDTO loginUserDTO(){ |
|||
return new LoginUserDTO(userId,nick,password,loginIp,typeFlag.getCode(),authorList,authorIds,tenant); |
|||
} |
|||
|
|||
public boolean verifyRole(String permitCode){ |
|||
return authorList.contains(permitCode); |
|||
} |
|||
|
|||
/** |
|||
* 是否拥有招生超管权限 |
|||
* @return |
|||
*/ |
|||
public boolean hasRegSuperRole(){ |
|||
return verifyRole("edu:reg:admin"); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.qs.serve.common.framework.security.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 用户登录信息 |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class LoginUserDTO { |
|||
|
|||
private String userId; |
|||
|
|||
private String nick; |
|||
|
|||
private String password; |
|||
|
|||
private String loginIp; |
|||
|
|||
private String typeFlagCode; |
|||
|
|||
private List<String> authorList; |
|||
private List<String> authorIds; |
|||
private String tenant; |
|||
|
|||
public LoginUser loginUser(){ |
|||
return new LoginUser(userId,nick,password,loginIp,LoginUserType.getByCode(typeFlagCode),authorList,authorIds,tenant); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.qs.serve.common.framework.security.model; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/7 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum LoginUserType { |
|||
|
|||
/** |
|||
* 人员分类 |
|||
*/ |
|||
APP_USER("00","微信用户"), |
|||
SYS_USER("01","系统人员"), |
|||
SYS_SUP_USER("02","超级管理员") |
|||
; |
|||
|
|||
private String code; |
|||
private String name; |
|||
|
|||
public static LoginUserType getByCode(String code){ |
|||
for (LoginUserType value : LoginUserType.values()) { |
|||
if(value.getCode().equals(code)){ |
|||
return value; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.qs.serve.common.framework.security.service; |
|||
|
|||
import com.qs.serve.common.framework.security.model.LoginUser; |
|||
import com.qs.serve.modules.sys.entity.SysUser; |
|||
import com.qs.serve.modules.sys.service.SysUserService; |
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
import org.springframework.security.core.userdetails.UserDetailsService; |
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
@Service |
|||
@AllArgsConstructor |
|||
public class SysUserDetailsServiceImpl implements UserDetailsService { |
|||
|
|||
private final SysUserService sysUserService; |
|||
|
|||
@Override |
|||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { |
|||
return sysUserService.getLoginUserByAccount(username); |
|||
} |
|||
|
|||
public LoginUser buildLoginUser(SysUser sysUser){ |
|||
return sysUserService.buildLoginUser(sysUser); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.qs.serve.common.framework.security.util; |
|||
|
|||
/** |
|||
* @Author JcYen |
|||
* @Date 2021/9/24 |
|||
* @Version 1.0 |
|||
*/ |
|||
public class SecurityPermitUtil { |
|||
|
|||
public static boolean verify(String reg, String input){ |
|||
if ("/*".equals(reg)) return true; |
|||
String[] reg_split = reg.split("\\*"); |
|||
int index = 0, reg_len = reg_split.length; |
|||
boolean b = reg.charAt(reg.length() - 1) == '*' ? true : false; |
|||
while (input.length() > 0) { |
|||
if (index == reg_len) { |
|||
if (b) return true; |
|||
else return false; |
|||
} |
|||
String r = reg_split[index++]; |
|||
int indexOf = input.indexOf(r); |
|||
if (indexOf != 0) return false; |
|||
input = input.substring(indexOf + r.length()); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,18 @@ |
|||
package com.qs.serve.common.model; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/7/31 |
|||
*/ |
|||
@Data |
|||
public class AmountDTO { |
|||
|
|||
private String id; |
|||
|
|||
private BigDecimal amount; |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.qs.serve.common.model; |
|||
|
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/30 |
|||
*/ |
|||
public class HttpResponsePrintUtil { |
|||
|
|||
public static void print(HttpServletResponse response,Integer code ,String msg) throws IOException,SecurityException{ |
|||
response.setHeader("Access-Control-Allow-Origin", "*"); |
|||
response.setHeader("Access-Control-Allow-Credentials", "true"); |
|||
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"); |
|||
response.setHeader("Access-Control-Max-Age", "86400"); |
|||
response.setHeader("Access-Control-Allow-Headers", "*"); |
|||
response.setStatus(200); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setContentType("application/json;charset=UTF-8"); |
|||
response.getWriter().print("{\"status\":"+code+",\"msg\":\""+msg+"\"}"); |
|||
response.getWriter().flush(); |
|||
} |
|||
|
|||
public static void print2(HttpServletResponse response,Integer code ,String msg)throws IOException,SecurityException{ |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setContentType("application/json;charset=UTF-8"); |
|||
response.getWriter().print("{\"status\":"+code+",\"msg\":\""+msg+"\"}"); |
|||
response.getWriter().flush(); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/4/25 |
|||
*/ |
|||
@Target({ElementType.FIELD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface BusinessDifference { |
|||
|
|||
/** |
|||
* 字段备注 |
|||
* @return |
|||
*/ |
|||
String value() default ""; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* 防止表单重复提交 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
@Target({ElementType.METHOD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface LimitSubmit { |
|||
|
|||
/** |
|||
* 间隔时间(ms),在世界间隔内超过执行次数,视为被限制 |
|||
*/ |
|||
int interval() default 1000; |
|||
|
|||
/** |
|||
* 提示消息 |
|||
*/ |
|||
String message() default ""; |
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
/** |
|||
* 防止表单重复提交 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
public @interface RepeatSubmit { |
|||
|
|||
/** |
|||
* 间隔时间(ms),小于此时间视为重复提交 |
|||
*/ |
|||
int interval() default 5000; |
|||
|
|||
/** |
|||
* 提示消息 |
|||
*/ |
|||
String message() default "不允许重复提交,请稍后再试"; |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.qs.serve.common.model.annotation; |
|||
|
|||
import com.qs.serve.common.model.enums.BizType; |
|||
import com.qs.serve.common.model.enums.InterType; |
|||
import com.qs.serve.common.model.enums.SystemModule; |
|||
|
|||
import java.lang.annotation.*; |
|||
|
|||
/** |
|||
* 记录用户登录操作 |
|||
* @author YenHex |
|||
* @since 2022-03-01 |
|||
*/ |
|||
@Target({ElementType.METHOD}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface SysLog { |
|||
|
|||
SystemModule module() default SystemModule.SYSTEM; |
|||
|
|||
/** 业务标题 */ |
|||
String title() default ""; |
|||
|
|||
/** 操作描述 */ |
|||
String desc() default ""; |
|||
|
|||
/** 操作类型 */ |
|||
BizType biz() default BizType.OTHER; |
|||
|
|||
/** 接口类型 */ |
|||
InterType inter() default InterType.ADMIN; |
|||
|
|||
boolean saveReqParam() default true; |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/7 |
|||
*/ |
|||
@Data |
|||
public class BiCommonCounter { |
|||
|
|||
/** |
|||
* 横轴下标的ID |
|||
*/ |
|||
private String label; |
|||
|
|||
private Long counts; |
|||
|
|||
} |
@ -0,0 +1,15 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/7 |
|||
*/ |
|||
@Data |
|||
public class BiMonthCounter { |
|||
|
|||
private String months; |
|||
private Long counts; |
|||
|
|||
} |
@ -0,0 +1,39 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/7 |
|||
*/ |
|||
@Data |
|||
public class ChartAxisColumn { |
|||
|
|||
/** 列Id */ |
|||
String columnId; |
|||
|
|||
/** 列名 */ |
|||
String columnName; |
|||
|
|||
/** 系列坐标值合计 */ |
|||
Double total; |
|||
|
|||
public static List<ChartAxisColumn> buildMonthsOfYear(){ |
|||
List<ChartAxisColumn> axisColumnList = new ArrayList<>(); |
|||
for (long i = 1; i < 13; i++) { |
|||
ChartAxisColumn axisColumn = new ChartAxisColumn(); |
|||
if(i>9){ |
|||
axisColumn.setColumnId(i+""); |
|||
}else { |
|||
axisColumn.setColumnId("0"+i); |
|||
} |
|||
axisColumn.setColumnName(i+"月"); |
|||
axisColumnList.add(axisColumn); |
|||
} |
|||
return axisColumnList; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,148 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/7 |
|||
*/ |
|||
@Data |
|||
public class ChartDataVo { |
|||
|
|||
/** |
|||
* 列(饼状图的主要参数) |
|||
*/ |
|||
List<ChartAxisColumn> axisColumnList; |
|||
|
|||
/** |
|||
* 系列 |
|||
*/ |
|||
List<ChartSeriesItem> seriesItemList; |
|||
|
|||
/** |
|||
* 分组 |
|||
*/ |
|||
List<ChartGroupItem> groupItemList; |
|||
|
|||
|
|||
public void initAxisColumnTotal(){ |
|||
for (int i = 0; i < axisColumnList.size(); i++) { |
|||
ChartAxisColumn axisColumn = axisColumnList.get(i); |
|||
BigDecimal total = BigDecimal.ZERO; |
|||
if(seriesItemList!=null&&seriesItemList.size()>0){ |
|||
for (ChartSeriesItem seriesItem : seriesItemList) { |
|||
double value = seriesItem.getValues().get(i); |
|||
total = total.add(new BigDecimal(value+"")); |
|||
} |
|||
} |
|||
axisColumn.setTotal(total.doubleValue()); |
|||
} |
|||
} |
|||
|
|||
public static ChartDataVo buildStateChartData(List<BiCommonCounter> stateCounterList, Map<String,String> stateMap){ |
|||
ChartDataVo chartDataVo = new ChartDataVo(); |
|||
List<ChartAxisColumn> axisColumnList = new ArrayList<>(); |
|||
List<ChartSeriesItem> seriesItemList = new ArrayList<>(); |
|||
List<ChartGroupItem> groupItemList = new ArrayList<>(); |
|||
ChartGroupItem groupItem = new ChartGroupItem(); |
|||
groupItem.setGroupId(100L); |
|||
groupItem.setGroupName("默认"); |
|||
groupItemList.add(groupItem); |
|||
for (String stateNum : stateMap.keySet()) { |
|||
String label = stateMap.get(stateNum); |
|||
ChartAxisColumn axisColumn = new ChartAxisColumn(); |
|||
axisColumn.setColumnId(stateNum); |
|||
axisColumn.setColumnName(label); |
|||
axisColumnList.add(axisColumn); |
|||
} |
|||
List<Double> values = new ArrayList<>(); |
|||
for (ChartAxisColumn chartAxisColumn : axisColumnList) { |
|||
Double currVal = 0.0; |
|||
for (BiCommonCounter counter : stateCounterList) { |
|||
if(chartAxisColumn.getColumnId().equals(counter.getLabel())){ |
|||
currVal = counter.getCounts().doubleValue(); |
|||
break; |
|||
} |
|||
} |
|||
values.add(currVal); |
|||
} |
|||
ChartSeriesItem seriesItem = new ChartSeriesItem(); |
|||
seriesItem.setGroupId(groupItem.getGroupId()); |
|||
seriesItem.setGroupName(groupItem.getGroupName()); |
|||
seriesItem.setValues(values); |
|||
seriesItemList.add(seriesItem); |
|||
//更新total
|
|||
chartDataVo.setAxisColumnList(axisColumnList); |
|||
chartDataVo.setGroupItemList(groupItemList); |
|||
chartDataVo.setSeriesItemList(seriesItemList); |
|||
chartDataVo.initAxisColumnTotal(); |
|||
return chartDataVo; |
|||
} |
|||
|
|||
public void tes(){ |
|||
|
|||
} |
|||
|
|||
public void buildStateMap(Map<String,String> stateMap){ |
|||
init(); |
|||
for (String stateNum : stateMap.keySet()) { |
|||
String label = stateMap.get(stateNum); |
|||
ChartAxisColumn axisColumn = new ChartAxisColumn(); |
|||
axisColumn.setColumnId(stateNum); |
|||
axisColumn.setColumnName(label); |
|||
this.axisColumnList.add(axisColumn); |
|||
} |
|||
} |
|||
public void buildStateMapOfMonths(){ |
|||
init(); |
|||
this.axisColumnList = ChartAxisColumn.buildMonthsOfYear(); |
|||
} |
|||
|
|||
/** |
|||
* 先调用buildStateMap |
|||
* @param groupId |
|||
* @param groupName |
|||
* @param stateCounterList |
|||
*/ |
|||
public void addData(Long groupId,String groupName,List<BiCommonCounter> stateCounterList){ |
|||
ChartGroupItem groupItem = new ChartGroupItem(); |
|||
groupItem.setGroupId(groupId); |
|||
groupItem.setGroupName(groupName); |
|||
groupItemList.add(groupItem); |
|||
List<Double> values = new ArrayList<>(); |
|||
for (ChartAxisColumn chartAxisColumn : axisColumnList) { |
|||
Double currVal = 0.0; |
|||
for (BiCommonCounter counter : stateCounterList) { |
|||
if(chartAxisColumn.getColumnId().equals(counter.getLabel())){ |
|||
currVal = counter.getCounts().doubleValue(); |
|||
break; |
|||
} |
|||
} |
|||
values.add(currVal); |
|||
} |
|||
ChartSeriesItem seriesItem = new ChartSeriesItem(); |
|||
seriesItem.setGroupId(groupItem.getGroupId()); |
|||
seriesItem.setGroupName(groupItem.getGroupName()); |
|||
seriesItem.setValues(values); |
|||
seriesItemList.add(seriesItem); |
|||
this.initAxisColumnTotal(); |
|||
} |
|||
|
|||
private void init(){ |
|||
if(axisColumnList==null){ |
|||
axisColumnList = new ArrayList<>(); |
|||
} |
|||
if(seriesItemList==null){ |
|||
seriesItemList = new ArrayList<>(); |
|||
} |
|||
if(groupItemList==null){ |
|||
groupItemList = new ArrayList<>(); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,18 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/7 |
|||
*/ |
|||
@Data |
|||
public class ChartGroupItem { |
|||
|
|||
/** 组id */ |
|||
private Long groupId; |
|||
|
|||
/** 组名 */ |
|||
private String groupName; |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/7 |
|||
*/ |
|||
@Data |
|||
public class ChartSeriesItem { |
|||
|
|||
|
|||
/** 组id */ |
|||
Long groupId; |
|||
|
|||
/** 组名 */ |
|||
String groupName; |
|||
|
|||
/** |
|||
* 值列表(列对应axisColumns) |
|||
*/ |
|||
List<Double> values; |
|||
|
|||
} |
@ -0,0 +1,75 @@ |
|||
package com.qs.serve.common.model.chart; |
|||
|
|||
import com.qs.serve.common.util.JsonUtil; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Random; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/2/17 |
|||
*/ |
|||
public class PrintChart { |
|||
|
|||
|
|||
public static void main(String[] args) { |
|||
|
|||
//eg. 统计最近5年入学人数,并区分男女数量
|
|||
ChartDataVo chartDataVo = new ChartDataVo(); |
|||
|
|||
List<ChartAxisColumn> axisColumnList = new ArrayList<>(); |
|||
List<ChartSeriesItem> seriesItemList = new ArrayList<>(); |
|||
List<ChartGroupItem> groupItemList = new ArrayList<>(); |
|||
|
|||
ChartGroupItem groupItem = new ChartGroupItem(); |
|||
groupItem.setGroupId(100L); |
|||
groupItem.setGroupName("男学生"); |
|||
groupItemList.add(groupItem); |
|||
|
|||
ChartGroupItem groupItem2 = new ChartGroupItem(); |
|||
groupItem2.setGroupId(200L); |
|||
groupItem2.setGroupName("女学生"); |
|||
groupItemList.add(groupItem2); |
|||
|
|||
for (long i = 1; i <= 5; i++) { |
|||
ChartAxisColumn axisColumn = new ChartAxisColumn(); |
|||
axisColumn.setColumnId(i+""); |
|||
axisColumn.setColumnName("202"+i+"年"); |
|||
axisColumnList.add(axisColumn); |
|||
} |
|||
|
|||
Random random = new Random(); |
|||
for (ChartGroupItem chartGroupItem : groupItemList) { |
|||
ChartSeriesItem seriesItem = new ChartSeriesItem(); |
|||
seriesItem.setGroupId(chartGroupItem.getGroupId()); |
|||
seriesItem.setGroupName(chartGroupItem.getGroupName()); |
|||
List<Double> values = new ArrayList<>(); |
|||
for (int i = 0; i < axisColumnList.size(); i++) { |
|||
double result = random.nextInt(1000)+3000; |
|||
values.add(result); |
|||
} |
|||
seriesItem.setValues(values); |
|||
seriesItemList.add(seriesItem); |
|||
} |
|||
//更新total
|
|||
for (int i = 0; i < axisColumnList.size(); i++) { |
|||
ChartAxisColumn axisColumn = axisColumnList.get(i); |
|||
BigDecimal total = BigDecimal.ZERO; |
|||
for (ChartSeriesItem seriesItem : seriesItemList) { |
|||
double value = seriesItem.getValues().get(i); |
|||
total = total.add(new BigDecimal(value+"")); |
|||
} |
|||
axisColumn.setTotal(total.doubleValue()); |
|||
} |
|||
chartDataVo.setAxisColumnList(axisColumnList); |
|||
chartDataVo.setGroupItemList(groupItemList); |
|||
chartDataVo.setSeriesItemList(seriesItemList); |
|||
String v = JsonUtil.objectToJson(chartDataVo); |
|||
System.out.println(v); |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/9/19 |
|||
*/ |
|||
public interface ApplyTypeConst { |
|||
|
|||
String CostBill = "CostBill"; |
|||
String CheckCost = "CheckCost"; |
|||
String ReleasePolicy = "ReleasePolicy"; |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/1/30 |
|||
*/ |
|||
public interface BudgetLogRollbackFlag { |
|||
|
|||
/** 正常(标记为最新记录) */ |
|||
Integer State_0 = 0; |
|||
|
|||
/** 回退 */ |
|||
Integer State_1 = 1; |
|||
|
|||
/** 回退已处理 */ |
|||
Integer State_2 = 2; |
|||
|
|||
/** 删除已处理 */ |
|||
Integer State_3 = 3; |
|||
|
|||
} |
@ -0,0 +1,11 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2024/3/20 |
|||
*/ |
|||
public interface DSName { |
|||
|
|||
String QiSheng = "qisheng"; |
|||
|
|||
} |
@ -0,0 +1,10 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/2/24 |
|||
*/ |
|||
public class DateConst { |
|||
|
|||
|
|||
} |
@ -0,0 +1,41 @@ |
|||
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 SUPPLIER_PROP = "supplierId"; |
|||
|
|||
/** 系统租户数据库列 */ |
|||
public static final String TENANT_COLUMN = "tenant_id"; |
|||
|
|||
/** 第三方应用id。如:公众号 */ |
|||
public static final String APP_ID_PROP = "appid"; |
|||
|
|||
/** 通用参数 */ |
|||
public static final int STATUS_YES_1 = 1; |
|||
public static final int STATUS_NO_1 = 0; |
|||
|
|||
public static final String LANG_ZH_CN = "zh_CN"; |
|||
|
|||
|
|||
public static final String ROLE_ADMIN_OPT_LOG = "sys:operationLog:admin"; |
|||
public static final String ROLE_ADMIN_REG = "edu:reg:admin"; |
|||
|
|||
public static final String DEFAULT_PASSWORD = "12345678"; |
|||
|
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/10 |
|||
*/ |
|||
public class OssConst { |
|||
|
|||
/** |
|||
* 访问类型 |
|||
*/ |
|||
public static final String ACCESS_OWNER = "own"; |
|||
public static final String ACCESS_DEPT = "dept"; |
|||
|
|||
/** |
|||
* 文件夹前缀 |
|||
*/ |
|||
public static final String OWNER_DIR = "owner/"; |
|||
public static final String DEPT_DIR = "dept/"; |
|||
public static final String SPLIT = "/"; |
|||
|
|||
/** |
|||
* Tag |
|||
*/ |
|||
public static final String TAG_CREATOR = "creator"; |
|||
public static final String TAG_CREATE_BY = "create_by"; |
|||
|
|||
public static final String FILE_TYPE_DIR = "文件夹"; |
|||
|
|||
public static final String DIR_TYPE_UNKNOWN = "未知"; |
|||
|
|||
} |
@ -0,0 +1,33 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
public class RedisCacheKeys { |
|||
|
|||
public final static String SUPPLIER_RELATE_TREE = "supplier_relate_tree"; |
|||
|
|||
/** 租户列表 */ |
|||
public final static String Tenant_ALL_LIST = "expire_tenant_list"; |
|||
|
|||
|
|||
|
|||
/** 用户登录APP {}客户端 {}用户ID */ |
|||
public final static String LOGIN_KEY_APP = "login_app_{}:{}:"; |
|||
|
|||
/** 微信用户ID */ |
|||
public final static String WX_KEY_USER = "wx_usr_id:{}:"; |
|||
|
|||
/** 手机号验证吗 */ |
|||
public final static String PHONE_KEY = "phone_code_:"; |
|||
|
|||
/** |
|||
* 0-无需操作 |
|||
* 1-未处理 |
|||
* 2-同步中 |
|||
* 3-同步完成,但未更新表 |
|||
*/ |
|||
public final static String HIS_UPDATE = "update_his_flag:"; |
|||
|
|||
} |
@ -0,0 +1,12 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/1 |
|||
*/ |
|||
public class RedisListenerKeys { |
|||
|
|||
public final static String EXPIRED_TYPE = "__keyevent@*__:expired"; |
|||
|
|||
|
|||
} |
@ -0,0 +1,12 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/2/15 |
|||
*/ |
|||
public interface ResultFlag { |
|||
|
|||
int OK = 1; |
|||
int NOT = 0; |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.qs.serve.common.model.consts; |
|||
|
|||
/** |
|||
* 系统配置 |
|||
* @author YenHex |
|||
* @since 2022/11/12 |
|||
*/ |
|||
public interface SysConfigKey { |
|||
|
|||
/** 费用超支 */ |
|||
String TbsBudgetOverspend = "TbsBudgetOverspend"; |
|||
|
|||
/** 活动预计核销日期 */ |
|||
String ActivityPreCheckDays = "ActivityPreCheckDays"; |
|||
|
|||
String DateCheckApplyExcelYear = "DateCheckApplyExcelYear"; |
|||
|
|||
String PolicyDelayDays = "PolicyDelayDays"; |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2024/6/11 |
|||
*/ |
|||
@Data |
|||
public class CommonIdsParam { |
|||
|
|||
private List<String> ids; |
|||
|
|||
} |
@ -0,0 +1,29 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.time.LocalDate; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/6/5 |
|||
*/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class DateSplitDTO { |
|||
|
|||
Integer days; |
|||
|
|||
LocalDate startDate; |
|||
|
|||
LocalDate endDate; |
|||
|
|||
Integer yearMonth; |
|||
|
|||
Integer sort; |
|||
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/4/25 |
|||
*/ |
|||
@Data |
|||
public class DiffFieldVal { |
|||
|
|||
String field; |
|||
String comment; |
|||
String orgValue; |
|||
String newValue; |
|||
|
|||
} |
@ -0,0 +1,27 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import com.qs.serve.common.util.PageUtil; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/5/17 |
|||
*/ |
|||
@Data |
|||
public class HeaderOption { |
|||
|
|||
private String name; |
|||
// private String width;
|
|||
// private String prefix;
|
|||
// private String suffix;
|
|||
private Boolean isSum; |
|||
private Integer decimalLength; |
|||
private String align; |
|||
private List<HeaderOption> children; |
|||
|
|||
public HeaderOption(String name){ |
|||
this.name = name; |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
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); |
|||
} |
|||
|
|||
public static <TYPE,R_TYPE> PageVo<R_TYPE> initNewList(PageVo<TYPE> oldVo,List<R_TYPE> list){ |
|||
PageVo<R_TYPE> newVo = new PageVo<>(); |
|||
newVo.setPageNum(oldVo.getPageNum()); |
|||
newVo.setPageSize(oldVo.getPageSize()); |
|||
newVo.setTotal(oldVo.getTotal()); |
|||
newVo.setTotalPage(oldVo.getTotalPage()); |
|||
newVo.setList(list); |
|||
return newVo; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,223 @@ |
|||
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 IMPORT_ERROR = 201; |
|||
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 errorImport(Object data) { |
|||
return new R<>(IMPORT_ERROR,"导入异常",data); |
|||
} |
|||
|
|||
public static <TYPE> R<TYPE> error(String message,TYPE data) { |
|||
return new R<>(FAILED_STATUS,FAILED_TIPS,data); |
|||
} |
|||
|
|||
|
|||
|
|||
public static R error() { |
|||
return new R<>(FAILED_STATUS,FAILED_TIPS,null); |
|||
} |
|||
public static R errorNotFound() { |
|||
return new R<>(FAILED_STATUS,NOT_FOUND_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){ |
|||
return R.ok(buildPageHelperList(data)); |
|||
} |
|||
|
|||
public static <TYPE> PageVo<TYPE> buildPageHelperList(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 pageVo; |
|||
} |
|||
|
|||
public static <TYPE,R_TYPE> PageVo<R_TYPE> buildPageHelperList(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 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); |
|||
} |
|||
|
|||
public static <TYPE> R<PageVo<TYPE>> byPageList(Integer pageNum,Integer pageSize,Long total,List<TYPE> data){ |
|||
PageVo<TYPE> pageVo = new PageVo<>(); |
|||
pageVo.setPageSize(pageSize); |
|||
pageVo.setPageNum(pageNum); |
|||
pageVo.setTotal(total); |
|||
pageVo.setList(data); |
|||
pageVo.setTotalPage(total%pageSize==0?total/pageSize:total/pageSize+1); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
public static <TYPE> R<PageVo<TYPE>> byPageList(Long total,List<TYPE> data){ |
|||
Integer pageNum = PageUtil.getPageNum(); |
|||
Integer pageSize = PageUtil.getPageSize(); |
|||
PageVo<TYPE> pageVo = new PageVo<>(); |
|||
pageVo.setPageSize(pageSize); |
|||
pageVo.setPageNum(pageNum); |
|||
pageVo.setTotal(total); |
|||
pageVo.setList(data); |
|||
pageVo.setTotalPage(total%pageSize==0?total/pageSize:total/pageSize+1); |
|||
return R.ok(pageVo); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import com.qs.serve.common.util.PageUtil; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/5/17 |
|||
*/ |
|||
@Data |
|||
public class RowParam { |
|||
|
|||
private Integer startIndex; |
|||
private Integer size; |
|||
|
|||
public RowParam(Integer startIndex, Integer size) { |
|||
this.startIndex = startIndex; |
|||
this.size = size; |
|||
} |
|||
|
|||
public RowParam(boolean pageable) { |
|||
this.size = PageUtil.getPageSize(); |
|||
this.startIndex = (PageUtil.getPageNum()-1)*size; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2024/8/29 |
|||
*/ |
|||
@Data |
|||
public class SimpleCountValue { |
|||
|
|||
String id; |
|||
|
|||
Long count; |
|||
|
|||
} |
@ -0,0 +1,28 @@ |
|||
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; |
|||
|
|||
public SimpleKeyValue(String label,T value){ |
|||
this.label = label; |
|||
this.value = value; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,29 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/4/7 |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor |
|||
public class TargetDTO { |
|||
String targetId; |
|||
String targetCode; |
|||
String targetName; |
|||
|
|||
public TargetDTO(String targetId, String targetCode, String targetName) { |
|||
this.targetId = targetId; |
|||
this.targetCode = targetCode; |
|||
this.targetName = targetName; |
|||
} |
|||
|
|||
//拜访相关业务
|
|||
String localX; |
|||
String localY; |
|||
String address; |
|||
String mapAddress; |
|||
|
|||
} |
@ -0,0 +1,23 @@ |
|||
package com.qs.serve.common.model.dto; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
|
|||
import java.time.LocalDate; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/4/7 |
|||
*/ |
|||
@Data |
|||
public class TargetObjectDTO { |
|||
|
|||
String targetType; |
|||
String targetId; |
|||
|
|||
String targetCode; |
|||
String targetName; |
|||
|
|||
} |
@ -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==null||children.size()<=0){ |
|||
return null; |
|||
} |
|||
return children; |
|||
} |
|||
} |
@ -0,0 +1,53 @@ |
|||
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("查询"), |
|||
|
|||
LEAVE("离职"), |
|||
EXPORT("导出"), |
|||
SUBMIT("提交"), |
|||
|
|||
RESET("重置"); |
|||
|
|||
String value; |
|||
} |
@ -0,0 +1,91 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/1/30 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum BudgetLogOptFlag { |
|||
|
|||
/** 0-预算新增 */ |
|||
State_0(0,true), |
|||
|
|||
/** 1-费用申请(费用申请,预算调减) */ |
|||
State_1(1,false), |
|||
|
|||
/** 2-预算调增 */ |
|||
State_2(2,true), |
|||
|
|||
/** 3-预算调减 */ |
|||
State_3(3,false), |
|||
|
|||
/** 4-费用释放(预算调增) */ |
|||
State_4(4,true), |
|||
|
|||
/** 5-费用申请调增(费用申请,预算调增) */ |
|||
State_5(5,true), |
|||
|
|||
/** 6-费用申请调减(费用申请,预算调减) */ |
|||
State_6(6,false), |
|||
|
|||
/** 已取消 7-销售区域迁移调增 */ |
|||
State_7(7,false), |
|||
|
|||
/** 已取消 8-销售区域迁移调减 */ |
|||
State_8(8,false), |
|||
|
|||
/** 已取消 9-行政区域迁移调增 */ |
|||
State_9(9,false), |
|||
|
|||
/** 已取消 10-行政区域迁移调减 */ |
|||
State_10(10,false), |
|||
|
|||
/** 11-政策申请 */ |
|||
State_11(11,false), |
|||
|
|||
/** 12-政策申请调增(政策申请,预算调增) */ |
|||
State_12(12,true), |
|||
|
|||
/** 13-政策因费用申请调增(政策申请,预算转移) */ |
|||
State_13(13,true), |
|||
|
|||
/** 14-政策申请释放(被拒绝后,政策释放) */ |
|||
State_14(14,true), |
|||
|
|||
/** 15-费用申请释放(被拒绝后,预算增加) */ |
|||
State_15(15,true), |
|||
|
|||
/** 16-政策释放(释放操作,预算增加) */ |
|||
State_16(16,true); |
|||
|
|||
/** |
|||
* 编码 |
|||
*/ |
|||
private Integer code; |
|||
|
|||
/** |
|||
* 是否取反,添加预算 |
|||
*/ |
|||
private boolean addBudget; |
|||
|
|||
private final static List<Integer> finalBudgetOptFlag = Arrays.asList(0, 2, 3); |
|||
|
|||
public final static List<Integer> finalCostOptFlag = Arrays.asList(1,4,5,6); |
|||
|
|||
public final static List<Integer> finalPolicyOptFlag = Arrays.asList(11,12,13,14,15); |
|||
|
|||
/** |
|||
* 预算规划,状态0,2,3,新增、调增、调减 |
|||
*/ |
|||
public static List<Integer> getFinalBudgetOptFlag(){ |
|||
return finalBudgetOptFlag; |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/10 |
|||
*/ |
|||
@AllArgsConstructor |
|||
public enum ExcelFilePath { |
|||
|
|||
/** 地址 */ |
|||
EDU_REG_ARCHIVE_IN("1000",""); |
|||
|
|||
String code; |
|||
|
|||
String path; |
|||
|
|||
} |
@ -0,0 +1,44 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* 请求状态码 |
|||
* @author YenHex |
|||
* @since 2022/2/28 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum HttpCode { |
|||
|
|||
/** 基本参数 */ |
|||
SUCCESS(200,"操作成功"), |
|||
ERROR(500,"操作失败"), |
|||
DEV_ERR(500,"部分功能开发中..."), |
|||
WX_ERR(500,"微信服务调用异常"), |
|||
|
|||
LOGIN_ERR(401,"登录信息已过期或失效"), |
|||
LOGIN_ERR_2(500,"该账号已被停用,请联系系统管理员"), |
|||
LOGIN_ERR_1(500,"账号或登录密码不正确"), |
|||
LOGIN_ERR_4001(4001,"商户信息错误"), |
|||
LOGIN_ERR_4002(4002,"应用信息错误"), |
|||
|
|||
|
|||
/** 资源权限相关 */ |
|||
FORBIDDEN_403(403,"权限不足,无法访问"), |
|||
FORBIDDEN_403_1(403,"账套未注册,无权访问"), |
|||
FORBIDDEN_403_2(403,"客户端未注册,无权访问"), |
|||
FORBIDDEN_403_3(403,"IP地址未注册,无权访问"), |
|||
FORBIDDEN_403_4(403,"无招生超管权限,无权访问"), |
|||
FORBIDDEN_404(404,"资源不存在或被移除"), |
|||
FORBIDDEN_406(406,"系统正常处理,请勿重复提交"), |
|||
|
|||
|
|||
ERROR_EDU_SCHOOL_YEAR(5031,"当前学年信息错误,请重新设置当前学年"), |
|||
; |
|||
|
|||
Integer code; |
|||
String msg; |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/3/8 |
|||
*/ |
|||
public enum InterType { |
|||
|
|||
/** |
|||
* 前台接口 |
|||
*/ |
|||
API, |
|||
|
|||
|
|||
|
|||
/** |
|||
* 后台接口 |
|||
*/ |
|||
ADMIN |
|||
|
|||
} |
@ -0,0 +1,40 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2023/9/13 |
|||
*/ |
|||
public class MonthValues { |
|||
|
|||
public static final int[] Q1 = {1,2,3}; |
|||
public static final int[] Q2 = {4,5,6}; |
|||
public static final int[] Q3 = {7,8,9}; |
|||
public static final int[] Q4 = {10,11,12}; |
|||
|
|||
|
|||
|
|||
public static int[] getQArr(Integer val){ |
|||
for (int i = 0; i < Q1.length; i++) { |
|||
if(val==Q1[i]){ |
|||
return Q1; |
|||
} |
|||
} |
|||
for (int i = 0; i < Q2.length; i++) { |
|||
if(val==Q2[i]){ |
|||
return Q2; |
|||
} |
|||
} |
|||
for (int i = 0; i < Q3.length; i++) { |
|||
if(val==Q3[i]){ |
|||
return Q3; |
|||
} |
|||
} |
|||
for (int i = 0; i < Q4.length; i++) { |
|||
if(val==Q4[i]){ |
|||
return Q4; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.qs.serve.common.model.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* minio策略配置 |
|||
* @author YenHex |
|||
* @since 2022/5/10 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum PolicyType { |
|||
|
|||
/** |
|||
* 只读 |
|||
*/ |
|||
READ("read-only"), |
|||
|
|||
/** |
|||
* 只写 |
|||
*/ |
|||
WRITE("write-only"), |
|||
|
|||
/** |
|||
* 读写 |
|||
*/ |
|||
READ_WRITE("read-write"); |
|||
|
|||
/** |
|||
* 类型 |
|||
*/ |
|||
private final String type; |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
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","问卷"), |
|||
BAZ("baz","拜访"), |
|||
Policy("tzc","政策"), |
|||
Payment("pay","支付"), |
|||
Budget("budget","预算"), |
|||
Verification("verification","核算"), |
|||
BIZ("biz","业务"), |
|||
BASE("bms","基础档案"), |
|||
GOODS("goods","商品"), |
|||
SYSTEM("sys","系统"), |
|||
UMS("ums","手机用户"), |
|||
DATA("data","数据"), |
|||
Excel("excel","excel数据"), |
|||
Tag("tag","标签"), |
|||
SALE("sale","销售"); |
|||
|
|||
String code; |
|||
|
|||
String vale; |
|||
|
|||
} |
@ -0,0 +1,8 @@ |
|||
package com.qs.serve.common.model.group; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/5/16 |
|||
*/ |
|||
public interface EditGroup { |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue