Browse Source

项目配置:基本模块

tangxiangan 2 years ago
commit
80ab000fdf
99 changed files with 11221 additions and 0 deletions
  1. 133 0
      pdf-tech-common/pom.xml
  2. 24 0
      pdf-tech-common/src/main/java/annotations/EnableCommonTools.java
  3. 20 0
      pdf-tech-common/src/main/java/config/AllowOriginConfig.java
  4. 70 0
      pdf-tech-common/src/main/java/config/RedisConfig.java
  5. 49 0
      pdf-tech-common/src/main/java/config/RedissonConfig.java
  6. 17 0
      pdf-tech-common/src/main/java/config/SpringContextUtilsConfig.java
  7. 160 0
      pdf-tech-common/src/main/java/config/SwaggerConfig.java
  8. 151 0
      pdf-tech-common/src/main/java/constant/CommonConstant.java
  9. 52 0
      pdf-tech-common/src/main/java/enums/CommonEnum.java
  10. 23 0
      pdf-tech-common/src/main/java/enums/EmailStatusEnum.java
  11. 19 0
      pdf-tech-common/src/main/java/enums/SensitiveDataTypeEnum.java
  12. 29 0
      pdf-tech-common/src/main/java/enums/ValidStatusEnum.java
  13. 23 0
      pdf-tech-common/src/main/java/exception/BackendRuntimeException.java
  14. 27 0
      pdf-tech-common/src/main/java/pojo/AccessControlAllowOriginBean.java
  15. 70 0
      pdf-tech-common/src/main/java/pojo/CustomHttpSessionStrategy.java
  16. 89 0
      pdf-tech-common/src/main/java/pojo/CustomUserDetails.java
  17. 17 0
      pdf-tech-common/src/main/java/pojo/MobSendResult.java
  18. 63 0
      pdf-tech-common/src/main/java/pojo/PageVO.java
  19. 64 0
      pdf-tech-common/src/main/java/pojo/QueryParams.java
  20. 83 0
      pdf-tech-common/src/main/java/pojo/ResultMap.java
  21. 35 0
      pdf-tech-common/src/main/java/pojo/SendResult.java
  22. 47 0
      pdf-tech-common/src/main/java/pojo/YunpianSendResult.java
  23. 145 0
      pdf-tech-common/src/main/java/utils/AccountValidatorUtil.java
  24. 133 0
      pdf-tech-common/src/main/java/utils/AesUtils.java
  25. 12 0
      pdf-tech-common/src/main/java/utils/BeanConverter.java
  26. 338 0
      pdf-tech-common/src/main/java/utils/CommonUtils.java
  27. 207 0
      pdf-tech-common/src/main/java/utils/DateUtils.java
  28. 60 0
      pdf-tech-common/src/main/java/utils/DistributedLocker.java
  29. 97 0
      pdf-tech-common/src/main/java/utils/EmailUtils.java
  30. 71 0
      pdf-tech-common/src/main/java/utils/FileUtils.java
  31. 326 0
      pdf-tech-common/src/main/java/utils/HttpClientUtils.java
  32. 82 0
      pdf-tech-common/src/main/java/utils/ImageCodeUtils.java
  33. 134 0
      pdf-tech-common/src/main/java/utils/JsonUtils.java
  34. 34 0
      pdf-tech-common/src/main/java/utils/MD5Utils.java
  35. 66 0
      pdf-tech-common/src/main/java/utils/PageUtils.java
  36. 72 0
      pdf-tech-common/src/main/java/utils/QueryUtils.java
  37. 626 0
      pdf-tech-common/src/main/java/utils/RedisUtils.java
  38. 65 0
      pdf-tech-common/src/main/java/utils/RedissonDistributedLocker.java
  39. 87 0
      pdf-tech-common/src/main/java/utils/SMSUtils.java
  40. 31 0
      pdf-tech-common/src/main/java/utils/SpringContextUtils.java
  41. 8 0
      pdf-tech-core/docker/Dockerfile
  42. 8 0
      pdf-tech-core/docker/dev/Dockerfile
  43. 211 0
      pdf-tech-core/pom.xml
  44. 37 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/PdfTechCoreApplication.java
  45. 34 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/BackendCoreConfig.java
  46. 67 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/ControllerExceptionHandler.java
  47. 12 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/PermissionConfig.java
  48. 81 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/RedisConfig.java
  49. 64 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/RestTemplateConfig.java
  50. 46 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/AuthExceptionEntryPoint.java
  51. 138 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/AuthorizationServerConfig.java
  52. 48 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/CustomPermissionEvaluator.java
  53. 30 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LoginAuthenticationFailHandler.java
  54. 75 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LoginAuthenticationProvider.java
  55. 50 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LoginAuthenticationSuccessHandler.java
  56. 43 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LogoutAuthenticationSuccessHandler.java
  57. 26 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/MethodSecurityConfig.java
  58. 29 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/ResourceServerConfig.java
  59. 106 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/WebSecurityConfig.java
  60. 121 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/constant/AuthConstant.java
  61. 14 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/constant/MembersConstant.java
  62. 95 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/controller/AuthController.java
  63. 45 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/CaptchaActionEnum.java
  64. 29 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/ClientEnum.java
  65. 32 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/ImageCodeTypeEnum.java
  66. 33 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/VerifyTypeEnum.java
  67. 59 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/filter/CorsFilter.java
  68. 35 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/mapper/OauthClientDetailsMapper.java
  69. 33 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/mapper/VppMemberMapper.java
  70. 635 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/Members.java
  71. 291 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/OauthClientDetails.java
  72. 1220 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/OauthClientDetailsExample.java
  73. 220 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/VppMember.java
  74. 890 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/VppMemberExample.java
  75. 39 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/params/MissionCreateParam.java
  76. 37 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/params/SubscriptionCreateParams.java
  77. 43 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/params/UserResetPwdParams.java
  78. 18 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/AesPasswordEncoder.java
  79. 21 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/LoginPasswordEncoder.java
  80. 34 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/TokenPOJO.java
  81. 35 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/UserInfo.java
  82. 30 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/properties/HttpMatchersProperties.java
  83. 116 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/AuthService.java
  84. 28 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/UserService.java
  85. 224 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/AuthServiceImpl.java
  86. 95 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/CustomAuthenticationCodeServicesImpl.java
  87. 103 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/CustomClientDetailsServiceImpl.java
  88. 47 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/CustomUserDetailsServiceImpl.java
  89. 76 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/UserServiceImpl.java
  90. 139 0
      pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/utils/TokenUtils.java
  91. 5 0
      pdf-tech-core/src/main/resources/application-local-db.properties
  92. 9 0
      pdf-tech-core/src/main/resources/application-local-redis.properties
  93. 36 0
      pdf-tech-core/src/main/resources/application-local.yml
  94. 86 0
      pdf-tech-core/src/main/resources/application.yml
  95. 42 0
      pdf-tech-core/src/main/resources/generatorConfig.xml
  96. 228 0
      pdf-tech-core/src/main/resources/logback-spring.xml
  97. 377 0
      pdf-tech-core/src/main/resources/sqlmap/OauthClientDetailsMapper.xml
  98. 305 0
      pdf-tech-core/src/main/resources/sqlmap/VppMemberMapper.xml
  99. 332 0
      pom.xml

+ 133 - 0
pdf-tech-common/pom.xml

@@ -0,0 +1,133 @@
+<?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>
+    <artifactId>pdf-tech-common</artifactId>
+    <packaging>jar</packaging>
+    <name>pdf-tech-common</name>
+    <description>pdf-tech-common</description>
+
+    <parent>
+        <groupId>cn.kdan.pdf.tech</groupId>
+        <artifactId>backend</artifactId>
+        <version>0.0.1</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.apache.commons</groupId>-->
+<!--            <artifactId>commons-pool2</artifactId>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <version>2.3.2.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>3.11.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.session</groupId>
+            <artifactId>spring-session-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk16</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>backend-common</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 24 - 0
pdf-tech-common/src/main/java/annotations/EnableCommonTools.java

@@ -0,0 +1,24 @@
+package annotations;
+
+
+import config.RedisConfig;
+import config.SpringContextUtilsConfig;
+import config.SwaggerConfig;
+import org.springframework.context.annotation.Import;
+import utils.EmailUtils;
+import utils.SMSUtils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author tangxiangan
+ */
+@Target(value = ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import({ RedisConfig.class,SwaggerConfig.class, SpringContextUtilsConfig.class, SMSUtils.class, EmailUtils.class})
+public @interface EnableCommonTools {
+
+}

+ 20 - 0
pdf-tech-common/src/main/java/config/AllowOriginConfig.java

@@ -0,0 +1,20 @@
+package config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import pojo.AccessControlAllowOriginBean;
+
+@Configuration
+public class AllowOriginConfig {
+
+    @Value("${access-control-allow-origin:*}")
+    private String accessControlAllowOrigin;
+
+    @Bean
+    public AccessControlAllowOriginBean accessControllerAllowOriginBean() {
+        AccessControlAllowOriginBean bean = new AccessControlAllowOriginBean();
+        bean.setAllowOrigin(accessControlAllowOrigin);
+        return bean;
+    }
+}

+ 70 - 0
pdf-tech-common/src/main/java/config/RedisConfig.java

@@ -0,0 +1,70 @@
+package config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+//import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.redis.connection.RedisClusterConfiguration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisNode;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import utils.RedisUtils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Configuration
+@Import({RedisUtils.class, SpringContextUtilsConfig.class, AllowOriginConfig.class})
+public class RedisConfig {
+
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(connectionFactory);
+        redisTemplate.setKeySerializer(keySerializer());
+        redisTemplate.setValueSerializer(valueSerializer());
+        redisTemplate.setHashKeySerializer(keySerializer());
+        redisTemplate.setHashValueSerializer(valueSerializer());
+        return redisTemplate;
+    }
+
+    @Bean
+    public RedisTemplate<String, Object> jdkRedisTemplate(LettuceConnectionFactory connectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(connectionFactory);
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
+        return redisTemplate;
+    }
+
+    private RedisSerializer<String> keySerializer() {
+        return new StringRedisSerializer();
+    }
+
+    private RedisSerializer<Object> valueSerializer() {
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+        return jackson2JsonRedisSerializer;
+    }
+
+
+}

+ 49 - 0
pdf-tech-common/src/main/java/config/RedissonConfig.java

@@ -0,0 +1,49 @@
+package config;
+
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import utils.DistributedLocker;
+import utils.RedissonDistributedLocker;
+
+/**
+ * redisson配置
+ */
+@Configuration
+public class RedissonConfig {
+    @Value("${spring.redis.cluster.nodes:}")
+    private  String cluster;
+    @Value("${spring.redis.password:}")
+    private String password;
+
+    @Bean
+    public RedissonClient getRedisson(){
+        String[] nodes = cluster.split(",");
+        //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
+        for (int i = 0; i < nodes.length; i++) {
+            StringBuilder buffer = new StringBuilder("redis://");
+            nodes[i] = String.valueOf(buffer.append(nodes[i]));
+        }
+        RedissonClient redisson;
+        Config config = new Config();
+        //这是用的集群server
+        config.useClusterServers()
+                //设置集群状态扫描时间
+                .setScanInterval(2000)
+                .addNodeAddress(nodes)
+                .setPassword(password);
+        redisson = Redisson.create(config);
+
+        //可通过打印redisson.getConfig().toJSON().toString()来检测是否配置成功
+        return redisson;
+    }
+
+    @Bean
+    public DistributedLocker distributedLocker(RedissonClient redissonClient){
+         return new RedissonDistributedLocker(redissonClient);
+    }
+
+}

+ 17 - 0
pdf-tech-common/src/main/java/config/SpringContextUtilsConfig.java

@@ -0,0 +1,17 @@
+package config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import utils.SpringContextUtils;
+
+@Configuration
+public class SpringContextUtilsConfig {
+    @Bean
+    /*
+     * SharedSession 中需要用到,直接在这里实例化好了
+     */
+    public SpringContextUtils springContextUtils() {
+        return new SpringContextUtils();
+    }
+
+}

+ 160 - 0
pdf-tech-common/src/main/java/config/SwaggerConfig.java

@@ -0,0 +1,160 @@
+package config;
+
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import constant.CommonConstant;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Profile;
+import springfox.documentation.RequestHandler;
+import springfox.documentation.builders.OAuthBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger.web.SecurityConfiguration;
+import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+
+@SuppressWarnings({"Guava", "deprecation"})
+@EnableSwagger2
+@Profile({"local", "test"})
+@Configuration
+@Import({AllowOriginConfig.class})
+public class SwaggerConfig {
+
+    @Value("${security.oauth2.client.access-token-uri:}")
+    private String tokenUri;
+
+    @Value("${security.oauth2.client.client-id:}")
+    private String clientId;
+
+    @Value("${security.oauth2.client.client-secret:}")
+    private String clientSecret;
+
+    @Value("${security.oauth2.client.user-authorization-uri:}")
+    private String authorizeUri;
+
+    @Value("${swagger.basePackage:cn.kdan.pdf.backend.core.controller}")
+    private String basePackage;
+
+    @Value("${swagger.title:sample Title}")
+    private String title;
+
+    @Value("${swagger.description:Sample Description}")
+    private String description;
+
+    private String version = "v1.0";
+
+    private String contactName = "";
+
+    private String contactEmail = "";
+
+    /**
+     * 主要是这个方法,其他的方法是抽出去的,所以大家不要害怕为啥有这么多方法
+     * 在 basePackage 里面写需要生成文档的 controller 路径
+     */
+    @Bean
+    public Docket api() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                .apis(basePackage())
+                .paths(PathSelectors.any())
+                .build()
+                .apiInfo(apiInfo())
+                .securitySchemes(Collections.singletonList(securityScheme()))
+                .securityContexts(Collections.singletonList(securityContext()));
+    }
+
+    private Predicate<RequestHandler> basePackage() {
+        return input -> {
+            assert input != null;
+            return declaringClass(input).transform(handlerPackage()).or(true);
+        };
+    }
+
+    private Function<Class<?>, Boolean> handlerPackage() {
+        return input -> {
+            // 循环判断匹配
+            for (String strPackage : basePackage.split(CommonConstant.STRING_SIGN_COMMA)) {
+                assert input != null;
+                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
+                if (isMatch) {
+                    return true;
+                }
+            }
+            return false;
+        };
+    }
+
+    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
+        return Optional.fromNullable(input.declaringClass());
+    }
+
+    /**
+     * 这个方法主要是写一些文档的描述
+     */
+    private ApiInfo apiInfo() {
+        return new ApiInfo(
+                title,
+                description,
+                version,
+                "",
+                new Contact(contactName, "", contactEmail),
+                "", "", Collections.emptyList());
+    }
+
+    /**
+     * 这个类决定了你使用哪种认证方式,我这里使用密码模式
+     * 其他方式自己摸索一下,完全莫问题啊
+     */
+    private SecurityScheme securityScheme() {
+        GrantType grantType = new ResourceOwnerPasswordCredentialsGrant(tokenUri);
+
+        return new OAuthBuilder()
+                .name("spring_oauth")
+                .grantTypes(Collections.singletonList(grantType))
+                .scopes(Arrays.asList(scopes()))
+                .build();
+    }
+
+    @Bean
+    public SecurityConfiguration security() {
+        return SecurityConfigurationBuilder.builder()
+                .clientId(clientId)
+                .clientSecret(clientSecret)
+                .scopeSeparator(" ")
+                .useBasicAuthenticationWithAccessCodeGrant(false)
+                .build();
+    }
+
+    /**
+     * 这里设置 swagger2 认证的安全上下文
+     */
+    private SecurityContext securityContext() {
+        return SecurityContext.builder()
+                .securityReferences(Collections.singletonList(new SecurityReference("spring_oauth", scopes())))
+                .forPaths(PathSelectors.any())
+                .build();
+    }
+
+    /**
+     * 这里是写允许认证的scope
+     */
+    private AuthorizationScope[] scopes() {
+        return new AuthorizationScope[]{
+                new AuthorizationScope("read", "for read operations"),
+                new AuthorizationScope("write", "for write operations")};
+    }
+
+
+}

+ 151 - 0
pdf-tech-common/src/main/java/constant/CommonConstant.java

@@ -0,0 +1,151 @@
+package constant;
+
+public interface CommonConstant {
+
+    // 字符串分割符
+    String STRING_SIGN_COMMA = ",";
+    String STRING_SIGN_PERIOD = ".";
+    String STRING_SIGN_COLON = ":";
+    String STRING_SIGN_STAR = "*";
+    String STRING_UNDERLINE = "_";
+    String STRING_VIRGULE = "/";
+    String STRIKE_THROUGH = "-";
+    String STRING_VERTICAL = "|";
+    String STRING_PERCENT = "%";
+    String STRING_WAVE = "~";
+    String STRING_ENTER = "\n";
+    String STRING_EMPTY = "";
+    String STRING_THROUGH_AND_SIGN_STAR = "-*";
+    String STRING_AT = "@";
+    String EQUALS_STR = "=";
+
+    String STRING_IMAGE = "image";
+
+    String STRING_CODE = "code";
+
+    String ENCODING_UTF8 = "utf-8";
+
+    String CODE_OK = "ok";
+
+    String SUCCESS_CODE = "200";
+
+    String AUTH_TOKEN = "x-auth-token";
+
+    String STRING_PNG = "png";
+    String STRING_NO_CACHE = "No-cache";
+    int SUCCESS = 200;
+    String RESULT_SUCCESS = "success";
+    String CODE_SUCCESS = "SUCCESS";
+    String PAGE_INFO = "pageInfo";
+
+    String PARAMS_MISSING_ERROR = "缺少必要参数!";
+
+    String DUPLICATED_REQUEST_ERROR = "重复请求!";
+    String DUPLICATED_REQUEST_HEADER_TIMESTAMP = "timeStamp";
+    String DUPLICATED_REQUEST_HEADER_RANDOM = "random";
+
+    String DEFAULT_ENCRYPT_KEY = "1234567890123456";
+    String DEFAULT_ENCRYPT_IV_KEY = "6543********4321";
+    /**
+     * 参数错误状态
+     */
+    int EXCEPTION_CODE_PARAMETERS_ERROR = 300;
+    String EXCEPTION_MSG_PARAMETERS_ERROR = "没有获取到必须的参数或参数格式错误";
+    String EXCEPTION_MSG_PARAMETERS_AT_LEAST_ONE = "请至少设置一个有效的参数";
+
+    int ERROR = 400; // 400 失败
+
+    /**
+     * 服务错误状态
+     */
+    int EXCEPTION_CODE_SERVER_ERROR = 500;
+    String EXCEPTION_MSG_SERVER_ERROR = "服务错误";
+
+    /**
+     * 数据库错误状态
+     */
+    int EXCEPTION_CODE_SERVER_DATA_ACCESS_ERROR = 600;
+    String EXCEPTION_MSG_SERVER_DATA_ACCESS_ERROR = "数据库服务错误";
+
+    /**
+     * 运行错误状态
+     */
+    int EXCEPTION_CODE_RUNTIME_ERROR = 700;
+    String EXCEPTION_MSG_RUNTIME_ERROR = "运行时错误";
+
+    /**
+     * 用户登录错误状态
+     */
+    int EXCEPTION_CODE_LOGIN_USER_NOT_FOUND = 800;
+    String EXCEPTION_MSG_LOGIN_USER_NOT_FOUND = "未找到登录用户信息";
+
+    int EXCEPTION_CODE_CURRENT_USER_NO_PERMISSION = 801;
+    String EXCEPTION_MSG_CURRENT_USER_NO_PERMISSION = "当前用户不具备操作权限";
+
+    int EXCEPTION_CODE_DATA_KEY_DECRYPT = 902;
+    String EXCEPTION_MSG_DATA_KEY_DECRYPT = "数据解密失败";
+
+    int EXCEPTION_CODE_DUPLICATED_REQUEST = 903;
+    String EXCEPTION_MSG_DUPLICATED_REQUEST = "请不要重复请求";
+
+    /**
+     * 字节编码
+     */
+    String ENCODE_UTF8 = "utf-8";
+
+
+    String TEXT_HTML_UTF8 = "text/html; charset=utf-8";
+
+    /**
+     * 分页默认第一页
+     */
+    int PAGENUM = 1;
+
+    /**
+     * 分页默认每页显示10条
+     */
+    int PAGESIZE = 10;
+
+    /**
+     * 分页默认排序规则
+     */
+    String ORDERBYCLAUSE = "create_time desc";
+
+    String verifyMessage = "您正在%s,验证码为%s,十五分钟后失效。请您按照提示填写验证码,切勿将验证码泄露于他人。";
+
+    String resetPasswordEmailTemplate = "<p>尊敬的用户您好:</p>\n" +
+            "<p>请点击下方的链接以便对您的17PDF Reader(Staging)账号密码进行重置</p>\n" +
+            "<table class=\"btn-primary\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n" +
+            "  <tr>\n" +
+            "    <td>\n" +
+            "    <a href=\"%s\"> <button>重置密码</button></a>\n" +
+            "    </td>\n" +
+            "  </tr>\n" +
+            "</table>\n" +
+            "<p>如果无法点击,请将链接复制到浏览器地址栏,访问该地址进行重置</p>\n" +
+            "<p>系统邮件,请勿回复。</p>";
+
+    /**
+     * 名称校验不通过信息
+     */
+    String NAME_INVALID_MESSAGE="名称必须以下划线、数字、字母开头,不能包含特殊字符";
+
+    /**
+     * 文件起始行
+     */
+    Integer SOURCE_LINE_FROM = 1;
+
+    String PASSWORD_STRENTH = "1";
+    /**
+     * 文件结束行
+     */
+    Integer SOURCE_LINE_TO = 500;
+
+    String ZERO_STRING = "0";
+
+    /** 手机号正则验证 */
+    String mobileRegex = "^(1[0-9]{10})$";
+    /** 邮箱正则验证 */
+    String emailRegex = "^([\\.a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\\.[a-zA-Z0-9_-]{2,3}){1,2})$";
+
+}

+ 52 - 0
pdf-tech-common/src/main/java/enums/CommonEnum.java

@@ -0,0 +1,52 @@
+package enums;
+
+public class CommonEnum {
+
+    /** 是否枚举 */
+    public static enum YesOrNoEnum{
+        YES(1), // 是
+        NO(0); // 否
+
+        private Integer value;
+
+        YesOrNoEnum(Integer value) {
+            this.value = value;
+        }
+
+        public Integer value() {
+            return value;
+        }
+    }
+
+    public static enum SubscriptionTypeEnum{
+        PRICING("Pricing"), // 券服务
+        SET_PRICING("SetPricing"); // 会员服务
+
+        private String value;
+
+        SubscriptionTypeEnum(String value) {
+            this.value = value;
+        }
+
+        public String value() {
+            return value;
+        }
+    }
+
+    public static enum DataTypeEnum{
+        MEMBERS("members"), // 用户
+        PRICINGS("pricings"), // 券服务
+        SET_PRICINGS("setPricings"); // 会员服务
+
+        private String value;
+
+        DataTypeEnum(String value) {
+            this.value = value;
+        }
+
+        public String value() {
+            return value;
+        }
+    }
+
+}

+ 23 - 0
pdf-tech-common/src/main/java/enums/EmailStatusEnum.java

@@ -0,0 +1,23 @@
+package enums;
+
+/**
+ * 邮箱状态
+ * CD502200
+ */
+public enum EmailStatusEnum {
+
+    //未生效
+    INACTIVE("1"),
+    //生效
+    ACTIVE("2");
+
+    private String value;
+
+    EmailStatusEnum(String value) {
+        this.value = value;
+    }
+
+    public String value() {
+        return value;
+    }
+}

+ 19 - 0
pdf-tech-common/src/main/java/enums/SensitiveDataTypeEnum.java

@@ -0,0 +1,19 @@
+package enums;
+
+/**
+ * 敏感数据类型
+ */
+public enum SensitiveDataTypeEnum {
+    /**
+     * 手机
+     */
+    MOBILE,
+    /**
+     *邮件
+     */
+    EMAIL,
+    /**
+     *其他
+     */
+    OTHER
+}

+ 29 - 0
pdf-tech-common/src/main/java/enums/ValidStatusEnum.java

@@ -0,0 +1,29 @@
+package enums;
+
+/**
+ * 有效状态(是否可用)
+ * CD101100
+ *
+ * @author tangxiangan
+ */
+public enum ValidStatusEnum {
+
+    /**
+     * 无效
+     */
+    INVALID("0"),
+    /**
+     * 有效
+     */
+    VALID("1");
+
+    private final String value;
+
+    ValidStatusEnum(String value) {
+        this.value = value;
+    }
+
+    public String value() {
+        return value;
+    }
+}

+ 23 - 0
pdf-tech-common/src/main/java/exception/BackendRuntimeException.java

@@ -0,0 +1,23 @@
+package exception;
+
+/**
+ * @author tangxiangan
+ */
+public class BackendRuntimeException extends RuntimeException {
+    public BackendRuntimeException() {
+        super();
+    }
+
+    public BackendRuntimeException(String message) {
+        super(message);
+    }
+
+    public BackendRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public BackendRuntimeException(Throwable cause) {
+        super(cause);
+    }
+
+}

+ 27 - 0
pdf-tech-common/src/main/java/pojo/AccessControlAllowOriginBean.java

@@ -0,0 +1,27 @@
+package pojo;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class AccessControlAllowOriginBean {
+
+    private String allowOrigin;
+
+    public String getAllowOrigin() {
+        return allowOrigin;
+    }
+
+    public void setAllowOrigin(String allowOrigin) {
+        this.allowOrigin = allowOrigin;
+    }
+
+    public boolean isAll() {
+        return "*".equals(allowOrigin);
+    }
+
+    public Set<String> getDomains() {
+        String[] domains = allowOrigin.trim().split(",");
+        return new HashSet<>(Arrays.asList(domains));
+    }
+}

+ 70 - 0
pdf-tech-common/src/main/java/pojo/CustomHttpSessionStrategy.java

@@ -0,0 +1,70 @@
+package pojo;
+
+import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author tangxiangan
+ */
+public class CustomHttpSessionStrategy extends HeaderHttpSessionIdResolver {
+
+    private static final String HEADER_X_AUTH_TOKEN = "x-auth-token";
+
+    private static final String HEADER_AUTHENTICATION_INFO = "Authentication-Info";
+
+    private final String headerName;
+
+    /**
+     * Convenience factory to create {@link HeaderHttpSessionIdResolver} that uses
+     * "X-Auth-AccessToken" header.
+     *
+     * @return the instance configured to use "X-Auth-AccessToken" header
+     */
+    public static HeaderHttpSessionIdResolver xAuthToken() {
+        return new HeaderHttpSessionIdResolver(HEADER_X_AUTH_TOKEN);
+    }
+
+    /**
+     * Convenience factory to create {@link HeaderHttpSessionIdResolver} that uses
+     * "Authentication-Info" header.
+     *
+     * @return the instance configured to use "Authentication-Info" header
+     */
+    public static HeaderHttpSessionIdResolver authenticationInfo() {
+        return new HeaderHttpSessionIdResolver(HEADER_AUTHENTICATION_INFO);
+    }
+
+
+    /**
+     * The name of the header to obtain the session id from.
+     *
+     * @param headerName the name of the header to obtain the session id from.
+     */
+    public CustomHttpSessionStrategy(String headerName) {
+        super(headerName);
+        this.headerName = headerName;
+    }
+
+    @Override
+    public List<String> resolveSessionIds(HttpServletRequest request) {
+        String headerValue = request.getHeader(this.headerName);
+        return headerValue != null ? Collections.singletonList(headerValue)
+                : Collections.emptyList();
+    }
+
+    @Override
+    public void setSessionId(HttpServletRequest request, HttpServletResponse response,
+                             String sessionId) {
+        response.setHeader(this.headerName, sessionId);
+    }
+
+    @Override
+    public void expireSession(HttpServletRequest request, HttpServletResponse response) {
+        response.setHeader(this.headerName, "");
+    }
+
+}

+ 89 - 0
pdf-tech-common/src/main/java/pojo/CustomUserDetails.java

@@ -0,0 +1,89 @@
+package pojo;
+
+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.Set;
+
+/**
+ * 用户信息的实体类
+ *
+ * @author tangxiangan
+ */
+public class CustomUserDetails implements UserDetails {
+    private static final long serialVersionUID = 6172991108803896779L;
+
+    private String id;
+    private String username;
+    private String password;
+    private Set<GrantedAuthority> authorities;
+    private boolean accountNonExpired;
+    private boolean accountNonLocked;
+    private boolean credentialsNonExpired;
+    private boolean enabled;
+
+    public CustomUserDetails(String id, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Set<SimpleGrantedAuthority> authorities) {
+        if (username != null && !"".equals(username) && password != null) {
+            this.id = id;
+            this.username = username;
+            this.password = password;
+            this.enabled = enabled;
+            this.accountNonExpired = accountNonExpired;
+            this.credentialsNonExpired = credentialsNonExpired;
+            this.accountNonLocked = accountNonLocked;
+            this.authorities = Collections.unmodifiableSet(authorities);
+        } else {
+            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
+        }
+    }
+
+    /**
+     * 对当前的用户赋予其应有的权限
+     */
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return this.authorities;
+    }
+
+    @Override
+    public String getPassword() {
+        return this.password;
+    }
+
+    @Override
+    public String getUsername() {
+        return this.username;
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return this.accountNonExpired;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return this.accountNonLocked;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return this.credentialsNonExpired;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return this.enabled;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+}

+ 17 - 0
pdf-tech-common/src/main/java/pojo/MobSendResult.java

@@ -0,0 +1,17 @@
+package pojo;
+
+/**
+ * @author tangxiangan
+ */
+public class MobSendResult {
+
+    private Integer status;
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 63 - 0
pdf-tech-common/src/main/java/pojo/PageVO.java

@@ -0,0 +1,63 @@
+package pojo;
+
+import java.util.List;
+
+public class PageVO<T> {
+	 //当前页
+    private int pageNum;
+    //每页的数量
+    private int pageSize;
+    //当前页的数量
+    private int size;
+    //总记录数
+    private long total;
+    //总页数
+    private int pages;
+    //list
+	private List<?> list;
+	
+	public PageVO(){
+		
+	}
+
+	public PageVO(List<T> list){
+		this.list = list;
+	}
+	
+	public int getPageNum() {
+		return pageNum;
+	}
+	public void setPageNum(int pageNum) {
+		this.pageNum = pageNum;
+	}
+	public int getPageSize() {
+		return pageSize;
+	}
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+	public int getSize() {
+		return size;
+	}
+	public void setSize(int size) {
+		this.size = size;
+	}
+	public long getTotal() {
+		return total;
+	}
+	public void setTotal(long total) {
+		this.total = total;
+	}
+	public int getPages() {
+		return pages;
+	}
+	public void setPages(int pages) {
+		this.pages = pages;
+	}
+	public List<?> getList() {
+		return list;
+	}
+	public void setList(List<?> list) {
+		this.list = list;
+	}
+}

+ 64 - 0
pdf-tech-common/src/main/java/pojo/QueryParams.java

@@ -0,0 +1,64 @@
+package pojo;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @author tangxiangan
+ */
+public class QueryParams {
+
+    @ApiModelProperty(name = "page", value = "页码", example = "1")
+    private Integer page;
+
+    @ApiModelProperty(name = "rows", value = "行数", example = "10")
+    private Integer rows;
+
+    @ApiModelProperty(name = "sidx", value = "排序字段", example = "name")
+    private String sidx;
+
+    @ApiModelProperty(name = "sord", value = "排序规则", example = "asc")
+    private String sord;
+
+    @ApiModelProperty(name = "orderByClause", value = "排序条件", example = "id asc, name asc")
+    private String orderByClause;
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getRows() {
+        return rows;
+    }
+
+    public void setRows(Integer rows) {
+        this.rows = rows;
+    }
+
+    public String getSidx() {
+        return sidx;
+    }
+
+    public void setSidx(String sidx) {
+        this.sidx = sidx;
+    }
+
+    public String getSord() {
+        return sord;
+    }
+
+    public void setSord(String sord) {
+        this.sord = sord;
+    }
+
+    public String getOrderByClause() {
+        return orderByClause;
+    }
+
+    public void setOrderByClause(String orderByClause) {
+        this.orderByClause = orderByClause;
+    }
+}

+ 83 - 0
pdf-tech-common/src/main/java/pojo/ResultMap.java

@@ -0,0 +1,83 @@
+package pojo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import constant.CommonConstant;
+import utils.JsonUtils;
+
+import java.util.Objects;
+
+/**
+ * @author tangxiangan
+ */
+public class ResultMap<T> {
+    private int code;
+    private String msg;
+    private T result;
+
+    public ResultMap() {
+        code = CommonConstant.SUCCESS;
+        msg = CommonConstant.RESULT_SUCCESS;
+    }
+
+    public ResultMap(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public ResultMap(int code, String msg, T result) {
+        super();
+        this.code = code;
+        this.msg = msg;
+        this.result = result;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public T getResult() {
+        return result;
+    }
+
+    public void setResult(T result) {
+        this.result = result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ResultMap<?> resultMap = (ResultMap<?>) o;
+        return code == resultMap.code &&
+                Objects.equals(msg, resultMap.msg) &&
+                Objects.equals(result, resultMap.result);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(code, msg, result);
+    }
+
+
+    @Override
+    public String toString() {
+        return JsonUtils.getJsonString(this);
+    }
+
+    @JsonIgnore
+    public boolean isSuccess() {
+        return this.code == CommonConstant.SUCCESS;
+    }
+}

+ 35 - 0
pdf-tech-common/src/main/java/pojo/SendResult.java

@@ -0,0 +1,35 @@
+package pojo;
+
+/**
+ * @author tangxiangan
+ */
+public class SendResult {
+
+    private Integer code;
+    private String msg;
+    private String mobile;
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public String getMobile() {
+        return mobile;
+    }
+
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+}

+ 47 - 0
pdf-tech-common/src/main/java/pojo/YunpianSendResult.java

@@ -0,0 +1,47 @@
+package pojo;
+
+import java.util.List;
+
+/**
+ * @author tangxiangan
+ */
+public class YunpianSendResult {
+
+    private Integer total_count;
+    private String total_fee;
+    private String unit;
+    private List<SendResult> data;
+
+    public Integer getTotal_count() {
+        return total_count;
+    }
+
+    public void setTotal_count(Integer total_count) {
+        this.total_count = total_count;
+    }
+
+    public String getTotal_fee() {
+        return total_fee;
+    }
+
+    public void setTotal_fee(String total_fee) {
+        this.total_fee = total_fee;
+    }
+
+    public String getUnit() {
+        return unit;
+    }
+
+    public void setUnit(String unit) {
+        this.unit = unit;
+    }
+
+    public List<SendResult> getData() {
+        return data;
+    }
+
+    public void setData(List<SendResult> data) {
+        this.data = data;
+    }
+
+}

+ 145 - 0
pdf-tech-common/src/main/java/utils/AccountValidatorUtil.java

@@ -0,0 +1,145 @@
+package utils;
+
+import java.util.regex.Pattern;
+
+/**
+ * 账户相关属性验证工具
+ * @author tangxiangan
+ */
+public class AccountValidatorUtil {
+    /**
+     * 正则表达式:验证用户名
+     */
+    public static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,20}$";
+
+    /**
+     * 正则表达式:验证密码
+     */
+    public static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,20}$";
+
+    /**
+     * 正则表达式:验证手机号
+     */
+    public static final String REGEX_MOBILE = "^1\\d{10}$";
+
+    /**
+     * 正则表达式:验证邮箱
+     */
+    public static final String REGEX_EMAIL = "^.*@.*$";
+
+    /**
+     * 正则表达式:验证汉字
+     */
+    public static final String REGEX_CHINESE = "^[\u4e00-\u9fa5],{0,}$";
+
+    /**
+     * 正则表达式:验证身份证
+     */
+    public static final String REGEX_ID_CARD = "(^\\d{18}$)|(^\\d{15}$)";
+
+    /**
+     * 正则表达式:验证URL
+     */
+    public static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?#%&=]*)?";
+
+    /**
+     * 正则表达式:验证IP地址
+     */
+    public static final String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)";
+
+    /**
+     * 正则表达式:验证IP制品库名称 只允许输入英文、数字、横杠、下划线
+     */
+    public static final String PROJECT_STORE_NAME = "^[A-Za-z0-9-_]+$";
+
+    /**
+     * 校验用户名
+     *
+     * @param username
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isUsername(String username) {
+        return Pattern.matches(REGEX_USERNAME, username);
+    }
+
+    /**
+     * 校验密码
+     *
+     * @param password
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isPassword(String password) {
+        return Pattern.matches(REGEX_PASSWORD, password);
+    }
+
+    /**
+     * 校验手机号
+     *
+     * @param mobile
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isMobile(String mobile) {
+        return Pattern.matches(REGEX_MOBILE, mobile);
+    }
+
+    /**
+     * 校验邮箱
+     *
+     * @param email
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isEmail(String email) {
+        return Pattern.matches(REGEX_EMAIL, email);
+    }
+
+    /**
+     * 校验汉字
+     *
+     * @param chinese
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isChinese(String chinese) {
+        return Pattern.matches(REGEX_CHINESE, chinese);
+    }
+
+    /**
+     * 校验身份证
+     *
+     * @param idCard
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isIDCard(String idCard) {
+        return Pattern.matches(REGEX_ID_CARD, idCard);
+    }
+
+    /**
+     * 校验URL
+     *
+     * @param url
+     * @return 校验通过返回true,否则返回false
+     */
+    public static boolean isUrl(String url) {
+        return Pattern.matches(REGEX_URL, url);
+    }
+
+    /**
+     * 校验IP地址
+     *
+     * @param ipAddr
+     * @return
+     */
+    public static boolean isIPAddr(String ipAddr) {
+        return Pattern.matches(REGEX_IP_ADDR, ipAddr);
+    }
+
+    /**
+     * 校验制品库名称
+     *
+     * @param
+     * @return
+     */
+    public static boolean isProjectStoreName(String name) {
+        return Pattern.matches(PROJECT_STORE_NAME, name);
+    }
+
+}

+ 133 - 0
pdf-tech-common/src/main/java/utils/AesUtils.java

@@ -0,0 +1,133 @@
+package utils;
+
+import constant.CommonConstant;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+
+public class AesUtils {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AesUtils.class);
+
+    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
+
+    private static final String STRIP_STRING = " \0";
+
+    private static Cipher encryptCipher;
+
+    private static Cipher decryptCipher;
+
+    public synchronized static String encrypt(String content) {
+        return encrypt(CommonConstant.DEFAULT_ENCRYPT_KEY, CommonConstant.DEFAULT_ENCRYPT_IV_KEY, content);
+    }
+
+    public synchronized static String encrypt(String keyString, String ivKeyString, String content) {
+        if (StringUtils.isBlank(content)) {
+            return CommonConstant.STRING_EMPTY;
+        }
+        String encryptText = null;
+        try {
+            Key key = new SecretKeySpec(keyString.getBytes(StandardCharsets.US_ASCII), "AES");
+            encryptCipher = Cipher.getInstance(ALGORITHM);
+            encryptCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivKeyString.getBytes(StandardCharsets.US_ASCII)));
+        } catch (InvalidKeyException e) {
+            LOGGER.error("[AesUtils] invalid key " + keyString, e);
+        } catch (InvalidAlgorithmParameterException e) {
+            LOGGER.error("[AesUtils] invalid algorithm param " + ivKeyString, e);
+        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+            LOGGER.error("[AesUtils] init cipher error", e);
+        }
+        try {
+            byte[] encryptBytes = encryptCipher.doFinal(extendKey(content.getBytes(StandardCharsets.UTF_8)));
+            encryptText = parseByte2Hex(encryptBytes);
+        } catch (Exception e) {
+            LOGGER.error("[AesUtils] encrypt error ", e);
+        }
+        return encryptText;
+    }
+
+    public synchronized static String decrypt(String content) {
+        return decrypt(CommonConstant.DEFAULT_ENCRYPT_KEY, CommonConstant.DEFAULT_ENCRYPT_IV_KEY, content);
+    }
+
+    public synchronized static String decrypt(String keyString, String ivKeyString, String content) {
+        if (StringUtils.isBlank(content)) {
+            return CommonConstant.STRING_EMPTY;
+        }
+        String decryptText = null;
+        try {
+            Key key = new SecretKeySpec(keyString.getBytes(StandardCharsets.US_ASCII), "AES");
+            IvParameterSpec ivKey = new IvParameterSpec(ivKeyString.getBytes(StandardCharsets.US_ASCII));
+            decryptCipher = Cipher.getInstance(ALGORITHM);
+            decryptCipher.init(Cipher.DECRYPT_MODE, key, ivKey);
+        } catch (InvalidKeyException e) {
+            LOGGER.error("[AesUtils]invalid key" + keyString, e);
+        } catch (InvalidAlgorithmParameterException e) {
+            LOGGER.error("[AesUtils] encoding unsupported " + keyString, e);
+        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+            LOGGER.error("[AesUtils] init cipher error", e);
+        }
+        try {
+            byte[] encryptBytes = parseHex2Byte(content);
+            byte[] decryptBytes = decryptCipher.doFinal(encryptBytes);
+            decryptText = new String(decryptBytes, StandardCharsets.UTF_8);
+            decryptText = StringUtils.strip(decryptText, STRIP_STRING);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            LOGGER.error("[AesUtils]invalid key " + keyString, e);
+        }
+        return decryptText;
+    }
+
+    private static byte[] parseHex2Byte(String hexText) {
+        if (StringUtils.isEmpty(hexText)) {
+            return null;
+        }
+        byte[] result = new byte[hexText.length() / 2];
+        for (int i = 0; i < hexText.length() / 2; i++) {
+            int high = Integer.parseInt(hexText.substring(i * 2, i * 2 + 1), 16);
+            int low = Integer.parseInt(hexText.substring(i * 2 + 1, i * 2 + 2), 16);
+            result[i] = (byte) (high * 16 + low);
+        }
+        return result;
+    }
+
+    private static String parseByte2Hex(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte aByte : bytes) {
+            String hex = Integer.toHexString(aByte & 0xFF);
+            if (hex.length() == 1) {
+                hex = '0' + hex;
+            }
+            sb.append(hex.toLowerCase());
+        }
+        return sb.toString();
+    }
+
+    private static byte[] extendKey(byte[] input) {
+        int rest = input.length % 16;
+        if (rest > 0) {
+            byte[] result = new byte[input.length + (16 - rest)];
+            System.arraycopy(input, 0, result, 0, input.length);
+            return result;
+        }
+        return input;
+    }
+
+    public static void main(String[] args) {
+        System.out.println(encrypt("18627945022"));
+        System.out.println(decrypt(CommonConstant.DEFAULT_ENCRYPT_KEY, CommonConstant.DEFAULT_ENCRYPT_IV_KEY, "54db89effcf52bb979778f718f75aa5493439afa76973581ceeee35150764066"));
+    }
+
+}

+ 12 - 0
pdf-tech-common/src/main/java/utils/BeanConverter.java

@@ -0,0 +1,12 @@
+package utils;
+
+/**
+ * BO转VO接口
+ *
+ * @author tangxiangan
+ */
+public interface BeanConverter<B,V> {
+    V convert(B b);
+
+    V detail(B b);
+}

+ 338 - 0
pdf-tech-common/src/main/java/utils/CommonUtils.java

@@ -0,0 +1,338 @@
+package utils;
+
+import enums.SensitiveDataTypeEnum;
+import org.apache.commons.lang.RandomStringUtils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.MessageDigest;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author tangxiangan
+ */
+public class CommonUtils {
+
+    public final static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+    private static final String NAME_REGEX="^[a-zA-Z\\d_][a-zA-Z\\d-_]*$";
+
+    private static final int DEFAULT_ID_LENGTH = 9;
+
+    private static final String EMAIL_SUFFIX = "@wondersgroup.com";
+
+    /**
+     * 校验名称
+     */
+    public static Boolean validateName(String name){
+        Pattern pattern= Pattern.compile(NAME_REGEX);
+        Matcher matcher = pattern.matcher(name);
+        return matcher.matches();
+    }
+
+    /**
+     * 功能描述:获取当前日期时间格式化字符串,默认yyyy-MM-dd HH:mm:ss格式
+     */
+    public static String getCurrentDateTime() {
+        return getDateTime(DEFAULT_DATE_FORMAT);
+    }
+
+    /**
+     * 功能描述:获取当前日期时间格式化字符串
+     *
+     * @param dateFormat 日期时间格式
+     */
+    public static String getDateTime(String dateFormat) {
+        return new SimpleDateFormat(dateFormat).format(Calendar.getInstance()
+                .getTime());
+    }
+
+    public static String getDateTimeAfterTenYears() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeInMillis(System.currentTimeMillis());
+        int currenYear = calendar.get(Calendar.YEAR);
+        calendar.set(Calendar.YEAR, currenYear + 100);
+        return new SimpleDateFormat(DEFAULT_DATE_FORMAT).format(calendar.getTime());
+    }
+
+    public static String getDateTimeBeforeTenYears() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeInMillis(System.currentTimeMillis());
+        int currenYear = calendar.get(Calendar.YEAR);
+        calendar.set(Calendar.YEAR, currenYear - 1);
+        return new SimpleDateFormat(DEFAULT_DATE_FORMAT).format(calendar.getTime());
+    }
+
+
+    /**
+     * 功能描述:获取指定日期时间格式化字符串
+     *
+     * @param dateFormat 日期时间格式
+     */
+    public static String getDateTime(String dateFormat, Date date) {
+        return new SimpleDateFormat(dateFormat).format(date);
+    }
+
+    /*
+     * 功能描述:生成对象的主键
+     *
+     * @return String
+     */
+    public static String generateId() {
+        UUID uuid = UUID.randomUUID();
+        String id = uuid.toString();
+        //17PDF主键存在-符号,故暂不考虑替换 by songfuqiang 2022年10月19日14:17:14
+//        id = id.replaceAll("-", "");
+        return id;
+    }
+
+
+    /**
+     * 生成随机邮箱地址
+     * @return
+     */
+    public static String generateEmail(){
+        return generateId() + EMAIL_SUFFIX;
+    }
+
+    /**
+     * 生成随机字符串形式的id,使用apache common包中的api
+     *
+     * @return 生成的id
+     */
+    public static String generateRandomStringId() {
+        return generateRandomStringId(DEFAULT_ID_LENGTH);
+    }
+
+
+    public static String generateRandomStringId(int count) {
+        return RandomStringUtils.random(count, true, true);
+    }
+
+    /**
+     * 生成Openstack的资源名称
+     *
+     * @param prefix
+     * @return
+     */
+    public static String generateName(String prefix) {
+        return prefix + "-" + Long.toHexString((new Date()).getTime());
+    }
+
+    public static boolean checkParams(String... params) {
+        if (params == null) {
+            return false;
+        }
+        for (String param : params) {
+            if (StringUtils.isEmpty(param)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static String generateDateId() {
+        Random random = new Random();
+        int i = random.nextInt(9999) + 10000;
+        String num = String.valueOf(i).substring(1);
+        return getDateTime("yyyyMMddHHmmss") + num;
+    }
+
+    public static String generateVerifyCode() {
+        return String.valueOf(new Random().nextInt(899999) + 100000);
+    }
+
+    public static Date parseDate(String dateFormat, String date) throws ParseException {
+        return new SimpleDateFormat(dateFormat).parse(date);
+    }
+
+    public static String listToString(List<String> list) {
+
+        if (list == null) {
+            return null;
+        }
+
+        StringBuilder result = new StringBuilder();
+        boolean first = true;
+
+        //第一个前面不拼接","
+        for (String string : list) {
+            if (first) {
+                first = false;
+            } else {
+                result.append(",");
+            }
+            result.append(string);
+        }
+        return result.toString();
+    }
+
+    /**
+     * 返回首字母大写后的字符串
+     *
+     * @param str
+     * @return
+     */
+    public static String upperFirstLetter(String str) {
+        if (str.length() > 0) {
+            return org.apache.commons.lang.StringUtils.upperCase(str.substring(0, 1)) + str.substring(1);
+        }
+        return null;
+    }
+
+    /**
+     * 覆盖部分内容(电话号码、邮件地址)
+     *
+     * @param content
+     * @param sensitiveDataTypeEnum
+     * @return
+     */
+    public static String coverSensitiveContent(String content, SensitiveDataTypeEnum sensitiveDataTypeEnum) {
+        if (StringUtils.isEmpty(content)) {
+            return content;
+        }
+
+        if (sensitiveDataTypeEnum == SensitiveDataTypeEnum.EMAIL) {
+            String headContent = content.substring(0, content.indexOf("@"));
+            String tailContent = content.substring(content.indexOf("@"));
+            return coverContent(headContent) + tailContent;
+        } else if (sensitiveDataTypeEnum == SensitiveDataTypeEnum.MOBILE) {
+            return coverContent(content);
+        } else {
+            return coverContent(content);
+        }
+    }
+
+    /**
+     * 用*覆盖内容
+     *
+     * @param content
+     * @return
+     */
+    private static String coverContent(String content) {
+        int length = content.length();
+        int coverLength = Double.valueOf(Math.floor(length / 2)).intValue();
+        StringBuilder stringBuilder = new StringBuilder();
+        if (coverLength == 0) {
+            return content;
+        } else {
+            int halfLength = Double.valueOf(Math.floor((length - coverLength) / 2)).intValue();
+            for (int i = 0; i < length; i++) {
+                if (i >= halfLength && (i < (halfLength + coverLength))) {
+                    stringBuilder.append("*");
+                } else {
+                    stringBuilder.append(content.charAt(i));
+                }
+            }
+        }
+        return stringBuilder.toString();
+    }
+
+    public static String generateUriVariables(Map<String, Object> param) {
+        StringBuilder variables = new StringBuilder();
+        if (!CollectionUtils.isEmpty(param)) {
+            variables.append("?");
+            for (String key : param.keySet()) {
+                if (param.get(key) != null) {
+                    variables.append(key).append("=").append(param.get(key).toString()).append("&");
+                }
+            }
+        }
+        return variables.toString();
+    }
+
+    public static Boolean judgeWildcards(final String param, final String realData) {
+        if(! Pattern.matches(param, realData)){
+            if(param.contains("*")) {
+                String noParam=param.replaceAll("\\*","");
+                if (realData.contains(noParam)){
+                    return true;
+                }else{
+                    if(noParam.length()>realData.length()) {
+                        return false;
+                    }
+                    String[] myparam=param.split("\\*");
+                    int curIndex=0;
+                    int prePostion=0;
+                    int num=0;
+                    for(String duan:myparam){
+                        num++;
+                        curIndex=realData.indexOf(duan);
+                        if(curIndex==-1) {
+                            return false;
+                        }
+                        if(curIndex>=prePostion){
+                            prePostion=curIndex+duan.length();
+                            if(num==myparam.length) {
+                                return true;
+                            }
+                        }else{
+                            return false;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static String getIpAddr(HttpServletRequest request) {
+        String ipAddress = null;
+        try {
+            ipAddress = request.getHeader("x-forwarded-for");
+            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+                ipAddress = request.getHeader("Proxy-Client-IP");
+            }
+            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+                ipAddress = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+                ipAddress = request.getRemoteAddr();
+                if (ipAddress.equals("127.0.0.1")) {
+                    // 根据网卡取本机配置的IP
+                    InetAddress inet = null;
+                    try {
+                        inet = InetAddress.getLocalHost();
+                    } catch (UnknownHostException e) {
+                        e.printStackTrace();
+                    }
+                    ipAddress = inet.getHostAddress();
+                }
+            }
+            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
+            if (ipAddress != null && ipAddress.length() > 15) {
+                // "***.***.***.***".length()
+                // = 15
+                if (ipAddress.indexOf(",") > 0) {
+                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
+                }
+            }
+        } catch (Exception e) {
+            ipAddress = "";
+        }
+        return ipAddress;
+    }
+
+    public static String MD5(String data) {
+        try {
+            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] array = md.digest(data.getBytes("UTF-8"));
+            StringBuilder sb = new StringBuilder();
+            for (byte item : array) {
+                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+            }
+            return sb.toString().toUpperCase();
+        } catch (Exception exception) {
+        }
+        return null;
+    }
+
+}

+ 207 - 0
pdf-tech-common/src/main/java/utils/DateUtils.java

@@ -0,0 +1,207 @@
+package utils;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author tangxiangan
+ * 时间工具类
+ */
+public class DateUtils {
+    public static final String COMMON_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    public static final String COMMON_FORMAT_DATE = "yyyy-MM-dd";
+
+    private static final DateTimeFormatter DTF_FOR_ID = DateTimeFormatter.ofPattern("yyMMddHHmmss");
+
+
+    /**
+     * 时间戳转化时间
+     *
+     * @param timestamp 已处理过的时间戳
+     * @return
+     */
+    public static Date longToDate(long timestamp){
+        Instant instant = Instant.ofEpochMilli(timestamp);
+        Date date = Date.from(instant);
+        return date;
+    }
+
+    /**
+     * 时间戳转化时间
+     *
+     * @param timestamp 已处理过的时间戳
+     * @param pattern
+     * @return
+     */
+    public static String longToDateStr(long timestamp, String pattern) {
+        DateFormat dt = new SimpleDateFormat(pattern);
+        return dt.format(new Date(timestamp));
+    }
+
+    /**
+     * 字符串转化时间
+     *
+     * @param str
+     * @param pattern
+     * @return
+     */
+    public static Date parseStringToDate(String str, String pattern) {
+        if (StringUtils.isBlank(str) || StringUtils.isBlank(pattern)) {
+            return null;
+        }
+        DateFormat df = new SimpleDateFormat(pattern);
+        try {
+            return df.parse(str);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Date -> String
+     * @param date
+     * @param pattern
+     * @return
+     */
+    public static String dateToString(Date date,String pattern){
+        DateFormat dt = new SimpleDateFormat(pattern);
+        return dt.format(date);
+    }
+
+    /**
+     * 增减分钟
+     * @param minutes 分钟
+     * @param sign 增减标识 正数加 复数减
+     * @return
+     */
+    public static Date addMinutes(Date date,int minutes,int sign){
+        LocalDateTime localDateTime = getLocalDateTimeFormDate(date);
+        if(sign > 0){
+            localDateTime = localDateTime.plusMinutes(minutes);
+        }else{
+            localDateTime = localDateTime.minusHours(minutes);
+        }
+
+        ZoneId zoneId = ZoneId.systemDefault();
+        ZonedDateTime zdt = localDateTime.atZone(zoneId);
+        return Date.from(zdt.toInstant());
+    }
+
+    public static Date addHours(Date date,int hours,int sign){
+        LocalDateTime localDateTime = getLocalDateTimeFormDate(date);
+        if(sign > 0){
+            localDateTime = localDateTime.plusHours(hours);
+        }else{
+            localDateTime = localDateTime.minusHours(hours);
+        }
+
+        ZoneId zoneId = ZoneId.systemDefault();
+        ZonedDateTime zdt = localDateTime.atZone(zoneId);
+        return Date.from(zdt.toInstant());
+    }
+
+    public static Date localDateTimeToDate(LocalDateTime localDate){
+        ZoneId zoneId = ZoneId.systemDefault();
+        ZonedDateTime zonedDateTime = localDate.atZone(zoneId);
+       return Date.from(zonedDateTime.toInstant());
+    }
+
+    public static Date localDateToDate(LocalDate localDate){
+        ZoneId zoneId = ZoneId.systemDefault();
+        ChronoZonedDateTime<LocalDate> zonedDateTime = localDate.atStartOfDay(zoneId);
+        return Date.from(zonedDateTime.toInstant());
+    }
+
+    /**
+     * @Description: Date -> LocalDateTime
+     */
+    public static LocalDateTime getLocalDateTimeFormDate(Date date){
+        Instant instant = date.toInstant();
+        ZoneId zone = ZoneId.systemDefault();
+        return LocalDateTime.ofInstant(instant, zone);
+
+    }
+    public static LocalDate getLocalDateFormDate(Date date){
+        LocalDateTime localDateTimeFormDate = getLocalDateTimeFormDate(date);
+        return localDateTimeFormDate.toLocalDate();
+    }
+
+    /**
+     * 获取最近一个月第一天的日期
+     * @return
+     */
+    public static String lastMothFirstDay(String pattern){
+        Date now = new Date();
+        Calendar lastMonth = Calendar.getInstance();
+        lastMonth.setTime(now);
+        lastMonth.add(Calendar.MONTH, -1);
+        String time = new SimpleDateFormat(pattern).format( lastMonth.getTime());
+        return time;
+    }
+
+    /**
+     * 时间比较 格式 yyyy-MM-dd
+     * @param dateStr
+     * @param targetDateStr
+     * @return
+     */
+    public static int compareDate(String dateStr,String targetDateStr){
+        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        LocalDateTime localDateTime = LocalDateTime.parse(dateStr + " 00:00:00",df);
+        LocalDateTime targetLocalDateTime = LocalDateTime.parse(targetDateStr + " 00:00:00",df);
+        return localDateTime.compareTo(targetLocalDateTime);
+    }
+
+
+
+    /**
+     * 时间改为最小(00:00:00)
+     *
+     * @param date date
+     */
+    public static Date timeMin(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
+        cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
+        cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
+        return cal.getTime();
+    }
+
+    /**
+     * 时间改为最大(23:59:59)
+     *
+     * @param date date
+     */
+    public static Date timeMax(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
+        cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE));
+        cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND));
+        return cal.getTime();
+    }
+
+
+    /**
+     * 日期相减,获取天数
+     * @param currentDate
+     * @param targetDate
+     * @return
+     */
+    public static long minusDate(Date currentDate,Date targetDate){
+        LocalDate currentLocalDate = currentDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+        LocalDate targetLocalDate = targetDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+        return targetLocalDate.until(currentLocalDate, ChronoUnit.DAYS);
+    }
+
+}

+ 60 - 0
pdf-tech-common/src/main/java/utils/DistributedLocker.java

@@ -0,0 +1,60 @@
+package utils;
+
+import org.redisson.api.RLock;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 获取和释放分布式锁
+ *
+ * @author tangxiangan
+ */
+public interface DistributedLocker {
+
+    /**
+     * 获取锁 拿不到lock就不罢休,不然线程就一直block
+     * @param lockKey
+     * @return
+     */
+    RLock lock(String lockKey);
+
+    /**
+     *获取锁 带有时间
+     * @param lockKey
+     * @param timeout 加锁时间 单位为秒
+     * @return
+     */
+    RLock lock(String lockKey, long timeout);
+
+    /**
+     * 获取锁 带有时间 时间单位有unit决定
+     * @param lockKey
+     * @param unit 加锁时间
+     * @param timeout 时间单位
+     * @return
+     */
+    RLock lock(String lockKey, TimeUnit unit, long timeout);
+
+    /**
+     *  带时间限制的获取锁,拿不到lock,就等一段时间,超时返回false.
+     * @param lockKey
+     * @param unit 时间单位
+     * @param waitTime 等待时间
+     * @param leaseTime 获取锁后加锁时间
+     * @return
+     */
+    boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);
+
+    /**
+     * 释放锁
+     * @param lockKey
+     */
+    void unlock(String lockKey);
+
+    /**
+     * 释放锁
+     * @param lock RLock类型
+     */
+    void unlock(RLock lock);
+
+}

+ 97 - 0
pdf-tech-common/src/main/java/utils/EmailUtils.java

@@ -0,0 +1,97 @@
+package utils;
+
+import com.sun.mail.util.MailSSLSocketFactory;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.mail.*;
+import javax.mail.internet.*;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+@Component
+@Slf4j
+public class EmailUtils {
+
+//    @Resource
+//    private JavaMailSender mailSender;
+
+//    public boolean sendMail(String subject, String content, List<String> emails) {
+//        try {
+//            MimeMessage mimeMessage = mailSender.createMimeMessage();
+//            for (String email : emails) {
+//                MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);
+//                // 设置收件人邮箱
+//                messageHelper.setTo(email);
+//                messageHelper.setSubject(subject);
+//                messageHelper.setText(content, true);
+//                messageHelper.setSentDate(new Date());
+//                mailSender.send(mimeMessage);
+//            }
+//        } catch (Exception e) {
+//            log.error("send mail error : " + e.getMessage(), e);
+//            return false;
+//        }
+//        return true;
+//    }
+
+    public boolean sendMail(String subject,String text,List<String> emails){
+        try {
+        //用于读取配置文件
+        Properties props=new Properties();
+        //开启Debug调试
+        props.setProperty("mail.debug", "true");
+        //发送服务器需要身份验证
+        props.setProperty("mail.smtp.auth", "true");
+        //发送邮件服务器的主机名
+        props.setProperty("mail.smtp.host", "smtp.qq.com");
+        //端口号
+        props.setProperty("mail.smtp.port", "465");
+        //发送邮件协议
+        props.setProperty("mail.transport.protocol", "smtp");
+        //开启ssl加密(并不是所有的邮箱服务器都需要,但是qq邮箱服务器是必须的)
+        MailSSLSocketFactory msf= new MailSSLSocketFactory();
+        msf.setTrustAllHosts(true);
+        props.put("mail.smtp.ssl.enable", "true");
+        props.put("mail.smtp.ssl.socketFactory",msf);
+        //获取Session会话实例(javamail Session与HttpSession的区别是Javamail的Session只是配置信息的集合)
+        Session session=Session.getInstance(props,new javax.mail.Authenticator(){
+            protected PasswordAuthentication getPasswordAuthentication(){
+                //用户名密码验证(取得的授权吗)
+                return new PasswordAuthentication ("316531990@qq.com","fcczojbobpusbjfh");
+            }
+        });
+
+        //抽象类MimeMessage为实现类 消息载体封装了邮件的所有消息
+        MimeMessage message=new MimeMessage(session);
+        //设置发件人地址
+        message.setFrom(new InternetAddress("316531990@qq.com"));
+        //此类的功能是发送邮件 又会话获得实例
+        Transport transport=session.getTransport();
+        //开启连接
+        transport.connect();
+        //设置收件人地址邮件信息
+        for (int i = 0; i < emails.size(); i++) {
+            MimeMessageHelper messageHelper = new MimeMessageHelper(message, true,"utf-8");
+                // 设置收件人邮箱
+            messageHelper.setTo(emails.get(i));
+            messageHelper.setSubject(subject);
+            messageHelper.setText(text, true);
+            messageHelper.setSentDate(new Date());
+            transport.sendMessage(message,new Address[]{new InternetAddress(emails.get(i))});
+            //邮件发送后关闭信息
+            transport.close();
+        }
+            return true;
+        } catch (Exception e) {
+            log.error("send mail error : " + e.getMessage(), e);
+        }
+       return true;
+    }
+
+}

+ 71 - 0
pdf-tech-common/src/main/java/utils/FileUtils.java

@@ -0,0 +1,71 @@
+package utils;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+
+/**
+ * @author tangxiangan
+ * file工具类
+ */
+public class FileUtils {
+
+    /**
+     * multipartFile转file
+     *
+     * @param multipartFile
+     * @return
+     */
+    public static File transferToFile(MultipartFile multipartFile) {
+        //选择用缓冲区来实现这个转换即使用java 创建的临时文件 使用 MultipartFile.transferto()方法 。
+        File file = null;
+        try {
+            //String tDir = System.getProperty("java.io.tmpdir");
+            String originalFilename = multipartFile.getOriginalFilename();
+            String[] filename = originalFilename.split("\\.", originalFilename.lastIndexOf("."));
+            //注意下面的 特别注意!!!
+            //file = new File(tDir+filename[0], "." + filename[1]);
+            file = File.createTempFile(filename[0], "." + filename[1]);
+            multipartFile.transferTo(file);
+            file.deleteOnExit();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return file;
+    }
+
+    public static File multipartFileToFile(MultipartFile file) {
+        File toFile = null;
+        try {
+        if (file.equals("") || file.getSize() <= 0) {
+            file = null;
+        } else {
+            InputStream ins = null;
+            ins = file.getInputStream();
+            toFile = new File(file.getOriginalFilename());
+            inputStreamToFile(ins, toFile);
+            ins.close();
+        }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return toFile;
+    }
+
+    //获取流文件
+    private static void inputStreamToFile(InputStream ins, File file) {
+        try {
+            OutputStream os = new FileOutputStream(file);
+            int bytesRead = 0;
+            byte[] buffer = new byte[8192];
+            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+            os.close();
+            ins.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}

+ 326 - 0
pdf-tech-common/src/main/java/utils/HttpClientUtils.java

@@ -0,0 +1,326 @@
+package utils;
+
+import constant.CommonConstant;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.*;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+import javax.net.ssl.SSLContext;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * 处理简单http请求的类
+ *
+ * @author tangxiangan
+ */
+public class HttpClientUtils {
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtils.class);
+
+    private static CloseableHttpClient getHttpClient() {
+        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
+
+        SSLContext sslContext;
+        try {
+            sslContext = org.apache.http.ssl.SSLContexts.custom()
+                    .loadTrustMaterial(null, acceptingTrustStrategy)
+                    .build();
+
+            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
+
+            return HttpClients.custom()
+                    .setSSLSocketFactory(csf)
+                    .build();
+
+        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
+            LOGGER.error(e.getMessage(), e);
+        }
+
+        LOGGER.warn("error create http client, create default client will not support https!!!!");
+        return HttpClients.createDefault();
+    }
+
+
+    // 处理GET类请求,参数URL应包含完整的请求参数
+    public static String doGet(String url) {
+        return doGet(url, null);
+    }
+
+    public static String doGet(String url, Map<String, Object> params) {
+        return doGet(url, params, null);
+    }
+
+    public static String doGet(String url, Map<String, Object> params, Map<String, String> headers) {
+        try {
+            CloseableHttpClient httpClient = getHttpClient();
+            StringBuilder urlBuilder = new StringBuilder(url);
+            int i = 0;
+            if (!CollectionUtils.isEmpty(params)) for (String key : params.keySet()) {
+                urlBuilder.append((i == 0) ? "?" : "&");
+                urlBuilder.append(key).append("=");
+                if (params.get(key) != null) {
+                    urlBuilder.append(URLEncoder.encode(String.valueOf(params.get(key)), "utf-8"));
+                }
+                i++;
+            }
+
+            HttpGet httpGet = new HttpGet(urlBuilder.toString());
+
+            if (!CollectionUtils.isEmpty(headers)) {
+                for (String key : headers.keySet()) {
+                    httpGet.setHeader(key, headers.get(key));
+                }
+            }
+
+            CloseableHttpResponse response = httpClient.execute(httpGet);
+            HttpEntity entity = response.getEntity();
+            String content = EntityUtils.toString(entity);
+            EntityUtils.consume(entity);
+            return content;
+        } catch (Exception e) {
+            LOGGER.error("ERROR, call http get" + e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * 处理POST类请求,表格类型
+     */
+    public static String doPost(String url, Map<String, String> params) {
+        return doPost(url, params, null);
+    }
+
+    // 处理POST类请求
+    public static String doPost(String url, Object object) {
+        return doPost(url, object, null);
+    }
+
+    /**
+     * 处理POST类请求,body类型
+     */
+    public static String doPost(String url, Object body, Map<String, String> headers) {
+        try {
+            CloseableHttpClient httpClient = getHttpClient();
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.setHeader("Accept", "application/json; charset=UTF-8");
+            httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
+            RequestConfig config = RequestConfig.custom()
+                    .setConnectionRequestTimeout(30000)
+                    .setConnectTimeout(30000).setSocketTimeout(30000).build();
+
+
+            if (body == null) {
+                body = new Object();
+            }
+
+            StringEntity strEntity = new StringEntity(JsonUtils.getJsonString(body),
+                    Charset.forName("UTF-8"));
+            httpPost.setEntity(strEntity);
+            httpPost.setConfig(config);
+
+            if (!CollectionUtils.isEmpty(headers)) {
+                for (Entry<String, String> entry : headers.entrySet()) {
+                    httpPost.setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+
+            CloseableHttpResponse response = httpClient.execute(httpPost);
+            //资源创建成功 返回状态码201 返回数据为空
+            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED){
+                return CommonConstant.RESULT_SUCCESS;
+            }
+            HttpEntity entity = response.getEntity();
+            String content = EntityUtils.toString(entity);
+            EntityUtils.consume(entity);
+            return content;
+        } catch (Exception e) {
+            LOGGER.error("ERROR, call http post" + e.getMessage(), e);
+        }
+        return null;
+    }
+
+
+    /**
+     * 处理POST类请求,表格类型
+     */
+    public static String doPost(String url, Map<String, String> params, Map<String, String> headers) {
+        try {
+            CloseableHttpClient httpClient = getHttpClient();
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.setHeader("Accept", "application/json; charset=UTF-8");
+            RequestConfig config = RequestConfig.custom()
+                    .setConnectionRequestTimeout(60000)
+                    .setConnectTimeout(30000).setSocketTimeout(30000).build();
+
+            List<BasicNameValuePair> formParams = new ArrayList<>();
+
+            if (!CollectionUtils.isEmpty(params)) {
+                for (Entry<String, String> entry : params.entrySet()) {
+                    formParams.add(new BasicNameValuePair(entry.getKey(), entry
+                            .getValue()));
+                }
+            }
+
+            StringEntity strEntity = new UrlEncodedFormEntity(formParams,
+                    Charset.forName("UTF-8"));
+            httpPost.setEntity(strEntity);
+            httpPost.setConfig(config);
+
+            if (!CollectionUtils.isEmpty(headers)) {
+                for (Entry<String, String> entry : headers.entrySet()) {
+                    httpPost.setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+
+            CloseableHttpResponse response = httpClient.execute(httpPost);
+            HttpEntity entity = response.getEntity();
+            String content = EntityUtils.toString(entity);
+            EntityUtils.consume(entity);
+            return content;
+        } catch (Exception e) {
+            LOGGER.error("ERROR, call http post" + e.getMessage(), e);
+        }
+        return null;
+    }
+
+    public static String doPut(String url, Map<String, Object> params) {
+        return doPut(url, params, null);
+    }
+
+    public static String doPut(String url, Map<String, Object> params, Map<String, String> headers) {
+        CloseableHttpClient httpClient = getHttpClient();
+        CloseableHttpResponse response;
+        try {
+
+            StringBuilder urlBuilder = new StringBuilder(url);
+            int i = 0;
+            if (!CollectionUtils.isEmpty(params)) {
+                for (Entry<String, Object> entry : params.entrySet()) {
+                    urlBuilder.append((i == 0) ? "?" : "&");
+                    urlBuilder.append(entry.getKey()).append("=").append(entry.getValue());
+                    i++;
+                }
+            }
+            HttpPut httpPut = new HttpPut(urlBuilder.toString());
+            if (!CollectionUtils.isEmpty(headers)) {
+                for (Entry<String, String> entry : headers.entrySet()) {
+                    httpPut.setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+
+            response = httpClient.execute(httpPut);
+            HttpEntity entity = response.getEntity();
+            String content = EntityUtils.toString(entity);
+            EntityUtils.consume(entity);
+            return content;
+        } catch (Exception e) {
+            LOGGER.error("ERROR, call http put" + e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * contentType 为text/plain 或application/json
+     * @param url
+     * @param body
+     * @param headers
+     * @return
+     */
+    public static String doPut(String url, Object body, Map<String, String> headers,String contentType) {
+        CloseableHttpClient httpClient = getHttpClient();
+        CloseableHttpResponse response;
+        HttpPut httpPut = new HttpPut(url);
+        httpPut.setHeader("Content-Type", contentType+"; charset=UTF-8");
+        try {
+            if (body == null) {
+                body = new Object();
+            }
+            if (!CollectionUtils.isEmpty(headers)) {
+                for (Entry<String, String> entry : headers.entrySet()) {
+                    httpPut.setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+
+            StringEntity strEntity ;
+            if(contentType.equals("application/json")){
+                strEntity = new StringEntity(JsonUtils.getJsonString(body), Charset.forName("UTF-8"));
+            }else{
+                strEntity = new StringEntity(body.toString());
+            }
+            httpPut.setEntity(strEntity);
+            response = httpClient.execute(httpPut);
+            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT){
+                return CommonConstant.RESULT_SUCCESS;
+            }
+            HttpEntity entity = response.getEntity();
+            String content = EntityUtils.toString(entity);
+            EntityUtils.consume(entity);
+            return content;
+        } catch (Exception e) {
+            LOGGER.error("ERROR, call http put" + e.getMessage(), e);
+        }
+        return null;
+    }
+
+    public static String doDelete(String url) {
+        return doDelete(url, null);
+    }
+
+    public static String doDelete(String url, Map<String, Object> params) {
+        return doDelete(url, params, null);
+    }
+
+    public static String doDelete(String url, Map<String, Object> params, Map<String, String> headers) {
+        try {
+            CloseableHttpClient httpClient = getHttpClient();
+            StringBuilder urlBuilder = new StringBuilder(url);
+            int i = 0;
+            if (!CollectionUtils.isEmpty(params)) for (String key : params.keySet()) {
+                urlBuilder.append((i == 0) ? "?" : "&");
+                urlBuilder.append(key).append("=").append(params.get(key));
+                i++;
+            }
+            HttpDelete httpDelete = new HttpDelete(urlBuilder.toString());
+
+            if (!CollectionUtils.isEmpty(headers)) {
+                for (String key : headers.keySet()) {
+                    httpDelete.setHeader(key, headers.get(key));
+                }
+            }
+
+            CloseableHttpResponse response = httpClient.execute(httpDelete);
+            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT){
+                return CommonConstant.STRING_EMPTY;
+            }
+            HttpEntity entity = response.getEntity();
+            String content = EntityUtils.toString(entity);
+            EntityUtils.consume(entity);
+            return content;
+        } catch (Exception e) {
+            LOGGER.error("ERROR, call http get" + e.getMessage(), e);
+        }
+        return null;
+    }
+}

+ 82 - 0
pdf-tech-common/src/main/java/utils/ImageCodeUtils.java

@@ -0,0 +1,82 @@
+package utils;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ *
+ * @author tangxiangan
+ * @date 22/10/20
+ */
+public class ImageCodeUtils {
+
+    private static char[] codeSequence = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+            'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
+            'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+    private static char[] codeSequenceLowerCase = {'a', 'b', 'c', 'd', 'e',  'g', 'h', 'i', 'j', 'k', 'l',
+            'm', 'n', 'p', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+            'z', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+
+    /**
+     * 生成验证码图片
+     *
+     * @param width     图片的宽度 160
+     * @param height    图片的高度 40
+     * @param codeCount 验证码字符个数 4
+     * @param lineCount 验证码干扰线数 100
+     */
+    public static Map<String, Object> generate(int width, int height, int codeCount, int lineCount) {
+        int x, fontHeight, codeY;
+        int red, green, blue;
+
+        x = width / (codeCount +3);//每个字符的宽度
+        fontHeight = height - 20;//字体的高度
+        codeY = height - 4;
+
+        // 图像buffer
+        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g = buffImg.createGraphics();
+        // 生成随机数
+        Random random = new Random();
+        // 将图像填充为白色
+        g.setColor(Color.WHITE);
+        g.fillRect(0, 0, width, height);
+        //创建字体
+        Font font = new Font(Font.SERIF, Font.ITALIC, fontHeight);
+        g.setFont(font);
+        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        for (int i = 0; i < lineCount; i++) {
+            int xs = random.nextInt(width);
+            int ys = random.nextInt(height);
+            int xe = xs + random.nextInt(width / 8);
+            int ye = ys + random.nextInt(height / 8);
+            red = random.nextInt(255);
+            g.setColor(new Color(143,139,137));
+            g.drawLine(xs, ys, xe, ye);
+        }
+
+        // randomCode记录随机产生的验证码
+        StringBuffer randomCode = new StringBuffer();
+        // 随机产生codeCount个字符的验证码。
+        for (int i = 0; i < codeCount; i++) {
+            String strRand = String.valueOf(codeSequenceLowerCase[random.nextInt(codeSequenceLowerCase.length)]);
+            // 产生随机的颜色值,让输出的每个字符的颜色值都将不同。
+            red = random.nextInt(255);
+            green = random.nextInt(255);
+            blue = random.nextInt(255);
+            g.setColor(new Color(143,139,137));
+            g.drawString(strRand, (i + 1) * x, codeY);
+            // 将产生的四个随机数组合在一起。
+            randomCode.append(strRand);
+        }
+        Map<String, Object> map = new HashMap<>();
+        map.put("code", randomCode.toString());
+        map.put("image", buffImg);
+        return map;
+    }
+}

+ 134 - 0
pdf-tech-common/src/main/java/utils/JsonUtils.java

@@ -0,0 +1,134 @@
+package utils;
+
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.util.StringUtils;
+import pojo.ResultMap;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JsonUtils {
+    private final static SimpleDateFormat DEFAULT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    /**
+     * 将对象转换成JSON字符串
+     *
+     * @param bean 可以为基本的object 或者为 数组
+     * @return JSON字符串
+     */
+    public static String getJsonString(Object bean) {
+        return getJsonString(bean, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 将对象转换成JSON字符串
+     *
+     * @param bean 可以为基本的object 或者为 数组
+     * @return JSON字符串
+     */
+    public static String getJsonString(Object bean, DateFormat dateFormat) {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        mapper.setDateFormat(dateFormat);
+        try {
+            return mapper.writeValueAsString(bean);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 将json格式的字符串的转化为Bean对象
+     *
+     * @param <T>
+     * @param jsonStr
+     * @param clazz
+     * @return
+     */
+    public static <T> T jsonStringToBean(String jsonStr, Class<T> clazz, DateFormat dateFormat) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+            mapper.setDateFormat(dateFormat);
+            return mapper.readValue(jsonStr, clazz);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将json格式的字符串的转化为Bean对象
+     *
+     * @param <T>
+     * @param jsonStr
+     * @param clazz
+     * @return
+     */
+    public static <T> T jsonStringToBean(String jsonStr, Class<T> clazz) {
+        return jsonStringToBean(jsonStr, clazz, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 将json格式的字符串(数组形式)的转化为List对象
+     *
+     * @param <T>
+     * @param jsonArrStr
+     * @param clazz      List中对象类型
+     * @return
+     */
+    public static <T> List<T> jsonStringToList(String jsonArrStr, Class<T> clazz) {
+        return jsonStringToList(jsonArrStr, clazz, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 将json格式的字符串(数组形式)的转化为List对象
+     *
+     * @param <T>
+     * @param jsonArrStr
+     * @param clazz      List中对象类型
+     * @return
+     */
+    public static <T> List<T> jsonStringToList(String jsonArrStr, Class<T> clazz, DateFormat dateFormat) {
+        if (StringUtils.isEmpty(jsonArrStr)) {
+            return new ArrayList<>();
+        }
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.setDateFormat(dateFormat);
+            JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, clazz);
+            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+            return mapper.readValue(jsonArrStr, javaType);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new ArrayList<>();
+        }
+    }
+
+
+    public static <T> ResultMap<T> jsonStringToResultMap(String jsonArrStr, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonArrStr)) {
+            return new ResultMap<>();
+        }
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.setDateFormat(DEFAULT_FORMAT);
+            JavaType javaType = mapper.getTypeFactory().constructParametricType(ResultMap.class, clazz);
+            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+            return mapper.readValue(jsonArrStr, javaType);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+}

+ 34 - 0
pdf-tech-common/src/main/java/utils/MD5Utils.java

@@ -0,0 +1,34 @@
+package utils;
+
+import java.security.MessageDigest;
+
+/**
+ * tangxiangan
+ */
+public class MD5Utils {
+
+    public static String encode(String s) {
+        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+        try {
+            byte[] btInput = s.getBytes();
+            // 获得MD5摘要算法的 MessageDigest 对象
+            MessageDigest mdInst = MessageDigest.getInstance("MD5");
+            // 使用指定的字节更新摘要
+            mdInst.update(btInput);
+            // 获得密文
+            byte[] md = mdInst.digest();
+            // 把密文转换成十六进制的字符串形式
+            int j = md.length;
+            char[] str = new char[j * 2];
+            int k = 0;
+            for (byte byte0 : md) {
+                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
+                str[k++] = hexDigits[byte0 & 0xf];
+            }
+            return new String(str);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 66 - 0
pdf-tech-common/src/main/java/utils/PageUtils.java

@@ -0,0 +1,66 @@
+package utils;
+
+import org.apache.commons.lang.StringUtils;
+import pojo.PageVO;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PageUtils {
+	public static Integer getParamToInt(Integer param, Integer defaultValue) {
+		Integer num = defaultValue;
+		if (param != null && param > 0) {
+			num = param;
+		}
+		return num;
+	}
+
+	public static String getOrderByClause(String orderColumn, String orderRule, String defaultOrderByClause) {
+		// orderColumn 排序字段
+		// orderRule 排序规则:ASC/DESC缺省为:DESC
+		// 获取排序信息
+		String orderByClause = defaultOrderByClause;
+		// 构造排序子句
+		if (StringUtils.isNotBlank(orderColumn)) {
+			orderByClause = orderColumn + " DESC";
+			if (StringUtils.isNotBlank(orderRule)) {
+				orderByClause = orderColumn + " " + orderRule;
+			}
+		}
+		return orderByClause;
+	}
+
+	public static String getLikeQuery(String name){
+		if (StringUtils.isNotBlank(name)) {
+			return "%"+name+"%";
+		}
+		return name;
+	}
+
+	public static <E> PageVO<E> pageByList(int pageNum, int pageSize, List<E> infoList) {
+		List<E> iList = new ArrayList<E>();
+		int begin = (pageNum - 1) * pageSize;
+		int end = pageNum * pageSize;
+
+		for (int i = 0; i < infoList.size(); i++) {
+			if (i >= begin && i < end) {
+				iList.add(infoList.get(i));
+			}
+		}
+
+		PageVO<E> pageVO = new PageVO<E>();
+		pageVO.setList(iList);
+		pageVO.setPageNum(pageNum);
+		pageVO.setPageSize(pageSize);
+		if (infoList.size() % pageSize == 0) {
+			pageVO.setPages(infoList.size() / pageSize);
+			pageVO.setSize(infoList.size() / pageSize);
+		} else {
+			pageVO.setPages(infoList.size() / pageSize + 1);
+			pageVO.setSize(infoList.size() / pageSize + 1);
+		}
+		pageVO.setTotal(infoList.size());
+		return pageVO;
+	}
+
+}

+ 72 - 0
pdf-tech-common/src/main/java/utils/QueryUtils.java

@@ -0,0 +1,72 @@
+package utils;
+
+
+import com.github.pagehelper.PageInfo;
+import constant.CommonConstant;
+import org.springframework.beans.BeanUtils;
+import org.springframework.util.CollectionUtils;
+import pojo.PageVO;
+import pojo.QueryParams;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
+
+public class QueryUtils {
+
+    public static String generateLikeString(String string){
+        try {
+            string = URLDecoder.decode(URLDecoder.decode(string, CommonConstant.ENCODING_UTF8), CommonConstant.ENCODING_UTF8);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("unsupport encoding");
+        }
+        return "%"+string+"%";
+    }
+
+    public static String generateLikeStringHalf(String string){
+        try {
+            //处理下%和_的问题,应该还有其他通配符
+            string = string.replace(CommonConstant.STRING_PERCENT,"\\" + CommonConstant.STRING_PERCENT)
+                    .replace(CommonConstant.STRING_UNDERLINE,"\\" + CommonConstant.STRING_UNDERLINE);
+            string = URLDecoder.decode(URLEncoder.encode(string, CommonConstant.ENCODING_UTF8), CommonConstant.ENCODING_UTF8);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("unsupport encoding");
+        }
+        return string+"%";
+    }
+
+
+
+    public static <T extends QueryParams> void handlePageAndRow(T param) {
+        param.setPage(PageUtils.getParamToInt(param.getPage(), CommonConstant.PAGENUM));
+        param.setRows(PageUtils.getParamToInt(param.getRows(), CommonConstant.PAGESIZE));
+    }
+
+    public static <T extends QueryParams> void handleParam(T param) {
+        Integer pageNum = PageUtils.getParamToInt(param.getPage(), CommonConstant.PAGENUM);
+        Integer pageSize = PageUtils.getParamToInt(param.getRows(), CommonConstant.PAGESIZE);
+        String orderByClause = PageUtils.getOrderByClause(param.getSidx(), param.getSord(), CommonConstant.ORDERBYCLAUSE);
+        param.setOrderByClause(orderByClause);
+        param.setPage(pageNum);
+        param.setRows(pageSize);
+    }
+
+    public static <T,V> PageVO<T> convertPageInfoToVO(PageInfo<V> pageInfo, BeanConverter<V,T> converter){
+        PageVO<T> pageVO = new PageVO<>();
+        BeanUtils.copyProperties(pageInfo,pageVO);
+        pageVO.setList(convertListToVO(pageInfo.getList(),converter));
+        return pageVO;
+    }
+
+
+    public static <T,V> List<T> convertListToVO(List<V> list, BeanConverter<V,T> converter){
+        List<T> voList = new CopyOnWriteArrayList<>();
+        if(!CollectionUtils.isEmpty(list)){
+            voList = list.stream().map(converter::convert).collect(Collectors.toList());
+        }
+        return voList;
+    }
+}

+ 626 - 0
pdf-tech-common/src/main/java/utils/RedisUtils.java

@@ -0,0 +1,626 @@
+package utils;
+
+import exception.BackendRuntimeException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisUtils<K, V> {
+
+    @Autowired
+    private RedisTemplate<K, V> redisTemplate;
+
+    public RedisTemplate getInstance() {
+        return redisTemplate;
+    }
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key  键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(K key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     *
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(K key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean exists(K key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(K... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    //============================String=============================
+
+    /**
+     * 普通缓存获取
+     *
+     * @param key 键
+     * @return 值
+     */
+    public V get(K key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     *
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(K key, V value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(K key, V value, long time) {
+        return set(key, value, time, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(K key, V value, long time, TimeUnit timeUnit) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, timeUnit);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     *
+     * @param key   键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(K key, long delta) {
+        if (delta < 0) {
+            throw new BackendRuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     *
+     * @param key   键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(K key, long delta) {
+        if (delta < 0) {
+            throw new BackendRuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    //================================Map=================================
+
+    /**
+     * HashGet
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public String hget(K key, String item) {
+        return (String) redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     *
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(K key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     *
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(K key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     *
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(K key, Map<String, String> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(K key, String item, String value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean hsetBean(K key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(K key, String item, V value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(K key, String... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(K key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     * @return
+     */
+    public double hincr(K key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     * @return
+     */
+    public double hdecr(K key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    //============================set=============================
+
+    /**
+     * 根据key获取Set中的所有值
+     *
+     * @param key 键
+     * @return
+     */
+    public Set<V> sGet(K key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     *
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(K key, V value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(K key, V... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     *
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(K key, long time, V... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0) expire(key, time);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    public long sGetSetSize(K key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(K key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    //===============================list=================================
+
+    /**
+     * 获取list缓存的内容
+     *
+     * @param key   键
+     * @param start 开始
+     * @param end   结束  0 到 -1代表所有值
+     * @return
+     */
+    public List<V> lrange(K key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    public long llen(K key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     *
+     * @param key   键
+     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object lGetIndex(K key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean lpush(K key, V value) {
+        try {
+            redisTemplate.opsForList().leftPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean lpop(K key) {
+        try {
+            redisTemplate.opsForList().leftPop(key);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean rpush(K key, V value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(K key, V value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(K key, List<V> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean lSet(K key, List<V> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     *
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(K key, long index, V value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     *
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(K key, long count, V value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    public Boolean hexists(K key, String item) {
+        return !StringUtils.isEmpty(hget(key, item));
+    }
+
+    public Set<K> queryKeys(K pattern) {
+        return redisTemplate.keys(pattern);
+    }
+}
+
+

+ 65 - 0
pdf-tech-common/src/main/java/utils/RedissonDistributedLocker.java

@@ -0,0 +1,65 @@
+package utils;
+
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 获取和释放分布式锁实现类
+ */
+public class RedissonDistributedLocker implements DistributedLocker {
+    //RedissonClient已经由配置类生成,这里自动装配即可
+    private final RedissonClient redissonClient;
+
+    public RedissonDistributedLocker(RedissonClient redissonClient) {
+        this.redissonClient = redissonClient;
+    }
+
+    //lock(), 拿不到lock就不罢休,不然线程就一直block
+    @Override
+    public RLock lock(String lockKey) {
+        RLock lock = redissonClient.getLock(lockKey);
+        lock.lock();
+        return lock;
+    }
+
+    //leaseTime为加锁时间,单位为秒
+    @Override
+    public RLock lock(String lockKey, long leaseTime) {
+        RLock lock = redissonClient.getLock(lockKey);
+        lock.lock(leaseTime, TimeUnit.SECONDS);
+        return lock;
+    }
+
+    //timeout为加锁时间,时间单位由unit确定
+    @Override
+    public RLock lock(String lockKey, TimeUnit unit ,long timeout) {
+        RLock lock = redissonClient.getLock(lockKey);
+        lock.lock(timeout, unit);
+        return lock;
+    }
+
+    //tryLock(),马上返回,拿到lock就返回true,不然返回false。
+    //带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false.
+    @Override
+    public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
+        RLock lock = redissonClient.getLock(lockKey);
+        try {
+            return lock.tryLock(waitTime, leaseTime, unit);
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public void unlock(String lockKey) {
+        RLock lock = redissonClient.getLock(lockKey);
+        lock.unlock();
+    }
+
+    @Override
+    public void unlock(RLock lock) {
+        lock.unlock();
+    }
+}

+ 87 - 0
pdf-tech-common/src/main/java/utils/SMSUtils.java

@@ -0,0 +1,87 @@
+package utils;
+
+
+import constant.CommonConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import pojo.MobSendResult;
+import pojo.YunpianSendResult;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Component
+@Slf4j
+public class SMSUtils {
+
+    @Value("${mob.sms.apikey:}")
+    private String SMSApiKey;
+
+    @Value("${mob.sms.sendUrl:}")
+    private String SendSMSUrl;
+
+    @Value("${mob.sms.verifyUrl:}")
+    private String VerifySMSUrl;
+
+
+    public boolean sendSMS(List<String> phoneList) {
+        String phoneListStr = StringUtils.join(phoneList, CommonConstant.STRING_SIGN_COMMA);
+        Map<String, String> params = new HashMap<>();
+        params.put("appkey", SMSApiKey);
+        params.put("phone", phoneListStr);
+        params.put("zone", "86");
+        String response = HttpClientUtils.doPost(SendSMSUrl, params);
+        MobSendResult mobSendResult = JsonUtils.jsonStringToBean(response, MobSendResult.class);
+        if(mobSendResult.getStatus().equals(CommonConstant.SUCCESS)){
+            return Boolean.TRUE;
+        } else {
+            log.error("sendSMS.error:"+response);
+            return Boolean.FALSE;
+        }
+    }
+
+    public boolean verifySMS(List<String> phoneList,String code) {
+        String phoneListStr = StringUtils.join(phoneList, CommonConstant.STRING_SIGN_COMMA);
+        Map<String, String> params = new HashMap<>();
+        params.put("appkey", SMSApiKey);
+        params.put("phone", phoneListStr);
+        params.put("zone", "86");
+        params.put("code", code);
+        String response = HttpClientUtils.doPost(VerifySMSUrl, params);
+        log.info("返回信息:" + response);
+        MobSendResult mobSendResult = JsonUtils.jsonStringToBean(response, MobSendResult.class);
+        if(mobSendResult.getStatus().equals(CommonConstant.SUCCESS)){
+           return Boolean.TRUE;
+        }
+        else {
+            return Boolean.FALSE;
+        }
+    }
+
+
+    public static String delHTMLTag(String htmlStr) {
+        String regEx_script = "<script[^>]*?>[\\s\\S]*?<\\/script>"; //定义script的正则表达式
+        String regEx_style = "<style[^>]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式
+        String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式
+
+        Pattern p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
+        Matcher m_script = p_script.matcher(htmlStr);
+        htmlStr = m_script.replaceAll(""); //过滤script标签
+
+        Pattern p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
+        Matcher m_style = p_style.matcher(htmlStr);
+        htmlStr = m_style.replaceAll(""); //过滤style标签
+
+        Pattern p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
+        Matcher m_html = p_html.matcher(htmlStr);
+        htmlStr = m_html.replaceAll(""); //过滤html标签
+
+        return htmlStr.trim(); //返回文本字符串
+    }
+
+    public static void main(String[] args) {
+    }
+}

+ 31 - 0
pdf-tech-common/src/main/java/utils/SpringContextUtils.java

@@ -0,0 +1,31 @@
+package utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ * Created by tangxiangan.
+ */
+public class SpringContextUtils implements ApplicationContextAware {
+
+    private static ApplicationContext context;
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        SpringContextUtils.context = context;
+    }
+
+    public static Object getBean(String beanName) {
+        return context.getBean(beanName);
+    }
+
+    public static <T> T getBean(Class<T> tClass) {
+        return context.getBean(tClass);
+    }
+
+    public static String getProperty(String key) {
+        return context.getEnvironment().getProperty(key);
+    }
+
+}

+ 8 - 0
pdf-tech-core/docker/Dockerfile

@@ -0,0 +1,8 @@
+FROM fabletang/jre8-alpine
+
+RUN echo "Asia/shanghai" > /etc/timezone;
+ADD backend-core.jar app.jar
+RUN bash -c 'touch /app.jar'
+#ENV JAVA_OPTS="-server -Xms128m -Xmx256m -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/oom.hprof -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled"
+ENTRYPOINT java -Xms128m -Xmx256m -Dfile.encoding=utf-8 -Duser.timezone=GMT+08 -jar /app.jar -Duser.timezone=GMT+08
+#ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS  -jar /app.jar" ]

+ 8 - 0
pdf-tech-core/docker/dev/Dockerfile

@@ -0,0 +1,8 @@
+FROM fabletang/jre8-alpine
+
+RUN echo "Asia/shanghai" > /etc/timezone;
+ADD backend-core.jar app.jar
+RUN bash -c 'touch /app.jar'
+ENV JAVA_OPTS="-server -Xmx1600m -Xms1600m -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/oom.hprof -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled"
+#ENTRYPOINT java -Xms128m -Xmx256m -Dfile.encoding=utf-8 -Duser.timezone=GMT+08 -jar /app.jar -Duser.timezone=GMT+08
+ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS  -jar /app.jar" ]

+ 211 - 0
pdf-tech-core/pom.xml

@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.kdan.pdf.tech</groupId>
+        <artifactId>backend</artifactId>
+        <version>0.0.1</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>cn.kdan.pdf.tech</groupId>
+    <artifactId>pdf-tech-core</artifactId>
+    <name>pdf-tech-core</name>
+    <url>http://maven.apache.org</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.kdan.pdf.tech</groupId>
+            <artifactId>pdf-tech-common</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+
+        <!-- logback  -->
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.logback-extensions</groupId>
+            <artifactId>logback-ext-spring</artifactId>
+            <version>0.1.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <version>1.7.25</version>
+        </dependency>
+        <!-- logback  -->
+
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <version>42.3.7</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- spring security start -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.session</groupId>
+            <artifactId>spring-session-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security.oauth</groupId>
+            <artifactId>spring-security-oauth2</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-beans</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-context</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security.oauth.boot</groupId>
+            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
+        </dependency>
+        <!-- end -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>pdf-tech-core</finalName>
+
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mybatis.generator</groupId>
+                <artifactId>mybatis-generator-maven-plugin</artifactId>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.postgresql</groupId>
+                        <artifactId>postgresql</artifactId>
+                        <version>42.3.7</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.22.2</version>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>${docker.version}</version>
+                <configuration>
+                    <!-- Docker 远程管理地址-->
+                    <dockerHost>http://${docker.host}</dockerHost>
+                    <!--镜像名称-->
+                    <imageName>${project.artifactId}</imageName>
+                    <!--Dockerfile-->
+                    <dockerDirectory>${project.basedir}/docker</dockerDirectory>
+                    <!--插件会将需要的资源拷贝到docker目录下,供Dockerfile里构建镜像使用-->
+                    <resources>
+                        <resource>
+                            <targetPath>/</targetPath>
+                            <directory>${project.build.directory}</directory>
+                            <include>${project.build.finalName}.jar</include>
+                        </resource>
+                    </resources>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 37 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/PdfTechCoreApplication.java

@@ -0,0 +1,37 @@
+package cn.kdan.pdf.tech.core;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * @author tangxiangan
+ */
+@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
+@MapperScan(basePackages = {"cn.kdan.pdf.tech.core.mapper"})
+@EnableScheduling
+public class PdfTechCoreApplication {
+
+    @Bean
+    public ObjectMapper objectMapper() {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+        return objectMapper;
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(PdfTechCoreApplication.class, args);
+    }
+}

+ 34 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/BackendCoreConfig.java

@@ -0,0 +1,34 @@
+package cn.kdan.pdf.tech.core.config;
+
+import annotations.EnableCommonTools;
+import config.RedissonConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Import;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.session.web.http.HttpSessionIdResolver;
+import pojo.CustomHttpSessionStrategy;
+
+/**
+ * @author tangxiangan
+ */
+@Configuration
+@ConfigurationPropertiesScan("cn.kdan.pdf.backend.core.properties")
+@EnableCommonTools
+@EnableRedisHttpSession
+@Slf4j
+@EnableAspectJAutoProxy
+@EnableCaching
+@Import({RedissonConfig.class})
+public class BackendCoreConfig {
+
+    @Bean
+    public HttpSessionIdResolver httpSessionStrategy() {
+        return new CustomHttpSessionStrategy("x-auth-token");
+    }
+
+}

+ 67 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/ControllerExceptionHandler.java

@@ -0,0 +1,67 @@
+package cn.kdan.pdf.tech.core.config;
+
+import constant.CommonConstant;
+import exception.BackendRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import pojo.ResultMap;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author tangxiangan
+ */
+@ControllerAdvice
+public class ControllerExceptionHandler {
+    private final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);
+
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseBody
+    public ResultMap<?> handlerArgumentCheck(MethodArgumentNotValidException ex) {
+        BindingResult bindingResult = ex.getBindingResult();
+        List<FieldError> fieldErrorList = bindingResult.getFieldErrors();
+
+        return new ResultMap<>(CommonConstant.EXCEPTION_CODE_PARAMETERS_ERROR,
+                fieldErrorList.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";")));
+    }
+
+    @ExceptionHandler(BackendRuntimeException.class)
+    @ResponseBody
+    public ResultMap<?> handlerDevOpsRuntimeException(BackendRuntimeException e) {
+        return new ResultMap<>(CommonConstant.EXCEPTION_CODE_RUNTIME_ERROR, e.getMessage());
+    }
+
+    @ExceptionHandler(ConstraintViolationException.class)
+    @ResponseBody
+    public ResultMap<?> handlerConstraintViolationException(ConstraintViolationException e) {
+        return new ResultMap<>(CommonConstant.EXCEPTION_CODE_RUNTIME_ERROR,
+                e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";")));
+    }
+
+    @ExceptionHandler(Exception.class)
+    @ResponseBody
+    public ResultMap<?> handlerException(Exception e) {
+        logger.info(e.getMessage(), e);
+        return new ResultMap<>(CommonConstant.EXCEPTION_CODE_SERVER_ERROR, "错误信息: " + e.getMessage());
+    }
+
+
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    @ResponseBody
+    public ResultMap<?> handlerMissingServletRequestParameterException(MissingServletRequestParameterException e) {
+        logger.info(e.getMessage(), e);
+        return new ResultMap<>(CommonConstant.EXCEPTION_CODE_SERVER_ERROR, "参数错误: " + e.getParameterName() + " 缺失");
+    }
+
+}

+ 12 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/PermissionConfig.java

@@ -0,0 +1,12 @@
+package cn.kdan.pdf.tech.core.config;
+
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author tangxiangan
+ */
+@Configuration
+public class PermissionConfig {
+
+}

+ 81 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/RedisConfig.java

@@ -0,0 +1,81 @@
+package cn.kdan.pdf.tech.core.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import config.AllowOriginConfig;
+import config.SpringContextUtilsConfig;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisClusterConfiguration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisNode;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.*;
+import utils.RedisUtils;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+@Configuration
+public class RedisConfig {
+
+
+    @Value("${spring.redis.cluster.nodes}")
+    private String nodes;
+    @Value("${spring.redis.password}")
+    private String password;
+    @Value("${spring.redis.lettuce.pool.max-idle}")
+    private Integer maxIdle;
+    @Value("${spring.redis.lettuce.pool.min-idle}")
+    private Integer minIdle;
+    @Value("${spring.redis.lettuce.pool.max-active}")
+    private Integer maxTotal;
+    @Value("${spring.redis.lettuce.pool.max-wait}")
+    private Long maxWaitMillis;
+
+    @Bean
+    LettuceConnectionFactory lettuceConnectionFactory() {
+        // 连接池配置
+        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
+        poolConfig.setMaxIdle(maxIdle == null ? 8 : maxIdle);
+        poolConfig.setMinIdle(minIdle == null ? 1 : minIdle);
+        poolConfig.setMaxTotal(maxTotal == null ? 8 : maxTotal);
+        poolConfig.setMaxWaitMillis(maxWaitMillis == null ? 5000L : maxWaitMillis);
+        LettucePoolingClientConfiguration lettucePoolingClientConfiguration = LettucePoolingClientConfiguration.builder()
+                .poolConfig(poolConfig)
+                .build();
+
+        // 集群redis
+        RedisClusterConfiguration redisConfig = new RedisClusterConfiguration();
+        Set<RedisNode> nodeses = new HashSet<>();
+        String[] hostses = nodes.split(",");
+        for (String h : hostses) {
+            h = h.replaceAll("\\s", "").replaceAll("\n", "");
+            if (!"".equals(h)) {
+                String host = h.split(":")[0];
+                int port = Integer.valueOf(h.split(":")[1]);
+                nodeses.add(new RedisNode(host, port));
+            }
+        }
+        redisConfig.setClusterNodes(nodeses);
+        // 跨集群执行命令时要遵循的最大重定向数量
+        redisConfig.setMaxRedirects(3);
+        redisConfig.setPassword(password);
+
+        return new LettuceConnectionFactory(redisConfig, lettucePoolingClientConfiguration);
+
+    }
+
+}

+ 64 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/RestTemplateConfig.java

@@ -0,0 +1,64 @@
+package cn.kdan.pdf.tech.core.config;
+
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.ssl.TrustStrategy;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author tangxiangan
+ * @date 2020/11/24,0024 11:04
+ * Description
+ */
+@Configuration
+public class RestTemplateConfig {
+
+    @Bean
+    public RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory factory) {
+        RestTemplate restTemplate = new RestTemplate(factory);
+        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
+            @Override
+            public boolean hasError(ClientHttpResponse response) throws IOException {
+                return true;
+            }
+
+            @Override
+            public void handleError(ClientHttpResponse response) throws IOException {
+            }
+        });
+        return restTemplate;
+    }
+
+    @Bean
+    public static HttpComponentsClientHttpRequestFactory generateHttpRequestFactory()
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
+        TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
+        SSLContext sslContext = SSLContexts
+                .custom()
+                .loadTrustMaterial(null, acceptingTrustStrategy)
+                .build();
+        SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
+
+        HttpClientBuilder httpClientBuilder = HttpClients.custom();
+        httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
+        CloseableHttpClient httpClient = httpClientBuilder.build();
+        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+        factory.setHttpClient(httpClient);
+        return factory;
+    }
+}

+ 46 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/AuthExceptionEntryPoint.java

@@ -0,0 +1,46 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
+    /**
+     * 处理token过期的返回内容
+     *
+     * @throws IOException
+     * @throws ServletException
+     */
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
+        Map<String, Object> map = new HashMap<>(7);
+        Throwable cause = authException.getCause();
+        if (cause instanceof InvalidTokenException) {
+            map.put("code", AuthConstant.EXCEPTION_CODE_TOKEN_IS_INVALID);
+            map.put("msg", AuthConstant.EXCEPTION_MSG_TOKEN_IS_INVALID);
+        } else {
+            map.put("code", AuthConstant.EXCEPTION_CODE_USER_NOT_LOGIN);
+            map.put("msg", AuthConstant.EXCEPTION_MSG_USER_NOT_LOGIN);
+        }
+        map.put("data", authException.getMessage());
+        map.put("path", request.getServletPath());
+        map.put("timestamp", String.valueOf(System.currentTimeMillis()));
+        response.setContentType("application/json");
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.writeValue(response.getOutputStream(), map);
+        } catch (Exception e) {
+            throw new ServletException();
+        }
+    }
+}

+ 138 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/AuthorizationServerConfig.java

@@ -0,0 +1,138 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import cn.kdan.pdf.tech.core.service.AuthService;
+import cn.kdan.pdf.tech.core.service.impl.CustomAuthenticationCodeServicesImpl;
+import cn.kdan.pdf.tech.core.service.impl.CustomClientDetailsServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.ClientDetailsService;
+import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
+
+/**
+ * 配置授权认证服务类
+ *
+ * @author tangxiangan
+ */
+@EnableAuthorizationServer //授权认证中心
+@Configuration
+public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
+
+    /**
+     * 权限验证控制器
+     */
+    @Autowired
+    private AuthenticationManager authenticationManager;
+    /**
+     * redis连接工厂
+     */
+    @Autowired
+    private LettuceConnectionFactory redisConnectionFactory;
+
+    @Autowired
+    private AuthService authService;
+
+    /**
+     * 配置AccessToken的存储方式:此处使用Redis存储
+     * Token的可选存储方式
+     * 1、InMemoryTokenStore
+     * 2、JdbcTokenStore
+     * 3、JwtTokenStore
+     * 4、RedisTokenStore
+     * 5、JwkTokenStore
+     */
+    @Bean
+    public TokenStore tokenStore() {
+        return new RedisTokenStore(redisConnectionFactory);
+    }
+
+    /**
+     * 用来配置令牌端点(Token Endpoint)的安全约束.
+     * 针对授权服务器的安全,我们干了两个事情,首先打开了验证Token的访问权限
+     * 然后允许ClientSecret明文方式保存并且可以通过表单提交(而不仅仅是Basic Auth方式提交)
+     *
+     * @param security
+     * @throws Exception
+     */
+    @Override
+    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
+        //允许所有资源服务器访问公钥端点(/oauth/token_key) //只允许验证用户访问令牌解析端点(/oauth/check_token)
+        security.tokenKeyAccess("permitAll()")
+                .checkTokenAccess("permitAll()")
+                .allowFormAuthenticationForClients();// 允许客户端发送表单来进行权限认证来获取令牌
+    }
+
+    /**
+     * 用来配置客户端详情服务(ClientDetailsService),
+     * 客户端详情信息在这里进行初始化,  数据库在进行client_id 与 client_secret验证时   会使用这个service进行验证
+     *
+     * @param clients
+     * @throws Exception
+     */
+    @Override
+    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+        clients.withClientDetails(clientDetails());
+
+    }
+
+    /**
+     * oauth的客户端凭证校验是通过ClientDetailsService来实现的
+     * oauth默认为我们提供了InMemoryClientDetailsService和JdbcClientDetailsService,使用自己实现的ClientDetailsService。
+     * 这里我们需要加上@Primary把该类当成是主类,因为在@EnableAuthorizationServer注解中会引入配置类ClientDetailsServiceConfiguration,该配置类会创建一个默认的ClientDetailsService,如果不加@Primary会导致循环
+     *
+     * @return
+     */
+    @Bean
+    public ClientDetailsService clientDetails() {
+        return new CustomClientDetailsServiceImpl(authService);
+    }
+
+
+    /**
+     * 用来配置授权(authorizatio)以及令牌(token)的访问端点和令牌服务   核心配置  在启动时就会进行配置
+     *
+     * @param endpoints
+     * @throws Exception
+     */
+    @Override
+    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+        //开启密码授权类型
+        endpoints.authenticationManager(authenticationManager);
+        //我们如果自己不配置AuthorizationCodeServices,那么默认实现就是InMemoryAuthorizationCodeServices授权码存在内存中
+        //这里使用我们自定义的服务让授权码存在redis中
+        endpoints.authorizationCodeServices(new CustomAuthenticationCodeServicesImpl(redisConnectionFactory));
+        //配置token存储方式
+        endpoints.tokenServices(defaultTokenServices());
+    }
+
+
+    /**
+     * <p>注意,自定义TokenServices的时候,需要设置@Primary,否则报错,</p>
+     * 自定义的token
+     * 认证的token是存到redis里的
+     *
+     * @return
+     */
+    @Primary
+    @Bean
+    public DefaultTokenServices defaultTokenServices() {
+        DefaultTokenServices tokenServices = new DefaultTokenServices();
+        tokenServices.setTokenStore(tokenStore());
+        tokenServices.setSupportRefreshToken(true);
+        // token有效期自定义设置,默认12小时
+        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 12);
+        // refresh_token默认30天
+        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);
+        return tokenServices;
+    }
+}

+ 48 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/CustomPermissionEvaluator.java

@@ -0,0 +1,48 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.PermissionEvaluator;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author tangxiangan
+ */
+@Configuration
+public class CustomPermissionEvaluator implements PermissionEvaluator {
+
+    private static final Logger log = LoggerFactory.getLogger(CustomPermissionEvaluator.class);
+
+    private List<SimpleGrantedAuthority> authorities = new ArrayList<>();
+
+    //普通的targetDomainObject判断
+    @Override
+    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
+        log.info("---------------账号中心鉴权----------------------");
+        log.info(targetDomainObject + ":" + permission);
+        log.info(authorities.toString());
+        if (authorities.isEmpty()) {
+            authorities = getSimpleGrantedAuthorities(authentication);
+        }
+        return authorities.contains(new SimpleGrantedAuthority(targetDomainObject + ":" + permission));
+    }
+
+    //用于ACL的访问控制
+    @Override
+    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
+        log.info("-------------------------------------");
+        return false;
+    }
+
+    private List<SimpleGrantedAuthority> getSimpleGrantedAuthorities(Authentication authentication) {
+        String clientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();
+        return authorities;
+    }
+}

+ 30 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LoginAuthenticationFailHandler.java

@@ -0,0 +1,30 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * 认证失败的处理
+ *
+ * @author tangxiangan
+ */
+@Component
+public class LoginAuthenticationFailHandler implements AuthenticationFailureHandler {
+
+    @Override
+    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
+
+        httpServletResponse.setContentType("application/json;charset=UTF-8");
+        PrintWriter writer = httpServletResponse.getWriter();
+        writer.write(e.getMessage());
+        writer.flush();
+        writer.close();
+    }
+}

+ 75 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LoginAuthenticationProvider.java

@@ -0,0 +1,75 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.model.Members;
+import cn.kdan.pdf.tech.core.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.InternalAuthenticationServiceException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+import pojo.CustomUserDetails;
+import pojo.ResultMap;
+import utils.JsonUtils;
+
+/**
+ * 接口认证类,定义了认证方法authenticate() 。
+ * AbstractUserDetailsAuthenticationProvider 为认证抽象类,实现了接口 AuthenticationProvider 定义的认证方法 authenticate()。还定义了抽象方法 retrieveUser() 用于查询数据库用户信息,以及抽象方法 additionalAuthenticationChecks() 用作额外的身份验证检查。
+ *
+ * @author tangxiangan
+ */
+@Component("loginAuthenticationProvider")
+public class LoginAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private UserDetailsService userDetailsService;
+
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+
+    /**
+     * 实现密码是否匹配的检测
+     */
+    @Override
+    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
+        if (userDetails instanceof CustomUserDetails) {
+            CustomUserDetails customUserDetails = (CustomUserDetails) userDetails;
+            if (authentication.getCredentials() != null) {
+                Members user = userService.getById(customUserDetails.getId());
+                if (user != null) {
+                    //判断密码是否正确
+                    String presentedPassword = authentication.getCredentials().toString();
+                    if (passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
+                        //验证通过
+                        return;
+                    }
+                }
+            }
+        }
+        throw new BadCredentialsException(JsonUtils.getJsonString(new ResultMap<>(AuthConstant.EXCEPTION_CODE_USER_PASSWORD_ERROR, AuthConstant.EXCEPTION_MSG_USER_PASSWORD_ERROR)));
+    }
+
+    /**
+     * authenticate方法里,会先从userCache里获取UserDetails
+     * 如果userCache里拿到的是null,就进行retrieveUser,也就是根据账号、密码到数据库里去取用户信息,
+     * 手动加载user
+     */
+    @Override
+    protected UserDetails retrieveUser(String s, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
+        UserDetails userDetail = userDetailsService.loadUserByUsername(s);
+        if (userDetail == null) {
+            throw new InternalAuthenticationServiceException(JsonUtils.getJsonString(new ResultMap<>(AuthConstant.EXCEPTION_CODE_USER_PASSWORD_ERROR, AuthConstant.EXCEPTION_MSG_USER_PASSWORD_ERROR)));
+        } else {
+            return userDetail;
+        }
+    }
+
+}

+ 50 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LoginAuthenticationSuccessHandler.java

@@ -0,0 +1,50 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.service.AuthService;
+import cn.kdan.pdf.tech.core.service.UserService;
+import constant.CommonConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+import pojo.CustomUserDetails;
+import pojo.ResultMap;
+import utils.JsonUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collections;
+
+/**
+ * @author tangxiangan
+ */
+@Component
+public class LoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
+
+    @Autowired
+    private UserService userService;
+    @Autowired
+    private AuthService authService;
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
+                                        Authentication authentication) throws IOException {
+        //Authentication接口封装认证信息
+        response.setContentType("application/json;charset=UTF-8");
+        //将authentication认证信息转换为json格式的字符串写到response里面去
+        PrintWriter writer = response.getWriter();
+        String userId = ((CustomUserDetails) authentication.getPrincipal()).getId();
+        //根据认证信息获取授权码
+        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), null, Collections.emptySet());
+
+        String code = authService.getAuthorizeCode(userId, authenticationToken);
+        //登录成功,返回授权码
+        writer.write(JsonUtils.getJsonString(new ResultMap<>(CommonConstant.SUCCESS, AuthConstant.GET_AUTHORIZE_CODE_SUCCESS, code)));
+        writer.flush();
+        writer.close();
+    }
+}

+ 43 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/LogoutAuthenticationSuccessHandler.java

@@ -0,0 +1,43 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import constant.CommonConstant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.stereotype.Component;
+import pojo.ResultMap;
+import utils.JsonUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author tangxiangan
+ */
+@Component
+public class LogoutAuthenticationSuccessHandler implements LogoutSuccessHandler {
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+
+    @Override
+    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
+        logger.info("登出成功");
+        httpServletResponse.setContentType("application/json;charset=utf-8");
+
+        httpServletResponse.setStatus(CommonConstant.SUCCESS);
+        PrintWriter writer = httpServletResponse.getWriter();
+//        String userId = httpServletRequest.getParameter("userId");
+//        UserInfo user = userService.getById(userId);
+//        if (user != null) {
+//            //清除session 暂时不用
+//        }
+        writer.write(JsonUtils.getJsonString(new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS, "登录退出成功")));
+        writer.flush();
+        writer.close();
+    }
+}

+ 26 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/MethodSecurityConfig.java

@@ -0,0 +1,26 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
+import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
+
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
+
+    private CustomPermissionEvaluator customPermissionEvaluator;
+
+    @Autowired
+    public void setCustomPermissionEvaluator(CustomPermissionEvaluator customPermissionEvaluator) {
+        this.customPermissionEvaluator = customPermissionEvaluator;
+    }
+
+    @Override
+    protected MethodSecurityExpressionHandler createExpressionHandler() {
+        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
+        expressionHandler.setPermissionEvaluator(customPermissionEvaluator);
+        return expressionHandler;
+        //return super.createExpressionHandler();
+    }
+}

+ 29 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/ResourceServerConfig.java

@@ -0,0 +1,29 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
+
+/**
+ * 资源提供端的配置
+ */
+@Configuration
+@EnableResourceServer //开启资源提供服务的配置  是默认情况下spring security oauth2的http配置会被WebSecurityConfigurerAdapter的配置覆盖
+@Order(3)
+public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
+
+    @Override
+    public void configure(HttpSecurity http) throws Exception {
+        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
+        http.authorizeRequests().anyRequest().authenticated();
+    }
+
+    @Override
+    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
+        resources.authenticationEntryPoint(new AuthExceptionEntryPoint());
+    }
+}

+ 106 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/config/oauth/WebSecurityConfig.java

@@ -0,0 +1,106 @@
+package cn.kdan.pdf.tech.core.config.oauth;
+
+
+import cn.kdan.pdf.tech.core.pojo.oauth2.LoginPasswordEncoder;
+import constant.CommonConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * 是默认情况下spring security的http配置 优于ResourceServerConfigurerAdapter的配置
+ */
+@Configuration
+@EnableWebSecurity
+@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
+@Order(1)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    /**
+     * 密码验证处理器
+     */
+    @Autowired
+    private LoginAuthenticationProvider loginAuthenticationProvider;
+
+    @Autowired
+    private LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;
+
+    @Autowired
+    private LoginAuthenticationFailHandler loginAuthenticationFailHandler;
+
+    @Autowired
+    private LogoutAuthenticationSuccessHandler logoutAuthenticationSuccessHandler;
+
+    @Value("${httpMatchers.request}")
+    private String httpRequestMatchers;
+    @Value("${httpMatchers.web}")
+    private String httpWebMatchers;
+
+
+    /**
+     * 所有security的加密都会使用该加密器
+     */
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        //密码加密问题,默认使用BCryptPasswordEncoder,在注册时是否也需要进行MD5PasswordEncoder加密
+        return new LoginPasswordEncoder();
+    }
+
+
+    /**
+     * AuthenticationManager 为认证管理接口类,其定义了认证方法authenticate()
+     * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
+     */
+    @Override
+    @Bean
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    /**
+     * spring security设置
+     */
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.requestMatchers().antMatchers(httpRequestMatchers.split(CommonConstant.STRING_SIGN_COMMA));
+        http.authorizeRequests().antMatchers(httpRequestMatchers.split(CommonConstant.STRING_SIGN_COMMA)).permitAll();
+        // CSRF禁用,因为不使用session
+        http.csrf().disable();
+        http.formLogin()
+                .successHandler(loginAuthenticationSuccessHandler)
+                .failureHandler(loginAuthenticationFailHandler)
+                .permitAll();//允许所有用户都有权限访问登录页面
+        http.logout()
+                .logoutSuccessHandler(logoutAuthenticationSuccessHandler)
+                .permitAll();
+        //图形验证码校验器
+        //      http.addFilterBefore(new LoginAuthenticationProcessingFilter("/login", "/login?error", authService), UsernamePasswordAuthenticationFilter.class);
+    }
+
+    /**
+     * 权限管理器  AuthorizationServerConfigurerAdapter认证中心需要的AuthenticationManager需要
+     * 所有 UserDetails 相关的它都管,包含 PasswordEncoder 密码等
+     */
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) { // @formatter:off
+        loginAuthenticationProvider.setHideUserNotFoundExceptions(false);
+        //目的是为了前端获取数据时获取到整个form-data的数据,提供验证器
+        auth.authenticationProvider(loginAuthenticationProvider);
+    }
+
+    @Override
+    public void configure(WebSecurity web) {
+        web.ignoring()
+                .antMatchers(httpWebMatchers.split(CommonConstant.STRING_SIGN_COMMA));
+    }
+}

+ 121 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/constant/AuthConstant.java

@@ -0,0 +1,121 @@
+package cn.kdan.pdf.tech.core.constant;
+
+/**
+ * @author tangxiangan
+ */
+public interface AuthConstant {
+
+    int EXCEPTION_CODE_IMAGE_CODE_ERROR = 301;
+    String EXCEPTION_MSG_IMAGE_CODE_ERROR = "图形验证码错误";
+
+
+    String SUCCESS_MSG_CODE_SEND = "验证码发送成功";
+
+    String SUCCESS_MSG_CODE_SEND_MAIL = "邮件发送成功";
+    String EXCEPTION_MSG_CODE_SEND_ERROR = "验证码发送失败";
+    String EXCEPTION_MAIL_CODE_SEND_ERROR = "邮件发送失败";
+    String EXCEPTION_MSG_CODE_ERROR = "验证码输入错误";
+
+    String EXCEPTION_MSG_RESET_PASSWORD_ERROR = "重置密码链接已过期";
+
+    String EXCEPTION_MSG_EMAIL_CODE_ERROR = "邮箱验证码错误";
+
+    String EXCEPTION_MSG_USER_VERIFY_ERROR = "身份认证失败";
+
+    String VERIFY_CODE_CHECK_SUCCESS = "验证通过";
+
+    String EXCEPTION_MSG_CODE_ACTION_ERROR = "操作类型错误";
+    String EXCEPTION_MSG_CODE_TYPE_ERROR = "发送方式错误";
+
+    int EXCEPTION_CODE_USER_NOT_FOUND = 302;
+    String EXCEPTION_MSG_USER_NOT_FOUND = "当前登录用户没有注册,详细信息请联系管理员";
+
+    int EXCEPTION_CODE_USER_STATUS_IS_FROZEN = 303;
+    String EXCEPTION_MSG_USER_STATUS_IS_FROZEN = "账号正处于冻结状态";
+
+    int EXCEPTION_CODE_USER_STATUS_IS_INACTIVE = 304;
+    String EXCEPTION_MSG_USER_STATUS_IS_INACTIVE = "当前登录用户没有激活或被禁用,详细信息请联系管理员";
+
+    int EXCEPTION_CODE_USER_IS_LOCKED = 305;
+    String EXCEPTION_MSG_USER_IS_LOCKED = "登录尝试次数过多,账号已被锁定";
+
+    int EXCEPTION_CODE_USER_PASSWORD_ERROR = 306;
+    String EXCEPTION_MSG_USER_PASSWORD_ERROR = "账号密码错误";
+
+    int EXCEPTION_CODE_SESSION_NOT_FOUND = 307;
+    String EXCEPTION_MSG_SESSION_NOT_FOUND = "账号不存在";
+
+    int EXCEPTION_CODE_USER_INFO_NOT_COMPLETE = 308;
+    String EXCEPTION_MSG_USER_INFO_NOT_COMPLETE = "用户资料尚未完善!";
+
+    int EXCEPTION_CODE_TOKEN_IS_INVALID = 310;
+    String EXCEPTION_MSG_TOKEN_IS_INVALID = "无效的token或者token已过期";
+
+    int EXCEPTION_CODE_USER_NOT_ORG = 311;
+    String EXCEPTION_MSG_USER_NOT_ORG = "该用户不属于任何机构,请联系系统管理员";
+
+    int EXCEPTION_CODE_USER_PASSWORD_EXPIRED = 312;
+    String EXCEPTION_MSG_USER_PASSWORD_EXPIRED = "用户密码已过期,请修改或重置密码";
+
+    int EXCEPTION_CODE_USER_NOT_LOGIN = 313;
+    String EXCEPTION_MSG_USER_NOT_LOGIN = "用户没有登录,请登录后再访问";
+
+    String EXCEPTION_MSG_NO_PERMISSION = "您没有当前平台的操作权限。";
+
+    int EXCEPTION_CODE_GET_CODE_FAIL = 314;
+    String EXCEPTION_MSG_GET_CODE_FAIL = "获取授权码失败";
+
+    String GET_AUTHORIZE_CODE_SUCCESS = "获取授权码成功";
+    String EXCEPTION_MSG_PLEASE_ADD_AUTH_CONFIG = "请添加OAuth2登录配置";
+
+    int EXCEPTION_CODE_GET_TOKEN_FAIL = 315;
+    String EXCEPTION_MSG_GET_TOKEN_FAIL = "获取token失败";
+
+    int EXCEPTION_CODE_EMAIL_LOGIN_FAIL = 316;
+    String EXCEPTION_MSG_EMAIL_NOT_VERIFY = "当前登陆邮箱未验证,请用手机号登陆";
+
+    String EXCEPTION_MSG_GET_APP_INFO_FAIL = "获取token失败,应用信息为空";
+    String EXCEPTION_MSG_CLIENT_ID_EXISTING = "OAuth2客户端已存在";
+
+    String MSG_LOGIN_SUCCESS = "用户登录成功";
+    String MSG_LOGOUT_SUCCESS = "用户登出成功";
+
+    String MSG_NOT_JOINED_TEAM = "尚未加入团队或已被团队所有者锁定,暂不能登陆";
+
+    String MSG_LOCKED_OR_DELETE = "用户已被锁定或删除";
+
+    String LOCK_PERIOD = "lock_period";
+    String LOCK_TIMES = "lock_times";
+    String LOCK_MINUTES = "lock_minutes";
+    String SESSION_ = "session_";
+    String IMAGE_CODE = "image_code";
+
+    String PASSWORD_ERROR_ = "password_error_";
+
+    String MOBILE_REGEX = "[1][3456789]\\d{9}";
+    String EMAIL_REGEX = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
+
+
+    String ACCESS = "access:";
+    String AUTH_TO_ACCESS = "auth_to_access:";
+    String AUTH = "auth:";
+    String REFRESH_AUTH = "refresh_auth:";
+    String ACCESS_TO_REFRESH = "access_to_refresh:";
+    String REFRESH = "refresh:";
+    String REFRESH_TO_ACCESS = "refresh_to_access:";
+    String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
+    String UNAME_TO_ACCESS = "uname_to_access:";
+
+    String DEFAULT_REDIRECT_URI = "https://127.0.0.1";
+
+    String VERIFY_CODE_KEY = "verifyCode_";
+
+    String VERIFY_CODE_TIME_OUT = "verifyCode_timeout_";
+
+    Long VERIFY_CODE_KEY_EXPIRE_TIME = 15 * 60L;
+
+    Long VERIFY_CODE_KEY_RESEND_TIME = 60L;
+
+    String VERIFY_CODE_SEND_TOO_QUICKLY = "您的操作过于频繁,请稍后再试";
+
+}

+ 14 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/constant/MembersConstant.java

@@ -0,0 +1,14 @@
+package cn.kdan.pdf.tech.core.constant;
+
+/**
+ * @author tangxiangan
+ */
+public interface MembersConstant {
+    public static final String EXCEPTION_MSG_USER_NOT_FOUND = "用户不存在";
+    public static final String PASSWORD_UPDATE_SUCCESS = "密码重置成功";
+
+    public static final String PASSWORD_UPDATE_ERROR = "密码重置失败";
+
+    public static final String MEMBERS_NOT_EXIST = "账号不存在";
+
+}

+ 95 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/controller/AuthController.java

@@ -0,0 +1,95 @@
+package cn.kdan.pdf.tech.core.controller;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.enums.CaptchaActionEnum;
+import cn.kdan.pdf.tech.core.enums.VerifyTypeEnum;
+import cn.kdan.pdf.tech.core.pojo.oauth2.TokenPOJO;
+import cn.kdan.pdf.tech.core.service.AuthService;
+import constant.CommonConstant;
+import org.springframework.web.bind.annotation.*;
+import pojo.ResultMap;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.validation.constraints.NotBlank;
+import java.io.IOException;
+
+
+/**
+ * @author tangxiangan
+ */
+@RestController
+@RequestMapping("/auth")
+public class AuthController {
+
+    @Resource
+    private AuthService authService;
+
+
+    /**
+     * 获取token
+     *
+     * @param code 授权码
+     */
+    @GetMapping("/getToken")
+    public ResultMap<TokenPOJO> getToken(@NotBlank(message = "授权码不能为空") @RequestParam String code) {
+        TokenPOJO token = authService.getToken(code);
+        if (token == null) {
+            return new ResultMap<>(AuthConstant.EXCEPTION_CODE_GET_TOKEN_FAIL, AuthConstant.EXCEPTION_MSG_GET_TOKEN_FAIL);
+        } else {
+            return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS, token);
+        }
+    }
+
+    /**
+     * 获取图片验证码
+     *
+     * @param width     图片的宽度
+     * @param height    图片的长度
+     * @param codeCount 验证码字符个数
+     * @param lineCount 干扰线的数量
+     */
+    @GetMapping("/getImageCode")
+    public void getImageCode(HttpServletResponse response, HttpSession session, @RequestParam(required = false) String type, @RequestParam int width,
+                             @RequestParam int height, @RequestParam int codeCount, @RequestParam int lineCount) throws IOException {
+        authService.generateImageCode(response, session, type, width, height, codeCount, lineCount);
+    }
+
+    /**
+     * 获取手机验证码或者邮箱验证邮件
+     * @param action 验证动作 CaptchaActionEnum
+     * @param type 验证类型 0邮箱 1手机
+     * @param receiver 接收者 (邮箱或者手机号)
+     * @return Boolean
+     */
+    @GetMapping("/getVerifyCode")
+    public ResultMap<Boolean> getVerifyCode(@RequestParam String action,@RequestParam Integer type,@NotBlank(message = "接收者不能为空") @RequestParam String receiver) {
+        CaptchaActionEnum actionEnum = CaptchaActionEnum.getEnumByValue(action);
+        if (actionEnum == null) {
+            return new ResultMap<>(CommonConstant.ERROR, AuthConstant.EXCEPTION_MSG_CODE_ACTION_ERROR, false);
+        }
+        VerifyTypeEnum verifyTypeEnum = VerifyTypeEnum.getEnumByCode(type);
+        if (verifyTypeEnum == null) {
+            return new ResultMap<>(CommonConstant.ERROR, AuthConstant.EXCEPTION_MSG_CODE_TYPE_ERROR, false);
+        }
+        if (authService.getVerifyCode(actionEnum, verifyTypeEnum, receiver)) {
+            return new ResultMap<>(CommonConstant.SUCCESS, VerifyTypeEnum.PHONE.code().equals(type) ?AuthConstant.SUCCESS_MSG_CODE_SEND:AuthConstant.SUCCESS_MSG_CODE_SEND_MAIL, true);
+        } else {
+            return new ResultMap<>(CommonConstant.ERROR, VerifyTypeEnum.PHONE.code().equals(type) ?AuthConstant.EXCEPTION_MSG_CODE_SEND_ERROR:AuthConstant.EXCEPTION_MAIL_CODE_SEND_ERROR, false);
+        }
+    }
+
+    /**
+     * 检查用户是否存在(用于忘记密码)
+     * @param code 图片验证码
+     * @param account 账户
+     * @return Boolean
+     */
+    @PostMapping("/checkExist")
+    public ResultMap<Boolean> checkExist(@NotBlank(message = "key不能为空") @RequestParam String key,@NotBlank(message = "验证码不能为空") @RequestParam String code,@RequestParam @NotBlank(message = "手机号/邮箱 不能为空")String account) {
+        return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS,authService.checkExist(key,account,code));
+    }
+
+
+}

+ 45 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/CaptchaActionEnum.java

@@ -0,0 +1,45 @@
+package cn.kdan.pdf.tech.core.enums;
+
+/**
+ * @author tangxiangan
+ */
+
+public enum CaptchaActionEnum {
+    //注册用户
+    USER_REGISTER("0", "注册用户", ""),
+    //忘记密码
+    FORGET_PASSWORD("1", "忘记密码", "17PDF Reader(Staging)账号密码重置");
+
+    private final String value;
+    private final String action;
+
+    private final String subject;
+
+    public String subject() {
+        return subject;
+    }
+
+    CaptchaActionEnum(String value, String action, String subject) {
+        this.value = value;
+        this.action = action;
+        this.subject = subject;
+    }
+
+    public static CaptchaActionEnum getEnumByValue(String value) {
+        for (CaptchaActionEnum modeEnum : CaptchaActionEnum.values()) {
+            if (modeEnum.value().equals(value)) {
+                return modeEnum;
+            }
+        }
+        return null;
+    }
+
+    public String value() {
+        return value;
+    }
+
+    public String action() {
+        return action;
+    }
+
+}

+ 29 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/ClientEnum.java

@@ -0,0 +1,29 @@
+package cn.kdan.pdf.tech.core.enums;
+
+/**
+ * 有效状态(是否可用)
+ * CD101100
+ *
+ * @author tangxiangan
+ */
+public enum ClientEnum {
+
+    /**
+     * web端
+     */
+    WEB("web"),
+    /**
+     * 安卓端
+     */
+    APP("android");
+
+    private final String value;
+
+    ClientEnum(String value) {
+        this.value = value;
+    }
+
+    public String value() {
+        return value;
+    }
+}

+ 32 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/ImageCodeTypeEnum.java

@@ -0,0 +1,32 @@
+package cn.kdan.pdf.tech.core.enums;
+
+/**
+ * @author tangxiangan
+ */
+
+public enum ImageCodeTypeEnum {
+
+    //登录
+    LOGIN("login"),
+    //忘记密码
+    FORGET_PASSWORD("forget_password");
+
+    private String value;
+
+    ImageCodeTypeEnum(String value) {
+        this.value = value;
+    }
+
+    public String value() {
+        return value;
+    }
+
+    public static ImageCodeTypeEnum getEnumByValue(String value) {
+        for (ImageCodeTypeEnum modeEnum : ImageCodeTypeEnum.values()) {
+            if (modeEnum.value().equals(value)) {
+                return modeEnum;
+            }
+        }
+        return ImageCodeTypeEnum.LOGIN;
+    }
+}

+ 33 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/enums/VerifyTypeEnum.java

@@ -0,0 +1,33 @@
+package cn.kdan.pdf.tech.core.enums;
+
+/**
+ * 验证类型
+ *
+ * @author tangxiangan
+ */
+public enum VerifyTypeEnum {
+    //邮箱
+    EMAIL(0),
+    //手机
+    PHONE(1);
+
+    private Integer code;
+
+    VerifyTypeEnum(Integer code) {
+        this.code = code;
+    }
+
+
+    public static VerifyTypeEnum getEnumByCode(int code) {
+        for (VerifyTypeEnum type : VerifyTypeEnum.values()) {
+            if (type.code() == code) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    public Integer code() {
+        return code;
+    }
+}

+ 59 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/filter/CorsFilter.java

@@ -0,0 +1,59 @@
+package cn.kdan.pdf.tech.core.filter;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@WebFilter
+@Order(Ordered.HIGHEST_PRECEDENCE)
+@Component
+public class CorsFilter implements Filter {
+
+    @Value("${cors.allow-headers}")
+    private String headers;
+    @Value("${cors.allow-methods}")
+    private String methods;
+    @Value("${cors.allow-expose}")
+    private String expose;
+    @Value("${cors.allow-credentials}")
+    private String credentials;
+    @Value("${cors.allow-origins}")
+    private String origins;
+    @Value("${cors.allow-max-age}")
+    private String maxAge;
+
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        response.setHeader("Access-Control-Allow-Origin", origins);
+        response.setHeader("Access-Control-Allow-Credentials", credentials);
+        response.setHeader("Access-Control-Allow-Methods", methods);
+        response.setHeader("Access-Control-Allow-Max-Age", maxAge);
+        response.setHeader("Access-Control-Expose-Headers", expose);
+        response.setHeader("Access-Control-Allow-Headers", headers);
+        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}

+ 35 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/mapper/OauthClientDetailsMapper.java

@@ -0,0 +1,35 @@
+package cn.kdan.pdf.tech.core.mapper;
+
+import cn.kdan.pdf.tech.core.model.OauthClientDetails;
+import cn.kdan.pdf.tech.core.model.OauthClientDetailsExample;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.session.RowBounds;
+
+public interface OauthClientDetailsMapper {
+    long countByExample(OauthClientDetailsExample example);
+
+    int deleteByExample(OauthClientDetailsExample example);
+
+    int deleteByPrimaryKey(String clientId);
+
+    int insert(OauthClientDetails record);
+
+    int insertSelective(OauthClientDetails record);
+
+    List<OauthClientDetails> selectByExampleWithRowbounds(OauthClientDetailsExample example, RowBounds rowBounds);
+
+    List<OauthClientDetails> selectByExample(OauthClientDetailsExample example);
+
+    OauthClientDetails selectByPrimaryKey(String clientId);
+
+    int updateByExampleSelective(@Param("record") OauthClientDetails record, @Param("example") OauthClientDetailsExample example);
+
+    int updateByExample(@Param("record") OauthClientDetails record, @Param("example") OauthClientDetailsExample example);
+
+    int updateByPrimaryKeySelective(OauthClientDetails record);
+
+    int updateByPrimaryKey(OauthClientDetails record);
+}

+ 33 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/mapper/VppMemberMapper.java

@@ -0,0 +1,33 @@
+package cn.kdan.pdf.tech.core.mapper;
+
+import cn.kdan.pdf.tech.core.model.VppMember;
+import cn.kdan.pdf.tech.core.model.VppMemberExample;
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.session.RowBounds;
+
+public interface VppMemberMapper {
+    long countByExample(VppMemberExample example);
+
+    int deleteByExample(VppMemberExample example);
+
+    int deleteByPrimaryKey(String id);
+
+    int insert(VppMember row);
+
+    int insertSelective(VppMember row);
+
+    List<VppMember> selectByExampleWithRowbounds(VppMemberExample example, RowBounds rowBounds);
+
+    List<VppMember> selectByExample(VppMemberExample example);
+
+    VppMember selectByPrimaryKey(String id);
+
+    int updateByExampleSelective(@Param("row") VppMember row, @Param("example") VppMemberExample example);
+
+    int updateByExample(@Param("row") VppMember row, @Param("example") VppMemberExample example);
+
+    int updateByPrimaryKeySelective(VppMember row);
+
+    int updateByPrimaryKey(VppMember row);
+}

+ 635 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/Members.java

@@ -0,0 +1,635 @@
+package cn.kdan.pdf.tech.core.model;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+public class Members implements Serializable {
+    private String id;
+
+    private String name;
+
+    private String phone;
+
+    private String email;
+
+    private Integer gender;
+
+    private String region;
+
+    private String signature;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+
+    private String passwordDigest;
+
+    private String timeZone;
+
+    private Integer viewedTimes;
+
+    private Integer status;
+
+    private String activationToken;
+
+    private Date activatedAt;
+
+    private String resetToken;
+
+    private Date resetTokenGeneratedAt;
+
+    private String phoneZone;
+
+    private String account;
+
+    private String location;
+
+    private String publicPhone;
+
+    private String publicEmail;
+
+    private String settings;
+
+    private Integer points;
+
+    private Date lastSignAt;
+
+    private Integer totalPoints;
+
+    private Integer totalSpace;
+
+    private BigDecimal usedSpace;
+
+    private BigDecimal leftSpace;
+
+    private Integer appsCode;
+
+    private Integer subscriberType;
+
+    private String referralCode;
+
+    private Integer referralCount;
+
+    private static final long serialVersionUID = 1L;
+
+    public String getId() {
+        return id;
+    }
+
+    public Members withId(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Members withName(String name) {
+        this.setName(name);
+        return this;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public Members withPhone(String phone) {
+        this.setPhone(phone);
+        return this;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public Members withEmail(String email) {
+        this.setEmail(email);
+        return this;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public Integer getGender() {
+        return gender;
+    }
+
+    public Members withGender(Integer gender) {
+        this.setGender(gender);
+        return this;
+    }
+
+    public void setGender(Integer gender) {
+        this.gender = gender;
+    }
+
+    public String getRegion() {
+        return region;
+    }
+
+    public Members withRegion(String region) {
+        this.setRegion(region);
+        return this;
+    }
+
+    public void setRegion(String region) {
+        this.region = region;
+    }
+
+    public String getSignature() {
+        return signature;
+    }
+
+    public Members withSignature(String signature) {
+        this.setSignature(signature);
+        return this;
+    }
+
+    public void setSignature(String signature) {
+        this.signature = signature;
+    }
+
+    public Date getCreatedAt() {
+        return createdAt;
+    }
+
+    public Members withCreatedAt(Date createdAt) {
+        this.setCreatedAt(createdAt);
+        return this;
+    }
+
+    public void setCreatedAt(Date createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public Date getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public Members withUpdatedAt(Date updatedAt) {
+        this.setUpdatedAt(updatedAt);
+        return this;
+    }
+
+    public void setUpdatedAt(Date updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+
+    public String getPasswordDigest() {
+        return passwordDigest;
+    }
+
+    public Members withPasswordDigest(String passwordDigest) {
+        this.setPasswordDigest(passwordDigest);
+        return this;
+    }
+
+    public void setPasswordDigest(String passwordDigest) {
+        this.passwordDigest = passwordDigest;
+    }
+
+    public String getTimeZone() {
+        return timeZone;
+    }
+
+    public Members withTimeZone(String timeZone) {
+        this.setTimeZone(timeZone);
+        return this;
+    }
+
+    public void setTimeZone(String timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    public Integer getViewedTimes() {
+        return viewedTimes;
+    }
+
+    public Members withViewedTimes(Integer viewedTimes) {
+        this.setViewedTimes(viewedTimes);
+        return this;
+    }
+
+    public void setViewedTimes(Integer viewedTimes) {
+        this.viewedTimes = viewedTimes;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public Members withStatus(Integer status) {
+        this.setStatus(status);
+        return this;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getActivationToken() {
+        return activationToken;
+    }
+
+    public Members withActivationToken(String activationToken) {
+        this.setActivationToken(activationToken);
+        return this;
+    }
+
+    public void setActivationToken(String activationToken) {
+        this.activationToken = activationToken;
+    }
+
+    public Date getActivatedAt() {
+        return activatedAt;
+    }
+
+    public Members withActivatedAt(Date activatedAt) {
+        this.setActivatedAt(activatedAt);
+        return this;
+    }
+
+    public void setActivatedAt(Date activatedAt) {
+        this.activatedAt = activatedAt;
+    }
+
+    public String getResetToken() {
+        return resetToken;
+    }
+
+    public Members withResetToken(String resetToken) {
+        this.setResetToken(resetToken);
+        return this;
+    }
+
+    public void setResetToken(String resetToken) {
+        this.resetToken = resetToken;
+    }
+
+    public Date getResetTokenGeneratedAt() {
+        return resetTokenGeneratedAt;
+    }
+
+    public Members withResetTokenGeneratedAt(Date resetTokenGeneratedAt) {
+        this.setResetTokenGeneratedAt(resetTokenGeneratedAt);
+        return this;
+    }
+
+    public void setResetTokenGeneratedAt(Date resetTokenGeneratedAt) {
+        this.resetTokenGeneratedAt = resetTokenGeneratedAt;
+    }
+
+    public String getPhoneZone() {
+        return phoneZone;
+    }
+
+    public Members withPhoneZone(String phoneZone) {
+        this.setPhoneZone(phoneZone);
+        return this;
+    }
+
+    public void setPhoneZone(String phoneZone) {
+        this.phoneZone = phoneZone;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public Members withAccount(String account) {
+        this.setAccount(account);
+        return this;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public Members withLocation(String location) {
+        this.setLocation(location);
+        return this;
+    }
+
+    public void setLocation(String location) {
+        this.location = location;
+    }
+
+    public String getPublicPhone() {
+        return publicPhone;
+    }
+
+    public Members withPublicPhone(String publicPhone) {
+        this.setPublicPhone(publicPhone);
+        return this;
+    }
+
+    public void setPublicPhone(String publicPhone) {
+        this.publicPhone = publicPhone;
+    }
+
+    public String getPublicEmail() {
+        return publicEmail;
+    }
+
+    public Members withPublicEmail(String publicEmail) {
+        this.setPublicEmail(publicEmail);
+        return this;
+    }
+
+    public void setPublicEmail(String publicEmail) {
+        this.publicEmail = publicEmail;
+    }
+
+    public String getSettings() {
+        return settings;
+    }
+
+    public Members withSettings(String settings) {
+        this.setSettings(settings);
+        return this;
+    }
+
+    public void setSettings(String settings) {
+        this.settings = settings;
+    }
+
+    public Integer getPoints() {
+        return points;
+    }
+
+    public Members withPoints(Integer points) {
+        this.setPoints(points);
+        return this;
+    }
+
+    public void setPoints(Integer points) {
+        this.points = points;
+    }
+
+    public Date getLastSignAt() {
+        return lastSignAt;
+    }
+
+    public Members withLastSignAt(Date lastSignAt) {
+        this.setLastSignAt(lastSignAt);
+        return this;
+    }
+
+    public void setLastSignAt(Date lastSignAt) {
+        this.lastSignAt = lastSignAt;
+    }
+
+    public Integer getTotalPoints() {
+        return totalPoints;
+    }
+
+    public Members withTotalPoints(Integer totalPoints) {
+        this.setTotalPoints(totalPoints);
+        return this;
+    }
+
+    public void setTotalPoints(Integer totalPoints) {
+        this.totalPoints = totalPoints;
+    }
+
+    public Integer getTotalSpace() {
+        return totalSpace;
+    }
+
+    public Members withTotalSpace(Integer totalSpace) {
+        this.setTotalSpace(totalSpace);
+        return this;
+    }
+
+    public void setTotalSpace(Integer totalSpace) {
+        this.totalSpace = totalSpace;
+    }
+
+    public BigDecimal getUsedSpace() {
+        return usedSpace;
+    }
+
+    public Members withUsedSpace(BigDecimal usedSpace) {
+        this.setUsedSpace(usedSpace);
+        return this;
+    }
+
+    public void setUsedSpace(BigDecimal usedSpace) {
+        this.usedSpace = usedSpace;
+    }
+
+    public BigDecimal getLeftSpace() {
+        return leftSpace;
+    }
+
+    public Members withLeftSpace(BigDecimal leftSpace) {
+        this.setLeftSpace(leftSpace);
+        return this;
+    }
+
+    public void setLeftSpace(BigDecimal leftSpace) {
+        this.leftSpace = leftSpace;
+    }
+
+    public Integer getAppsCode() {
+        return appsCode;
+    }
+
+    public Members withAppsCode(Integer appsCode) {
+        this.setAppsCode(appsCode);
+        return this;
+    }
+
+    public void setAppsCode(Integer appsCode) {
+        this.appsCode = appsCode;
+    }
+
+    public Integer getSubscriberType() {
+        return subscriberType;
+    }
+
+    public Members withSubscriberType(Integer subscriberType) {
+        this.setSubscriberType(subscriberType);
+        return this;
+    }
+
+    public void setSubscriberType(Integer subscriberType) {
+        this.subscriberType = subscriberType;
+    }
+
+    public String getReferralCode() {
+        return referralCode;
+    }
+
+    public Members withReferralCode(String referralCode) {
+        this.setReferralCode(referralCode);
+        return this;
+    }
+
+    public void setReferralCode(String referralCode) {
+        this.referralCode = referralCode;
+    }
+
+    public Integer getReferralCount() {
+        return referralCount;
+    }
+
+    public Members withReferralCount(Integer referralCount) {
+        this.setReferralCount(referralCount);
+        return this;
+    }
+
+    public void setReferralCount(Integer referralCount) {
+        this.referralCount = referralCount;
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        Members other = (Members) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+                && (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName()))
+                && (this.getPhone() == null ? other.getPhone() == null : this.getPhone().equals(other.getPhone()))
+                && (this.getEmail() == null ? other.getEmail() == null : this.getEmail().equals(other.getEmail()))
+                && (this.getGender() == null ? other.getGender() == null : this.getGender().equals(other.getGender()))
+                && (this.getRegion() == null ? other.getRegion() == null : this.getRegion().equals(other.getRegion()))
+                && (this.getSignature() == null ? other.getSignature() == null : this.getSignature().equals(other.getSignature()))
+                && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()))
+                && (this.getUpdatedAt() == null ? other.getUpdatedAt() == null : this.getUpdatedAt().equals(other.getUpdatedAt()))
+                && (this.getPasswordDigest() == null ? other.getPasswordDigest() == null : this.getPasswordDigest().equals(other.getPasswordDigest()))
+                && (this.getTimeZone() == null ? other.getTimeZone() == null : this.getTimeZone().equals(other.getTimeZone()))
+                && (this.getViewedTimes() == null ? other.getViewedTimes() == null : this.getViewedTimes().equals(other.getViewedTimes()))
+                && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()))
+                && (this.getActivationToken() == null ? other.getActivationToken() == null : this.getActivationToken().equals(other.getActivationToken()))
+                && (this.getActivatedAt() == null ? other.getActivatedAt() == null : this.getActivatedAt().equals(other.getActivatedAt()))
+                && (this.getResetToken() == null ? other.getResetToken() == null : this.getResetToken().equals(other.getResetToken()))
+                && (this.getResetTokenGeneratedAt() == null ? other.getResetTokenGeneratedAt() == null : this.getResetTokenGeneratedAt().equals(other.getResetTokenGeneratedAt()))
+                && (this.getPhoneZone() == null ? other.getPhoneZone() == null : this.getPhoneZone().equals(other.getPhoneZone()))
+                && (this.getAccount() == null ? other.getAccount() == null : this.getAccount().equals(other.getAccount()))
+                && (this.getLocation() == null ? other.getLocation() == null : this.getLocation().equals(other.getLocation()))
+                && (this.getPublicPhone() == null ? other.getPublicPhone() == null : this.getPublicPhone().equals(other.getPublicPhone()))
+                && (this.getPublicEmail() == null ? other.getPublicEmail() == null : this.getPublicEmail().equals(other.getPublicEmail()))
+                && (this.getSettings() == null ? other.getSettings() == null : this.getSettings().equals(other.getSettings()))
+                && (this.getPoints() == null ? other.getPoints() == null : this.getPoints().equals(other.getPoints()))
+                && (this.getLastSignAt() == null ? other.getLastSignAt() == null : this.getLastSignAt().equals(other.getLastSignAt()))
+                && (this.getTotalPoints() == null ? other.getTotalPoints() == null : this.getTotalPoints().equals(other.getTotalPoints()))
+                && (this.getTotalSpace() == null ? other.getTotalSpace() == null : this.getTotalSpace().equals(other.getTotalSpace()))
+                && (this.getUsedSpace() == null ? other.getUsedSpace() == null : this.getUsedSpace().equals(other.getUsedSpace()))
+                && (this.getLeftSpace() == null ? other.getLeftSpace() == null : this.getLeftSpace().equals(other.getLeftSpace()))
+                && (this.getAppsCode() == null ? other.getAppsCode() == null : this.getAppsCode().equals(other.getAppsCode()))
+                && (this.getSubscriberType() == null ? other.getSubscriberType() == null : this.getSubscriberType().equals(other.getSubscriberType()))
+                && (this.getReferralCode() == null ? other.getReferralCode() == null : this.getReferralCode().equals(other.getReferralCode()))
+                && (this.getReferralCount() == null ? other.getReferralCount() == null : this.getReferralCount().equals(other.getReferralCount()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
+        result = prime * result + ((getPhone() == null) ? 0 : getPhone().hashCode());
+        result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
+        result = prime * result + ((getGender() == null) ? 0 : getGender().hashCode());
+        result = prime * result + ((getRegion() == null) ? 0 : getRegion().hashCode());
+        result = prime * result + ((getSignature() == null) ? 0 : getSignature().hashCode());
+        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
+        result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
+        result = prime * result + ((getPasswordDigest() == null) ? 0 : getPasswordDigest().hashCode());
+        result = prime * result + ((getTimeZone() == null) ? 0 : getTimeZone().hashCode());
+        result = prime * result + ((getViewedTimes() == null) ? 0 : getViewedTimes().hashCode());
+        result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
+        result = prime * result + ((getActivationToken() == null) ? 0 : getActivationToken().hashCode());
+        result = prime * result + ((getActivatedAt() == null) ? 0 : getActivatedAt().hashCode());
+        result = prime * result + ((getResetToken() == null) ? 0 : getResetToken().hashCode());
+        result = prime * result + ((getResetTokenGeneratedAt() == null) ? 0 : getResetTokenGeneratedAt().hashCode());
+        result = prime * result + ((getPhoneZone() == null) ? 0 : getPhoneZone().hashCode());
+        result = prime * result + ((getAccount() == null) ? 0 : getAccount().hashCode());
+        result = prime * result + ((getLocation() == null) ? 0 : getLocation().hashCode());
+        result = prime * result + ((getPublicPhone() == null) ? 0 : getPublicPhone().hashCode());
+        result = prime * result + ((getPublicEmail() == null) ? 0 : getPublicEmail().hashCode());
+        result = prime * result + ((getSettings() == null) ? 0 : getSettings().hashCode());
+        result = prime * result + ((getPoints() == null) ? 0 : getPoints().hashCode());
+        result = prime * result + ((getLastSignAt() == null) ? 0 : getLastSignAt().hashCode());
+        result = prime * result + ((getTotalPoints() == null) ? 0 : getTotalPoints().hashCode());
+        result = prime * result + ((getTotalSpace() == null) ? 0 : getTotalSpace().hashCode());
+        result = prime * result + ((getUsedSpace() == null) ? 0 : getUsedSpace().hashCode());
+        result = prime * result + ((getLeftSpace() == null) ? 0 : getLeftSpace().hashCode());
+        result = prime * result + ((getAppsCode() == null) ? 0 : getAppsCode().hashCode());
+        result = prime * result + ((getSubscriberType() == null) ? 0 : getSubscriberType().hashCode());
+        result = prime * result + ((getReferralCode() == null) ? 0 : getReferralCode().hashCode());
+        result = prime * result + ((getReferralCount() == null) ? 0 : getReferralCount().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", name=").append(name);
+        sb.append(", phone=").append(phone);
+        sb.append(", email=").append(email);
+        sb.append(", gender=").append(gender);
+        sb.append(", region=").append(region);
+        sb.append(", signature=").append(signature);
+        sb.append(", createdAt=").append(createdAt);
+        sb.append(", updatedAt=").append(updatedAt);
+        sb.append(", passwordDigest=").append(passwordDigest);
+        sb.append(", timeZone=").append(timeZone);
+        sb.append(", viewedTimes=").append(viewedTimes);
+        sb.append(", status=").append(status);
+        sb.append(", activationToken=").append(activationToken);
+        sb.append(", activatedAt=").append(activatedAt);
+        sb.append(", resetToken=").append(resetToken);
+        sb.append(", resetTokenGeneratedAt=").append(resetTokenGeneratedAt);
+        sb.append(", phoneZone=").append(phoneZone);
+        sb.append(", account=").append(account);
+        sb.append(", location=").append(location);
+        sb.append(", publicPhone=").append(publicPhone);
+        sb.append(", publicEmail=").append(publicEmail);
+        sb.append(", settings=").append(settings);
+        sb.append(", points=").append(points);
+        sb.append(", lastSignAt=").append(lastSignAt);
+        sb.append(", totalPoints=").append(totalPoints);
+        sb.append(", totalSpace=").append(totalSpace);
+        sb.append(", usedSpace=").append(usedSpace);
+        sb.append(", leftSpace=").append(leftSpace);
+        sb.append(", appsCode=").append(appsCode);
+        sb.append(", subscriberType=").append(subscriberType);
+        sb.append(", referralCode=").append(referralCode);
+        sb.append(", referralCount=").append(referralCount);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 291 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/OauthClientDetails.java

@@ -0,0 +1,291 @@
+package cn.kdan.pdf.tech.core.model;
+
+import java.io.Serializable;
+
+public class OauthClientDetails implements Serializable {
+    private String clientId;
+
+    private String resourceIds;
+
+    private String clientSecret;
+
+    private String scope;
+
+    private String authorizedGrantTypes;
+
+    private String webServerRedirectUri;
+
+    private String authorities;
+
+    private Integer accessTokenValidity;
+
+    private Integer refreshTokenValidity;
+
+    private String additionalInformation;
+
+    private String autoapprove;
+
+    private String state;
+
+    private String tokenUrl;
+
+    private String userInfoUrl;
+
+    private static final long serialVersionUID = 1L;
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public OauthClientDetails withClientId(String clientId) {
+        this.setClientId(clientId);
+        return this;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getResourceIds() {
+        return resourceIds;
+    }
+
+    public OauthClientDetails withResourceIds(String resourceIds) {
+        this.setResourceIds(resourceIds);
+        return this;
+    }
+
+    public void setResourceIds(String resourceIds) {
+        this.resourceIds = resourceIds;
+    }
+
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    public OauthClientDetails withClientSecret(String clientSecret) {
+        this.setClientSecret(clientSecret);
+        return this;
+    }
+
+    public void setClientSecret(String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public OauthClientDetails withScope(String scope) {
+        this.setScope(scope);
+        return this;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public String getAuthorizedGrantTypes() {
+        return authorizedGrantTypes;
+    }
+
+    public OauthClientDetails withAuthorizedGrantTypes(String authorizedGrantTypes) {
+        this.setAuthorizedGrantTypes(authorizedGrantTypes);
+        return this;
+    }
+
+    public void setAuthorizedGrantTypes(String authorizedGrantTypes) {
+        this.authorizedGrantTypes = authorizedGrantTypes;
+    }
+
+    public String getWebServerRedirectUri() {
+        return webServerRedirectUri;
+    }
+
+    public OauthClientDetails withWebServerRedirectUri(String webServerRedirectUri) {
+        this.setWebServerRedirectUri(webServerRedirectUri);
+        return this;
+    }
+
+    public void setWebServerRedirectUri(String webServerRedirectUri) {
+        this.webServerRedirectUri = webServerRedirectUri;
+    }
+
+    public String getAuthorities() {
+        return authorities;
+    }
+
+    public OauthClientDetails withAuthorities(String authorities) {
+        this.setAuthorities(authorities);
+        return this;
+    }
+
+    public void setAuthorities(String authorities) {
+        this.authorities = authorities;
+    }
+
+    public Integer getAccessTokenValidity() {
+        return accessTokenValidity;
+    }
+
+    public OauthClientDetails withAccessTokenValidity(Integer accessTokenValidity) {
+        this.setAccessTokenValidity(accessTokenValidity);
+        return this;
+    }
+
+    public void setAccessTokenValidity(Integer accessTokenValidity) {
+        this.accessTokenValidity = accessTokenValidity;
+    }
+
+    public Integer getRefreshTokenValidity() {
+        return refreshTokenValidity;
+    }
+
+    public OauthClientDetails withRefreshTokenValidity(Integer refreshTokenValidity) {
+        this.setRefreshTokenValidity(refreshTokenValidity);
+        return this;
+    }
+
+    public void setRefreshTokenValidity(Integer refreshTokenValidity) {
+        this.refreshTokenValidity = refreshTokenValidity;
+    }
+
+    public String getAdditionalInformation() {
+        return additionalInformation;
+    }
+
+    public OauthClientDetails withAdditionalInformation(String additionalInformation) {
+        this.setAdditionalInformation(additionalInformation);
+        return this;
+    }
+
+    public void setAdditionalInformation(String additionalInformation) {
+        this.additionalInformation = additionalInformation;
+    }
+
+    public String getAutoapprove() {
+        return autoapprove;
+    }
+
+    public OauthClientDetails withAutoapprove(String autoapprove) {
+        this.setAutoapprove(autoapprove);
+        return this;
+    }
+
+    public void setAutoapprove(String autoapprove) {
+        this.autoapprove = autoapprove;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public OauthClientDetails withState(String state) {
+        this.setState(state);
+        return this;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    public String getTokenUrl() {
+        return tokenUrl;
+    }
+
+    public OauthClientDetails withTokenUrl(String tokenUrl) {
+        this.setTokenUrl(tokenUrl);
+        return this;
+    }
+
+    public void setTokenUrl(String tokenUrl) {
+        this.tokenUrl = tokenUrl;
+    }
+
+    public String getUserInfoUrl() {
+        return userInfoUrl;
+    }
+
+    public OauthClientDetails withUserInfoUrl(String userInfoUrl) {
+        this.setUserInfoUrl(userInfoUrl);
+        return this;
+    }
+
+    public void setUserInfoUrl(String userInfoUrl) {
+        this.userInfoUrl = userInfoUrl;
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        OauthClientDetails other = (OauthClientDetails) that;
+        return (this.getClientId() == null ? other.getClientId() == null : this.getClientId().equals(other.getClientId()))
+                && (this.getResourceIds() == null ? other.getResourceIds() == null : this.getResourceIds().equals(other.getResourceIds()))
+                && (this.getClientSecret() == null ? other.getClientSecret() == null : this.getClientSecret().equals(other.getClientSecret()))
+                && (this.getScope() == null ? other.getScope() == null : this.getScope().equals(other.getScope()))
+                && (this.getAuthorizedGrantTypes() == null ? other.getAuthorizedGrantTypes() == null : this.getAuthorizedGrantTypes().equals(other.getAuthorizedGrantTypes()))
+                && (this.getWebServerRedirectUri() == null ? other.getWebServerRedirectUri() == null : this.getWebServerRedirectUri().equals(other.getWebServerRedirectUri()))
+                && (this.getAuthorities() == null ? other.getAuthorities() == null : this.getAuthorities().equals(other.getAuthorities()))
+                && (this.getAccessTokenValidity() == null ? other.getAccessTokenValidity() == null : this.getAccessTokenValidity().equals(other.getAccessTokenValidity()))
+                && (this.getRefreshTokenValidity() == null ? other.getRefreshTokenValidity() == null : this.getRefreshTokenValidity().equals(other.getRefreshTokenValidity()))
+                && (this.getAdditionalInformation() == null ? other.getAdditionalInformation() == null : this.getAdditionalInformation().equals(other.getAdditionalInformation()))
+                && (this.getAutoapprove() == null ? other.getAutoapprove() == null : this.getAutoapprove().equals(other.getAutoapprove()))
+                && (this.getState() == null ? other.getState() == null : this.getState().equals(other.getState()))
+                && (this.getTokenUrl() == null ? other.getTokenUrl() == null : this.getTokenUrl().equals(other.getTokenUrl()))
+                && (this.getUserInfoUrl() == null ? other.getUserInfoUrl() == null : this.getUserInfoUrl().equals(other.getUserInfoUrl()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getClientId() == null) ? 0 : getClientId().hashCode());
+        result = prime * result + ((getResourceIds() == null) ? 0 : getResourceIds().hashCode());
+        result = prime * result + ((getClientSecret() == null) ? 0 : getClientSecret().hashCode());
+        result = prime * result + ((getScope() == null) ? 0 : getScope().hashCode());
+        result = prime * result + ((getAuthorizedGrantTypes() == null) ? 0 : getAuthorizedGrantTypes().hashCode());
+        result = prime * result + ((getWebServerRedirectUri() == null) ? 0 : getWebServerRedirectUri().hashCode());
+        result = prime * result + ((getAuthorities() == null) ? 0 : getAuthorities().hashCode());
+        result = prime * result + ((getAccessTokenValidity() == null) ? 0 : getAccessTokenValidity().hashCode());
+        result = prime * result + ((getRefreshTokenValidity() == null) ? 0 : getRefreshTokenValidity().hashCode());
+        result = prime * result + ((getAdditionalInformation() == null) ? 0 : getAdditionalInformation().hashCode());
+        result = prime * result + ((getAutoapprove() == null) ? 0 : getAutoapprove().hashCode());
+        result = prime * result + ((getState() == null) ? 0 : getState().hashCode());
+        result = prime * result + ((getTokenUrl() == null) ? 0 : getTokenUrl().hashCode());
+        result = prime * result + ((getUserInfoUrl() == null) ? 0 : getUserInfoUrl().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", clientId=").append(clientId);
+        sb.append(", resourceIds=").append(resourceIds);
+        sb.append(", clientSecret=").append(clientSecret);
+        sb.append(", scope=").append(scope);
+        sb.append(", authorizedGrantTypes=").append(authorizedGrantTypes);
+        sb.append(", webServerRedirectUri=").append(webServerRedirectUri);
+        sb.append(", authorities=").append(authorities);
+        sb.append(", accessTokenValidity=").append(accessTokenValidity);
+        sb.append(", refreshTokenValidity=").append(refreshTokenValidity);
+        sb.append(", additionalInformation=").append(additionalInformation);
+        sb.append(", autoapprove=").append(autoapprove);
+        sb.append(", state=").append(state);
+        sb.append(", tokenUrl=").append(tokenUrl);
+        sb.append(", userInfoUrl=").append(userInfoUrl);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

File diff suppressed because it is too large
+ 1220 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/OauthClientDetailsExample.java


+ 220 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/VppMember.java

@@ -0,0 +1,220 @@
+package cn.kdan.pdf.tech.core.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class VppMember implements Serializable {
+    private String id;
+
+    private String email;
+
+    private String fullName;
+
+    private Integer subscriberType;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+
+    private Integer subscribed;
+
+    private String digestPassword;
+
+    private String phone;
+
+    private String validFlag;
+
+    private static final long serialVersionUID = 1L;
+
+    public String getId() {
+        return id;
+    }
+
+    public VppMember withId(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public VppMember withEmail(String email) {
+        this.setEmail(email);
+        return this;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getFullName() {
+        return fullName;
+    }
+
+    public VppMember withFullName(String fullName) {
+        this.setFullName(fullName);
+        return this;
+    }
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+
+    public Integer getSubscriberType() {
+        return subscriberType;
+    }
+
+    public VppMember withSubscriberType(Integer subscriberType) {
+        this.setSubscriberType(subscriberType);
+        return this;
+    }
+
+    public void setSubscriberType(Integer subscriberType) {
+        this.subscriberType = subscriberType;
+    }
+
+    public Date getCreatedAt() {
+        return createdAt;
+    }
+
+    public VppMember withCreatedAt(Date createdAt) {
+        this.setCreatedAt(createdAt);
+        return this;
+    }
+
+    public void setCreatedAt(Date createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public Date getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public VppMember withUpdatedAt(Date updatedAt) {
+        this.setUpdatedAt(updatedAt);
+        return this;
+    }
+
+    public void setUpdatedAt(Date updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+
+    public Integer getSubscribed() {
+        return subscribed;
+    }
+
+    public VppMember withSubscribed(Integer subscribed) {
+        this.setSubscribed(subscribed);
+        return this;
+    }
+
+    public void setSubscribed(Integer subscribed) {
+        this.subscribed = subscribed;
+    }
+
+    public String getDigestPassword() {
+        return digestPassword;
+    }
+
+    public VppMember withDigestPassword(String digestPassword) {
+        this.setDigestPassword(digestPassword);
+        return this;
+    }
+
+    public void setDigestPassword(String digestPassword) {
+        this.digestPassword = digestPassword;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public VppMember withPhone(String phone) {
+        this.setPhone(phone);
+        return this;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getValidFlag() {
+        return validFlag;
+    }
+
+    public VppMember withValidFlag(String validFlag) {
+        this.setValidFlag(validFlag);
+        return this;
+    }
+
+    public void setValidFlag(String validFlag) {
+        this.validFlag = validFlag;
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        VppMember other = (VppMember) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getEmail() == null ? other.getEmail() == null : this.getEmail().equals(other.getEmail()))
+            && (this.getFullName() == null ? other.getFullName() == null : this.getFullName().equals(other.getFullName()))
+            && (this.getSubscriberType() == null ? other.getSubscriberType() == null : this.getSubscriberType().equals(other.getSubscriberType()))
+            && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()))
+            && (this.getUpdatedAt() == null ? other.getUpdatedAt() == null : this.getUpdatedAt().equals(other.getUpdatedAt()))
+            && (this.getSubscribed() == null ? other.getSubscribed() == null : this.getSubscribed().equals(other.getSubscribed()))
+            && (this.getDigestPassword() == null ? other.getDigestPassword() == null : this.getDigestPassword().equals(other.getDigestPassword()))
+            && (this.getPhone() == null ? other.getPhone() == null : this.getPhone().equals(other.getPhone()))
+            && (this.getValidFlag() == null ? other.getValidFlag() == null : this.getValidFlag().equals(other.getValidFlag()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
+        result = prime * result + ((getFullName() == null) ? 0 : getFullName().hashCode());
+        result = prime * result + ((getSubscriberType() == null) ? 0 : getSubscriberType().hashCode());
+        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
+        result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
+        result = prime * result + ((getSubscribed() == null) ? 0 : getSubscribed().hashCode());
+        result = prime * result + ((getDigestPassword() == null) ? 0 : getDigestPassword().hashCode());
+        result = prime * result + ((getPhone() == null) ? 0 : getPhone().hashCode());
+        result = prime * result + ((getValidFlag() == null) ? 0 : getValidFlag().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", email=").append(email);
+        sb.append(", fullName=").append(fullName);
+        sb.append(", subscriberType=").append(subscriberType);
+        sb.append(", createdAt=").append(createdAt);
+        sb.append(", updatedAt=").append(updatedAt);
+        sb.append(", subscribed=").append(subscribed);
+        sb.append(", digestPassword=").append(digestPassword);
+        sb.append(", phone=").append(phone);
+        sb.append(", validFlag=").append(validFlag);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 890 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/model/VppMemberExample.java

@@ -0,0 +1,890 @@
+package cn.kdan.pdf.tech.core.model;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class VppMemberExample {
+    protected String orderByClause;
+
+    protected boolean distinct;
+
+    protected List<Criteria> oredCriteria;
+
+    public VppMemberExample() {
+        oredCriteria = new ArrayList<>();
+    }
+
+    public void setOrderByClause(String orderByClause) {
+        this.orderByClause = orderByClause;
+    }
+
+    public String getOrderByClause() {
+        return orderByClause;
+    }
+
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    public List<Criteria> getOredCriteria() {
+        return oredCriteria;
+    }
+
+    public void or(Criteria criteria) {
+        oredCriteria.add(criteria);
+    }
+
+    public Criteria or() {
+        Criteria criteria = createCriteriaInternal();
+        oredCriteria.add(criteria);
+        return criteria;
+    }
+
+    public Criteria createCriteria() {
+        Criteria criteria = createCriteriaInternal();
+        if (oredCriteria.size() == 0) {
+            oredCriteria.add(criteria);
+        }
+        return criteria;
+    }
+
+    protected Criteria createCriteriaInternal() {
+        Criteria criteria = new Criteria();
+        return criteria;
+    }
+
+    public void clear() {
+        oredCriteria.clear();
+        orderByClause = null;
+        distinct = false;
+    }
+
+    protected abstract static class GeneratedCriteria {
+        protected List<Criterion> criteria;
+
+        protected GeneratedCriteria() {
+            super();
+            criteria = new ArrayList<>();
+        }
+
+        public boolean isValid() {
+            return criteria.size() > 0;
+        }
+
+        public List<Criterion> getAllCriteria() {
+            return criteria;
+        }
+
+        public List<Criterion> getCriteria() {
+            return criteria;
+        }
+
+        protected void addCriterion(String condition) {
+            if (condition == null) {
+                throw new RuntimeException("Value for condition cannot be null");
+            }
+            criteria.add(new Criterion(condition));
+        }
+
+        protected void addCriterion(String condition, Object value, String property) {
+            if (value == null) {
+                throw new RuntimeException("Value for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value));
+        }
+
+        protected void addCriterion(String condition, Object value1, Object value2, String property) {
+            if (value1 == null || value2 == null) {
+                throw new RuntimeException("Between values for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value1, value2));
+        }
+
+        public Criteria andIdIsNull() {
+            addCriterion("id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIsNotNull() {
+            addCriterion("id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdEqualTo(String value) {
+            addCriterion("id =", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotEqualTo(String value) {
+            addCriterion("id <>", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThan(String value) {
+            addCriterion("id >", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThanOrEqualTo(String value) {
+            addCriterion("id >=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThan(String value) {
+            addCriterion("id <", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThanOrEqualTo(String value) {
+            addCriterion("id <=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLike(String value) {
+            addCriterion("id like", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotLike(String value) {
+            addCriterion("id not like", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIn(List<String> values) {
+            addCriterion("id in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotIn(List<String> values) {
+            addCriterion("id not in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdBetween(String value1, String value2) {
+            addCriterion("id between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotBetween(String value1, String value2) {
+            addCriterion("id not between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailIsNull() {
+            addCriterion("email is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailIsNotNull() {
+            addCriterion("email is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailEqualTo(String value) {
+            addCriterion("email =", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailNotEqualTo(String value) {
+            addCriterion("email <>", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailGreaterThan(String value) {
+            addCriterion("email >", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailGreaterThanOrEqualTo(String value) {
+            addCriterion("email >=", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailLessThan(String value) {
+            addCriterion("email <", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailLessThanOrEqualTo(String value) {
+            addCriterion("email <=", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailLike(String value) {
+            addCriterion("email like", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailNotLike(String value) {
+            addCriterion("email not like", value, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailIn(List<String> values) {
+            addCriterion("email in", values, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailNotIn(List<String> values) {
+            addCriterion("email not in", values, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailBetween(String value1, String value2) {
+            addCriterion("email between", value1, value2, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailNotBetween(String value1, String value2) {
+            addCriterion("email not between", value1, value2, "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameIsNull() {
+            addCriterion("full_name is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameIsNotNull() {
+            addCriterion("full_name is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameEqualTo(String value) {
+            addCriterion("full_name =", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameNotEqualTo(String value) {
+            addCriterion("full_name <>", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameGreaterThan(String value) {
+            addCriterion("full_name >", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameGreaterThanOrEqualTo(String value) {
+            addCriterion("full_name >=", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameLessThan(String value) {
+            addCriterion("full_name <", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameLessThanOrEqualTo(String value) {
+            addCriterion("full_name <=", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameLike(String value) {
+            addCriterion("full_name like", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameNotLike(String value) {
+            addCriterion("full_name not like", value, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameIn(List<String> values) {
+            addCriterion("full_name in", values, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameNotIn(List<String> values) {
+            addCriterion("full_name not in", values, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameBetween(String value1, String value2) {
+            addCriterion("full_name between", value1, value2, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameNotBetween(String value1, String value2) {
+            addCriterion("full_name not between", value1, value2, "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeIsNull() {
+            addCriterion("subscriber_type is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeIsNotNull() {
+            addCriterion("subscriber_type is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeEqualTo(Integer value) {
+            addCriterion("subscriber_type =", value, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeNotEqualTo(Integer value) {
+            addCriterion("subscriber_type <>", value, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeGreaterThan(Integer value) {
+            addCriterion("subscriber_type >", value, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeGreaterThanOrEqualTo(Integer value) {
+            addCriterion("subscriber_type >=", value, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeLessThan(Integer value) {
+            addCriterion("subscriber_type <", value, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeLessThanOrEqualTo(Integer value) {
+            addCriterion("subscriber_type <=", value, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeIn(List<Integer> values) {
+            addCriterion("subscriber_type in", values, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeNotIn(List<Integer> values) {
+            addCriterion("subscriber_type not in", values, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeBetween(Integer value1, Integer value2) {
+            addCriterion("subscriber_type between", value1, value2, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscriberTypeNotBetween(Integer value1, Integer value2) {
+            addCriterion("subscriber_type not between", value1, value2, "subscriberType");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtIsNull() {
+            addCriterion("created_at is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtIsNotNull() {
+            addCriterion("created_at is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtEqualTo(Date value) {
+            addCriterion("created_at =", value, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtNotEqualTo(Date value) {
+            addCriterion("created_at <>", value, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtGreaterThan(Date value) {
+            addCriterion("created_at >", value, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtGreaterThanOrEqualTo(Date value) {
+            addCriterion("created_at >=", value, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtLessThan(Date value) {
+            addCriterion("created_at <", value, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtLessThanOrEqualTo(Date value) {
+            addCriterion("created_at <=", value, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtIn(List<Date> values) {
+            addCriterion("created_at in", values, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtNotIn(List<Date> values) {
+            addCriterion("created_at not in", values, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtBetween(Date value1, Date value2) {
+            addCriterion("created_at between", value1, value2, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreatedAtNotBetween(Date value1, Date value2) {
+            addCriterion("created_at not between", value1, value2, "createdAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtIsNull() {
+            addCriterion("updated_at is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtIsNotNull() {
+            addCriterion("updated_at is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtEqualTo(Date value) {
+            addCriterion("updated_at =", value, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtNotEqualTo(Date value) {
+            addCriterion("updated_at <>", value, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtGreaterThan(Date value) {
+            addCriterion("updated_at >", value, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtGreaterThanOrEqualTo(Date value) {
+            addCriterion("updated_at >=", value, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtLessThan(Date value) {
+            addCriterion("updated_at <", value, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtLessThanOrEqualTo(Date value) {
+            addCriterion("updated_at <=", value, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtIn(List<Date> values) {
+            addCriterion("updated_at in", values, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtNotIn(List<Date> values) {
+            addCriterion("updated_at not in", values, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtBetween(Date value1, Date value2) {
+            addCriterion("updated_at between", value1, value2, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdatedAtNotBetween(Date value1, Date value2) {
+            addCriterion("updated_at not between", value1, value2, "updatedAt");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedIsNull() {
+            addCriterion("subscribed is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedIsNotNull() {
+            addCriterion("subscribed is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedEqualTo(Integer value) {
+            addCriterion("subscribed =", value, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedNotEqualTo(Integer value) {
+            addCriterion("subscribed <>", value, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedGreaterThan(Integer value) {
+            addCriterion("subscribed >", value, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedGreaterThanOrEqualTo(Integer value) {
+            addCriterion("subscribed >=", value, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedLessThan(Integer value) {
+            addCriterion("subscribed <", value, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedLessThanOrEqualTo(Integer value) {
+            addCriterion("subscribed <=", value, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedIn(List<Integer> values) {
+            addCriterion("subscribed in", values, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedNotIn(List<Integer> values) {
+            addCriterion("subscribed not in", values, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedBetween(Integer value1, Integer value2) {
+            addCriterion("subscribed between", value1, value2, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andSubscribedNotBetween(Integer value1, Integer value2) {
+            addCriterion("subscribed not between", value1, value2, "subscribed");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordIsNull() {
+            addCriterion("digest_password is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordIsNotNull() {
+            addCriterion("digest_password is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordEqualTo(String value) {
+            addCriterion("digest_password =", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordNotEqualTo(String value) {
+            addCriterion("digest_password <>", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordGreaterThan(String value) {
+            addCriterion("digest_password >", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordGreaterThanOrEqualTo(String value) {
+            addCriterion("digest_password >=", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordLessThan(String value) {
+            addCriterion("digest_password <", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordLessThanOrEqualTo(String value) {
+            addCriterion("digest_password <=", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordLike(String value) {
+            addCriterion("digest_password like", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordNotLike(String value) {
+            addCriterion("digest_password not like", value, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordIn(List<String> values) {
+            addCriterion("digest_password in", values, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordNotIn(List<String> values) {
+            addCriterion("digest_password not in", values, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordBetween(String value1, String value2) {
+            addCriterion("digest_password between", value1, value2, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordNotBetween(String value1, String value2) {
+            addCriterion("digest_password not between", value1, value2, "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneIsNull() {
+            addCriterion("phone is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneIsNotNull() {
+            addCriterion("phone is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneEqualTo(String value) {
+            addCriterion("phone =", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneNotEqualTo(String value) {
+            addCriterion("phone <>", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneGreaterThan(String value) {
+            addCriterion("phone >", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneGreaterThanOrEqualTo(String value) {
+            addCriterion("phone >=", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneLessThan(String value) {
+            addCriterion("phone <", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneLessThanOrEqualTo(String value) {
+            addCriterion("phone <=", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneLike(String value) {
+            addCriterion("phone like", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneNotLike(String value) {
+            addCriterion("phone not like", value, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneIn(List<String> values) {
+            addCriterion("phone in", values, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneNotIn(List<String> values) {
+            addCriterion("phone not in", values, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneBetween(String value1, String value2) {
+            addCriterion("phone between", value1, value2, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneNotBetween(String value1, String value2) {
+            addCriterion("phone not between", value1, value2, "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagIsNull() {
+            addCriterion("valid_flag is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagIsNotNull() {
+            addCriterion("valid_flag is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagEqualTo(String value) {
+            addCriterion("valid_flag =", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagNotEqualTo(String value) {
+            addCriterion("valid_flag <>", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagGreaterThan(String value) {
+            addCriterion("valid_flag >", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagGreaterThanOrEqualTo(String value) {
+            addCriterion("valid_flag >=", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagLessThan(String value) {
+            addCriterion("valid_flag <", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagLessThanOrEqualTo(String value) {
+            addCriterion("valid_flag <=", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagLike(String value) {
+            addCriterion("valid_flag like", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagNotLike(String value) {
+            addCriterion("valid_flag not like", value, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagIn(List<String> values) {
+            addCriterion("valid_flag in", values, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagNotIn(List<String> values) {
+            addCriterion("valid_flag not in", values, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagBetween(String value1, String value2) {
+            addCriterion("valid_flag between", value1, value2, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagNotBetween(String value1, String value2) {
+            addCriterion("valid_flag not between", value1, value2, "validFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLikeInsensitive(String value) {
+            addCriterion("upper(id) like", value.toUpperCase(), "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andEmailLikeInsensitive(String value) {
+            addCriterion("upper(email) like", value.toUpperCase(), "email");
+            return (Criteria) this;
+        }
+
+        public Criteria andFullNameLikeInsensitive(String value) {
+            addCriterion("upper(full_name) like", value.toUpperCase(), "fullName");
+            return (Criteria) this;
+        }
+
+        public Criteria andDigestPasswordLikeInsensitive(String value) {
+            addCriterion("upper(digest_password) like", value.toUpperCase(), "digestPassword");
+            return (Criteria) this;
+        }
+
+        public Criteria andPhoneLikeInsensitive(String value) {
+            addCriterion("upper(phone) like", value.toUpperCase(), "phone");
+            return (Criteria) this;
+        }
+
+        public Criteria andValidFlagLikeInsensitive(String value) {
+            addCriterion("upper(valid_flag) like", value.toUpperCase(), "validFlag");
+            return (Criteria) this;
+        }
+    }
+
+    public static class Criteria extends GeneratedCriteria {
+        protected Criteria() {
+            super();
+        }
+    }
+
+    public static class Criterion {
+        private String condition;
+
+        private Object value;
+
+        private Object secondValue;
+
+        private boolean noValue;
+
+        private boolean singleValue;
+
+        private boolean betweenValue;
+
+        private boolean listValue;
+
+        private String typeHandler;
+
+        public String getCondition() {
+            return condition;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object getSecondValue() {
+            return secondValue;
+        }
+
+        public boolean isNoValue() {
+            return noValue;
+        }
+
+        public boolean isSingleValue() {
+            return singleValue;
+        }
+
+        public boolean isBetweenValue() {
+            return betweenValue;
+        }
+
+        public boolean isListValue() {
+            return listValue;
+        }
+
+        public String getTypeHandler() {
+            return typeHandler;
+        }
+
+        protected Criterion(String condition) {
+            super();
+            this.condition = condition;
+            this.typeHandler = null;
+            this.noValue = true;
+        }
+
+        protected Criterion(String condition, Object value, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.typeHandler = typeHandler;
+            if (value instanceof List<?>) {
+                this.listValue = true;
+            } else {
+                this.singleValue = true;
+            }
+        }
+
+        protected Criterion(String condition, Object value) {
+            this(condition, value, null);
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.secondValue = secondValue;
+            this.typeHandler = typeHandler;
+            this.betweenValue = true;
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue) {
+            this(condition, value, secondValue, null);
+        }
+    }
+}

+ 39 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/params/MissionCreateParam.java

@@ -0,0 +1,39 @@
+package cn.kdan.pdf.tech.core.params;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotBlank;
+import java.math.BigDecimal;
+
+/**
+ * @author tangxiangan
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MissionCreateParam {
+
+	@NotBlank(message = "文件输出类型不能为空")
+	private String output;
+
+	@NotBlank(message = "文件入类型不能为空")
+	private String input;
+
+	@NotBlank (message = "文件名不能为空")
+	private String filename;
+
+	@NotBlank (message = "文件大小不能为空")
+	private BigDecimal size;
+
+	/**
+	 * web 0 安卓1
+	 */
+	@NotBlank (message = "来源类型不能为空")
+	private Integer sourceType;
+
+}

+ 37 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/params/SubscriptionCreateParams.java

@@ -0,0 +1,37 @@
+package cn.kdan.pdf.tech.core.params;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author tangxiangan
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class SubscriptionCreateParams {
+    /**
+     * 客户端
+     */
+    private String client;
+    /**
+     * 支付方式 0 阿里支付 1 微信支付
+     */
+    @NotBlank(message = "支付类型不能为空")
+    private Integer payment;
+    /**
+     * 目标类型 Pricing或者SetPricing
+     */
+    @NotBlank(message = "目标服务类型不能为空")
+    private String targetType;
+    /**
+     * 目标id 券服务或者会员服务的id
+     */
+    @NotBlank(message = "目标服务Id不能为空")
+    private String targetId;
+}

+ 43 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/params/UserResetPwdParams.java

@@ -0,0 +1,43 @@
+package cn.kdan.pdf.tech.core.params;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author tangxiangan
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class UserResetPwdParams {
+
+    /**
+     * 邮箱或者手机号
+     */
+    @NotBlank(message = "用户账号不能为空")
+    private String account;
+
+    /**
+     * 新密码
+     */
+    @NotBlank(message = "新密码不能为空")
+    private String newPassword;
+
+    /**
+     * 验证码
+     */
+    @NotBlank(message = "验证码不能为空")
+    private String verifyCode;
+
+    /**
+     * 验证类型 邮箱0 手机1
+     */
+    @NotBlank(message = "验证类型不能为空")
+    private Integer type;
+}

+ 18 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/AesPasswordEncoder.java

@@ -0,0 +1,18 @@
+package cn.kdan.pdf.tech.core.pojo.oauth2;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+import utils.AesUtils;
+
+public class AesPasswordEncoder implements PasswordEncoder {
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        //加密之后对比密码
+        return encodedPassword.equals(AesUtils.encrypt((String) rawPassword));
+    }
+
+    @Override
+    public String encode(CharSequence rawPassword) {
+        return AesUtils.encrypt((String) rawPassword);
+    }
+}

+ 21 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/LoginPasswordEncoder.java

@@ -0,0 +1,21 @@
+package cn.kdan.pdf.tech.core.pojo.oauth2;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import utils.AesUtils;
+
+public class LoginPasswordEncoder implements PasswordEncoder {
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        //加密之后对比密码
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        return encoder.matches(rawPassword, encodedPassword);
+    }
+
+    @Override
+    public String encode(CharSequence rawPassword) {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        return encoder.encode(rawPassword);
+    }
+}

+ 34 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/TokenPOJO.java

@@ -0,0 +1,34 @@
+package cn.kdan.pdf.tech.core.pojo.oauth2;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * oauth2令牌信息
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TokenPOJO {
+
+    /**
+     * 刷新用令牌
+     */
+    private String refreshToken;
+
+    /**
+     * 访问用令牌
+     */
+    private String accessToken;
+
+    /**
+     * 令牌类型
+     */
+    private String tokenType;
+
+    /**
+     * 过期时间
+     */
+    private Integer expiresIn;
+}

+ 35 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/pojo/oauth2/UserInfo.java

@@ -0,0 +1,35 @@
+package cn.kdan.pdf.tech.core.pojo.oauth2;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@ApiModel(value = "用户信息")
+public class UserInfo {
+
+    @ApiModelProperty(value = "用户ID")
+    private String id;
+
+    @ApiModelProperty(value = "昵称")
+    private String name;
+
+    @ApiModelProperty(value = "邮箱")
+    private Integer email;
+
+    @ApiModelProperty(value = "手机号")
+    private String phone;
+
+    /**
+     * 登录成功返回给页面,页面在请求头返回,spring根据此sessionId获取缓存的用户信息
+     */
+    private String sessionId;
+}

+ 30 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/properties/HttpMatchersProperties.java

@@ -0,0 +1,30 @@
+package cn.kdan.pdf.tech.core.properties;//package properties;
+
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+@ConfigurationProperties("http")
+public class HttpMatchersProperties {
+
+    private List<String> requestMatchers;
+
+    private List<String> webMatchers;
+
+    public List<String> getRequestMatchers() {
+        return requestMatchers;
+    }
+
+    public void setRequestMatchers(List<String> requestMatchers) {
+        this.requestMatchers = requestMatchers;
+    }
+
+    public List<String> getWebMatchers() {
+        return webMatchers;
+    }
+
+    public void setWebMatchers(List<String> webMatchers) {
+        this.webMatchers = webMatchers;
+    }
+}

+ 116 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/AuthService.java

@@ -0,0 +1,116 @@
+package cn.kdan.pdf.tech.core.service;
+
+import cn.kdan.pdf.tech.core.enums.CaptchaActionEnum;
+import cn.kdan.pdf.tech.core.enums.ImageCodeTypeEnum;
+import cn.kdan.pdf.tech.core.enums.VerifyTypeEnum;
+import cn.kdan.pdf.tech.core.model.OauthClientDetails;
+import cn.kdan.pdf.tech.core.pojo.oauth2.TokenPOJO;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.List;
+
+/**
+ * 认证service(提供客户端的获取方法以及获取授权码和token的方法)
+ *
+ * @author tangxiangan
+ */
+public interface AuthService {
+
+    /**
+     * 获取oauth2 Client配置(用于oauth的客户端凭证校验)
+     *
+     * @param clientId clientId
+     * @return OauthClientDetails
+     */
+    OauthClientDetails getById(String clientId);
+
+    /**
+     * 列表获取oauth2配置(用于oauth的客户端凭证校验)
+     *
+     * @return List
+     */
+    List<OauthClientDetails> listAll();
+
+    /**
+     * 获取授权码
+     *
+     * @param userId    用户id
+     * @param principal 凭证
+     * @return String
+     */
+    String getAuthorizeCode(String userId, Principal principal);
+
+    /**
+     * 获取token
+     *
+     * @param code 授权码
+     * @return TokenPOJO
+     */
+    TokenPOJO getToken(String code);
+
+    /**
+     * 根据用户的id直接获取token(用于第三方回调)
+     *
+     * @param userId 用户id
+     * @return TokenPOJO
+     */
+    TokenPOJO getTokenByUserId(String userId);
+
+    /**
+     * 初始化验证码
+     *
+     * @param response  response
+     * @param session   session
+     * @param type      图片用途
+     * @param width     图片的宽度
+     * @param height    图片的长度
+     * @param codeCount 验证码字符个数
+     * @param lineCount 干扰线的数量
+     * @throws IOException IOException
+     */
+    void generateImageCode(HttpServletResponse response, HttpSession session, String type, int width, int height, int codeCount, int lineCount) throws IOException;
+
+    /**
+     * 判断图片验证码
+     *
+     * @param key  session
+     * @param type 验证码类型(登录或者忘记秘密)
+     * @param code 验证码
+     * @return boolean
+     */
+    boolean isImageCodeValid(String key, ImageCodeTypeEnum type, String code);
+
+    /**
+     * 获取验证码(发送短信,或者邮件)
+     *
+     * @param action   动作(例如登录)
+     * @param type     类型 (发短信还是邮件)
+     * @param receiver 接受人
+     * @return boolean
+     */
+    boolean getVerifyCode(CaptchaActionEnum action, VerifyTypeEnum type, String receiver);
+
+
+    /**
+     * 判断账户是否存在
+     *
+     * @param key     session
+     * @param account 手机号或者邮箱
+     * @param code    验证码
+     * @return boolean
+     */
+    boolean checkExist(String key, String account, String code);
+
+    /**
+     * 检查验证码
+     *
+     * @param action   动作
+     * @param receiver 接收人
+     * @param code     验证码
+     * @return boolean
+     */
+    boolean checkVerifyCode(String action, String receiver, String code);
+}

+ 28 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/UserService.java

@@ -0,0 +1,28 @@
+package cn.kdan.pdf.tech.core.service;
+
+import cn.kdan.pdf.tech.core.model.Members;
+import cn.kdan.pdf.tech.core.pojo.oauth2.UserInfo;
+import utils.BeanConverter;
+
+/**
+ * 用户service
+ */
+public interface UserService extends BeanConverter<Members, UserInfo> {
+    /**
+     * 根据ID获取用户
+     *
+     * @param userId 用户ID
+     * @return Members
+     */
+    Members getById(String userId);
+
+
+    /**
+     * 根据用户手机号和邮箱查找用户
+     *
+     * @param account 账号
+     * @return Members
+     */
+    Members getByAccount(String account);
+
+}

+ 224 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/AuthServiceImpl.java

@@ -0,0 +1,224 @@
+package cn.kdan.pdf.tech.core.service.impl;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.constant.MembersConstant;
+import cn.kdan.pdf.tech.core.enums.CaptchaActionEnum;
+import cn.kdan.pdf.tech.core.enums.ImageCodeTypeEnum;
+import cn.kdan.pdf.tech.core.enums.VerifyTypeEnum;
+import cn.kdan.pdf.tech.core.mapper.OauthClientDetailsMapper;
+import cn.kdan.pdf.tech.core.model.Members;
+import cn.kdan.pdf.tech.core.model.OauthClientDetails;
+import cn.kdan.pdf.tech.core.model.OauthClientDetailsExample;
+import cn.kdan.pdf.tech.core.pojo.oauth2.TokenPOJO;
+import cn.kdan.pdf.tech.core.service.AuthService;
+import cn.kdan.pdf.tech.core.service.UserService;
+import cn.kdan.pdf.tech.core.utils.TokenUtils;
+import constant.CommonConstant;
+import enums.ValidStatusEnum;
+import exception.BackendRuntimeException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.entity.ContentType;
+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.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import pojo.CustomUserDetails;
+import utils.*;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.*;
+
+/**
+ * 认证实现类
+ *
+ * @author tangxiangan
+ */
+@Service
+@Slf4j
+public class AuthServiceImpl implements AuthService {
+
+
+
+    @Autowired
+    private OauthClientDetailsMapper oauthClientDetailsMapper;
+
+    @Autowired
+    private SMSUtils smsUtils;
+
+    @Autowired
+    private EmailUtils emailUtils;
+
+    @Autowired
+    private RedisUtils<String, String> redisUtils;
+
+    @Value("${security.oauth2.client.client-id:}")
+    private String clientId;
+
+    @Value("${security.oauth2.client.client-secret:}")
+    private String secretKey;
+
+    @Resource
+    private UserService userService;
+
+    @Override
+    public OauthClientDetails getById(String clientId) {
+        return oauthClientDetailsMapper.selectByPrimaryKey(clientId);
+    }
+
+    @Override
+    public List<OauthClientDetails> listAll() {
+        return oauthClientDetailsMapper.selectByExample(new OauthClientDetailsExample());
+    }
+
+    @Override
+    public String getAuthorizeCode(String userId, Principal principal) {
+        return TokenUtils.getAuthorizeCode(clientId, principal);
+    }
+
+    @Override
+    public TokenPOJO getToken(String code) {
+        OAuth2AccessToken token = TokenUtils.getToken(code, clientId, secretKey);
+        return TokenUtils.convert(token);
+    }
+
+    @Override
+    public TokenPOJO getTokenByUserId(String userId) {
+        Members members = userService.getById(userId);
+        Set<SimpleGrantedAuthority> authorities = new LinkedHashSet<>();
+        String account = "";
+        if(!StringUtils.isEmpty(members.getPhone())){
+            account = members.getPhone();
+        }else{
+            account = members.getEmail();
+        }
+        CustomUserDetails principal = new CustomUserDetails(members.getId(),
+                account,
+                members.getPasswordDigest(),
+                true,
+                true,
+                true,
+                true,
+                authorities);
+        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(principal, null, Collections.emptySet());
+        OauthClientDetails clientDetails = this.getUsedClient();
+        if (clientDetails == null) {
+            throw new BackendRuntimeException(AuthConstant.EXCEPTION_MSG_PLEASE_ADD_AUTH_CONFIG);
+        }
+        //应用内部登录的授权码
+        String authCode = TokenUtils.getAuthorizeCode(clientDetails.getClientId(), authenticationToken);
+        OAuth2AccessToken token = TokenUtils.getToken(authCode, clientId, secretKey);
+        return TokenUtils.convert(token);
+    }
+
+    private OauthClientDetails getUsedClient() {
+
+        OauthClientDetailsExample example = new OauthClientDetailsExample();
+        example
+                .createCriteria()
+                .andStateEqualTo(ValidStatusEnum.VALID.value());
+
+        List<OauthClientDetails> oauthClientDetailsList = oauthClientDetailsMapper.selectByExample(example);
+
+        if (CollectionUtils.isEmpty(oauthClientDetailsList)) {
+            return null;
+        } else {
+            return oauthClientDetailsList.get(0);
+        }
+    }
+
+
+    @Override
+    public void generateImageCode(HttpServletResponse response, HttpSession session, String type, int width, int height, int codeCount, int lineCount) throws IOException {
+        ImageCodeTypeEnum imageCodeType;
+        //设置相应类型,告诉浏览器输出的内容为图片
+        response.setContentType(ContentType.IMAGE_PNG.getMimeType());
+        //设置响应头信息,告诉浏览器不要缓存此内容
+        response.setHeader(HttpHeaders.PRAGMA, CommonConstant.STRING_NO_CACHE);
+        if (StringUtils.isEmpty(type)) {
+            imageCodeType = ImageCodeTypeEnum.LOGIN;
+        } else {
+            imageCodeType = ImageCodeTypeEnum.getEnumByValue(type);
+        }
+        Map<String, Object> map = ImageCodeUtils.generate(width, height, codeCount, lineCount);
+        BufferedImage image = (BufferedImage) map.get(CommonConstant.STRING_IMAGE);
+        redisUtils.hset(AuthConstant.SESSION_ + session.getId(), imageCodeType.value(), (String) map.get(CommonConstant.STRING_CODE).toString().toLowerCase());
+        response.setHeader(CommonConstant.AUTH_TOKEN, session.getId());
+        ServletOutputStream os = response.getOutputStream();
+        ImageIO.write(image, CommonConstant.STRING_PNG, os);
+        os.close();
+    }
+
+    @Override
+    public boolean isImageCodeValid(String key, ImageCodeTypeEnum type, String code) {
+        String redisCode = redisUtils.hget(AuthConstant.SESSION_ + key, type.value().toLowerCase());
+        redisUtils.hdel(AuthConstant.SESSION_ + key, type.value());
+        return !StringUtils.isEmpty(code) && code.equalsIgnoreCase(redisCode);
+    }
+
+    @Override
+    public boolean getVerifyCode(CaptchaActionEnum action, VerifyTypeEnum type, String receiver) {
+        boolean flag = false;
+        if (redisUtils.hexists(AuthConstant.VERIFY_CODE_TIME_OUT + action.value(), receiver)) {
+            throw new BackendRuntimeException(AuthConstant.VERIFY_CODE_SEND_TOO_QUICKLY);
+        }
+        String code = CommonUtils.generateVerifyCode();
+        switch (type) {
+            //发送手机验证码
+            case PHONE:
+                //忘记密码时手机验证 发送手机短信
+                if (smsUtils.sendSMS(Collections.singletonList(receiver))) {
+                    flag = true;
+                }
+                break;
+            //发送邮件
+            case EMAIL:
+                //忘记密码时邮箱验证 发送重置密码邮件
+                if (CaptchaActionEnum.FORGET_PASSWORD.value().equals(action.value())) {
+                    String content = "";
+                    flag = emailUtils.sendMail(action.subject(), content, Collections.singletonList(receiver));
+                }
+                break;
+            default:
+                break;
+        }
+        //存入缓存
+        if (flag) {
+            redisUtils.hset(AuthConstant.VERIFY_CODE_KEY + action.value(), receiver, code, AuthConstant.VERIFY_CODE_KEY_EXPIRE_TIME);
+            //再次发送计时
+            redisUtils.hset(AuthConstant.VERIFY_CODE_TIME_OUT + action.value(), receiver, "1", AuthConstant.VERIFY_CODE_KEY_RESEND_TIME);
+        }
+        return flag;
+    }
+
+
+    @Override
+    public boolean checkExist(String key, String account, String code) {
+        if (!isImageCodeValid(key, ImageCodeTypeEnum.FORGET_PASSWORD, code)) {
+            throw new BackendRuntimeException(AuthConstant.EXCEPTION_MSG_IMAGE_CODE_ERROR);
+        }
+        if (ObjectUtils.isEmpty(userService.getByAccount(account))) {
+            throw new BackendRuntimeException(MembersConstant.EXCEPTION_MSG_USER_NOT_FOUND);
+        }
+        return Boolean.TRUE;
+    }
+
+    @Override
+    public boolean checkVerifyCode(String action, String receiver, String code) {
+        String captchaCode = redisUtils.hget(AuthConstant.VERIFY_CODE_KEY + action, receiver);
+        return StringUtils.isNotEmpty(code) && code.equals(captchaCode);
+    }
+
+}

+ 95 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/CustomAuthenticationCodeServicesImpl.java

@@ -0,0 +1,95 @@
+package cn.kdan.pdf.tech.core.service.impl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
+import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
+import org.springframework.security.oauth2.common.util.SerializationUtils;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
+import org.springframework.util.Assert;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+/**
+ * AuthorizationCodeServices的自定义,只要继承RandomValueAuthorizationCodeServices,实现他的store和remove方法。
+ * @author tangxiangan
+ */
+public class CustomAuthenticationCodeServicesImpl extends RandomValueAuthorizationCodeServices {
+    private final Logger logger = LoggerFactory.getLogger(CustomAuthenticationCodeServicesImpl.class);
+
+    private static final String AUTH_CODE_KEY = "auth_code";
+    private RedisConnectionFactory connectionFactory;
+
+    public CustomAuthenticationCodeServicesImpl(RedisConnectionFactory connectionFactory) {
+
+        Assert.notNull(connectionFactory, "RedisConnectionFactory required");
+        this.connectionFactory = connectionFactory;
+    }
+
+    @Override
+    protected void store(String code, OAuth2Authentication authentication) {
+        RedisConnection conn = getConnection();
+        try {
+            conn.hSet(AUTH_CODE_KEY.getBytes(StandardCharsets.UTF_8), code.getBytes(StandardCharsets.UTF_8),
+                    SerializationUtils.serialize(authentication));
+        } catch (Exception e) {
+            logger.error("保存authentication code 失败", e);
+        } finally {
+            conn.close();
+        }
+
+    }
+
+    @Override
+    protected OAuth2Authentication remove(String code) {
+        RedisConnection conn = getConnection();
+        try {
+            OAuth2Authentication authentication;
+
+            try {
+                authentication = SerializationUtils
+                        .deserialize(Objects.requireNonNull(conn.hGet(AUTH_CODE_KEY.getBytes(StandardCharsets.UTF_8), code.getBytes(StandardCharsets.UTF_8))));
+            } catch (Exception e) {
+                return null;
+            }
+
+            if (null != authentication) {
+                conn.hDel(AUTH_CODE_KEY.getBytes(StandardCharsets.UTF_8), code.getBytes(StandardCharsets.UTF_8));
+            }
+
+            return authentication;
+        } catch (Exception e) {
+            return null;
+        } finally {
+            conn.close();
+        }
+    }
+
+    private RandomValueStringGenerator generator = new RandomValueStringGenerator();
+
+    @Override
+    public String createAuthorizationCode(OAuth2Authentication authentication) {
+        String code = generator.generate();
+        this.store(code, authentication);
+        return code;
+    }
+
+    @Override
+    public OAuth2Authentication consumeAuthorizationCode(String code)
+            throws InvalidGrantException {
+        OAuth2Authentication auth = this.remove(code);
+        if (auth == null) {
+            throw new InvalidGrantException("Invalid authorization code: " + code);
+        }
+        return auth;
+    }
+
+    private RedisConnection getConnection() {
+        return connectionFactory.getConnection();
+    }
+
+}

+ 103 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/CustomClientDetailsServiceImpl.java

@@ -0,0 +1,103 @@
+package cn.kdan.pdf.tech.core.service.impl;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.model.OauthClientDetails;
+import cn.kdan.pdf.tech.core.service.AuthService;
+import constant.CommonConstant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.oauth2.provider.*;
+import org.springframework.security.oauth2.provider.client.BaseClientDetails;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+
+/**
+ * @author tangxiangan
+ */
+public class CustomClientDetailsServiceImpl implements ClientDetailsService, ClientRegistrationService {
+
+    private Logger logger = LoggerFactory.getLogger(CustomClientDetailsServiceImpl.class);
+
+    private AuthService authService;
+
+    public CustomClientDetailsServiceImpl(AuthService authService) {
+        this.authService = authService;
+    }
+
+    @Override
+    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
+        OauthClientDetails clientDetailsVO = authService.getById(clientId);
+        return convert(clientDetailsVO);
+    }
+
+    @Override
+    public void addClientDetails(ClientDetails clientDetails) throws ClientAlreadyExistsException {
+        logger.info("addClientDetails");
+    }
+
+    @Override
+    public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException {
+        logger.info("updateClientDetails");
+    }
+
+    @Override
+    public void updateClientSecret(String clientId, String secret) throws NoSuchClientException {
+        logger.info("updateClientSecret");
+    }
+
+    @Override
+    public void removeClientDetails(String clientId) throws NoSuchClientException {
+        logger.info("removeClientDetails");
+    }
+
+    @Override
+    public List<ClientDetails> listClientDetails() {
+        List<OauthClientDetails> list = authService.listAll();
+        List<ClientDetails> res = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(list)) {
+            list.forEach(details -> res.add(convert(details)));
+        }
+        return res;
+    }
+
+    private BaseClientDetails convert(OauthClientDetails client) {
+        BaseClientDetails clientDetails = new BaseClientDetails();
+        clientDetails.setClientId(client.getClientId());
+        clientDetails.setClientSecret(client.getClientSecret());
+        clientDetails.setAccessTokenValiditySeconds(client.getAccessTokenValidity());
+        clientDetails.setRefreshTokenValiditySeconds(client.getRefreshTokenValidity());
+        if (!StringUtils.isEmpty(client.getScope())) {
+            clientDetails.setScope(Arrays.asList(client.getScope().split(CommonConstant.STRING_SIGN_COMMA)));
+        } else {
+            clientDetails.setScope(Collections.emptySet());
+        }
+        if (!StringUtils.isEmpty(client.getAuthorizedGrantTypes())) {
+            clientDetails.setAuthorizedGrantTypes(Arrays.asList(client.getAuthorizedGrantTypes().split(CommonConstant.STRING_SIGN_COMMA)));
+        } else {
+            clientDetails.setAuthorizedGrantTypes(Collections.emptyList());
+        }
+
+        if (!StringUtils.isEmpty(client.getWebServerRedirectUri())) {
+            clientDetails.setRegisteredRedirectUri(new LinkedHashSet<>(Collections.singletonList(AuthConstant.DEFAULT_REDIRECT_URI)));
+        } else {
+            clientDetails.setRegisteredRedirectUri(Collections.emptySet());
+        }
+
+        if (!StringUtils.isEmpty(client.getAutoapprove())) {
+            String[] strAutoapproves = client.getAutoapprove().split(CommonConstant.STRING_SIGN_COMMA);
+            HashSet<String> autoapprove = new HashSet<>(Arrays.asList(strAutoapproves));
+            clientDetails.setAutoApproveScopes(autoapprove);
+        } else {
+            clientDetails.setAutoApproveScopes(Collections.emptySet());
+        }
+
+        //暂时不用
+        //clientDetails.setAuthorities(Collections.emptyList());
+        // clientDetails.setResourceIds(Collections.emptySet());
+        // clientDetails.setAdditionalInformation(Collections.emptyMap());
+        return clientDetails;
+    }
+
+}

+ 47 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/CustomUserDetailsServiceImpl.java

@@ -0,0 +1,47 @@
+package cn.kdan.pdf.tech.core.service.impl;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.model.Members;
+import cn.kdan.pdf.tech.core.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+import org.springframework.util.ObjectUtils;
+import pojo.CustomUserDetails;
+import pojo.ResultMap;
+import utils.JsonUtils;
+
+import java.util.HashSet;
+
+/**
+ * @author tangxiangan
+ */
+@Service
+public class CustomUserDetailsServiceImpl implements UserDetailsService {
+
+    @Autowired
+    private UserService userService;
+
+    /**
+     * 用于认证时如果从内存中找不到用户信息,从数据库查找用户信息
+     *
+     * @param account
+     * @return UserDetails
+     * @throws UsernameNotFoundException
+     */
+    @Override
+    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
+        Members user = userService.getByAccount(account);
+        if (ObjectUtils.isEmpty(user)) {
+            throw new UsernameNotFoundException(JsonUtils.getJsonString(new ResultMap<>(AuthConstant.EXCEPTION_CODE_USER_NOT_FOUND, AuthConstant.EXCEPTION_MSG_USER_NOT_FOUND)));
+        }
+        // 邮箱登陆时判断邮箱是否验证 没验证不能登陆
+//        if (user.getEmail().equals(account) && user.getEmailStatus().equals(EmailStatusEnum.INACTIVE.value())) {
+//            throw new UsernameNotFoundException(JsonUtils.getJsonString(new ResultMap<>(AuthConstant.EXCEPTION_CODE_EMAIL_LOGIN_FAIL, AuthConstant.EXCEPTION_MSG_EMAIL_NOT_VERIFY)));
+//        }
+
+        return new CustomUserDetails(user.getId(), user.getName(), user.getPasswordDigest(), true, true, true, true, new HashSet<>());
+    }
+}

+ 76 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/service/impl/UserServiceImpl.java

@@ -0,0 +1,76 @@
+package cn.kdan.pdf.tech.core.service.impl;
+
+import cn.kdan.pdf.tech.core.model.Members;
+import cn.kdan.pdf.tech.core.pojo.oauth2.UserInfo;
+import cn.kdan.pdf.tech.core.service.UserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+
+/**
+ * oauth2 用户方法类
+ * @author tangxiangan
+ */
+@Slf4j
+@Service
+public class UserServiceImpl implements UserService {
+    @Override
+    public UserInfo convert(Members members) {
+        return null;
+    }
+
+    @Override
+    public UserInfo detail(Members members) {
+        return null;
+    }
+
+    @Override
+    public Members getById(String userId) {
+        return null;
+    }
+
+    @Override
+    public Members getByAccount(String account) {
+        return null;
+    }
+
+//    @Autowired
+//    private ExtMembersMapper userMapper;
+//
+//
+//    @Override
+//    public Members getById(String userId) {
+//        return userMapper.selectByPrimaryKey(userId);
+//    }
+//
+//    @Override
+//    public Members getByAccount(String account) {
+//
+//        List<Members> users = userMapper.getMemberByAccount(account);
+//
+//        return CollectionUtils.isEmpty(users) ? null : users.get(0);
+//    }
+//
+//    @Override
+//    public UserInfo convert(Members members) {
+//        UserInfo userInfo = null;
+//        if (members != null) {
+//            userInfo = new UserInfo();
+//            BeanUtils.copyProperties(members, userInfo);
+//        }
+//
+//        return userInfo;
+//    }
+//
+//
+//
+//    @Override
+//    public UserInfo detail(Members user) {
+//        return this.convert(user);
+//    }
+}

+ 139 - 0
pdf-tech-core/src/main/java/cn/kdan/pdf/tech/core/utils/TokenUtils.java

@@ -0,0 +1,139 @@
+package cn.kdan.pdf.tech.core.utils;
+
+import cn.kdan.pdf.tech.core.constant.AuthConstant;
+import cn.kdan.pdf.tech.core.model.OauthClientDetails;
+import cn.kdan.pdf.tech.core.pojo.oauth2.TokenPOJO;
+import com.alibaba.fastjson.JSON;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint;
+import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.support.SessionStatus;
+import org.springframework.web.bind.support.SimpleSessionStatus;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.view.RedirectView;
+import utils.AesUtils;
+import utils.SpringContextUtils;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * oauth2令牌获取
+ * @author tangxiangan
+ */
+public class TokenUtils {
+    private static final Logger logger = LoggerFactory.getLogger(TokenUtils.class);
+
+    /**
+     * 应用内部获取令牌
+     *
+     * @param code     授权码
+     * @param clientId 客户端ID
+     * @param secret   凭证
+     */
+    public static OAuth2AccessToken getToken(String code, String clientId, String secret) {
+
+        Map<String, String> params = new HashMap<>();
+        params.put("grant_type", "authorization_code");
+        params.put("code", code);
+        params.put("client_id", clientId);
+        params.put("redirect_uri", AuthConstant.DEFAULT_REDIRECT_URI);
+        params.put("client_secret", "secret");
+        User principal = new User(clientId, secret, Collections.emptySet());
+        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(principal, null, Collections.emptySet());
+        try {
+            TokenEndpoint tokenEndpoint = SpringContextUtils.getBean(TokenEndpoint.class);
+            ResponseEntity<OAuth2AccessToken> responseEntity = tokenEndpoint.postAccessToken(authenticationToken, params);
+            return responseEntity.getBody();
+        } catch (HttpRequestMethodNotSupportedException e) {
+            logger.error(e.getMessage(), e);
+
+            return null;
+        }
+    }
+
+    /**
+     * 应用内部获取授权码(用户验证完账号名和密码之后,访问获取授权码的端口authorizationEndpoint,authorize获取授权码,然后截取返回的授权码)
+     *
+     * @param clientId  客户端ID
+     * @param principal 认证信息
+     * @return 授权码
+     */
+    public static String getAuthorizeCode(String clientId, Principal principal) {
+        Map<String, String> params = new HashMap<>();
+        params.put("response_type", "code");
+        params.put("client_id", clientId);
+        params.put("redirect_uri", AuthConstant.DEFAULT_REDIRECT_URI);
+        SessionStatus status = new SimpleSessionStatus();
+
+        AuthorizationEndpoint authorizationEndpoint = SpringContextUtils.getBean(AuthorizationEndpoint.class);
+        ModelAndView authorizeMV = authorizationEndpoint.authorize(new HashMap<>(), params, status, principal);
+        RedirectView view = (RedirectView) authorizeMV.getView();
+        if (view != null && StringUtils.isNotEmpty(view.getUrl())) {
+            return view.getUrl().substring(view.getUrl().indexOf("code=") + 5);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * 远程获取oauth2令牌
+     *
+     * @param clientDetails oauth2客户端配置信息
+     * @param code          授权码
+     * @param state         声明
+     */
+    public static TokenPOJO getOAuth2Token(OauthClientDetails clientDetails, String code, String state) {
+
+        HttpHeaders httpHeaders = new HttpHeaders();
+        httpHeaders.set("Content-Type", "application/x-www-form-urlencoded");
+        httpHeaders.set("User-Agent", "PostmanRuntime/7.29.0");
+
+        MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
+        param.add("grant_type", clientDetails.getAuthorizedGrantTypes());
+        param.add("code", code);
+        param.add("client_id", clientDetails.getClientId());
+        param.add("client_secret", AesUtils.decrypt(clientDetails.getClientSecret()));
+        param.add("state", state);
+        param.add("redirect_uri", clientDetails.getWebServerRedirectUri());
+
+        RestTemplate restTemplate = SpringContextUtils.getBean(RestTemplate.class);
+        ResponseEntity<String> responseEntity = restTemplate.exchange(clientDetails.getTokenUrl(), HttpMethod.POST, new HttpEntity<>(param, httpHeaders), String.class);
+        if (responseEntity.getStatusCode().is2xxSuccessful()) {
+            return JSON.parseObject(responseEntity.getBody(), TokenPOJO.class);
+
+        } else {
+            logger.warn("OAuth2令牌获取失败,失败内容:{}", responseEntity);
+            return null;
+        }
+    }
+
+    public static TokenPOJO convert(OAuth2AccessToken token) {
+
+        if (token == null) {
+            return null;
+        }
+        TokenPOJO tokenPojo = new TokenPOJO();
+        tokenPojo.setAccessToken(token.getValue());
+        tokenPojo.setExpiresIn(token.getExpiresIn());
+        tokenPojo.setTokenType(token.getTokenType());
+        tokenPojo.setRefreshToken(token.getRefreshToken().getValue());
+
+        return tokenPojo;
+    }
+}

+ 5 - 0
pdf-tech-core/src/main/resources/application-local-db.properties

@@ -0,0 +1,5 @@
+jdbc.driverClassName=org.postgresql.Driver
+jdbc.url=jdbc\:postgresql\://139.196.224.157\:5432/db_larger1
+jdbc.username=postgres
+jdbc.password=hYG59287
+

+ 9 - 0
pdf-tech-core/src/main/resources/application-local-redis.properties

@@ -0,0 +1,9 @@
+redis.max-redirects=3
+redis.maxIdle=100
+redis.connectionTimeout=100000
+redis.socketTimeout=100000
+redis.maxAttempts=5
+redis.maxTotal=200
+redis.minIdle=10
+redis.nodes=81.68.234.235:6371,81.68.234.235:6372,81.68.234.235:6373,81.68.234.235:6374,81.68.234.235:6375,81.68.234.235:6376
+redis.password=1234

+ 36 - 0
pdf-tech-core/src/main/resources/application-local.yml

@@ -0,0 +1,36 @@
+server:
+  port: 8032
+spring:
+  profiles:
+    include: local-db,local-redis
+#  mail:
+#    host: smtp.mxhichina.com
+#    port: 465
+#    username: support@pdf-tech.com
+#    password: Kdan17PDF01
+#    protocol: smtp
+#    properties:
+#      mail:
+#        smtp:
+#          from: support@pdf-tech.com
+#          port: 465
+#          socketFactory:
+#            port: 465
+#            class: javax.net.ssl.SSLSocketFactory
+#          host: smtp.mxhichina.com
+#          auth: true
+#          ssl:
+#            enable: true
+#        debug: true
+security:
+  oauth2:
+    client:
+      client-id: pdf-tech-backend-core
+      client-secret: kdan@2022
+      user-authorization-uri: http://localhost:8083/pdf-tech-backend-core/oauth/authorize
+      access-token-uri: http://localhost:8083/pdf-tech-backend-core/oauth/token
+      resource:
+        user-info-uri: http://localhost:8083/pdf-tech-backend-core/user/me
+
+
+

+ 86 - 0
pdf-tech-core/src/main/resources/application.yml

@@ -0,0 +1,86 @@
+server:
+  servlet:
+    context-path: /pdf-tech-core
+spring:
+  application:
+    name: pdf-tech-core
+  profiles:
+    active: @spring.profiles.active@
+  datasource:
+    name: mydb
+    url: ${jdbc.url}
+    username: ${jdbc.username}
+    password: ${jdbc.password}
+    # 使用druid数据源
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: ${jdbc.driverClassName}
+    filters: stat
+    maxActive: 20
+    initialSize: 1
+    maxWait: 60000
+    minIdle: 1
+    timeBetweenEvictionRunsMillis: 60000
+    minEvictableIdleTimeMillis: 300000
+    validationQuery: select 'x'
+    testWhileIdle: true
+    testOnBorrow: false
+    testOnReturn: false
+    poolPreparedStatements: true
+    maxOpenPreparedStatements: 20
+  servlet:
+    multipart:
+      max-file-size: 20MB
+      max-request-size: 20MB
+  #redis连接池
+  redis:
+    cluster:
+      nodes: ${redis.nodes}
+      max-redirects: ${redis.max-redirects}
+    password: ${redis.password}
+    lettuce:
+      pool:
+        max-active: ${redis.maxTotal}
+        max-wait: ${redis.socketTimeout}
+        max-idle: ${redis.maxIdle}
+        min-idle: ${redis.minIdle}
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
+
+#不共享一级缓存
+mybatis:
+  configuration:
+    local-cache-scope: statement
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  #构建数据对象映射文件
+  mapper-locations: classpath:sqlmap/**/*.xml
+  type-aliases-package: cn.kdan.pdf.backend.core.model
+
+pagehelper:
+  helperDialect: mysql
+  reasonable: true
+  supportMethodsArguments: true
+
+swagger:
+  base-package: cn.kdan.pdf.backend.core.controller
+  description: pdf-tech api test
+  title: pdf-tech api test
+
+logging:
+  level:
+    cn.kdan.pdf.tech.core.mapper: info
+
+cors:
+  allow-headers: "X-Requested-With,Content-Type,Cookie,Accept,authorization,Authorization,credential,keyword,x-auth-token,iv,jsonFlag,timeStamp,random"
+  allow-methods: "*"
+  allow-expose: "x-auth-token,Content-Disposition"
+  allow-credentials: "false"
+  allow-origins: "*"
+  allow-max-age: "3600"
+httpMatchers:
+  request: "/login,/logout,/members/resetPassword,/oauth/**,/auth/**,/members/register,/order/syncOrder,/order/alipaySyncOrder,/alipayRedirect/callback,/pricing/list,/setPricing/listForVisitor,/mission/saasCallback"
+  web: "/hystrix.stream,/webjars/**,/resources/**,/swagger-ui.html,/swagger-resources/**,/v2/api-docs"
+
+
+

+ 42 - 0
pdf-tech-core/src/main/resources/generatorConfig.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE generatorConfiguration
+        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
+        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
+
+<generatorConfiguration>
+    <context id="MySQLTables" targetRuntime="MyBatis3">
+        <property name="autoDelimitKeywords" value="true"/>
+        <property name="beginningDelimiter" value="`"/>
+        <property name="endingDelimiter" value="`"/>
+        <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
+        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
+        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
+        <plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin"/>
+        <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin"/>
+        <plugin type="org.mybatis.generator.plugins.FluentBuilderMethodsPlugin"/>
+        <commentGenerator>
+            <property name="suppressAllComments" value="true"/>
+        </commentGenerator>
+        <jdbcConnection driverClass="org.postgresql.Driver"
+                        connectionURL="jdbc:postgresql://139.196.224.157:5432/db_larger1"
+                        userId="postgres" password="hYG59287"
+        >
+            <property name="nullCatalogMeansCurrent" value="true"/>
+        </jdbcConnection>
+
+        <javaModelGenerator targetPackage="cn.kdan.pdf.tech.core.model"
+                            targetProject="src/main/java">
+            <property name="enableSubPackages" value="true"/>
+        </javaModelGenerator>
+        <sqlMapGenerator targetPackage="sqlmap" targetProject="src/main/resources">
+            <property name="enableSubPackages" value="true"/>
+        </sqlMapGenerator>
+        <javaClientGenerator type="XMLMAPPER"
+                             targetPackage="cn.kdan.pdf.tech.core.mapper" targetProject="src/main/java">
+            <property name="enableSubPackages" value="true"/>
+        </javaClientGenerator>
+        <table tableName="vpp_member"/>
+
+
+    </context>
+</generatorConfiguration>

+ 228 - 0
pdf-tech-core/src/main/resources/logback-spring.xml

@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration scan="true" scanPeriod="10 seconds">
+
+    <!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
+
+    <contextName>logback</contextName>
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+    <property name="log.path" value="/logs/backend-core"/>
+
+    <!-- 彩色日志 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex"
+                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx"
+                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+    <!-- 彩色日志格式 -->
+    <property name="CONSOLE_LOG_PATTERN"
+              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+
+    <!--输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>info</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+
+    <!--输出到文件-->
+
+    <!-- 时间滚动输出 level为 DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_debug.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [PtxId : %X{PtxId} , SpanId : %X{PspanId}] %-5level
+                %logger{50} - %msg%n
+            </pattern>
+            <charset>UTF-8</charset> <!-- 设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志归档 -->
+            <fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>debug</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_info.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [PtxId : %X{PtxId} , SpanId : %X{PspanId}] %-5level
+                %logger{50} - %msg%n
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 每天日志归档路径以及格式 -->
+            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>300MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录info级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_warn.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [PtxId : %X{PtxId} , SpanId : %X{PspanId}] %-5level
+                %logger{50} - %msg%n
+            </pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>300MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录warn级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>warn</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <!-- 时间滚动输出 level为 ERROR 日志 -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log_error.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [PtxId : %X{PtxId} , SpanId : %X{PspanId}] %-5level
+                %logger{50} - %msg%n
+            </pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>300MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录ERROR级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--
+        <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+        以及指定<appender>。<logger>仅有一个name属性,
+        一个可选的level和一个可选的addtivity属性。
+        name:用来指定受此logger约束的某一个包或者具体的某一个类。
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+              还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+              如果未设置此属性,那么当前logger将会继承上级的级别。
+        addtivity:是否向上级logger传递打印信息。默认是true。
+    -->
+    <!--<logger name="org.springframework.web" level="info"/>-->
+    <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
+    <!--
+        使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+        第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+        第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+     -->
+
+
+    <!--
+        root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+        不能设置为INHERITED或者同义词NULL。默认是DEBUG
+        可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <!--本地环境:打印控制台-->
+    <springProfile name="local">
+        <logger name="cn.kdan.pdf.backend.core" level="debug"/>
+        <root level="info">
+            <appender-ref ref="CONSOLE"/>
+        </root>
+    </springProfile>
+
+    <!--开发环境:打印控制台-->
+    <springProfile name="dev">
+        <logger name="cn.kdan.pdf.backend.core" level="debug"/>
+        <root level="info">
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+        </root>
+    </springProfile>
+
+    <!--生产环境:输出到文件-->
+    <springProfile name="test">
+        <logger name="cn.kdan.pdf.backend.core" level="debug"/>
+        <root level="info">
+            <appender-ref ref="CONSOLE"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="beta">
+        <root level="info">
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="pro">
+        <root level="info">
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+        </root>
+    </springProfile>
+
+</configuration>

+ 377 - 0
pdf-tech-core/src/main/resources/sqlmap/OauthClientDetailsMapper.xml

@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.kdan.pdf.tech.core.mapper.OauthClientDetailsMapper">
+    <resultMap id="BaseResultMap" type="cn.kdan.pdf.tech.core.model.OauthClientDetails">
+        <id column="client_id" jdbcType="VARCHAR" property="clientId"/>
+        <result column="resource_ids" jdbcType="VARCHAR" property="resourceIds"/>
+        <result column="client_secret" jdbcType="VARCHAR" property="clientSecret"/>
+        <result column="scope" jdbcType="VARCHAR" property="scope"/>
+        <result column="authorized_grant_types" jdbcType="VARCHAR" property="authorizedGrantTypes"/>
+        <result column="web_server_redirect_uri" jdbcType="VARCHAR" property="webServerRedirectUri"/>
+        <result column="authorities" jdbcType="VARCHAR" property="authorities"/>
+        <result column="access_token_validity" jdbcType="INTEGER" property="accessTokenValidity"/>
+        <result column="refresh_token_validity" jdbcType="INTEGER" property="refreshTokenValidity"/>
+        <result column="additional_information" jdbcType="VARCHAR" property="additionalInformation"/>
+        <result column="autoapprove" jdbcType="VARCHAR" property="autoapprove"/>
+        <result column="state" jdbcType="VARCHAR" property="state"/>
+        <result column="token_url" jdbcType="VARCHAR" property="tokenUrl"/>
+        <result column="user_info_url" jdbcType="VARCHAR" property="userInfoUrl"/>
+    </resultMap>
+    <sql id="Example_Where_Clause">
+        <where>
+            <foreach collection="oredCriteria" item="criteria" separator="or">
+                <if test="criteria.valid">
+                    <trim prefix="(" prefixOverrides="and" suffix=")">
+                        <foreach collection="criteria.criteria" item="criterion">
+                            <choose>
+                                <when test="criterion.noValue">
+                                    and ${criterion.condition}
+                                </when>
+                                <when test="criterion.singleValue">
+                                    and ${criterion.condition} #{criterion.value}
+                                </when>
+                                <when test="criterion.betweenValue">
+                                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                                </when>
+                                <when test="criterion.listValue">
+                                    and ${criterion.condition}
+                                    <foreach close=")" collection="criterion.value" item="listItem" open="("
+                                             separator=",">
+                                        #{listItem}
+                                    </foreach>
+                                </when>
+                            </choose>
+                        </foreach>
+                    </trim>
+                </if>
+            </foreach>
+        </where>
+    </sql>
+    <sql id="Update_By_Example_Where_Clause">
+        <where>
+            <foreach collection="example.oredCriteria" item="criteria" separator="or">
+                <if test="criteria.valid">
+                    <trim prefix="(" prefixOverrides="and" suffix=")">
+                        <foreach collection="criteria.criteria" item="criterion">
+                            <choose>
+                                <when test="criterion.noValue">
+                                    and ${criterion.condition}
+                                </when>
+                                <when test="criterion.singleValue">
+                                    and ${criterion.condition} #{criterion.value}
+                                </when>
+                                <when test="criterion.betweenValue">
+                                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                                </when>
+                                <when test="criterion.listValue">
+                                    and ${criterion.condition}
+                                    <foreach close=")" collection="criterion.value" item="listItem" open="("
+                                             separator=",">
+                                        #{listItem}
+                                    </foreach>
+                                </when>
+                            </choose>
+                        </foreach>
+                    </trim>
+                </if>
+            </foreach>
+        </where>
+    </sql>
+    <sql id="Base_Column_List">
+        client_id
+        , resource_ids, client_secret, `scope`, authorized_grant_types, web_server_redirect_uri,
+    authorities, access_token_validity, refresh_token_validity, additional_information,
+    autoapprove, `state`, token_url, user_info_url
+    </sql>
+    <select id="selectByExample" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetailsExample"
+            resultMap="BaseResultMap">
+        select
+        <if test="distinct">
+            distinct
+        </if>
+        <include refid="Base_Column_List"/>
+        from oauth_client_details
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+        <if test="orderByClause != null">
+            order by ${orderByClause}
+        </if>
+    </select>
+    <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from oauth_client_details
+        where client_id = #{clientId,jdbcType=VARCHAR}
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
+        delete
+        from oauth_client_details
+        where client_id = #{clientId,jdbcType=VARCHAR}
+    </delete>
+    <delete id="deleteByExample" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetailsExample">
+        delete from oauth_client_details
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+    </delete>
+    <insert id="insert" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetails">
+        insert into oauth_client_details (client_id, resource_ids, client_secret,
+                                          `scope`, authorized_grant_types, web_server_redirect_uri,
+                                          authorities, access_token_validity, refresh_token_validity,
+                                          additional_information, autoapprove, `state`,
+                                          token_url, user_info_url)
+        values (#{clientId,jdbcType=VARCHAR}, #{resourceIds,jdbcType=VARCHAR}, #{clientSecret,jdbcType=VARCHAR},
+                #{scope,jdbcType=VARCHAR}, #{authorizedGrantTypes,jdbcType=VARCHAR},
+                #{webServerRedirectUri,jdbcType=VARCHAR},
+                #{authorities,jdbcType=VARCHAR}, #{accessTokenValidity,jdbcType=INTEGER},
+                #{refreshTokenValidity,jdbcType=INTEGER},
+                #{additionalInformation,jdbcType=VARCHAR}, #{autoapprove,jdbcType=VARCHAR}, #{state,jdbcType=VARCHAR},
+                #{tokenUrl,jdbcType=VARCHAR}, #{userInfoUrl,jdbcType=VARCHAR})
+    </insert>
+    <insert id="insertSelective" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetails">
+        insert into oauth_client_details
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="clientId != null">
+                client_id,
+            </if>
+            <if test="resourceIds != null">
+                resource_ids,
+            </if>
+            <if test="clientSecret != null">
+                client_secret,
+            </if>
+            <if test="scope != null">
+                `scope`,
+            </if>
+            <if test="authorizedGrantTypes != null">
+                authorized_grant_types,
+            </if>
+            <if test="webServerRedirectUri != null">
+                web_server_redirect_uri,
+            </if>
+            <if test="authorities != null">
+                authorities,
+            </if>
+            <if test="accessTokenValidity != null">
+                access_token_validity,
+            </if>
+            <if test="refreshTokenValidity != null">
+                refresh_token_validity,
+            </if>
+            <if test="additionalInformation != null">
+                additional_information,
+            </if>
+            <if test="autoapprove != null">
+                autoapprove,
+            </if>
+            <if test="state != null">
+                `state`,
+            </if>
+            <if test="tokenUrl != null">
+                token_url,
+            </if>
+            <if test="userInfoUrl != null">
+                user_info_url,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="clientId != null">
+                #{clientId,jdbcType=VARCHAR},
+            </if>
+            <if test="resourceIds != null">
+                #{resourceIds,jdbcType=VARCHAR},
+            </if>
+            <if test="clientSecret != null">
+                #{clientSecret,jdbcType=VARCHAR},
+            </if>
+            <if test="scope != null">
+                #{scope,jdbcType=VARCHAR},
+            </if>
+            <if test="authorizedGrantTypes != null">
+                #{authorizedGrantTypes,jdbcType=VARCHAR},
+            </if>
+            <if test="webServerRedirectUri != null">
+                #{webServerRedirectUri,jdbcType=VARCHAR},
+            </if>
+            <if test="authorities != null">
+                #{authorities,jdbcType=VARCHAR},
+            </if>
+            <if test="accessTokenValidity != null">
+                #{accessTokenValidity,jdbcType=INTEGER},
+            </if>
+            <if test="refreshTokenValidity != null">
+                #{refreshTokenValidity,jdbcType=INTEGER},
+            </if>
+            <if test="additionalInformation != null">
+                #{additionalInformation,jdbcType=VARCHAR},
+            </if>
+            <if test="autoapprove != null">
+                #{autoapprove,jdbcType=VARCHAR},
+            </if>
+            <if test="state != null">
+                #{state,jdbcType=VARCHAR},
+            </if>
+            <if test="tokenUrl != null">
+                #{tokenUrl,jdbcType=VARCHAR},
+            </if>
+            <if test="userInfoUrl != null">
+                #{userInfoUrl,jdbcType=VARCHAR},
+            </if>
+        </trim>
+    </insert>
+    <select id="countByExample" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetailsExample"
+            resultType="java.lang.Long">
+        select count(*) from oauth_client_details
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+    </select>
+    <update id="updateByExampleSelective" parameterType="map">
+        update oauth_client_details
+        <set>
+            <if test="record.clientId != null">
+                client_id = #{record.clientId,jdbcType=VARCHAR},
+            </if>
+            <if test="record.resourceIds != null">
+                resource_ids = #{record.resourceIds,jdbcType=VARCHAR},
+            </if>
+            <if test="record.clientSecret != null">
+                client_secret = #{record.clientSecret,jdbcType=VARCHAR},
+            </if>
+            <if test="record.scope != null">
+                `scope` = #{record.scope,jdbcType=VARCHAR},
+            </if>
+            <if test="record.authorizedGrantTypes != null">
+                authorized_grant_types = #{record.authorizedGrantTypes,jdbcType=VARCHAR},
+            </if>
+            <if test="record.webServerRedirectUri != null">
+                web_server_redirect_uri = #{record.webServerRedirectUri,jdbcType=VARCHAR},
+            </if>
+            <if test="record.authorities != null">
+                authorities = #{record.authorities,jdbcType=VARCHAR},
+            </if>
+            <if test="record.accessTokenValidity != null">
+                access_token_validity = #{record.accessTokenValidity,jdbcType=INTEGER},
+            </if>
+            <if test="record.refreshTokenValidity != null">
+                refresh_token_validity = #{record.refreshTokenValidity,jdbcType=INTEGER},
+            </if>
+            <if test="record.additionalInformation != null">
+                additional_information = #{record.additionalInformation,jdbcType=VARCHAR},
+            </if>
+            <if test="record.autoapprove != null">
+                autoapprove = #{record.autoapprove,jdbcType=VARCHAR},
+            </if>
+            <if test="record.state != null">
+                `state` = #{record.state,jdbcType=VARCHAR},
+            </if>
+            <if test="record.tokenUrl != null">
+                token_url = #{record.tokenUrl,jdbcType=VARCHAR},
+            </if>
+            <if test="record.userInfoUrl != null">
+                user_info_url = #{record.userInfoUrl,jdbcType=VARCHAR},
+            </if>
+        </set>
+        <if test="_parameter != null">
+            <include refid="Update_By_Example_Where_Clause"/>
+        </if>
+    </update>
+    <update id="updateByExample" parameterType="map">
+        update oauth_client_details
+        set client_id = #{record.clientId,jdbcType=VARCHAR},
+        resource_ids = #{record.resourceIds,jdbcType=VARCHAR},
+        client_secret = #{record.clientSecret,jdbcType=VARCHAR},
+        `scope` = #{record.scope,jdbcType=VARCHAR},
+        authorized_grant_types = #{record.authorizedGrantTypes,jdbcType=VARCHAR},
+        web_server_redirect_uri = #{record.webServerRedirectUri,jdbcType=VARCHAR},
+        authorities = #{record.authorities,jdbcType=VARCHAR},
+        access_token_validity = #{record.accessTokenValidity,jdbcType=INTEGER},
+        refresh_token_validity = #{record.refreshTokenValidity,jdbcType=INTEGER},
+        additional_information = #{record.additionalInformation,jdbcType=VARCHAR},
+        autoapprove = #{record.autoapprove,jdbcType=VARCHAR},
+        `state` = #{record.state,jdbcType=VARCHAR},
+        token_url = #{record.tokenUrl,jdbcType=VARCHAR},
+        user_info_url = #{record.userInfoUrl,jdbcType=VARCHAR}
+        <if test="_parameter != null">
+            <include refid="Update_By_Example_Where_Clause"/>
+        </if>
+    </update>
+    <update id="updateByPrimaryKeySelective" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetails">
+        update oauth_client_details
+        <set>
+            <if test="resourceIds != null">
+                resource_ids = #{resourceIds,jdbcType=VARCHAR},
+            </if>
+            <if test="clientSecret != null">
+                client_secret = #{clientSecret,jdbcType=VARCHAR},
+            </if>
+            <if test="scope != null">
+                `scope` = #{scope,jdbcType=VARCHAR},
+            </if>
+            <if test="authorizedGrantTypes != null">
+                authorized_grant_types = #{authorizedGrantTypes,jdbcType=VARCHAR},
+            </if>
+            <if test="webServerRedirectUri != null">
+                web_server_redirect_uri = #{webServerRedirectUri,jdbcType=VARCHAR},
+            </if>
+            <if test="authorities != null">
+                authorities = #{authorities,jdbcType=VARCHAR},
+            </if>
+            <if test="accessTokenValidity != null">
+                access_token_validity = #{accessTokenValidity,jdbcType=INTEGER},
+            </if>
+            <if test="refreshTokenValidity != null">
+                refresh_token_validity = #{refreshTokenValidity,jdbcType=INTEGER},
+            </if>
+            <if test="additionalInformation != null">
+                additional_information = #{additionalInformation,jdbcType=VARCHAR},
+            </if>
+            <if test="autoapprove != null">
+                autoapprove = #{autoapprove,jdbcType=VARCHAR},
+            </if>
+            <if test="state != null">
+                `state` = #{state,jdbcType=VARCHAR},
+            </if>
+            <if test="tokenUrl != null">
+                token_url = #{tokenUrl,jdbcType=VARCHAR},
+            </if>
+            <if test="userInfoUrl != null">
+                user_info_url = #{userInfoUrl,jdbcType=VARCHAR},
+            </if>
+        </set>
+        where client_id = #{clientId,jdbcType=VARCHAR}
+    </update>
+    <update id="updateByPrimaryKey" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetails">
+        update oauth_client_details
+        set resource_ids            = #{resourceIds,jdbcType=VARCHAR},
+            client_secret           = #{clientSecret,jdbcType=VARCHAR},
+            `scope`                 = #{scope,jdbcType=VARCHAR},
+            authorized_grant_types  = #{authorizedGrantTypes,jdbcType=VARCHAR},
+            web_server_redirect_uri = #{webServerRedirectUri,jdbcType=VARCHAR},
+            authorities             = #{authorities,jdbcType=VARCHAR},
+            access_token_validity   = #{accessTokenValidity,jdbcType=INTEGER},
+            refresh_token_validity  = #{refreshTokenValidity,jdbcType=INTEGER},
+            additional_information  = #{additionalInformation,jdbcType=VARCHAR},
+            autoapprove             = #{autoapprove,jdbcType=VARCHAR},
+            `state`                 = #{state,jdbcType=VARCHAR},
+            token_url               = #{tokenUrl,jdbcType=VARCHAR},
+            user_info_url           = #{userInfoUrl,jdbcType=VARCHAR}
+        where client_id = #{clientId,jdbcType=VARCHAR}
+    </update>
+    <select id="selectByExampleWithRowbounds" parameterType="cn.kdan.pdf.tech.core.model.OauthClientDetailsExample"
+            resultMap="BaseResultMap">
+        select
+        <if test="distinct">
+            distinct
+        </if>
+        <include refid="Base_Column_List"/>
+        from oauth_client_details
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+        <if test="orderByClause != null">
+            order by ${orderByClause}
+        </if>
+    </select>
+</mapper>

+ 305 - 0
pdf-tech-core/src/main/resources/sqlmap/VppMemberMapper.xml

@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.kdan.pdf.tech.core.mapper.VppMemberMapper">
+  <resultMap id="BaseResultMap" type="cn.kdan.pdf.tech.core.model.VppMember">
+    <id column="id" jdbcType="VARCHAR" property="id" />
+    <result column="email" jdbcType="VARCHAR" property="email" />
+    <result column="full_name" jdbcType="VARCHAR" property="fullName" />
+    <result column="subscriber_type" jdbcType="INTEGER" property="subscriberType" />
+    <result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
+    <result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" />
+    <result column="subscribed" jdbcType="INTEGER" property="subscribed" />
+    <result column="digest_password" jdbcType="VARCHAR" property="digestPassword" />
+    <result column="phone" jdbcType="VARCHAR" property="phone" />
+    <result column="valid_flag" jdbcType="VARCHAR" property="validFlag" />
+  </resultMap>
+  <sql id="Example_Where_Clause">
+    <where>
+      <foreach collection="oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Update_By_Example_Where_Clause">
+    <where>
+      <foreach collection="example.oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Base_Column_List">
+    id, email, full_name, subscriber_type, created_at, updated_at, subscribed, digest_password, 
+    phone, valid_flag
+  </sql>
+  <select id="selectByExample" parameterType="cn.kdan.pdf.tech.core.model.VppMemberExample" resultMap="BaseResultMap">
+    select
+    <if test="distinct">
+      distinct
+    </if>
+    <include refid="Base_Column_List" />
+    from vpp_member
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+    <if test="orderByClause != null">
+      order by ${orderByClause}
+    </if>
+  </select>
+  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select 
+    <include refid="Base_Column_List" />
+    from vpp_member
+    where id = #{id,jdbcType=VARCHAR}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
+    delete from vpp_member
+    where id = #{id,jdbcType=VARCHAR}
+  </delete>
+  <delete id="deleteByExample" parameterType="cn.kdan.pdf.tech.core.model.VppMemberExample">
+    delete from vpp_member
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </delete>
+  <insert id="insert" parameterType="cn.kdan.pdf.tech.core.model.VppMember">
+    insert into vpp_member (id, email, full_name, 
+      subscriber_type, created_at, updated_at, 
+      subscribed, digest_password, phone, 
+      valid_flag)
+    values (#{id,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{fullName,jdbcType=VARCHAR}, 
+      #{subscriberType,jdbcType=INTEGER}, #{createdAt,jdbcType=TIMESTAMP}, #{updatedAt,jdbcType=TIMESTAMP}, 
+      #{subscribed,jdbcType=INTEGER}, #{digestPassword,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, 
+      #{validFlag,jdbcType=VARCHAR})
+  </insert>
+  <insert id="insertSelective" parameterType="cn.kdan.pdf.tech.core.model.VppMember">
+    insert into vpp_member
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="email != null">
+        email,
+      </if>
+      <if test="fullName != null">
+        full_name,
+      </if>
+      <if test="subscriberType != null">
+        subscriber_type,
+      </if>
+      <if test="createdAt != null">
+        created_at,
+      </if>
+      <if test="updatedAt != null">
+        updated_at,
+      </if>
+      <if test="subscribed != null">
+        subscribed,
+      </if>
+      <if test="digestPassword != null">
+        digest_password,
+      </if>
+      <if test="phone != null">
+        phone,
+      </if>
+      <if test="validFlag != null">
+        valid_flag,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=VARCHAR},
+      </if>
+      <if test="email != null">
+        #{email,jdbcType=VARCHAR},
+      </if>
+      <if test="fullName != null">
+        #{fullName,jdbcType=VARCHAR},
+      </if>
+      <if test="subscriberType != null">
+        #{subscriberType,jdbcType=INTEGER},
+      </if>
+      <if test="createdAt != null">
+        #{createdAt,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updatedAt != null">
+        #{updatedAt,jdbcType=TIMESTAMP},
+      </if>
+      <if test="subscribed != null">
+        #{subscribed,jdbcType=INTEGER},
+      </if>
+      <if test="digestPassword != null">
+        #{digestPassword,jdbcType=VARCHAR},
+      </if>
+      <if test="phone != null">
+        #{phone,jdbcType=VARCHAR},
+      </if>
+      <if test="validFlag != null">
+        #{validFlag,jdbcType=VARCHAR},
+      </if>
+    </trim>
+  </insert>
+  <select id="countByExample" parameterType="cn.kdan.pdf.tech.core.model.VppMemberExample" resultType="java.lang.Long">
+    select count(*) from vpp_member
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </select>
+  <update id="updateByExampleSelective" parameterType="map">
+    update vpp_member
+    <set>
+      <if test="row.id != null">
+        id = #{row.id,jdbcType=VARCHAR},
+      </if>
+      <if test="row.email != null">
+        email = #{row.email,jdbcType=VARCHAR},
+      </if>
+      <if test="row.fullName != null">
+        full_name = #{row.fullName,jdbcType=VARCHAR},
+      </if>
+      <if test="row.subscriberType != null">
+        subscriber_type = #{row.subscriberType,jdbcType=INTEGER},
+      </if>
+      <if test="row.createdAt != null">
+        created_at = #{row.createdAt,jdbcType=TIMESTAMP},
+      </if>
+      <if test="row.updatedAt != null">
+        updated_at = #{row.updatedAt,jdbcType=TIMESTAMP},
+      </if>
+      <if test="row.subscribed != null">
+        subscribed = #{row.subscribed,jdbcType=INTEGER},
+      </if>
+      <if test="row.digestPassword != null">
+        digest_password = #{row.digestPassword,jdbcType=VARCHAR},
+      </if>
+      <if test="row.phone != null">
+        phone = #{row.phone,jdbcType=VARCHAR},
+      </if>
+      <if test="row.validFlag != null">
+        valid_flag = #{row.validFlag,jdbcType=VARCHAR},
+      </if>
+    </set>
+    <if test="example != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByExample" parameterType="map">
+    update vpp_member
+    set id = #{row.id,jdbcType=VARCHAR},
+      email = #{row.email,jdbcType=VARCHAR},
+      full_name = #{row.fullName,jdbcType=VARCHAR},
+      subscriber_type = #{row.subscriberType,jdbcType=INTEGER},
+      created_at = #{row.createdAt,jdbcType=TIMESTAMP},
+      updated_at = #{row.updatedAt,jdbcType=TIMESTAMP},
+      subscribed = #{row.subscribed,jdbcType=INTEGER},
+      digest_password = #{row.digestPassword,jdbcType=VARCHAR},
+      phone = #{row.phone,jdbcType=VARCHAR},
+      valid_flag = #{row.validFlag,jdbcType=VARCHAR}
+    <if test="example != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByPrimaryKeySelective" parameterType="cn.kdan.pdf.tech.core.model.VppMember">
+    update vpp_member
+    <set>
+      <if test="email != null">
+        email = #{email,jdbcType=VARCHAR},
+      </if>
+      <if test="fullName != null">
+        full_name = #{fullName,jdbcType=VARCHAR},
+      </if>
+      <if test="subscriberType != null">
+        subscriber_type = #{subscriberType,jdbcType=INTEGER},
+      </if>
+      <if test="createdAt != null">
+        created_at = #{createdAt,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updatedAt != null">
+        updated_at = #{updatedAt,jdbcType=TIMESTAMP},
+      </if>
+      <if test="subscribed != null">
+        subscribed = #{subscribed,jdbcType=INTEGER},
+      </if>
+      <if test="digestPassword != null">
+        digest_password = #{digestPassword,jdbcType=VARCHAR},
+      </if>
+      <if test="phone != null">
+        phone = #{phone,jdbcType=VARCHAR},
+      </if>
+      <if test="validFlag != null">
+        valid_flag = #{validFlag,jdbcType=VARCHAR},
+      </if>
+    </set>
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="cn.kdan.pdf.tech.core.model.VppMember">
+    update vpp_member
+    set email = #{email,jdbcType=VARCHAR},
+      full_name = #{fullName,jdbcType=VARCHAR},
+      subscriber_type = #{subscriberType,jdbcType=INTEGER},
+      created_at = #{createdAt,jdbcType=TIMESTAMP},
+      updated_at = #{updatedAt,jdbcType=TIMESTAMP},
+      subscribed = #{subscribed,jdbcType=INTEGER},
+      digest_password = #{digestPassword,jdbcType=VARCHAR},
+      phone = #{phone,jdbcType=VARCHAR},
+      valid_flag = #{validFlag,jdbcType=VARCHAR}
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+  <select id="selectByExampleWithRowbounds" parameterType="cn.kdan.pdf.tech.core.model.VppMemberExample" resultMap="BaseResultMap">
+    select
+    <if test="distinct">
+      distinct
+    </if>
+    <include refid="Base_Column_List" />
+    from vpp_member
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+    <if test="orderByClause != null">
+      order by ${orderByClause}
+    </if>
+  </select>
+</mapper>

+ 332 - 0
pom.xml

@@ -0,0 +1,332 @@
+<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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>backend</artifactId>
+    <packaging>pom</packaging>
+    <groupId>cn.kdan.pdf.tech</groupId>
+    <version>0.0.1</version>
+    <name>pdf-tech</name>
+    <description>项目总模块</description>
+
+    <url>http://maven.apache.org</url>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.2.RELEASE</version>
+    </parent>
+
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <mybatis.generator.configurationFile>${basedir}/src/main/resources/generatorConfig.xml</mybatis.generator.configurationFile>
+        <mybatis.generator.overwrite>true</mybatis.generator.overwrite>
+        <docker.path>src/main/docker</docker.path>
+
+        <docker.version>1.2.2</docker.version>
+        <docker.host>81.68.234.235:12375</docker.host>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>1.18.8</version>
+                <scope>provided</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>Greenwich.RELEASE</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-databind</artifactId>
+                <version>2.11.0</version>
+            </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-annotations</artifactId>
+                <version>2.9.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpcore</artifactId>
+                <version>4.4.9</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpclient</artifactId>
+                <version>4.5.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpmime</artifactId>
+                <version>4.5.5</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.pagehelper</groupId>
+                <artifactId>pagehelper</artifactId>
+                <version>5.1.8</version>
+            </dependency>
+            <dependency>
+                <groupId>org.aspectj</groupId>
+                <artifactId>aspectjweaver</artifactId>
+                <version>1.8.13</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-fileupload</groupId>
+                <artifactId>commons-fileupload</artifactId>
+                <version>1.2.1</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>2.4</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-lang</groupId>
+                <artifactId>commons-lang</artifactId>
+                <version>2.6</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-codec</groupId>
+                <artifactId>commons-codec</artifactId>
+                <version>1.8</version>
+            </dependency>
+
+            <!-- logback  -->
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>1.2.3</version>
+            </dependency>
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-core</artifactId>
+                <version>1.2.3</version>
+            </dependency>
+            <dependency>
+                <groupId>org.logback-extensions</groupId>
+                <artifactId>logback-ext-spring</artifactId>
+                <version>0.1.4</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>1.7.25</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>jcl-over-slf4j</artifactId>
+                <version>1.7.25</version>
+            </dependency>
+            <!-- logback  -->
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid</artifactId>
+                <version>1.1.10</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>1.1.10</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis.spring.boot</groupId>
+                <artifactId>mybatis-spring-boot-starter</artifactId>
+                <version>1.3.1</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.pagehelper</groupId>
+                <artifactId>pagehelper-spring-boot-starter</artifactId>
+                <version>1.2.10</version>
+            </dependency>
+
+
+            <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>8.0.11</version>
+            </dependency>
+            <dependency>
+                <groupId>org.postgresql</groupId>
+                <artifactId>postgresql</artifactId>
+                <version>42.3.3</version>
+            </dependency>
+            <dependency>
+                <groupId>com.google.code.gson</groupId>
+                <artifactId>gson</artifactId>
+                <version>2.7</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>1.2.51</version>
+                <scope>compile</scope>
+            </dependency>
+
+            <!-- poi start -->
+            <dependency>
+                <groupId>org.apache.poi</groupId>
+                <artifactId>poi</artifactId>
+                <version>3.17</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.poi</groupId>
+                <artifactId>poi-ooxml</artifactId>
+                <version>3.17</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.poi</groupId>
+                <artifactId>poi-ooxml-schemas</artifactId>
+                <version>3.17</version>
+            </dependency>
+            <!-- poi end -->
+
+            <dependency>
+                <groupId>dom4j</groupId>
+                <artifactId>dom4j</artifactId>
+                <version>1.6.1</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.validation</groupId>
+                <artifactId>validation-api</artifactId>
+                <version>2.0.1.Final</version>
+            </dependency>
+            <dependency>
+                <groupId>org.hibernate.validator</groupId>
+                <artifactId>hibernate-validator</artifactId>
+                <version>6.0.13.Final</version>
+            </dependency>
+
+            <!-- test start -->
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.12</version>
+                <scope>test</scope>
+            </dependency>
+            <!-- test end -->
+
+            <dependency>
+                <groupId>io.springfox</groupId>
+                <artifactId>springfox-swagger2</artifactId>
+                <version>2.9.2</version>
+            </dependency>
+            <!-- swagger-ui -->
+            <dependency>
+                <groupId>io.springfox</groupId>
+                <artifactId>springfox-swagger-ui</artifactId>
+                <version>2.9.2</version>
+            </dependency>
+            <!-- security start-->
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>20.0</version>
+            </dependency>
+
+
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-security</artifactId>
+                <version>2.3.0.RELEASE</version>
+                <exclusions>
+                    <exclusion>
+                        <artifactId>spring-security-oauth2</artifactId>
+                        <groupId>org.springframework.security.oauth</groupId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.security.oauth</groupId>
+                <artifactId>spring-security-oauth2</artifactId>
+                <version>2.3.8.RELEASE</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.security.oauth.boot</groupId>
+                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
+                <version>2.3.0.RELEASE</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.security</groupId>
+                <artifactId>spring-security-core</artifactId>
+                <version>5.3.3.RELEASE</version>
+            </dependency>
+            <!-- security end -->
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-pool2</artifactId>
+                <version>2.8.0</version>
+            </dependency>
+            <!--email发送-->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-mail</artifactId>
+                <version>2.3.2.RELEASE</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jsoup</groupId>
+                <artifactId>jsoup</artifactId>
+                <version>1.13.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk16</artifactId>
+                <version>1.46</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <modules>
+        <module>pdf-tech-core</module>
+        <module>pdf-tech-common</module>
+    </modules>
+
+    <profiles>
+        <!-- 本地开发环境 -->
+        <profile>
+            <id>local</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <spring.profiles.active>local</spring.profiles.active>
+            </properties>
+        </profile>
+        <!-- 测试环境 -->
+        <profile>
+            <id>test</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <spring.profiles.active>test</spring.profiles.active>
+            </properties>
+        </profile>
+        <!-- 生产环境 -->
+        <profile>
+            <id>pro</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <spring.profiles.active>pro</spring.profiles.active>
+            </properties>
+        </profile>
+    </profiles>
+
+
+
+</project>