guoenzhou 2 years ago
parent
commit
87de673d87
87 changed files with 8603 additions and 174 deletions
  1. 17 8
      base-common/common-cloud-starter/pom.xml
  2. 37 0
      base-common/pom.xml
  3. 2 2
      base-common/ruoyi-common-core/pom.xml
  4. 2 2
      base-common/ruoyi-common-datascope/pom.xml
  5. 2 2
      base-common/ruoyi-common-datasource/pom.xml
  6. 2 2
      base-common/ruoyi-common-log/pom.xml
  7. 2 2
      base-common/ruoyi-common-redis/pom.xml
  8. 2 2
      base-common/ruoyi-common-security/pom.xml
  9. 3 3
      base-common/ruoyi-common-swagger/pom.xml
  10. 59 0
      base-common/usky-common-core/pom.xml
  11. 98 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/bean/ApiResult.java
  12. 27 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/bean/BeanConstraintViolationResult.java
  13. 51 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/bean/CommonPage.java
  14. 25 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/bean/PageRequest.java
  15. 36 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/bean/UnifiedUser.java
  16. 24 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/constants/CommonConst.java
  17. 154 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/constants/SymbolConst.java
  18. 93 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/enums/DatePatternEnum.java
  19. 5 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/enums/IEnum.java
  20. 56 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ApplicationCheckException.java
  21. 59 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ApplicationException.java
  22. 59 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/BusinessCheckException.java
  23. 78 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/BusinessErrorCode.java
  24. 59 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/BusinessException.java
  25. 52 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ErrorCode.java
  26. 24 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ErrorMessageConsts.java
  27. 79 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/exception/SystemErrorCode.java
  28. 217 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/Assert.java
  29. 98 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/BeanMapperUtils.java
  30. 53 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/BeanValidatorUtils.java
  31. 853 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/Convert.java
  32. 722 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/DateUtils.java
  33. 187 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/FileTypeUtils.java
  34. 320 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/FileUtils.java
  35. 341 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/HttpUtils.java
  36. 37 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/JacksonComponent.java
  37. 172 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/JsonMapper.java
  38. 153 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/ShardingTaskExecutorUtils.java
  39. 63 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/StopWatchRecorder.java
  40. 55 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/StringUtils.java
  41. 44 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/URLUtils.java
  42. 84 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/UUIDUtils.java
  43. 28 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/IdWorker.java
  44. 77 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/IdWorkerContainer.java
  45. 15 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/IdWorkerTypeEnum.java
  46. 144 0
      base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/SnowflakeIdWorker.java
  47. 45 0
      base-common/usky-common-mvc/pom.xml
  48. 100 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/AbstractCrudService.java
  49. 26 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/BaseModel.java
  50. 13 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/CrudMapper.java
  51. 47 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/CrudService.java
  52. 52 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/BaseEnumConverterFactory.java
  53. 18 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/DateStringConvert.java
  54. 30 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/FormatterConfiguration.java
  55. 18 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/StringDateConverter.java
  56. 22 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/multipartFile/CommonsMultipartResolverConfiguration.java
  57. 39 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/validator/ValidatorExtensionAutoConfiguration.java
  58. 267 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/handler/UnifiedExceptionHandler.java
  59. 171 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/utils/IpUtils.java
  60. 31 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/utils/JdbcUtils.java
  61. 120 0
      base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/utils/ServletUtils.java
  62. 60 0
      base-common/usky-common-spring/pom.xml
  63. 34 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/annotion/ConditionalOnPropertyNotEmpty.java
  64. 26 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/id/IdWorkerAutoConfiguration.java
  65. 17 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/id/IdWorkerConfigProperties.java
  66. 47 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/jackson/JacksonConfig.java
  67. 45 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/EhCacheCacheConfiguration.java
  68. 119 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisExtensionAutoConfiguration.java
  69. 1802 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisHelper.java
  70. 38 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisObjectMapper.java
  71. 23 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisOverdueTimeProperties.java
  72. 5 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/injectself/BeanSelfAware.java
  73. 25 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/injectself/InjectSelfBeanPostProcessor.java
  74. 45 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/ApplicationStartupUtils.java
  75. 21 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/GlobalUtils.java
  76. 54 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/JsonUtils.java
  77. 83 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/MultipartFileUtil.java
  78. 114 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/SpringContextUtils.java
  79. 39 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/SpringEnvUtils.java
  80. 24 0
      base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/ThreadUtils.java
  81. 5 0
      base-components/gateway/pom.xml
  82. 4 4
      base-components/gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java
  83. 5 0
      base-modules/service-job/pom.xml
  84. 2 1
      base-modules/service-job/src/main/java/com/ruoyi/job/controller/SysJobController.java
  85. 0 10
      base-modules/service-system/service-system-biz/src/main/resources/banner.txt
  86. 62 1
      pom.xml
  87. 135 135
      ruoyi-ui/vue.config.js

+ 17 - 8
base-common/common-cloud-starter/pom.xml

@@ -3,12 +3,12 @@
          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>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-<artifactId>common-cloud-starter</artifactId>
+    <artifactId>common-cloud-starter</artifactId>
 
     <dependencies>
         <!-- SpringCloud Alibaba Nacos -->
@@ -93,11 +93,20 @@
         </dependency>
 
         <!-- RuoYi Common Security-->
-        <dependency>
-            <groupId>com.ruoyi</groupId>
-            <artifactId>ruoyi-common-security</artifactId>
-        </dependency>
-
+<!--        <dependency>-->
+<!--            <groupId>com.ruoyi</groupId>-->
+<!--            <artifactId>ruoyi-common-security</artifactId>-->
+<!--        </dependency>-->
+
+<!--        <dependency>-->
+<!--            <groupId>com.usky</groupId>-->
+<!--            <artifactId>usky-common-core</artifactId>-->
+<!--        </dependency>-->
+
+<!--        <dependency>-->
+<!--            <groupId>com.usky</groupId>-->
+<!--            <artifactId>usky-common-mvc</artifactId>-->
+<!--        </dependency>-->
 
     </dependencies>
 </project>

+ 37 - 0
base-common/pom.xml

@@ -14,6 +14,40 @@
         base-common通用模块
     </description>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
     <modules>
         <module>ruoyi-common-log</module>
         <module>ruoyi-common-core</module>
@@ -23,6 +57,9 @@
         <module>ruoyi-common-datascope</module>
 		<module>ruoyi-common-datasource</module>
         <module>common-cloud-starter</module>
+        <module>usky-common-core</module>
+        <module>usky-common-mvc</module>
+        <module>usky-common-spring</module>
     </modules>
 
 

+ 2 - 2
base-common/ruoyi-common-core/pom.xml

@@ -3,9 +3,9 @@
          xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 2 - 2
base-common/ruoyi-common-datascope/pom.xml

@@ -3,9 +3,9 @@
          xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     

+ 2 - 2
base-common/ruoyi-common-datasource/pom.xml

@@ -3,9 +3,9 @@
          xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     

+ 2 - 2
base-common/ruoyi-common-log/pom.xml

@@ -3,9 +3,9 @@
          xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     

+ 2 - 2
base-common/ruoyi-common-redis/pom.xml

@@ -3,9 +3,9 @@
          xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     

+ 2 - 2
base-common/ruoyi-common-security/pom.xml

@@ -2,9 +2,9 @@
 <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     

+ 3 - 3
base-common/ruoyi-common-swagger/pom.xml

@@ -3,16 +3,16 @@
          xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>com.ruoyi</groupId>
+        <groupId>com.usky</groupId>
         <artifactId>base-common</artifactId>
-        <version>3.5.0</version>
+        <version>0.0.1</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     
     <artifactId>ruoyi-common-swagger</artifactId>
 	
     <description>
-        ruoyi-common-swagger系统接口
+        common-swagger系统接口
     </description>
 
 	<dependencies>

+ 59 - 0
base-common/usky-common-core/pom.xml

@@ -0,0 +1,59 @@
+<?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>
+        <artifactId>base-common</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>usky-common-core</artifactId>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>ma.glasnost.orika</groupId>
+            <artifactId>orika-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+
+
+    </dependencies>
+
+</project>

+ 98 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/bean/ApiResult.java

@@ -0,0 +1,98 @@
+package com.usky.common.core.bean;
+
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.beans.Transient;
+import java.io.Serializable;
+
+/**
+ * <p>Api 调用结果统一包装类</p>
+ *
+ * @param <T>
+ * @author chenpeng
+ * Create time 2018年11月30日 下午5:17:42
+ */
+@Data
+@ApiModel(description = "API 响应结果")
+public class ApiResult<T> implements Serializable {
+
+    private static final long serialVersionUID = 5247512550376272642L;
+
+    private static final String SUCCESS_CODE = "0";
+
+    @ApiModelProperty(value = "状态", required = true)
+    private ResultStatus status;
+
+    @ApiModelProperty(value = "状态码", required = true)
+    private String code;
+
+    @ApiModelProperty(value = "消息")
+    private String msg;
+
+    @ApiModelProperty(value = "内容")
+    private T data;
+
+    @ApiModelProperty(value = "异常类")
+    private String exception;
+
+    public ApiResult() {
+    }
+
+    public ApiResult(T content) {
+        this.status = ResultStatus.SUCCESS;
+        this.code = SUCCESS_CODE;
+        this.data = content;
+    }
+
+    public ApiResult(String code, String message) {
+        this.status = ResultStatus.ERROR;
+        this.code = code;
+        this.msg = message;
+    }
+
+    public static <T> ApiResult<T> success(T content) {
+        return new ApiResult<>(content);
+    }
+
+    public static ApiResult<Void> success() {
+        return new ApiResult<>(null);
+    }
+
+    public static <T> ApiResult<T> error(String code, String message) {
+        return new ApiResult<>(code, message);
+    }
+
+    public static <T> ApiResult<T> error(String code, String exception, String message) {
+        ApiResult<T> apiResult = new ApiResult<>(code, message);
+        apiResult.setException(exception);
+        return apiResult;
+    }
+
+    @Transient
+    public boolean isSuccess() {
+        return ResultStatus.SUCCESS == status;
+    }
+
+    @Transient
+    public boolean isError() {
+        return !isSuccess();
+    }
+
+    /**
+     * 业务响应状态
+     */
+    public enum ResultStatus {
+        /**
+         * 返回状态:成功
+         */
+        SUCCESS,
+        /**
+         * 返回状态:失败
+         */
+        ERROR
+    }
+}

+ 27 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/bean/BeanConstraintViolationResult.java

@@ -0,0 +1,27 @@
+package com.usky.common.core.bean;
+
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * <p>Bean 参数验证结果</p>
+ *
+ * @author chenpeng
+ * Create at March 12, 2019 at 14:34:43 GMT+8
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class BeanConstraintViolationResult implements Serializable {
+
+    private static final long serialVersionUID = -9026735732302420866L;
+
+    private boolean valid;
+    private List<String> errors;
+}

+ 51 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/bean/CommonPage.java

@@ -0,0 +1,51 @@
+package com.usky.common.core.bean;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 分页类. 用于分页接口响应
+ * 结构从IPage复制而来.
+ *
+ * @param <T>
+ */
+@Data
+public class CommonPage<T> {
+    private static final long serialVersionUID = 8545996863226528798L;
+    protected List<T> records;
+    protected long total;
+    protected long size;
+    protected long current;
+
+    public CommonPage() {
+
+    }
+
+    public CommonPage(List<T> records, long total, long size, long current) {
+        this.records = records;
+        this.total = total;
+        this.size = size;
+        this.current = current;
+    }
+
+    /**
+     * 计算虚假total.
+     * 该total不是真实的数量, 只能给出是否还有下一页.
+     *
+     * @param current
+     * @param size
+     * @param currentSize
+     * @return
+     */
+    public static int fakeTotal(long current, long size, long currentSize) {
+        int total = 0;
+        if (currentSize >= size) {
+            total += current * size + 1;
+        } else {
+            total += (current - 1) * size + currentSize;
+        }
+        return total;
+    }
+
+}

+ 25 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/bean/PageRequest.java

@@ -0,0 +1,25 @@
+package com.usky.common.core.bean;
+
+import lombok.Data;
+
+/**
+ * @author yangqiang
+ * @date 2020-10-13
+ */
+@Data
+public class PageRequest {
+
+    private Integer current;
+
+    private Integer size;
+
+    public PageRequest() {
+
+    }
+
+    public PageRequest(Integer current, Integer size) {
+        this.current = current;
+        this.size = size;
+    }
+
+}

+ 36 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/bean/UnifiedUser.java

@@ -0,0 +1,36 @@
+package com.usky.common.core.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * <p>系统统一用户</p>
+ *
+ * @author chenpeng
+ * Create at January 2, 2019 at 19:34:29 GMT+8
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public final class UnifiedUser implements Serializable {
+
+    private static final long serialVersionUID = -2736561754154136210L;
+
+    @NotNull(message = "operator 的 id 不能为 null")
+    @ApiModelProperty(value = "id 主键", required = true)
+    private String id;
+
+    @NotBlank(message = "operator 的 name 不能为空")
+    @ApiModelProperty(value = "名字", required = true)
+    private String name;
+
+    public static UnifiedUser create(String id, String name) {
+        return new UnifiedUser(id, name);
+    }
+}

+ 24 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/constants/CommonConst.java

@@ -0,0 +1,24 @@
+package com.usky.common.core.constants;
+
+/**
+ * @author barry chen
+ */
+public final class CommonConst {
+    public static final Integer TRUE_NUM = 1;
+    public static final Integer FALSE_NUM = 0;
+    public static final String TRUE_NUM_STR = "1";
+    public static final String FALSE_NUM_STR = "0";
+    public static final String TURE_STRING = "true";
+    public static final String FALSE_STRING = "false";
+    public static final String YES_STR_CODE = "Y";
+    public static final String NO_STR_CODE = "N";
+    public static final String YES_STR = "Y";
+    public static final String NO_STR = "N";
+    public static final String HTTP = "http";
+    public static final String HTTPS = "http";
+    public static final String HTTP_PREFIX = "http://";
+    public static final String HTTPS_PREFIX = "https://";
+    private CommonConst() {
+    }
+
+}

+ 154 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/constants/SymbolConst.java

@@ -0,0 +1,154 @@
+package com.usky.common.core.constants;
+
+/**
+ * @author barry chen
+ * @date 2021/1/19 2:53 下午
+ */
+public final class SymbolConst {
+    /**
+     * "."
+     */
+    public static final char DOT_CHAR = '.';
+    public static final String DTO = DOT_CHAR + "";
+    /**
+     * 逗号","
+     */
+    public static final char COMMA_CHAR = ',';
+    public static final String COMMA = COMMA_CHAR + "";
+    /**
+     * 分号 ";"
+     */
+    public static final char SEMICOLON_CHAR = ';';
+    public static final String SEMICOLON = SEMICOLON_CHAR + "";
+    /**
+     * 冒号 ":"
+     */
+    public static final char COLON_CHAR = ':';
+    public static final String COLON = COLON_CHAR + "";
+    /**
+     * 感叹号 "!"
+     */
+    public static final char EXCLAMATION_CHAR = '!';
+    public static final String EXCLAMATION = EXCLAMATION_CHAR + "";
+    /**
+     * "$"
+     */
+    public static final char DOLLAR_CHAR = '$';
+    public static final String DOLLAR = DOLLAR_CHAR + "";
+    /**
+     * "%"
+     */
+    public static final char PERCENT_CHAR = '%';
+    public static final String PERCENT = PERCENT_CHAR + "";
+    /**
+     * "&"
+     */
+    public static final char AND_CHAR = '&';
+    public static final String AND = AND_CHAR + "";
+    /**
+     * "|"
+     */
+    public static final char OR_CHAR = '|';
+    public static final String OR = OR_CHAR + "";
+    /**
+     * "*"
+     */
+    public static final char STAR_CHAR = '*';
+    public static final String STAR = STAR_CHAR + "";
+    /**
+     * "_"
+     */
+    public static final char UNDERLINE_CHAR = '_';
+    public static final String UNDERLINE = UNDERLINE_CHAR + "";
+    /**
+     * "-"
+     */
+    public static final char EN_DASH_CHAR = '-';
+    public static final String EN_DASH = EN_DASH_CHAR + "";
+    /**
+     * "+"
+     */
+    public static final char PLUS_CHAR = '+';
+    public static final String PLUS = PLUS_CHAR + "";
+    /**
+     * "="
+     */
+    public static final char EQUAL_CHAR = '=';
+    public static final String EQUAL = EQUAL_CHAR + "";
+    /**
+     * "("
+     */
+    public static final char LEFT_ROUND_BRACKET_CHAR = '(';
+    public static final String LEFT_ROUND_BRACKET = LEFT_ROUND_BRACKET_CHAR + "";
+    /**
+     * ")"
+     */
+    public static final char RIGHT_ROUND_BRACKET_CHAR = ')';
+    public static final String RIGHT_ROUND_BRACKET = RIGHT_ROUND_BRACKET_CHAR + "";
+    /**
+     * "["
+     */
+    public static final char LEFT_SQUARE_BRACKET_CHAR = '[';
+    public static final String LEFT_SQUARE_BRACKET = LEFT_SQUARE_BRACKET_CHAR + "";
+    /**
+     * "]"
+     */
+    public static final char RIGHT_SQUARE_BRACKET_CHAR = ']';
+    public static final String RIGHT_SQUARE_BRACKET = RIGHT_SQUARE_BRACKET_CHAR + "";
+    /**
+     * "{"
+     */
+    public static final char LEFT_BRACE_CHAR = '{';
+    public static final String LEFT_BRACE = LEFT_BRACE_CHAR + "";
+    /**
+     * "}"
+     */
+    public static final char RIGHT_BRACE_CHAR = '}';
+    public static final String RIGHT_BRACE = RIGHT_BRACE_CHAR + "";
+    /**
+     * 双引号
+     */
+    public static final char DOUBLE_QUOTE_CHAR = '"';
+    public static final String DOUBLE_QUOTE = DOUBLE_QUOTE_CHAR + "";
+    /**
+     * 单引号
+     */
+    public static final char QUOTE_CHAR = '\'';
+    public static final String QUOTE = QUOTE_CHAR + "";
+    /**
+     * "\"
+     */
+    public static final char BACK_SLASH_CHAR = '\\';
+    public static final String BACK_SLASH = BACK_SLASH_CHAR + "";
+    /**
+     * pound '#'
+     */
+    static final char POUND_CHAR = '#';
+    static final String POUND = POUND_CHAR + "";
+    /**
+     * '@'
+     */
+    static final char AT_CHAR = '@';
+    static final String AT = AT_CHAR + "";
+    /**
+     * CR
+     */
+    static final char CR_CHAR = '\r';
+    static final String CR = CR_CHAR + "";
+    /**
+     * RFC 4180 defines line breaks as CRLF
+     */
+    static final String CRLF = "\r\n";
+    /**
+     * The end of stream symbol
+     */
+    static final int END_OF_STREAM = -1;
+    static final char FF = '\f';
+    static final char LF = '\n';
+    static final char TAB = '\t';
+    static final char SP = ' ';
+
+    private SymbolConst() {
+    }
+
+}

+ 93 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/enums/DatePatternEnum.java

@@ -0,0 +1,93 @@
+package com.usky.common.core.enums;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+public enum DatePatternEnum {
+
+    DATE_TIME_MS_PATTERN(0, "yyyy-MM-dd HH:mm:ss.SSS", "年-月-日 时:分:秒.毫秒"),
+
+    DATE_TIME_PATTERN(1, "yyyy-MM-dd HH:mm:ss", "年-月-日 时:分:秒"),
+
+    TIME_PATTERN(2, "HH:mm:ss", "时:分:秒"),
+
+    MINUTE_PATTERN(3, "yyyy-MM-dd HH:mm", "年-月-日 时:分"),
+
+
+    DATE_PATTERN(4, "yyyy-MM-dd", "年-月-日"),
+
+    MONTH_PATTERN(5, "yyyy-MM", "年-月"),
+
+    ONLY_YEAR_PATTERN(6, "yyyy", "年"),
+
+    ONLY_MONTH_PATTERN(7, "MM", "月"),
+
+    ONLY_DAY_PATTERN(8, "dd", "日"),
+
+    ONLY_HOUR_PATTERN(9, "HH", "时"),
+
+    ONLY_MINUTE_PATTERN(10, "mm", "分"),
+
+    ONLY_SECOND_PATTERN(11, "ss", "秒"),
+
+    ZN_DATE_TIME_MS_PATTERN(12, "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒", "中文格式年月日时分秒毫秒"),
+
+    ZN_DATE_TIME_PATTERN(13, "yyyy年MM月dd日 HH时mm分ss秒", "中文格式年月日时分秒"),
+
+    ZN_DATE_PATTERN(14, "yyyy年MM月dd日", "中文格式年月日"),
+
+    ZN_MONTH_PATTERN(15, "yyyy年MM月", "中文格式年月"),
+
+    ZN_YEAR_ONLY_PATTERN(16, "yyyy年", "中文格式年"),
+
+    ZN_TIME_PATTERN(17, "HH时mm分ss秒", "中文格式时分秒"),
+
+    GAP_LESS_DATE_TIME_PATTERN(18, "yyyyMMddHHmmss", "无间隔符的年月日时分秒"),
+
+    GAP_LESS_DATE_TIME_MS_PATTERN(19, "yyyyMMddHHmmssSSS", "无间隔符的年月日时分秒毫秒"),
+
+    GAP_LESS_DATE_PATTERN(20, "yyyyMMdd", "无间隔符的年月日");
+
+    private static final Map<DatePatternEnum, DateTimeFormatter> formatterCache = new WeakHashMap<>(initialCapacity());
+    private int index;
+    private String pattern;
+    private String desc;
+
+    DatePatternEnum(int index, String pattern, String desc) {
+        this.index = index;
+        this.pattern = pattern;
+        this.desc = desc;
+    }
+
+    private static int initialCapacity() {
+        return (values().length & 1) == 1 ? values().length + 1 : values().length;
+    }
+
+    private static void checkCache() {
+        if (formatterCache.isEmpty() || formatterCache.size() != values().length) {
+            formatterCache.clear();
+            for (DatePatternEnum datePatternEnum : values()) {
+                formatterCache.put(datePatternEnum, DateTimeFormatter.ofPattern(datePatternEnum.getPattern()));
+            }
+        }
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public DateTimeFormatter getFormatter() {
+        checkCache();
+        return formatterCache.getOrDefault(this, DateTimeFormatter.ofPattern(getPattern()));
+    }
+
+}

+ 5 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/enums/IEnum.java

@@ -0,0 +1,5 @@
+package com.usky.common.core.enums;
+
+public interface IEnum {
+
+}

+ 56 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ApplicationCheckException.java

@@ -0,0 +1,56 @@
+package com.usky.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * <p>系统异常(check exception)</p>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午2:16:37
+ * @see ErrorCode
+ */
+@Getter
+public class ApplicationCheckException extends Exception {
+
+    private static final long serialVersionUID = 5852486390437353773L;
+
+    private ErrorCode errorCode;
+    private String customMessage;
+
+    public ApplicationCheckException(String customMessage) {
+        this(SystemErrorCode.SYS_SYSTEM_ERROR, customMessage);
+    }
+
+    public ApplicationCheckException(String customMessage, Throwable cause) {
+        this(SystemErrorCode.SYS_SYSTEM_ERROR, cause, customMessage);
+    }
+
+    public ApplicationCheckException(ErrorCode errorCode) {
+        this(errorCode, null, null);
+    }
+
+    public ApplicationCheckException(ErrorCode errorCode, String customMessage) {
+        this(errorCode, null, customMessage);
+    }
+
+    public ApplicationCheckException(ErrorCode errorCode, Throwable cause) {
+        this(errorCode, cause, null);
+    }
+
+    public ApplicationCheckException(ErrorCode errorCode, Throwable cause, String customMessage) {
+        super(cause);
+        this.errorCode = errorCode;
+        this.customMessage = StringUtils.isBlank(customMessage)
+                ? ErrorMessageConsts.EXCEPTION_DEFAULT_CUSTOM_MESSAGE : customMessage;
+    }
+
+    @Override
+    public String getMessage() {
+        Throwable cause = getCause();
+        return String.format(ErrorMessageConsts.SYS_EXCEPTION_MESSAGE_TEMPLATE,
+                errorCode.getCode(),
+                errorCode.getDefaultMessage(),
+                cause != null ? cause.getMessage() : customMessage);
+    }
+}

+ 59 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ApplicationException.java

@@ -0,0 +1,59 @@
+package com.usky.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import static com.usky.common.core.exception.ErrorMessageConsts.EXCEPTION_DEFAULT_CUSTOM_MESSAGE;
+import static com.usky.common.core.exception.ErrorMessageConsts.SYS_EXCEPTION_MESSAGE_TEMPLATE;
+
+/**
+ * <p>系统异常</p>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午2:16:37
+ * @see ErrorCode
+ */
+@Getter
+public class ApplicationException extends RuntimeException {
+
+    private static final long serialVersionUID = -5548103768989678861L;
+
+    private ErrorCode errorCode;
+    private String customMessage;
+
+    public ApplicationException(String customMessage) {
+        this(SystemErrorCode.SYS_SYSTEM_ERROR, customMessage);
+    }
+
+    public ApplicationException(String customMessage, Throwable cause) {
+        this(SystemErrorCode.SYS_SYSTEM_ERROR, cause, customMessage);
+    }
+
+    public ApplicationException(ErrorCode errorCode) {
+        this(errorCode, null, null);
+    }
+
+    public ApplicationException(ErrorCode errorCode, String customMessage) {
+        this(errorCode, null, customMessage);
+    }
+
+    public ApplicationException(ErrorCode errorCode, Throwable cause) {
+        this(errorCode, cause, null);
+    }
+
+    public ApplicationException(ErrorCode errorCode, Throwable cause, String customMessage) {
+        super(cause);
+        this.errorCode = errorCode;
+        this.customMessage = StringUtils.isBlank(customMessage)
+                ? EXCEPTION_DEFAULT_CUSTOM_MESSAGE : customMessage;
+    }
+
+    @Override
+    public String getMessage() {
+        Throwable cause = getCause();
+        return String.format(SYS_EXCEPTION_MESSAGE_TEMPLATE,
+                errorCode.getCode(),
+                errorCode.getDefaultMessage(),
+                cause != null ? cause.getMessage() : customMessage);
+    }
+}

+ 59 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/BusinessCheckException.java

@@ -0,0 +1,59 @@
+package com.usky.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import static com.usky.common.core.exception.ErrorMessageConsts.BIZ_EXCEPTION_MESSAGE_TEMPLATE;
+import static com.usky.common.core.exception.ErrorMessageConsts.EXCEPTION_DEFAULT_CUSTOM_MESSAGE;
+
+/**
+ * <p>业务逻辑异常(check exception)</p>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午2:16:37
+ * @see ErrorCode
+ */
+@Getter
+public class BusinessCheckException extends Exception {
+
+    private static final long serialVersionUID = 1816635195935177106L;
+
+    private ErrorCode errorCode;
+    private String customMessage;
+
+    public BusinessCheckException(String customMessage) {
+        this(BusinessErrorCode.BIZ_BUSINESS_ERROR, customMessage);
+    }
+
+    public BusinessCheckException(String customMessage, Throwable cause) {
+        this(BusinessErrorCode.BIZ_BUSINESS_ERROR, cause, customMessage);
+    }
+
+    public BusinessCheckException(ErrorCode errorCode) {
+        this(errorCode, null, null);
+    }
+
+    public BusinessCheckException(ErrorCode errorCode, Throwable cause) {
+        this(errorCode, cause, null);
+    }
+
+    public BusinessCheckException(ErrorCode errorCode, String customMessage) {
+        this(errorCode, null, customMessage);
+    }
+
+    public BusinessCheckException(ErrorCode errorCode, Throwable cause, String customMessage) {
+        super(cause);
+        this.errorCode = errorCode;
+        this.customMessage = StringUtils.isBlank(customMessage)
+                ? EXCEPTION_DEFAULT_CUSTOM_MESSAGE : customMessage;
+    }
+
+    @Override
+    public String getMessage() {
+        Throwable cause = getCause();
+        return String.format(BIZ_EXCEPTION_MESSAGE_TEMPLATE,
+                errorCode.getCode(),
+                errorCode.getDefaultMessage(),
+                cause != null ? cause.getMessage() : customMessage);
+    }
+}

+ 78 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/BusinessErrorCode.java

@@ -0,0 +1,78 @@
+package com.usky.common.core.exception;
+
+import java.util.stream.Stream;
+
+import static com.usky.common.core.exception.ErrorMessageConsts.BIZ_ERROR_CODE_PREFIX;
+
+
+/**
+ * <p>业务错误码定义</p>
+ * <ul>
+ * <li>该类仅包含预定义业务错误码,自定义错误不允许添加到该类中</li>
+ * <li>若需要自定,请自行在所属模块新建 XXXErrorCode.java 类使用</li>
+ * </ul>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午1:10:10
+ * @see ErrorCode
+ */
+public enum BusinessErrorCode implements ErrorCode {
+
+    /**
+     * 操作错误
+     */
+    BIZ_BUSINESS_ERROR(BIZ_ERROR_CODE_PREFIX + "0000", "business error."),
+    /**
+     * 无效的参数
+     */
+    BIZ_INVALID_PARAM_ERROR(BIZ_ERROR_CODE_PREFIX + "0001", "invalid param"),
+    /**
+     * 必要参数缺失
+     */
+    BIZ_LACK_NECESSARY_PARAM_ERROR(BIZ_ERROR_CODE_PREFIX + "0002", "lack necessary param"),
+    /**
+     * 重复的 key
+     */
+    BIZ_DUPLICATE_KEY_ERROR(BIZ_ERROR_CODE_PREFIX + "0003", "duplicate key"),
+
+    /**
+     * API 路径找不到
+     */
+    BIZ_PATH_NOT_FOUND(BIZ_ERROR_CODE_PREFIX + "004", "can't found path"),
+    /**
+     * 数据修改失败
+     */
+    BIZ_MODIFY_FAIL(BIZ_ERROR_CODE_PREFIX + "004", "modify fail");
+
+
+    private String code;
+    private String defaultMessage;
+
+    BusinessErrorCode(String code, String defaultMessage) {
+        this.code = code;
+        this.defaultMessage = defaultMessage;
+    }
+
+    /**
+     * <p>通过 code 获取对应 BusinessErrorCode 实例</p>
+     *
+     * @author chenpeng
+     * Create at May 29, 2019 at 14:30:37 GMT+8
+     */
+    public static BusinessErrorCode getByCode(String code) {
+        return Stream.of(values())
+                .filter(e -> e.getCode().equals(code))
+                .findFirst()
+                .orElse(null);
+    }
+
+    @Override
+    public String getCode() {
+        return code;
+    }
+
+    @Override
+    public String getDefaultMessage() {
+        return defaultMessage;
+    }
+}

+ 59 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/BusinessException.java

@@ -0,0 +1,59 @@
+package com.usky.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import static com.usky.common.core.exception.ErrorMessageConsts.BIZ_EXCEPTION_MESSAGE_TEMPLATE;
+import static com.usky.common.core.exception.ErrorMessageConsts.EXCEPTION_DEFAULT_CUSTOM_MESSAGE;
+
+/**
+ * <p>业务逻辑异常</p>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午2:16:37
+ * @see ErrorCode
+ */
+@Getter
+public class BusinessException extends RuntimeException {
+
+    private static final long serialVersionUID = -5548103768989678861L;
+
+    private ErrorCode errorCode;
+    private String customMessage;
+
+    public BusinessException(String customMessage) {
+        this(BusinessErrorCode.BIZ_BUSINESS_ERROR, customMessage);
+    }
+
+    public BusinessException(String customMessage, Throwable cause) {
+        this(BusinessErrorCode.BIZ_BUSINESS_ERROR, cause, customMessage);
+    }
+
+    public BusinessException(ErrorCode errorCode) {
+        this(errorCode, null, null);
+    }
+
+    public BusinessException(ErrorCode errorCode, Throwable cause) {
+        this(errorCode, cause, null);
+    }
+
+    public BusinessException(ErrorCode errorCode, String customMessage) {
+        this(errorCode, null, customMessage);
+    }
+
+    public BusinessException(ErrorCode errorCode, Throwable cause, String customMessage) {
+        super(cause);
+        this.errorCode = errorCode;
+        this.customMessage = StringUtils.isBlank(customMessage)
+                ? EXCEPTION_DEFAULT_CUSTOM_MESSAGE : customMessage;
+    }
+
+    @Override
+    public String getMessage() {
+        Throwable cause = getCause();
+        return String.format(BIZ_EXCEPTION_MESSAGE_TEMPLATE,
+                errorCode.getCode(),
+                errorCode.getDefaultMessage(),
+                cause != null ? cause.getMessage() : customMessage);
+    }
+}

+ 52 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ErrorCode.java

@@ -0,0 +1,52 @@
+package com.usky.common.core.exception;
+
+/**
+ * <p>错误码接口</p>
+ * 自定错误码类/枚举应该实现该接口,以达成统一约定:
+ * <ul>
+ *     <li>{@link ErrorCode#getCode()} 获取错误码</li>
+ *     <li>{@link ErrorCode#getDefaultMessage()} 获取默认错误消息</li>
+ * </ul>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午1:57:41
+ * @see SystemErrorCode
+ */
+public interface ErrorCode {
+
+    /**
+     * <p>根据 code 和 默认 message 创建一个 ErrorCode 实例(运行时)</p>
+     *
+     * @author chenpeng
+     * Create at May 29, 2019 at 14:32:24 GMT+8
+     */
+    static ErrorCode create(String code, String message) {
+        return new ErrorCode() {
+            @Override
+            public String getCode() {
+                return code;
+            }
+
+            @Override
+            public String getDefaultMessage() {
+                return message;
+            }
+        };
+    }
+
+    /**
+     * <p>获取错误码</p>
+     *
+     * @author chenpeng
+     * Create at May 29, 2019 at 14:31:38 GMT+8
+     */
+    String getCode();
+
+    /**
+     * <p>获取默认消息</p>
+     *
+     * @author chenpeng
+     * Create at May 29, 2019 at 14:31:53 GMT+8
+     */
+    String getDefaultMessage();
+}

+ 24 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/ErrorMessageConsts.java

@@ -0,0 +1,24 @@
+package com.usky.common.core.exception;
+
+/**
+ * <p>错误消息常量</p>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午4:48:10
+ */
+public final class ErrorMessageConsts {
+
+    public static final String EXCEPTION_DEFAULT_CUSTOM_MESSAGE = "";
+
+    public static final String SYS_ERROR_CODE_PREFIX = "SYS-";
+    public static final String BIZ_ERROR_CODE_PREFIX = "BIZ-";
+
+    public static final String BIZ_EXCEPTION_MESSAGE_TEMPLATE = "BusinessException{errorCode:%s, " + "defaultMessage:%s, detailMessage: %s}";
+    public static final String SYS_EXCEPTION_MESSAGE_TEMPLATE = "ApplicationException{errorCode:%s, " + "defaultMessage:%s, detailMessage: %s}";
+
+    public static final String BIZ_ERROR_LOG_MESSAGE_TEMPLATE = "%s --- BIZ >> business error, ERROR:%s";
+    public static final String SYS_ERROR_LOG_MESSAGE_TEMPLATE = "%s --- SYS >> sys error,ERROR:%s";
+
+    private ErrorMessageConsts() {
+    }
+}

+ 79 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/exception/SystemErrorCode.java

@@ -0,0 +1,79 @@
+package com.usky.common.core.exception;
+
+import java.util.stream.Stream;
+
+import static com.usky.common.core.exception.ErrorMessageConsts.SYS_ERROR_CODE_PREFIX;
+
+/**
+ * <p>系统错误码定义</p>
+ * <ul>
+ * <li>该类仅包含预定义系统错误码,自定义错误不允许添加到该类中</li>
+ * <li>若需要自定,请自行在所属模块新建 XXXErrorCode.java 类使用</li>
+ * </ul>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午1:10:10
+ * @see ErrorCode
+ */
+public enum SystemErrorCode implements ErrorCode {
+
+    /**
+     * 系统内部错误
+     */
+    SYS_SYSTEM_ERROR(SYS_ERROR_CODE_PREFIX + "0000", "Internal system error"),
+    /**
+     * 系统堆内存不足
+     */
+    SYS_SYSTEM_OOM_ERROR(SYS_ERROR_CODE_PREFIX + "0001", "Insufficient heap memory"),
+    /**
+     * 系统栈溢出
+     */
+    SYS_SYSTEM_SOF_ERROR(SYS_ERROR_CODE_PREFIX + "0002", "stack overflow"),
+    /*
+     * Feign 调用结果解码错误
+     */
+    SYS_SYSTEM_FEIGN_RESULT_DECODE_ERROR(SYS_ERROR_CODE_PREFIX + "0003", "Feign decode result error"),
+    /**
+     * package 路径查找错误
+     */
+    SYS_SYSTEM_PKG_RESOLVE_ERROR(SYS_ERROR_CODE_PREFIX + "0004", "package path can't be found"),
+    /**
+     * MQ 消息发送失败
+     */
+    SYS_SYSTEM_MQ_SEND_ERROR(SYS_ERROR_CODE_PREFIX + "0005", "MQ send message fail"),
+    /**
+     * MQ 消费失败
+     */
+    SYS_SYSTEM_MQ_CONSUME_ERROR(SYS_ERROR_CODE_PREFIX + "0006", "MQ consume message fail");
+
+    private String code;
+    private String defaultMessage;
+
+    SystemErrorCode(String code, String defaultMessage) {
+        this.code = code;
+        this.defaultMessage = defaultMessage;
+    }
+
+    /**
+     * <p>通过 code 获得对应 SystemErrorCode 实例</p>
+     *
+     * @author chenpeng
+     * Create at May 29, 2019 at 14:33:32 GMT+8
+     */
+    public static SystemErrorCode getByCode(String code) {
+        return Stream.of(values())
+                .filter(e -> e.getCode().equals(code))
+                .findFirst()
+                .orElse(null);
+    }
+
+    @Override
+    public String getCode() {
+        return code;
+    }
+
+    @Override
+    public String getDefaultMessage() {
+        return defaultMessage;
+    }
+}

+ 217 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/Assert.java

@@ -0,0 +1,217 @@
+package com.usky.common.core.util;
+
+import com.usky.common.core.exception.BusinessException;
+import com.usky.common.core.exception.ErrorCode;
+import org.apache.commons.collections.CollectionUtils;
+
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * check相关方法, 用于判断,并抛出异常.
+ * default相关方法, 类似三元表达式
+ * apply相关方法, 用于判断, 并执行.
+ *
+ * @author barry chen
+ * @date 2021/1/20 7:34 下午
+ */
+public final class Assert {
+    private Assert() {
+    }
+
+    /**
+     * 如果表达式false, 则抛出异常.
+     *
+     * @param expression
+     * @param message
+     */
+    public static void check(boolean expression, String message) {
+        if (!expression) {
+            throw new BusinessException(message);
+        }
+    }
+
+    /**
+     * 如果表达式false, 则抛出异常.
+     *
+     * @param expression
+     * @param errorCode
+     * @param message
+     */
+    public static void check(boolean expression, ErrorCode errorCode, String message) {
+        if (!expression) {
+            throw new BusinessException(errorCode, message);
+        }
+    }
+
+    /**
+     * 如果表达式false, 则抛出异常.
+     *
+     * @param expression
+     * @param exceptionSupplier
+     */
+    public static <X extends Throwable> void check(boolean expression, Supplier<? extends X> exceptionSupplier) throws X {
+        if (!expression) {
+            throw exceptionSupplier.get();
+        }
+    }
+
+    /**
+     * 判断字符串是否blank, 如果blank, 抛出异常
+     *
+     * @param str
+     * @param message
+     */
+    public static void checkNotBlank(String str, String message) {
+        if (StringUtils.isBlank(str)) {
+            throw new BusinessException(message);
+        }
+    }
+
+    /**
+     * 判断字符串是否blank, 如果blank, 抛出异常
+     *
+     * @param str
+     * @param errorCode
+     * @param message
+     */
+    public static void checkNotBlank(String str, ErrorCode errorCode, String message) {
+        if (StringUtils.isBlank(str)) {
+            throw new BusinessException(errorCode, message);
+        }
+    }
+
+    /**
+     * 判断字符串是否blank, 如果blank, 抛出异常
+     *
+     * @param str
+     * @param exceptionSupplier
+     */
+    public static <X extends Throwable> void checkNotBlank(String str, Supplier<? extends X> exceptionSupplier) throws X {
+        if (StringUtils.isBlank(str)) {
+            throw exceptionSupplier.get();
+        }
+    }
+
+    /**
+     * 判断集合是否为空, 如果空, 抛出异常
+     *
+     * @param collection
+     * @param message
+     */
+    public static void checkNotEmpty(Collection<?> collection, String message) {
+        if (CollectionUtils.isEmpty(collection)) {
+            throw new BusinessException(message);
+        }
+    }
+
+    /**
+     * 判断集合是否为空, 如果空, 抛出异常
+     *
+     * @param collection
+     * @param errorCode
+     * @param message
+     */
+    public static void checkNotEmpty(Collection<?> collection, ErrorCode errorCode, String message) {
+        if (CollectionUtils.isEmpty(collection)) {
+            throw new BusinessException(errorCode, message);
+        }
+    }
+
+    /**
+     * 判断集合是否为空, 如果空, 抛出异常
+     *
+     * @param collection
+     * @param exceptionSupplier
+     */
+    public static <X extends Throwable> void checkNotEmpty(Collection<?> collection, Supplier<? extends X> exceptionSupplier) throws X {
+        if (CollectionUtils.isEmpty(collection)) {
+            throw exceptionSupplier.get();
+        }
+    }
+
+    /**
+     * 如果assertFunction执行结果为true, 则执行runnable
+     *
+     * @param t
+     * @param assertFunction
+     * @param runnable
+     * @param <T>
+     */
+    public static <T> void apply(T t, Function<T, Boolean> assertFunction, Runnable runnable) {
+        apply(assertFunction.apply(t), runnable::run);
+    }
+
+    public static void apply(boolean expression, Runnable runnable) {
+        if (expression) {
+            runnable.run();
+        }
+    }
+
+    /**
+     * 如果assertFunction执行结果为true, 则执行consumer
+     *
+     * @param t
+     * @param assertFunction
+     * @param consumer
+     * @param <T>
+     */
+    public static <T> void apply(T t, Function<T, Boolean> assertFunction, Consumer<T> consumer) {
+        if (assertFunction.apply(t)) {
+            consumer.accept(t);
+        }
+    }
+
+    /**
+     * 如果assertFunction执行结果为true, 则执行trueFunction, 否则执行falseFunction
+     *
+     * @param t
+     * @param assertFunction
+     * @param trueFunction
+     * @param falseFunction
+     * @param <T>
+     * @param <R>
+     * @return
+     */
+    public static <T, R> R apply(T t, Function<T, Boolean> assertFunction, Function<T, R> trueFunction, Function<T, R> falseFunction) {
+        if (assertFunction.apply(t)) {
+            return trueFunction.apply(t);
+        } else {
+            return falseFunction.apply(t);
+        }
+    }
+
+    public static <T> T defaultIfNull(T t, T defaultT) {
+        return defaultIfTrue(t, Objects::isNull, defaultT);
+    }
+
+    /**
+     * 如果assertFunction执行结果为True, 则返回defaultT
+     *
+     * @param t
+     * @param assertFunction
+     * @param defaultT
+     * @param <T>
+     * @return
+     */
+    public static <T> T defaultIfTrue(T t, Function<T, Boolean> assertFunction, T defaultT) {
+        if (assertFunction.apply(t)) {
+            return defaultT;
+        } else {
+            return t;
+        }
+    }
+
+    public static String defaultIfBlank(String str, String defaultStr) {
+        return defaultIfTrue(str, StringUtils::isBlank, defaultStr);
+    }
+
+    public static Collection<?> defaultIfEmpty(Collection<?> collection, Collection<?> defaultCollection) {
+        return defaultIfTrue(collection, CollectionUtils::isEmpty, defaultCollection);
+    }
+
+}

+ 98 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/BeanMapperUtils.java

@@ -0,0 +1,98 @@
+package com.usky.common.core.util;
+
+
+
+import ma.glasnost.orika.MapperFacade;
+import ma.glasnost.orika.MapperFactory;
+import ma.glasnost.orika.impl.DefaultMapperFactory;
+import ma.glasnost.orika.metadata.Type;
+import ma.glasnost.orika.metadata.TypeFactory;
+
+import java.util.List;
+
+/**
+ * <p>Bean copy 工具类</p>
+ *
+ * @author chenpeng
+ * Create time 2018年12月3日 下午2:49:47
+ */
+public final class BeanMapperUtils {
+
+    private static final MapperFactory MAP_NONE_NULL_FIELDS_MAPPER_FACTORY = new DefaultMapperFactory.Builder().mapNulls(false).build();
+    private static final MapperFactory MAP_NULL_FIELDS_MAPPER_FACTORY = new DefaultMapperFactory.Builder().mapNulls(true).build();
+
+    private BeanMapperUtils() {
+    }
+
+    /**
+     * <p><简单的复制出新类型对象</p>
+     * <p>
+     * 通过source.getClass() 获得源Class
+     */
+    public static <S, D> D map(S source, Class<D> destinationClass) {
+        return buildClassMapperFacade(false).map(source, destinationClass);
+    }
+
+    /**
+     * <p>极致性能的复制出新类型对象.</p>
+     * <p>
+     * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入
+     */
+    public static <S, D> D map(S source, Type<S> sourceType, Type<D> destinationType) {
+        return buildClassMapperFacade(false).map(source, sourceType, destinationType);
+    }
+
+    /**
+     * <p>对象拷贝</p>
+     *
+     * @author chenpeng
+     * Create at March 6, 2019 at 17:22:38 GMT+8
+     */
+    public static <S, D> void copy(S source, D dest) {
+        buildClassMapperFacade(false).map(source, dest);
+    }
+
+    /**
+     * <p>简单的复制出新对象列表到ArrayList</p>
+     * <p>
+     * 不建议使用mapper.mapAsList(Iterable<S>,Class<D>)接口, sourceClass需要反射,实在有点慢
+     */
+    public static <S, D> List<D> mapList(Iterable<S> sourceList, Class<S> sourceClass, Class<D> destinationClass) {
+        return buildClassMapperFacade(false).mapAsList(sourceList,
+                TypeFactory.valueOf(sourceClass),
+                TypeFactory.valueOf(destinationClass));
+    }
+
+    /**
+     * <p>极致性能的复制出新类型对象到ArrayList.<p>
+     * <p>
+     * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入
+     */
+    public static <S, D> List<D> mapList(Iterable<S> sourceList, Type<S> sourceType, Type<D> destinationType) {
+        return buildClassMapperFacade(false).mapAsList(sourceList, sourceType, destinationType);
+    }
+
+    /**
+     * <p>简单复制出新对象列表到数组<p>
+     * <p>
+     * 通过source.getComponentType() 获得源Class
+     */
+    public static <S, D> D[] mapArray(final D[] destination, final S[] source, final Class<D> destinationClass) {
+        return buildClassMapperFacade(false).mapAsArray(destination, source, destinationClass);
+    }
+
+    /**
+     * <p>极致性能的复制出新类型对象到数组<p>
+     * <p>
+     * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入
+     */
+    public static <S, D> D[] mapArray(D[] destination, S[] source, Type<S> sourceType, Type<D> destinationType) {
+        return buildClassMapperFacade(false).mapAsArray(destination, source, sourceType, destinationType);
+    }
+
+    private static MapperFacade buildClassMapperFacade(boolean mapNulls) {
+        return (mapNulls
+                ? MAP_NULL_FIELDS_MAPPER_FACTORY.getMapperFacade()
+                : MAP_NONE_NULL_FIELDS_MAPPER_FACTORY.getMapperFacade());
+    }
+}

+ 53 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/BeanValidatorUtils.java

@@ -0,0 +1,53 @@
+package com.usky.common.core.util;
+
+import com.usky.common.core.bean.BeanConstraintViolationResult;
+import com.google.common.collect.Lists;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * <p>参数校验工具类(手动)</p>
+ * 当需要手动校验 Bean 参数时,请直接使用该工具类
+ *
+ * @author chenpeng
+ * Create at March 12, 2019 at 15:10:54 GMT+8
+ */
+public final class BeanValidatorUtils {
+
+    private static final ValidatorFactory DEFAULT_VALIDATOR_FACTORY = Validation.buildDefaultValidatorFactory();
+    private static final Validator DEFAULT_VALIDATOR = DEFAULT_VALIDATOR_FACTORY.getValidator();
+
+    private BeanValidatorUtils() {
+    }
+
+
+    /**
+     * <p>校验 Bean 的方法,JSR303</p>
+     *
+     * @author chenpeng
+     * Create at March 12, 2019 at 15:18:07 GMT+8
+     */
+    public static <T> BeanConstraintViolationResult validate(T bean) {
+
+        List<String> errors = null;
+
+        Set<ConstraintViolation<T>> constraintViolations = DEFAULT_VALIDATOR.validate(bean);
+        Iterator<ConstraintViolation<T>> iterator = constraintViolations.iterator();
+
+        if (iterator != null) {
+            errors = Lists.newArrayList();
+            while (iterator.hasNext()) {
+                ConstraintViolation<T> error = iterator.next();
+                errors.add(error.getMessage());
+            }
+        }
+
+        return new BeanConstraintViolationResult(errors == null || errors.size() == 0, errors);
+    }
+}

+ 853 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/Convert.java

@@ -0,0 +1,853 @@
+package com.usky.common.core.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.NumberFormat;
+import java.util.Set;
+
+/**
+ * 类型转换器
+ *
+ * @author ruoyi
+ */
+public final class Convert {
+
+    private Convert() {
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static String toStr(Object value, String defaultValue) {
+        if (null == value) {
+            return defaultValue;
+        }
+        if (value instanceof String) {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static String toStr(Object value) {
+        return toStr(value, null);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Character toChar(Object value, Character defaultValue) {
+        if (null == value) {
+            return defaultValue;
+        }
+        if (value instanceof Character) {
+            return (Character) value;
+        }
+
+        final String valueStr = toStr(value, null);
+        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Character toChar(Object value) {
+        return toChar(value, null);
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Byte toByte(Object value, Byte defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Byte) {
+            return (Byte) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).byteValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return Byte.parseByte(valueStr);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Byte toByte(Object value) {
+        return toByte(value, null);
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Short toShort(Object value, Short defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Short) {
+            return (Short) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).shortValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return Short.parseShort(valueStr.trim());
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Short toShort(Object value) {
+        return toShort(value, null);
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Number toNumber(Object value, Number defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Number) {
+            return (Number) value;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return NumberFormat.getInstance().parse(valueStr);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Number toNumber(Object value) {
+        return toNumber(value, null);
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Integer toInt(Object value, Integer defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Integer) {
+            return (Integer) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return Integer.parseInt(valueStr.trim());
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Integer toInt(Object value) {
+        return toInt(value, null);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String str) {
+        return toIntArray(",", str);
+    }
+
+    /**
+     * 转换为Long数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String str) {
+        return toLongArray(",", str);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     *
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String split, String str) {
+        if (StringUtils.isEmpty(str)) {
+            return new Integer[]{};
+        }
+        String[] arr = str.split(split);
+        final Integer[] ints = new Integer[arr.length];
+        for (int i = 0; i < arr.length; i++) {
+            final Integer v = toInt(arr[i], 0);
+            ints[i] = v;
+        }
+        return ints;
+    }
+
+    /**
+     * 转换为Long数组<br>
+     *
+     * @param split 分隔符
+     * @param str   被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String split, String str) {
+        if (StringUtils.isEmpty(str)) {
+            return new Long[]{};
+        }
+        String[] arr = str.split(split);
+        final Long[] longs = new Long[arr.length];
+        for (int i = 0; i < arr.length; i++) {
+            final Long v = toLong(arr[i], null);
+            longs[i] = v;
+        }
+        return longs;
+    }
+
+    /**
+     * 转换为String数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String str) {
+        return toStrArray(",", str);
+    }
+
+    /**
+     * 转换为String数组<br>
+     *
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String split, String str) {
+        return str.split(split);
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Long toLong(Object value, Long defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Long) {
+            return (Long) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).longValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).longValue();
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Long toLong(Object value) {
+        return toLong(value, null);
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Double toDouble(Object value, Double defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Double) {
+            return (Double) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).doubleValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).doubleValue();
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Double toDouble(Object value) {
+        return toDouble(value, null);
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Float toFloat(Object value, Float defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Float) {
+            return (Float) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).floatValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return Float.parseFloat(valueStr.trim());
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Float toFloat(Object value) {
+        return toFloat(value, null);
+    }
+
+    /**
+     * 转换为boolean<br>
+     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value, Boolean defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        }
+        String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        valueStr = valueStr.trim().toLowerCase();
+        switch (valueStr) {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            case "yes":
+                return true;
+            case "ok":
+                return true;
+            case "no":
+                return false;
+            case "1":
+                return true;
+            case "0":
+                return false;
+            default:
+                return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为boolean<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value) {
+        return toBool(value, null);
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     *
+     * @param clazz        Enum的Class
+     * @param value        值
+     * @param defaultValue 默认值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (clazz.isAssignableFrom(value.getClass())) {
+            @SuppressWarnings("unchecked")
+            E myE = (E) value;
+            return myE;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return Enum.valueOf(clazz, valueStr);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     *
+     * @param clazz Enum的Class
+     * @param value 值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) {
+        return toEnum(clazz, value, null);
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value, BigInteger defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof BigInteger) {
+            return (BigInteger) value;
+        }
+        if (value instanceof Long) {
+            return BigInteger.valueOf((Long) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return new BigInteger(valueStr);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value) {
+        return toBigInteger(value, null);
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value        被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof BigDecimal) {
+            return (BigDecimal) value;
+        }
+        if (value instanceof Long) {
+            return new BigDecimal((Long) value);
+        }
+        if (value instanceof Double) {
+            return new BigDecimal((Double) value);
+        }
+        if (value instanceof Integer) {
+            return new BigDecimal((Integer) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return new BigDecimal(valueStr);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value) {
+        return toBigDecimal(value, null);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @return 字符串
+     */
+    public static String utf8Str(Object obj) {
+        return str(obj, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj         对象
+     * @param charsetName 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, String charsetName) {
+        return str(obj, Charset.forName(charsetName));
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj     对象
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, Charset charset) {
+        if (null == obj) {
+            return null;
+        }
+
+        if (obj instanceof String) {
+            return (String) obj;
+        } else if (obj instanceof byte[] || obj instanceof Byte[]) {
+            return str((Byte[]) obj, charset);
+        } else if (obj instanceof ByteBuffer) {
+            return str((ByteBuffer) obj, charset);
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将byte数组转为字符串
+     *
+     * @param bytes   byte数组
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(byte[] bytes, String charset) {
+        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+    }
+
+    /**
+     * 解码字节码
+     *
+     * @param data    字符串
+     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+     * @return 解码后的字符串
+     */
+    public static String str(byte[] data, Charset charset) {
+        if (data == null) {
+            return null;
+        }
+
+        if (null == charset) {
+            return new String(data);
+        }
+        return new String(data, charset);
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     *
+     * @param data    数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, String charset) {
+        if (data == null) {
+            return null;
+        }
+
+        return str(data, Charset.forName(charset));
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     *
+     * @param data    数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, Charset charset) {
+        if (null == charset) {
+            charset = Charset.defaultCharset();
+        }
+        return charset.decode(data).toString();
+    }
+
+    // ----------------------------------------------------------------------- 全角半角转换
+
+    /**
+     * 半角转全角
+     *
+     * @param input String.
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input) {
+        return toSBC(input, null);
+    }
+
+    /**
+     * 半角转全角
+     *
+     * @param input         String
+     * @param notConvertSet 不替换的字符集合
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input, Set<Character> notConvertSet) {
+        char[] c = input.toCharArray();
+        for (int i = 0; i < c.length; i++) {
+            if (null != notConvertSet && notConvertSet.contains(c[i])) {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == ' ') {
+                c[i] = '\u3000';
+            } else if (c[i] < '\177') {
+                c[i] = (char) (c[i] + 65248);
+
+            }
+        }
+        return new String(c);
+    }
+
+    /**
+     * 全角转半角
+     *
+     * @param input String.
+     * @return 半角字符串
+     */
+    public static String toDBC(String input) {
+        return toDBC(input, null);
+    }
+
+    /**
+     * 替换全角为半角
+     *
+     * @param text          文本
+     * @param notConvertSet 不替换的字符集合
+     * @return 替换后的字符
+     */
+    public static String toDBC(String text, Set<Character> notConvertSet) {
+        char[] c = text.toCharArray();
+        for (int i = 0; i < c.length; i++) {
+            if (null != notConvertSet && notConvertSet.contains(c[i])) {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == '\u3000') {
+                c[i] = ' ';
+            } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
+                c[i] = (char) (c[i] - 65248);
+            }
+        }
+        String returnString = new String(c);
+
+        return returnString;
+    }
+
+    /**
+     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+     *
+     * @param n 数字
+     * @return 中文大写数字
+     */
+    public static String digitUppercase(double n) {
+        String[] fraction = {"角", "分"};
+        String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
+        String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};
+
+        String head = n < 0 ? "负" : "";
+        n = Math.abs(n);
+
+        String s = "";
+        for (int i = 0; i < fraction.length; i++) {
+            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+        }
+        if (s.length() < 1) {
+            s = "整";
+        }
+        int integerPart = (int) Math.floor(n);
+
+        for (int i = 0; i < unit[0].length && integerPart > 0; i++) {
+            String p = "";
+            for (int j = 0; j < unit[1].length && n > 0; j++) {
+                p = digit[integerPart % 10] + unit[1][j] + p;
+                integerPart = integerPart / 10;
+            }
+            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+        }
+        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+    }
+}

+ 722 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/DateUtils.java

@@ -0,0 +1,722 @@
+package com.usky.common.core.util;
+
+import com.usky.common.core.exception.BusinessException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.text.ParseException;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Optional;
+
+/**
+ * <p>时间工具类</p>
+ *
+ * @author chenpeng
+ * Create at January 24, 2019 at 14:36:57 GMT+8
+ */
+@Slf4j
+public final class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+
+    public static final String[] PARSE_PATTERNS = {
+            "yyyy-MM-dd HH:mm:ss.SSS Z",
+            "yyyy-MM-dd HH:mm:ss Z",
+            "yyyy-MM-dd HH:mm Z",
+            "yyyy-MM-dd HH Z",
+            "yyyy-MM-dd HH:mm:ss.SSS",
+            "yyyy-MM-dd HH:mm:ss",
+            "yyyy-MM-dd HH:mm",
+            "yyyy-MM-dd HH",
+            "yyyy-MM-dd",
+            "yyyy-MM",
+            "yyyy/MM/dd HH:mm:ss.SSS Z",
+            "yyyy/MM/dd HH:mm:ss Z",
+            "yyyy/MM/dd HH:mm Z",
+            "yyyy/MM/dd HH Z",
+            "yyyy/MM/dd HH:mm:ss.SSS",
+            "yyyy/MM/dd HH:mm:ss",
+            "yyyy/MM/dd HH:mm",
+            "yyyy/MM/dd HH",
+            "yyyy/MM/dd",
+            "yyyy/MM",
+            "yyyy.MM.dd HH:mm:ss.SSS Z",
+            "yyyy.MM.dd HH:mm:ss Z",
+            "yyyy.MM.dd HH:mm Z",
+            "yyyy.MM.dd HH Z",
+            "yyyy.MM.dd HH:mm:ss.SSS",
+            "yyyy.MM.dd HH:mm:ss",
+            "yyyy.MM.dd HH:mm",
+            "yyyy.MM.dd HH",
+            "yyyy.MM.dd",
+            "yyyy.MM",
+            "yyyyMMdd",
+            "yyyyMM",
+            "yyyy-MM-dd'T'HH:mm:ssZ",
+            "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
+    };
+    /**
+     * 默认日期格式
+     */
+    private static final String DEFAULT_LOCAL_DATE_FMT = "yyyy-MM-dd";
+    /**
+     * 默认时间格式
+     */
+    private static final String DEFAULT_LOCAL_DATE_TIME_FMT = "yyyy-MM-dd'T'HH:mm:ss";
+    private static final String DEFAULT_DATE_TIME_FMT = "yyyy-MM-dd'T'HH:mm:ssZ";
+
+    private DateUtils() {
+    }
+
+    /**
+     * <p>将 java.util.Date 类型转为 JDK8 LocalDateTime,使用系统默认时区</p>
+     *
+     * @param date 日期,java.util.Date
+     * @return LocalDate
+     * @author chenpeng
+     * Create at January 24, 2019 at 14:42:17 GMT+8
+     */
+    public static LocalDate toLocalDate(Date date) {
+        return toLocalDate(date, null);
+    }
+
+    /**
+     * <p>将 java.util.Date 类型转为 JDK8 LocalDate</p>
+     *
+     * @author chenpeng
+     * Create at January 24, 2019 at 14:34:56 GMT+8
+     */
+    public static LocalDate toLocalDate(Date date, ZoneId zoneId) {
+        LocalDate local = null;
+        if (date != null) {
+            zoneId = Optional.ofNullable(zoneId).orElse(ZoneId.systemDefault());
+            Instant instant = date.toInstant();
+            local = instant.atZone(zoneId).toLocalDate();
+        }
+        return local;
+    }
+
+    /**
+     * <p>将字符串日期转换为 LocalDate ,格式为 yyyy-MM-dd</p>
+     * 注意:LocalDate 不包含 HH:mm:ss 时间部分
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:05:29 GMT+8
+     */
+    public static LocalDate toLocalDate(String text) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        return toLocalDate(text, DEFAULT_LOCAL_DATE_FMT);
+    }
+
+    /**
+     * <p>将给定字符串按照给定格式转换为 LocalDate </p>
+     *
+     * @param text   待转换的日期字符串
+     * @param format text 参数字符串本身的格式
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:06:52 GMT+8
+     */
+    public static LocalDate toLocalDate(String text, String format) {
+        if (StringUtils.isBlank(text) || StringUtils.isBlank(format)) {
+            return null;
+        }
+        return LocalDate.parse(text, DateTimeFormatter.ofPattern(format));
+    }
+
+    /**
+     * <p>将给定 Date 转换为 LocalDateTime </p>
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:08:56 GMT+8
+     */
+    public static LocalDateTime toLocalDateTime(Date date) {
+        return toLocalDateTime(date, null);
+    }
+
+    /**
+     * <p>将 java.util.Date 类型转为 JDK8 LocalDateTime</p>
+     *
+     * @param date   日期,java.util.Date
+     * @param zoneId 时区
+     * @return LocalDateTime
+     * @author chenpeng
+     * Create at January 24, 2019 at 14:34:56 GMT+8
+     */
+    public static LocalDateTime toLocalDateTime(Date date, ZoneId zoneId) {
+        LocalDateTime local = null;
+        if (date != null) {
+            zoneId = Optional.ofNullable(zoneId).orElse(ZoneId.systemDefault());
+            Instant instant = date.toInstant();
+            local = instant.atZone(zoneId).toLocalDateTime();
+        }
+        return local;
+    }
+
+    /**
+     * <p>将字符串日期转换为 LocalDateTime ,格式为 yyyy-MM-dd HH:mm:ss</p>
+     * 注意:LocalDateTime 包含 HH:mm:ss 时间部分
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:05:29 GMT+8
+     */
+    public static LocalDateTime toLocalDateTime(String text) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        return toLocalDateTime(text, DEFAULT_LOCAL_DATE_TIME_FMT);
+    }
+
+    /**
+     * <p>将给定字符串按照给定格式转换为 LocalDateTime </p>
+     *
+     * @param text   待转换的日期字符串
+     * @param format text 参数字符串本身的格式
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:06:52 GMT+8
+     */
+    public static LocalDateTime toLocalDateTime(String text, String format) {
+        if (StringUtils.isBlank(text) || StringUtils.isBlank(format)) {
+            return null;
+        }
+        return LocalDateTime.parse(text, DateTimeFormatter.ofPattern(format));
+    }
+
+    /**
+     * 将 LocalDate 转换为 Date,采用系统默认时区
+     *
+     * @param localDate
+     * @return Date
+     * @author chenpeng
+     * Create at January 24, 2019 at 15:06:12 GMT+8
+     */
+    public static Date toDate(LocalDate localDate) {
+        return toDate(localDate, null);
+    }
+
+    /**
+     * 将 LocalDate 转换为 Date
+     *
+     * @param localDate
+     * @param zoneId
+     * @return Date
+     * @author chenpeng
+     * Create at January 24, 2019 at 15:05:24 GMT+8
+     */
+    public static Date toDate(LocalDate localDate, ZoneId zoneId) {
+        Date date = null;
+        if (localDate != null) {
+            zoneId = Optional.ofNullable(zoneId).orElse(ZoneId.systemDefault());
+            ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
+            date = Date.from(zdt.toInstant());
+        }
+        return date;
+    }
+
+    /**
+     * <p>将 LocalDateTime 转为 Date</p>
+     *
+     * @author chenpeng
+     * Create at January 24, 2019 at 15:11:10 GMT+8
+     */
+    public static Date toDate(LocalDateTime localDateTime) {
+        return toDate(localDateTime, null);
+    }
+
+    /**
+     * <p>将 LocalDateTime 转为 Date</p>
+     *
+     * @param localDateTime
+     * @param zoneId
+     * @return Date
+     * @author chenpeng
+     * Create at January 24, 2019 at 15:10:29 GMT+8
+     */
+    public static Date toDate(LocalDateTime localDateTime, ZoneId zoneId) {
+        Date date = null;
+        if (localDateTime != null) {
+            zoneId = Optional.ofNullable(zoneId).orElse(ZoneId.systemDefault());
+            ZonedDateTime zdt = localDateTime.atZone(zoneId);
+            date = Date.from(zdt.toInstant());
+        }
+        return date;
+    }
+
+    /**
+     * <p>获取两个日期间相隔天数</p>
+     * 注意:两个参数均表示“日期”,即不包含 时:分:秒 部分
+     * 若传入 Date 包含 时:分:秒 则截断该部分
+     *
+     * @param d1
+     * @param d2
+     * @return Long
+     * @author chenpeng
+     * Create at January 24, 2019 at 15:37:11 GMT+8
+     */
+    public static Long getDaysBetween(Date d1, Date d2) {
+        return getDaysBetween(toLocalDate(d1), toLocalDate(d2));
+    }
+
+    /**
+     * <p>获取两个日期间相隔天数</p>
+     * 注意:入参类型为 LocalDate,即“日期”,即不包含 时:分:秒 部分
+     *
+     * @param d1
+     * @param d2
+     * @return Long
+     * @author chenpeng
+     * Create at January 24, 2019 at 15:40:20 GMT+8
+     */
+    public static Long getDaysBetween(LocalDate d1, LocalDate d2) {
+        Long days = null;
+        if (d1 != null && d2 != null) {
+            days = d1.until(d2, ChronoUnit.DAYS);
+        }
+        return days;
+    }
+
+    /**
+     * <p>将给定日期按给定格式转换为字符串</p>
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:24:58 GMT+8
+     */
+    public static String format(LocalDate localDate, String format) {
+        if (localDate == null) {
+            return null;
+        }
+        return localDate.format(DateTimeFormatter.ofPattern(format));
+    }
+
+    /**
+     * <p>将给定日期换为字符串,格式为 yyyy-MM-dd</p>
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:24:58 GMT+8
+     */
+    public static String format(LocalDate localDate) {
+        if (localDate == null) {
+            return null;
+        }
+        return format(localDate, DEFAULT_LOCAL_DATE_FMT);
+    }
+
+    /**
+     * <p>将给定时间按给定格式转换为字符串</p>
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:24:58 GMT+8
+     */
+    public static String format(LocalDateTime localDateTime, String format) {
+        if (localDateTime == null) {
+            return null;
+        }
+        return localDateTime.format(DateTimeFormatter.ofPattern(format));
+    }
+
+    /**
+     * <p>将给定时间换为字符串,格式为 yyyy-MM-dd HH:mm:ss</p>
+     *
+     * @author chenpeng
+     * Create at February 13, 2019 at 16:24:58 GMT+8
+     */
+    public static String format(LocalDateTime localDateTime) {
+        if (localDateTime == null) {
+            return null;
+        }
+        return format(localDateTime, DEFAULT_LOCAL_DATE_TIME_FMT);
+    }
+
+    /**
+     * 得到日期字符串 默认格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
+     */
+    public static String format(Date date, String pattern) {
+        String formatDate = null;
+        if (StringUtils.isNotBlank(pattern)) {
+            formatDate = DateFormatUtils.format(date, pattern);
+        } else {
+            formatDate = DateFormatUtils.format(date, DEFAULT_DATE_TIME_FMT);
+        }
+        return formatDate;
+    }
+
+    /**
+     * 得到日期时间字符串,转换格式(yyyy-MM-dd HH:mm:ss)
+     */
+    public static String format(Date date) {
+        return format(date, DEFAULT_DATE_TIME_FMT);
+    }
+
+    /**
+     * 转换为时间字符串(天,时:分:秒.毫秒)
+     *
+     * @param timeMillis
+     * @return
+     */
+    public static String format(long timeMillis) {
+        long day = timeMillis / (24 * 60 * 60 * 1000);
+        long hour = (timeMillis / (60 * 60 * 1000) - day * 24);
+        long min = ((timeMillis / (60 * 1000)) - day * 24 * 60 - hour * 60);
+        long s = (timeMillis / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
+        long sss = (timeMillis - day * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000 - min * 60 * 1000 - s * 1000);
+        return (day > 0 ? day + "," : "") + hour + ":" + min + ":" + s + "." + sss;
+    }
+
+    /**
+     * 得到当前时间字符串 格式(HH:mm:ss)
+     */
+    public static String getTime(Date date) {
+        return format(date, "HH:mm:ss");
+    }
+
+    /**
+     * 得到当前年份字符串 格式(yyyy)
+     */
+    public static String getYear(Date date) {
+        return format(date, "yyyy");
+    }
+
+    /**
+     * 得到当前月份字符串 格式(MM)
+     */
+    public static String getMonth(Date date) {
+        return format(date, "MM");
+    }
+
+    /**
+     * 得到当天字符串 格式(dd)
+     */
+    public static String getDay(Date date) {
+        return format(date, "dd");
+    }
+
+    /**
+     * 得到当前星期字符串 格式(E)星期几
+     */
+    public static String getWeek(Date date) {
+        return format(date, "E");
+    }
+
+    public static Date parseDate(String str) {
+        if (StringUtils.isBlank(str)) {
+            return null;
+        }
+        try {
+            return parseDate(str.toString(), PARSE_PATTERNS);
+        } catch (ParseException e) {
+            log.error("Error:parseDate(dateStr:{}, error:{})", str, e);
+            throw new BusinessException(String.format("Error occur parseDate(dateStr:%s). error:%s", str), e);
+        }
+    }
+
+    /**
+     * 获取过去的天数
+     *
+     * @param date
+     * @return
+     */
+    public static long pastDays(Date date) {
+        long t = System.currentTimeMillis() - date.getTime();
+        return t / (24 * 60 * 60 * 1000);
+    }
+
+    /**
+     * 获取过去的小时
+     *
+     * @param date
+     * @return
+     */
+    public static long pastHour(Date date) {
+        long t = System.currentTimeMillis() - date.getTime();
+        return t / (60 * 60 * 1000);
+    }
+
+    /**
+     * 获取过去的分钟
+     *
+     * @param date
+     * @return
+     */
+    public static long pastMinutes(Date date) {
+        long t = System.currentTimeMillis() - date.getTime();
+        return t / (60 * 1000);
+    }
+
+    /**
+     * 获取两个日期之间的天数
+     *
+     * @param before
+     * @param after
+     * @return
+     */
+    public static int getDistanceOfTwoDate(Date before, Date after) {
+        long beforeTime = before.getTime();
+        long afterTime = after.getTime();
+        return (int) (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
+    }
+
+    public static int getDistanceOfTwoDateNew(Date before, Date after) {
+        long beforeTime = before.getTime();
+        long afterTime = after.getTime();
+        return Integer.parseInt(String.valueOf((afterTime - beforeTime)/(1000 * 60 * 60 * 24)));
+    }
+
+    /**
+     * @Description 获取最大天数差
+     * @Author Peter
+     * @Date ${DATE}
+     */
+    public static int getMaxDistanceOfTwoDate(Date before, Date after) {
+        long beforeTime = before.getTime();
+        long afterTime = after.getTime();
+        long res = afterTime - beforeTime;
+        if (res < 0) {
+            return 0;
+        }
+        return (int) ((afterTime - beforeTime) / (1000 * 60 * 60 * 24) + 0.5);
+    }
+
+    public static int getDistanceDayOfTwoDate(Date before, Date after) {
+        Date beforeDate = getDayEndTime(before);
+        Date afterDate = getDayEndTime(after);
+        long res = afterDate.getTime() - beforeDate.getTime();
+        if (res < 0) {
+            return 0;
+        }
+        return (int) (res / (60 * 60 * 24 * 1000));
+    }
+
+    /**
+     * 返回本周周一的date
+     */
+    public static Date getCurrentMonday() {
+        int mondayPlus = getMondayPlus();
+        GregorianCalendar currentDate = new GregorianCalendar();
+        currentDate.add(GregorianCalendar.DATE, mondayPlus);
+        Date monday = currentDate.getTime();
+        monday.setHours(0);
+        monday.setMinutes(0);
+        monday.setSeconds(0);
+        return monday;
+    }
+
+    /**
+     * 返回本周周日的date
+     */
+    public static Date getPreviousSunday() {
+        int mondayPlus = getMondayPlus();
+        GregorianCalendar currentDate = new GregorianCalendar();
+        currentDate.add(GregorianCalendar.DATE, mondayPlus + 6);
+        Date sunday = currentDate.getTime();
+        sunday.setHours(23);
+        sunday.setMinutes(59);
+        sunday.setSeconds(59);
+        return sunday;
+    }
+
+    private static int getMondayPlus() {
+        Calendar cd = Calendar.getInstance();
+        // 获得今天是一周的第几天,星期日是第一天,星期二是第二天......
+        int dayOfWeek = cd.get(Calendar.DAY_OF_WEEK);
+        if (dayOfWeek == 1) {
+            return -6;
+        } else {
+            return 2 - dayOfWeek;
+        }
+    }
+
+    /**
+     * 获取某天的中午 2020-12-31 12:00:00.000
+     *
+     * @param startDate
+     * @return
+     */
+    public static Date getDayMidTime(Date startDate) {
+        Calendar todayStart = Calendar.getInstance();
+        todayStart.setTime(startDate);
+        todayStart.set(Calendar.HOUR_OF_DAY, 12);
+        todayStart.set(Calendar.MINUTE, 0);
+        todayStart.set(Calendar.SECOND, 0);
+        todayStart.set(Calendar.MILLISECOND, 0);
+        return todayStart.getTime();
+    }
+
+    /**
+     * 获取某天的开始一刻 2020-12-31 00:00:00.000
+     *
+     * @param startDate
+     * @return
+     */
+    public static Date getDayStartTime(Date startDate) {
+        Calendar todayStart = Calendar.getInstance();
+        todayStart.setTime(startDate);
+        todayStart.set(Calendar.HOUR_OF_DAY, 0);
+        todayStart.set(Calendar.MINUTE, 0);
+        todayStart.set(Calendar.SECOND, 0);
+        todayStart.set(Calendar.MILLISECOND, 0);
+        return todayStart.getTime();
+    }
+
+    /**
+     * 获取某天的最后一刻 2020-12-31 23:59:59.999
+     *
+     * @param endDate
+     * @return
+     */
+    public static Date getDayEndTime(Date endDate) {
+        Calendar todayEnd = Calendar.getInstance();
+        todayEnd.setTime(endDate);
+        todayEnd.set(Calendar.HOUR_OF_DAY, 23);
+        todayEnd.set(Calendar.MINUTE, 59);
+        todayEnd.set(Calendar.SECOND, 59);
+        todayEnd.set(Calendar.MILLISECOND, 999);
+        return todayEnd.getTime();
+    }
+
+    /**
+     * 获取某天的下一天的开始一刻 2021-01-01 00:00:00.000
+     *
+     * @param date
+     * @return
+     */
+    public static Date getDateOfNextMorning(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.add(Calendar.DATE, 1);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTime();
+    }
+
+
+    /**
+     * 获取当月第一天
+     *
+     * @param date
+     * @return
+     */
+    public static Date getFirstDayOfMonth(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.DAY_OF_MONTH, 1);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTime();
+    }
+
+    /**
+     * 根据入参时间返回当前月最后的时间
+     *
+     * @param date
+     * @return
+     */
+    public static Date getLastDayOfMonth(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        return calendar.getTime();
+    }
+
+    /**
+     * 判断日期是否为月末
+     */
+    public static boolean isLastDayOfMonth(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
+        if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 获取下个月的当前日期
+     *
+     * @param date
+     * @return
+     */
+    public static Date getDayOfNextMonth(Date date) {
+        Calendar calendar = new GregorianCalendar();
+        calendar.setTime(date);
+        //把日期往后增加一个月.整数往后推,负数往前移动
+        calendar.add(Calendar.MONTH, 1);
+        date = calendar.getTime();
+        return date;
+    }
+
+    public static Date getSpecialTime(Date date, int hour, int minute, int second) {
+        Calendar todayStart = Calendar.getInstance();
+        todayStart.setTime(date);
+        todayStart.set(Calendar.HOUR_OF_DAY, hour);
+        todayStart.set(Calendar.MINUTE, minute);
+        todayStart.set(Calendar.SECOND, second);
+        todayStart.set(Calendar.MILLISECOND, 0);
+        return todayStart.getTime();
+    }
+
+
+    /**
+     * 获得time所在年的第一天
+     * @param time
+     * @return
+     */
+    public static Date getBeginDayOfYear(Date time){
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(time);
+        cal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
+        cal.set(Calendar.MONTH, Calendar.JANUARY);
+        cal.set(Calendar.DATE, 1);
+
+        return getDayStartTime(cal.getTime());
+    }
+
+
+    /**
+     * 获取本年的结束时间
+     * @return
+     */
+    public static Date getEndDayOfYear(Date time) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(time);
+        cal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
+        cal.set(Calendar.MONTH, Calendar.DECEMBER);
+        cal.set(Calendar.DATE, 31);
+        return getDayEndTime(cal.getTime());
+    }
+
+
+    /**
+     * 时间筛选
+     * @param nowTime
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
+        if (nowTime.getTime() == startTime.getTime()
+                || nowTime.getTime() == endTime.getTime()) {
+            return true;
+        }
+        Calendar date = Calendar.getInstance();
+        date.setTime(nowTime);
+        Calendar begin = Calendar.getInstance();
+        begin.setTime(startTime);
+        Calendar end = Calendar.getInstance();
+        end.setTime(endTime);
+        return date.after(begin) && date.before(end);
+    }
+
+}

+ 187 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/FileTypeUtils.java

@@ -0,0 +1,187 @@
+package com.usky.common.core.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author barrychen
+ */
+public final class FileTypeUtils {
+
+    public static final List<String> IMG_TYPE_LIST = Arrays.asList(new String[]{"bmp", "jpg", "jpeg", "png", "gif"});
+    public static final List<String> COMPRESSED_FILE_TYPE_LIST = Arrays.asList(new String[]{"rar", "zip", "arj", "gz", "z"});
+    public static final String QUESTION_MARKETS = "?";
+
+    private FileTypeUtils() {
+    }
+
+    /**
+     * 判断是否是图片
+     *
+     * @param fileType
+     * @return
+     */
+    public static boolean isImage(String fileType) {
+        return IMG_TYPE_LIST.contains(fileType.toLowerCase());
+    }
+
+    /**
+     * 判断是压缩文件
+     *
+     * @param fileType
+     * @return
+     */
+    public static boolean isCompressedFile(String fileType) {
+        return COMPRESSED_FILE_TYPE_LIST.contains(fileType.toLowerCase());
+    }
+
+    public static String getFileTypeByPath(String path) {
+        if (!StringUtils.isBlank(path)) {
+            int index = path.lastIndexOf("/");
+            if (index >= 0) {
+                path = path.substring(index + 1);
+            }
+            index = path.lastIndexOf(".");
+            if (index >= 0) {
+                String fileExtension = path.substring(index + 1);
+                if (fileExtension.indexOf(QUESTION_MARKETS) > -1) {
+                    fileExtension = fileExtension.substring(0, fileExtension.indexOf("?"));
+                }
+                return fileExtension.toLowerCase();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 通过文件头换取文件类型
+     *
+     * @param file
+     * @return
+     */
+    public static String getFileTypeByFile(File file) {
+        if (file.exists() && file.isFile()) {
+            InputStream is = null;
+            try {
+                byte[] b = new byte[50];
+                is = new FileInputStream(file);
+                is.read(b);
+                return getFileTypeByStream(b);
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    if (is != null) {
+                        is.close();
+                    }
+                } catch (IOException e) {
+                }
+            }
+        }
+        return null;
+    }
+
+    public static String getFileTypeByStream(byte[] b) {
+        String fileHex = String.valueOf(getFileHexString(b));
+        FileTypeHexEnum[] enums = FileTypeHexEnum.values();
+        for (FileTypeHexEnum hexEnum : enums) {
+            String fileTypeHex = hexEnum.getHex();
+            if (fileHex.toUpperCase().startsWith(fileTypeHex)) {
+                return hexEnum.getType();
+            }
+        }
+        return null;
+    }
+
+
+    public static String getFileHexString(byte[] b) {
+        if (b == null || b.length <= 0) {
+            return null;
+        }
+        StringBuilder stringBuilder = new StringBuilder();
+        for (int i = 0; i < b.length; i++) {
+            int v = b[i] & 0xFF;
+            String hv = Integer.toHexString(v);
+            if (hv.length() < 2) {
+                stringBuilder.append(0);
+            }
+            stringBuilder.append(hv);
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     * 文件类型
+     */
+    public enum FileTypeHexEnum {
+        JPG("jpg", "FFD8FF", "JPEG (jpg)"),
+        JPEG("jpeg", "FFD8FF", "JPEG (jpg)"),
+        PNG("png", "89504E47", "PNG (png)"),
+        GIF("gif", "47494638", "GIF (gif)"),
+        TIF("tif", "49492A00", "TIFF (tif)"),
+        BMP("bmp", "424D", "Windows Bitmap (bmp)"),
+        DWG("dwg", "41433130", "CAD (dwg)"),
+        HTML("html", "68746D6C3E", "HTML (html)"),
+        RTF("rtf", "7B5C727466", "Rich Text Format (rtf)"),
+        XML("xml", "3C3F786D6C", "xml"),
+        ZIP("zip", "504B0304", "zip"),
+        RAR("rar", "52617221", "rar"),
+        PSD("psd", "38425053", "PhotoShop (psd)"),
+        EML("eml", "44656C69766572792D646174653A", "Email"),
+        DBX("dbx", "CFAD12FEC5FD746F", "Outlook Express (dbx)"),
+        PST("pst", "2142444E", "Outlook (pst)"),
+        XLS("xls", "D0CF11E0", "MS Word"),
+        DOC("doc", "D0CF11E0", "MS Excel 注意:word 和 excel的文件头一样"),
+        MDB("mdb", "5374616E64617264204A", "MS Access (mdb)"),
+        WPD("wpd", "FF575043", "WordPerfect (wpd)"),
+        EPS("eps", "252150532D41646F6265", "eps"),
+        PS("ps", "252150532D41646F6265", "ps"),
+        PDF("pdf", "255044462D312E", "Adobe Acrobat (pdf)"),
+        QDF("qdf", "AC9EBD8F", "Quicken (qdf)"),
+        PWL("pwl", "E3828596", "Windows Password (pwl)"),
+        WAV("wav", "57415645", "Wave (wav)"),
+        AVI("avi", "41564920", "avi"),
+        RAM("ram", "2E7261FD", "Real Audio (ram)"),
+        RM("rm", "2E524D46", "Real Media (rm)"),
+        MPG("mpg", "000001BA", "mpg"),
+        MOV("mov", "6D6F6F76", "QuickTime (mov)"),
+        ASF("asf", "3026B2758E66CF11", "Windows Media (asf)"),
+        MID("mid", "4D546864", "MIDI (mid)"),
+        NULL("null", "null", "null");
+
+        private final String type;
+        private final String hex;
+        private final String desc;
+
+        FileTypeHexEnum(String type, String hex, String desc) {
+            this.type = type;
+            this.hex = hex;
+            this.desc = desc;
+        }
+
+        public static FileTypeHexEnum typeOf(String type) {
+            for (FileTypeHexEnum hexEnum : values()) {
+                if (hexEnum.getType().equalsIgnoreCase(type)) {
+                    return hexEnum;
+                }
+            }
+            return NULL;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public String getHex() {
+            return hex;
+        }
+
+        public String getDesc() {
+            return desc;
+        }
+    }
+}

+ 320 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/FileUtils.java

@@ -0,0 +1,320 @@
+package com.usky.common.core.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * @author barrychen
+ */
+@Slf4j
+public final class FileUtils extends org.apache.commons.io.FileUtils {
+
+    private static final String CONTENT_DISPOSITION_FILE_NAME = "filename=";
+
+    private FileUtils() {
+    }
+
+    /**
+     * 创建单个文件
+     *
+     * @param descFileName 文件名,包含路径
+     * @return 如果创建成功,则返回true,否则返回false
+     */
+    public static boolean createFile(String descFileName) {
+        File file = new File(descFileName);
+        if (file.exists()) {
+            log.info("文件 " + descFileName + " 已存在!");
+            return false;
+        }
+        if (descFileName.endsWith(File.separator)) {
+            log.info(descFileName + " 为目录,不能创建目录!");
+            return false;
+        }
+        if (!file.getParentFile().exists()) {
+            file.getParentFile().mkdirs();
+            return true;
+        }
+
+        // 创建文件
+        try {
+            if (file.createNewFile()) {
+                return true;
+            } else {
+                log.info(descFileName + " 文件创建失败!");
+                return false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.info(descFileName + " 文件创建失败!");
+            return false;
+        }
+    }
+
+    /**
+     * 创建目录
+     *
+     * @param descDirName 目录名,包含路径
+     * @return 如果创建成功,则返回true,否则返回false
+     */
+    public static boolean createDirectory(String descDirName) {
+        String descDirNames = descDirName;
+        if (!descDirNames.endsWith(File.separator)) {
+            descDirNames = descDirNames + File.separator;
+        }
+        File descDir = new File(descDirNames);
+        if (descDir.exists()) {
+            log.info("目录 " + descDirNames + " 已存在!");
+            return false;
+        }
+        // 创建目录
+        if (descDir.mkdirs()) {
+            return true;
+        } else {
+            log.info("目录 " + descDirNames + " 创建失败!");
+            return false;
+        }
+
+    }
+
+    /**
+     * 写入文件
+     *
+     * @param fileName 要写入的文件
+     */
+    public static void writeToFile(String fileName, String content, boolean append) {
+        try {
+            FileUtils.write(new File(fileName), content, "utf-8", append);
+            log.info("文件 " + fileName + " 写入成功!");
+        } catch (IOException e) {
+            log.error("文件 " + fileName + " 写入失败! " + e.getMessage());
+        }
+    }
+
+    /**
+     * 写入文件
+     *
+     * @param fileName 要写入的文件
+     */
+    public static void writeToFile(String fileName, String content, String encoding, boolean append) {
+        try {
+            FileUtils.write(new File(fileName), content, encoding, append);
+            log.info("文件 " + fileName + " 写入成功!");
+        } catch (IOException e) {
+            log.error("文件 " + fileName + " 写入失败! " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取待压缩文件在ZIP文件中entry的名字,即相对于跟目录的相对路径名
+     *
+     * @param dirPath 目录名
+     * @param file    entry文件名
+     * @return
+     */
+    private static String getEntryName(String dirPath, File file) {
+        String dirPaths = dirPath;
+        if (!dirPaths.endsWith(File.separator)) {
+            dirPaths = dirPaths + File.separator;
+        }
+        String filePath = file.getAbsolutePath();
+        // 对于目录,必须在entry名字后面加上"/",表示它将以目录项存储
+        if (file.isDirectory()) {
+            filePath += "/";
+        }
+        int index = filePath.indexOf(dirPaths);
+
+        return filePath.substring(index + dirPaths.length());
+    }
+
+    /**
+     * 删除文件,可以删除单个文件或文件夹
+     *
+     * @param fileName 被删除的文件名
+     * @return 如果删除成功,则返回true,否是返回false
+     */
+    public static boolean delFile(String fileName) {
+        File file = new File(fileName);
+        if (!file.exists()) {
+            log.info(fileName + " 文件不存在!");
+            return true;
+        } else {
+            if (file.isFile()) {
+                return FileUtils.deleteFile(fileName);
+            } else {
+                return FileUtils.deleteDirectory(fileName);
+            }
+        }
+    }
+
+    /**
+     * 删除单个文件
+     *
+     * @param fileName 被删除的文件名
+     * @return 如果删除成功,则返回true,否则返回false
+     */
+    public static boolean deleteFile(String fileName) {
+        File file = new File(fileName);
+        return deleteFile(file);
+    }
+
+    public static boolean deleteFile(File file) {
+        if (file.exists() && file.isFile()) {
+            if (file.delete()) {
+                log.info("删除文件 " + file.getName() + " 成功!");
+                return true;
+            } else {
+                log.info("删除文件 " + file.getName() + " 失败!");
+                return false;
+            }
+        } else {
+            log.info(file.getName() + " 文件不存在!");
+            return true;
+        }
+    }
+
+    /**
+     * 删除目录及目录下的文件
+     *
+     * @param dirName 被删除的目录所在的文件路径
+     * @return 如果目录删除成功,则返回true,否则返回false
+     */
+    public static boolean deleteDirectory(String dirName) {
+        String dirNames = dirName;
+        if (!dirNames.endsWith(File.separator)) {
+            dirNames = dirNames + File.separator;
+        }
+        File dirFile = new File(dirNames);
+        if (!dirFile.exists() || !dirFile.isDirectory()) {
+            log.info(dirNames + " 目录不存在!");
+            return true;
+        }
+        boolean flag = true;
+        // 列出全部文件及子目录
+        File[] files = dirFile.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            // 删除子文件
+            if (files[i].isFile()) {
+                flag = FileUtils.deleteFile(files[i].getAbsolutePath());
+                // 如果删除文件失败,则退出循环
+                if (!flag) {
+                    break;
+                }
+            }
+            // 删除子目录
+            else if (files[i].isDirectory()) {
+                flag = FileUtils.deleteDirectory(files[i]
+                        .getAbsolutePath());
+                // 如果删除子目录失败,则退出循环
+                if (!flag) {
+                    break;
+                }
+            }
+        }
+
+        if (!flag) {
+            log.info("删除目录失败!");
+            return false;
+        }
+        // 删除当前目录
+        if (dirFile.delete()) {
+            log.info("删除目录 " + dirName + " 成功!");
+            return true;
+        } else {
+            log.info("删除目录 " + dirName + " 失败!");
+            return false;
+        }
+
+    }
+
+    /**
+     * 下载远程文件
+     *
+     * @param remotePath   远程地址
+     * @param localDirPath 下载文件所在的目录
+     * @return
+     */
+    public static File downloadRemoteFile(String remotePath, String localDirPath) {
+        return downloadRemoteFile(remotePath, localDirPath, null);
+    }
+
+    /**
+     * 下载远程文件
+     *
+     * @param remotePath   远程地址
+     * @param localDirPath 下载后文件所在的目录
+     * @param fileName     下载后的文件名
+     * @return
+     */
+    public static File downloadRemoteFile(String remotePath, String localDirPath, String fileName) {
+        BufferedInputStream bis = null;
+        BufferedOutputStream bos = null;
+        if (localDirPath == null) {
+            localDirPath = "download";
+        }
+        try {
+            HttpURLConnection httpUrl = (HttpURLConnection) new URL(remotePath).openConnection();
+            httpUrl.connect();
+
+            bis = new BufferedInputStream(httpUrl.getInputStream());
+            byte[] b = null;
+            int len = 50;
+
+            String remoteFileName = "";
+            String contentDisposition = httpUrl.getHeaderField("Content-Disposition");
+            if (StringUtils.isNotBlank(fileName)) {
+                remoteFileName = String.format("%s%s%s", localDirPath, File.separator, fileName);
+            } else if (StringUtils.isNotBlank(contentDisposition) && contentDisposition.contains(CONTENT_DISPOSITION_FILE_NAME)) {
+                remoteFileName = localDirPath + File.separator + contentDisposition.substring(contentDisposition.indexOf(CONTENT_DISPOSITION_FILE_NAME));
+            } else {
+                String fileType = FileTypeUtils.getFileTypeByPath(remotePath);
+                if (StringUtils.isBlank(fileType)) {
+                    b = new byte[len];
+                    len = bis.read(b);
+                    fileType = FileTypeUtils.getFileTypeByStream(b);
+                }
+
+                if (!StringUtils.isBlank(fileType)) {
+                    remoteFileName = String.format("%s%s%s.%s", localDirPath, File.separator, UUIDUtils.shortUUID(), fileType);
+                }
+            }
+
+            File file = new File(remoteFileName);
+            File fileDir = new File(file.getParent());
+            if (!fileDir.exists()) {
+                fileDir.mkdirs();
+            }
+
+            bos = new BufferedOutputStream(new FileOutputStream(file));
+            //1、通过文件获取扩展名没读完,2、通过url获取扩展名没读
+            if (len != -1) {
+                if (b != null) {
+                    //没读b为null
+                    bos.write(b, 0, len);
+                }
+                len = 2048;
+                b = new byte[len];
+                while ((len = bis.read(b)) != -1) {
+                    bos.write(b, 0, len);
+                }
+            }
+
+            bos.flush();
+            httpUrl.disconnect();
+            return file;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                bis.close();
+                bos.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+}

+ 341 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/HttpUtils.java

@@ -0,0 +1,341 @@
+package com.usky.common.core.util;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author chenpeng
+ * @date 2019-03-21
+ * @time 10:42
+ */
+public final class HttpUtils {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
+
+    private static final JsonMapper JSON_MAPPER = new JsonMapper();
+    private static final String DEFAULT_CLIENT = "default";
+    private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
+    private static final Map<String, OkHttpClient> OK_HTTP_CLIENT_MAP = new HashMap<>(16);
+
+    static {
+        OK_HTTP_CLIENT_MAP.put(DEFAULT_CLIENT, produceClient(2L, 5L, 5L, 10));
+    }
+
+    private HttpUtils() {
+    }
+
+    /**
+     * get请求
+     *
+     * @param url 请求地址
+     * @return 返回结果
+     * @throws IOException 错误
+     */
+    public static String get(String url,Map<String, String> headerMap) throws IOException {
+        OkHttpClient okHttpClient = getClient(url);
+        Request request = buildRequest(url,headerMap);
+        Response response = okHttpClient.newCall(request).execute();
+        return responseToString(response);
+    }
+
+    /**
+     * get异步请求
+     *
+     * @param url      请求地址
+     * @param callback 回调
+     */
+    public static void get(String url, Callback callback,Map<String, String> headerMap) {
+        OkHttpClient okHttpClient = getClient(url);
+        Request request = buildRequest(url,headerMap);
+        if (callback == null) {
+            callback = getDefaultCallback();
+        }
+        okHttpClient.newCall(request).enqueue(callback);
+    }
+
+    /**
+     * post 表单
+     *
+     * @param url    请求地址
+     * @param reqObj 请求参数
+     * @return 返回结果
+     * @throws IOException 异常
+     */
+    public static String postForm(String url, Object reqObj,Map<String, String> headerMap) throws IOException {
+        OkHttpClient okHttpClient = getClient(url);
+        return postForm(okHttpClient,url, reqObj,headerMap);
+    }
+
+    public static String postForm(OkHttpClient okHttpClient, String url, Object reqObj,Map<String, String> headerMap) throws IOException {
+        if (okHttpClient == null) {
+            okHttpClient = getClient(url);
+        }
+        RequestBody formBody = buildFormBody(reqObj);
+        Request request = buildRequest(url, formBody,headerMap);
+        Response response = okHttpClient.newCall(request).execute();
+        return responseToString(response);
+    }
+
+    public static String responseToString(Response response) throws IOException {
+        if (response.isSuccessful()) {
+            String result = null;
+            if (response.body() != null) {
+                result = response.body().string();
+                response.body().close();
+            }
+            response.close();
+            return result;
+        } else {
+            throw new IOException("Unexpected code " + response);
+        }
+    }
+
+    /**
+     * 异步 post 表单 提交
+     *
+     * @param url      请求地址
+     * @param reqObj   请求参数
+     * @param callback 回调处理
+     */
+    public static void postForm(String url, Object reqObj, Callback callback,Map<String, String> headerMap) {
+        OkHttpClient okHttpClient = getClient(url);
+        FormBody formBody = buildFormBody(reqObj);
+        Request request = buildRequest(url, formBody,headerMap);
+        if (callback == null) {
+            callback = getDefaultCallback();
+        }
+        okHttpClient.newCall(request).enqueue(callback);
+
+    }
+
+    /**
+     * post json
+     *
+     * @param url    请求地址
+     * @param reqObj 请求参数
+     * @return 返回字符串
+     * @throws IOException 异常
+     */
+    public static String postJson(String url, Object reqObj,Map<String, String> headerMap) throws IOException {
+        OkHttpClient okHttpClient = getClient(url);
+        return postJson(okHttpClient,url, reqObj,headerMap);
+    }
+
+    public static String postJson(OkHttpClient okHttpClient, String url, Object reqObj,Map<String, String> headerMap) throws IOException {
+        if (okHttpClient == null) {
+            okHttpClient = getClient(url);
+        }
+        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, JSON_MAPPER.toJson(reqObj));
+        Response response = okHttpClient.newCall(buildRequest(url, body,headerMap)).execute();
+        if (response.isSuccessful()) {
+            return Objects.requireNonNull(response.body()).string();
+        } else {
+            throw new IOException("Unexpected code " + response);
+        }
+    }
+
+    /**
+     * 异步提交
+     *
+     * @param url      请求地址
+     * @param reqObj   请求参数
+     * @param callback 回调处理
+     */
+    public static void postJson(String url, Object reqObj, Callback callback,Map<String, String> headerMap) {
+        OkHttpClient okHttpClient = getClient(url);
+        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, JSON_MAPPER.toJson(reqObj));
+        if (callback == null) {
+            callback = getDefaultCallback();
+        }
+        okHttpClient.newCall(buildRequest(url, body,headerMap)).enqueue(callback);
+    }
+
+    public static Request buildRequest(String url, RequestBody body,Map<String, String> headerMap) {
+        Request.Builder builder = new Request.Builder().url(url).post(body);
+        addHeaders(headerMap,builder);
+        return builder.build();
+    }
+
+    public static Request buildRequest(String url,Map<String, String> headerMap) {
+        Request.Builder builder = new Request.Builder().url(url);
+        addHeaders(headerMap,builder);
+        return builder.build();
+    }
+
+    public static FormBody buildFormBody(Object reqObj) {
+        FormBody.Builder builder = new FormBody.Builder();
+
+        Map<String, Object> mapObj = beanToMap(reqObj);
+        for (Map.Entry<String, Object> entry : mapObj.entrySet()) {
+            if (entry.getValue() != null) {
+                builder.add(entry.getKey(), entry.getValue().toString());
+            }
+        }
+
+        return builder.build();
+    }
+
+
+    private static OkHttpClient getClient(String url) {
+
+        String hostName = getHost(url);
+        if (OK_HTTP_CLIENT_MAP.containsKey(hostName)) {
+            return OK_HTTP_CLIENT_MAP.get(hostName);
+        } else {
+            return OK_HTTP_CLIENT_MAP.get(DEFAULT_CLIENT);
+        }
+    }
+
+    /**
+     * 获取默认callback处理
+     *
+     * @return
+     */
+    private static Callback getDefaultCallback() {
+        return new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws IOException {
+                if (response.isSuccessful()) {
+                    response.close();
+                } else {
+                    LOGGER.error("请求:{} 失败", call.request().url());
+                }
+            }
+        };
+    }
+
+    /**
+     * 获取 url 的 host
+     *
+     * @param url 请求地址
+     * @return host
+     */
+    public static String getHost(String url) {
+        URI uri = URI.create(url);
+        String hostName = uri.getHost();
+        if (StringUtils.isEmpty(hostName)) {
+            hostName = DEFAULT_CLIENT;
+        }
+        return hostName;
+    }
+
+    /**
+     * 构造 httpClient
+     *
+     * @param connectTime        连接超时(秒)
+     * @param writeTime          写入超时(秒)
+     * @param readTime           读取超时(秒)
+     * @param maxRequestsPerHost 正在执行的总任务数及相同host下正在执行的任务数小于阈值时,直接执行任务
+     * @return OkHttpClient
+     */
+    public static OkHttpClient produceClient(Long connectTime, Long writeTime, Long readTime,
+            Integer maxRequestsPerHost) {
+        return produceClient(connectTime, writeTime, readTime, maxRequestsPerHost, null);
+    }
+
+    public static OkHttpClient produceClient(Long connectTime, Long writeTime, Long readTime,
+            Integer maxRequestsPerHost, List<Interceptor> interceptors) {
+
+        OkHttpClient.Builder builder = builder(connectTime, writeTime, readTime);
+        if (interceptors != null && interceptors.size() > 0) {
+            for (Interceptor interceptor : interceptors) {
+                builder.addInterceptor(interceptor);
+            }
+        }
+        OkHttpClient client = builder.build();
+        if (maxRequestsPerHost != null && maxRequestsPerHost > 0) {
+            client.dispatcher().setMaxRequestsPerHost(maxRequestsPerHost);
+        }
+        return client;
+    }
+
+    /**
+     * 预设 httpClientMap 超时时间
+     *
+     * @param url
+     * @param connectTime
+     * @param writeTime
+     * @param readTime
+     * @param maxRequestsPerHost
+     */
+    public static void putHttpClient(String url, Long connectTime, Long writeTime, Long readTime,
+            Integer maxRequestsPerHost) {
+        String hostName = getHost(url);
+        synchronized (hostName.intern()) {
+            OK_HTTP_CLIENT_MAP.put(hostName, produceClient(connectTime, writeTime, readTime, maxRequestsPerHost));
+        }
+    }
+
+    /**
+     * 设置url 对应的客户端
+     *
+     * @param url          url
+     * @param okHttpClient ok http client
+     */
+    public static void putHttpClient(String url, OkHttpClient okHttpClient) {
+        String hostName = getHost(url);
+        synchronized (hostName.intern()) {
+            OK_HTTP_CLIENT_MAP.put(hostName, okHttpClient);
+        }
+    }
+
+    /**
+     * 获取默认的客户端
+     *
+     * @return ok http client
+     */
+    public static OkHttpClient getDefaultClient() {
+        return OK_HTTP_CLIENT_MAP.get(DEFAULT_CLIENT);
+    }
+
+    public static OkHttpClient.Builder builder(Long connectTime, Long writeTime, Long readTime) {
+        OkHttpClient.Builder builder = new OkHttpClient.Builder();
+        //连接超时
+        if (connectTime != null && connectTime.intValue() > 0) {
+            builder.connectTimeout(connectTime, TimeUnit.SECONDS);
+        }
+        //连接超时
+        if (writeTime != null && writeTime.intValue() > 0) {
+            builder.writeTimeout(writeTime, TimeUnit.SECONDS);
+        }
+        //连接超时
+        if (readTime != null && readTime.intValue() > 0) {
+            builder.readTimeout(readTime, TimeUnit.SECONDS);
+        }
+        return builder;
+    }
+
+    public static Map<String, Object> beanToMap(Object bean) {
+        return JSON_MAPPER.fromJson(JSON_MAPPER.toJson(bean), new TypeReference<Map<String, Object>>() {
+        });
+    }
+
+
+    /**
+     * 添加header信息
+     *
+     * @param headerMap
+     * @param builder
+     */
+    private static void addHeaders(Map<String, String> headerMap, Request.Builder builder) {
+        if (headerMap != null && !headerMap.isEmpty()) {
+            headerMap.forEach(builder::addHeader);
+        }
+    }
+
+}

+ 37 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/JacksonComponent.java

@@ -0,0 +1,37 @@
+package com.usky.common.core.util;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.util.Date;
+
+@Slf4j
+public class JacksonComponent {
+
+    /**
+     * 日期格式化
+     * 会忽略实体类JsonFormat注解方式
+     */
+    /*public static class DateJsonSerializer extends JsonSerializer<Date> {
+        @Override
+        public void serialize(Date date, JsonGenerator jsonGenerator,
+                SerializerProvider serializerProvider) throws IOException {
+            jsonGenerator.writeString(dateFormat.format(date));
+        }
+    }*/
+
+    /**
+     * 解析日期字符串
+     */
+    public static class DateJsonDeserializer extends JsonDeserializer<Date> {
+        @Override
+        public Date deserialize(JsonParser jsonParser,
+                DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+            return DateUtils.parseDate(jsonParser.getText());
+        }
+    }
+}

+ 172 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/JsonMapper.java

@@ -0,0 +1,172 @@
+package com.usky.common.core.util;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.util.JSONPObject;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+/**
+ * @author barrychen
+ * <p>
+ * 对jackson ObjectMapper的扩展.
+ * 可以传入你自定义的ObjectMapper,来进行增强.如果不传入ObjectMapper, 则使用默认的ObjectMapper.
+ * <p>
+ * 使用方式如下:
+ * <p>
+ * ObjectMapper ObjectMapper = new ObjectMapper();
+ * ...
+ * ...对objectMapper进行自定义配置
+ * ...
+ * JsonMapper jsonMapper = new JsonMapper(objectMapper);
+ * jsonMapper.toJson(new Object());
+ */
+@Slf4j
+public final class JsonMapper extends ObjectMapper {
+
+    public static final String DEFAULT_DATE_SERIALIZER_PATTERN = "yyyy-MM-dd HH:mm:ss Z";
+    public static final String DEFAULT_LOCAL_DATE_TIME_SERIALIZER_PATTERN = "yyyy-MM-dd HH:mm:ss";
+    public static final String DEFAULT_LOCAL_DATE_SERIALIZER_PATTERN = "yyyy-MM-dd";
+
+    public static final String DEFAULT_LOCAL_DATE_TIME_DESERIALIZER_PATTERN = "[yyyy/MM/dd HH[:mm][:ss][.SSS]]"
+            + "[yyyy-MM-dd HH[:mm][:ss][.SSS]]";
+    public static final String DEFAULT_LOCAL_DATE_DESERIALIZER_PATTERN = "[yyyy/MM/dd]"
+            + "[yyyy-MM-dd]";
+
+    public JsonMapper() {
+        super();
+        //设置时间格式
+        this.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_SERIALIZER_PATTERN));
+        //允许单引号
+        this.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+        //允许无引号
+        this.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+        this.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+        //允许在json中有,java类中没有的字段
+        this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        this.setSerializationInclusion(JsonInclude.Include.ALWAYS);
+
+        //注册时间反序列化
+        SimpleModule module = new SimpleModule();
+
+        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_LOCAL_DATE_TIME_SERIALIZER_PATTERN)));
+        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_LOCAL_DATE_SERIALIZER_PATTERN)));
+
+        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(
+                DateTimeFormatter.ofPattern(DEFAULT_LOCAL_DATE_TIME_DESERIALIZER_PATTERN).withZone(ZoneOffset.UTC)));
+        module.addDeserializer(LocalDate.class, new LocalDateDeserializer(
+                DateTimeFormatter.ofPattern(DEFAULT_LOCAL_DATE_DESERIALIZER_PATTERN).withZone(ZoneOffset.UTC)));
+        module.addDeserializer(Date.class, new JacksonComponent.DateJsonDeserializer());
+
+        this.registerModule(module);
+    }
+
+    public JsonMapper(ObjectMapper objectMapper) {
+        super(objectMapper);
+    }
+
+    public String toJson(Object object) {
+        try {
+            return this.writeValueAsString(object);
+        } catch (IOException e) {
+            log.warn("write to json string error:" + object, e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化POJO或简单Collection如 \List<String>.
+     * <p>
+     * 如果JSON字符串为Null或"null"字符串, 返回Null.
+     * 如果JSON字符串为"[]", 返回空集合.
+     * <p>
+     * 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType) @see #fromJson(String, JavaType)
+     */
+    public <T> T fromJson(String jsonString, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return this.readValue(jsonString, clazz);
+        } catch (IOException e) {
+            log.warn("parse json string error:" + jsonString, e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化复杂Collection如List<Bean>, 先使用函數createCollectionType构造类型,然后调用本函数.
+     *
+     * @see #createCollectionType(Class, Class...)
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T fromJson(String jsonString, JavaType javaType) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return (T) this.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.warn("parse json string error:" + jsonString, e);
+            return null;
+        }
+    }
+
+    public <T> T fromJson(String jsonString, TypeReference<T> valueTypeRef) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return this.readValue(jsonString, valueTypeRef);
+        } catch (IOException e) {
+            log.warn("parse json string error:" + jsonString, e);
+            return null;
+        }
+    }
+
+    /**
+     * 構造泛型的Collection Type如:
+     * ArrayList<MyBean>, 则调用constructCollectionType(ArrayList.class,MyBean.class)
+     * HashMap<String,MyBean>, 则调用(HashMap.class,String.class, MyBean.class)
+     */
+    public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
+        return this.getTypeFactory().constructParametricType(collectionClass, elementClasses);
+    }
+
+    /**
+     * 當JSON裡只含有Bean的部分屬性時,更新一個已存在Bean,只覆蓋該部分的屬性.
+     */
+    public <T> T update(String jsonString, T object) {
+        try {
+            return (T) this.readerForUpdating(object).readValue(jsonString);
+        } catch (JsonProcessingException e) {
+            log.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
+        }
+        return null;
+    }
+
+    /**
+     * 輸出JSONP格式數據.
+     */
+    public String toJsonP(String functionName, Object object) {
+        return toJson(new JSONPObject(functionName, object));
+    }
+
+}

+ 153 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/ShardingTaskExecutorUtils.java

@@ -0,0 +1,153 @@
+package com.usky.common.core.util;
+
+import com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+/**
+ * <p>分片执行器工具类</p>
+ *
+ * @author chenpeng
+ * Create at February 19, 2019 at 17:54:32 GMT+8
+ */
+public final class ShardingTaskExecutorUtils {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ShardingTaskExecutorUtils.class);
+
+    /**
+     * <p>默认分片数</p>
+     *
+     * @author chenpeng
+     * Create at February 19, 2019 at 18:33:17 GMT+8
+     */
+    private static final int DEFAULT_LIMIT = 2000;
+
+    private ShardingTaskExecutorUtils() {
+    }
+
+    public static ShardingExecutor apply(Integer limit) {
+        return new TaskShardingExecutor(limit);
+    }
+
+    /**
+     * <p>分片执行器</p>
+     *
+     * @author chenpeng
+     * Create at February 19, 2019 at 18:31:20 GMT+8
+     */
+    public interface ShardingExecutor {
+        /**
+         * <p>初始化</p>
+         *
+         * @author chenpeng
+         * Create at February 19, 2019 at 19:40:25 GMT+8
+         */
+        void initialize(Map<String, Object> ctx);
+
+        /**
+         * <p>同步执行任务</p>
+         * <p>需要注意的是:如果 breakOnError 为 false,即忽略异常继续执行,则事务不会被回滚</p>
+         *
+         * @author chenpeng
+         * Create at February 19, 2019 at 18:31:54 GMT+8
+         */
+        void execute(ShardingTask task, boolean breakOnError);
+
+        /**
+         * <p>同步执行任务</p>
+         *
+         * @author chenpeng
+         * Create at February 19, 2019 at 18:31:54 GMT+8
+         */
+        default void execute(ShardingTask task) {
+            execute(task, false);
+        }
+
+        /**
+         * <p>绑定参数</p>
+         *
+         * @author chenpeng
+         * Create at February 19, 2019 at 19:36:49 GMT+8
+         */
+        default ShardingExecutor bind(Supplier<Map<String, Object>> supplier) {
+            initialize(supplier == null ? null : supplier.get());
+            return this;
+        }
+
+        /**
+         * 绑定参数
+         *
+         * @author chenpeng
+         * Create at February 19, 2019 at 19:57:07 GMT+8
+         */
+        default ShardingExecutor bind(String key, Object value) {
+            return bind(() -> {
+                Map<String, Object> item = Maps.newHashMap();
+                item.put(key, value);
+                return item;
+            });
+        }
+    }
+
+    /**
+     * <p>分片任务</p>
+     *
+     * @author chenpeng
+     * Create at February 19, 2019 at 20:09:23 GMT+8
+     */
+    @FunctionalInterface
+    public interface ShardingTask {
+
+        int run(int index, int limit, Map<String, Object> context);
+    }
+
+    /**
+     * <p>分片执行器默认实现</p>
+     *
+     * @author chenpeng
+     * Create at February 19, 2019 at 18:32:16 GMT+8
+     */
+    private static class TaskShardingExecutor implements ShardingExecutor {
+
+        private final Map<String, Object> ctx = Maps.newHashMap();
+
+        private Integer limit;
+
+        TaskShardingExecutor(Integer limit) {
+            this.limit = Optional.ofNullable(limit).filter(i -> i != null && i > 0).orElse(DEFAULT_LIMIT);
+        }
+
+        @Override
+        public void initialize(Map<String, Object> ctx) {
+            if (ctx != null && ctx.size() > 0) {
+                this.ctx.putAll(ctx);
+            }
+        }
+
+        @Override
+        public void execute(ShardingTask task, boolean breakOnError) {
+            if (task != null) {
+                boolean stop;
+                int index = 0;
+                do {
+                    int count = 0;
+                    try {
+                        count = task.run(index, limit, ctx);
+                        index++;
+                    } catch (Throwable t) {
+                        if (breakOnError) {
+                            throw t;
+                        }
+                        LOGGER.error(t.getMessage(), t);
+                        LOGGER.info("### 分片执行错误,但 breakOnError 为 {},故仍然继续执行", breakOnError);
+                    }
+                    stop = count < limit;
+                } while (!stop);
+            }
+        }
+    }
+}

+ 63 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/StopWatchRecorder.java

@@ -0,0 +1,63 @@
+package com.usky.common.core.util;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * 停表记录器.
+ *
+ * @author barry chen
+ * @date 2020/9/21 5:37 下午
+ */
+public class StopWatchRecorder {
+    private final StringBuilder content = new StringBuilder();
+    private LocalDateTime startTime;
+    private LocalDateTime stopTime;
+
+    public StopWatchRecorder() {
+    }
+
+    public void start(String words) {
+        startTime = LocalDateTime.now();
+        this.record(startTime, words);
+    }
+
+    public void split(String words) {
+        if (startTime == null) {
+            startTime = LocalDateTime.now();
+        }
+        LocalDateTime now = LocalDateTime.now();
+        this.record(now, ChronoUnit.MILLIS.between(startTime, now) + "ms  " + words);
+    }
+
+    public String stop(String words) {
+        if (startTime == null) {
+            startTime = LocalDateTime.now();
+        }
+        stopTime = LocalDateTime.now();
+        this.record(stopTime, ChronoUnit.MILLIS.between(startTime, stopTime) + "ms=" + ChronoUnit.SECONDS.between(startTime, stopTime) + "s  " + words);
+        return this.getContent().toString();
+    }
+
+    public String stopAndOutput(String words) {
+        stop(words);
+        return content.toString();
+    }
+
+    private void record(LocalDateTime time, String words) {
+        content.append(time.toString()).append("  ").append(words).append("\n");
+    }
+
+    public StringBuilder getContent() {
+        return content;
+    }
+
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    public LocalDateTime getStopTime() {
+        return stopTime;
+    }
+
+}

+ 55 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/StringUtils.java

@@ -0,0 +1,55 @@
+package com.usky.common.core.util;
+
+/**
+ * @author barrychen
+ */
+public final class StringUtils extends org.apache.commons.lang3.StringUtils {
+
+    /**
+     * 下划线
+     */
+    private static final char SEPARATOR = '_';
+
+    private StringUtils() {
+    }
+
+    /**
+     * 下划线转驼峰命名
+     */
+    public static String toUnderScoreCase(String str) {
+        if (str == null) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (i > 0) {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            } else {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1)) {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
+                sb.append(SEPARATOR);
+            } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+}

+ 44 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/URLUtils.java

@@ -0,0 +1,44 @@
+package com.usky.common.core.util;
+
+import com.usky.common.core.constants.CommonConst;
+import okhttp3.HttpUrl;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * @author barrychen
+ */
+public final class URLUtils {
+
+    private URLUtils() {
+    }
+
+    public static String format(String url) {
+        if (!url.contains(CommonConst.HTTP)) {
+            url = url + CommonConst.HTTP_PREFIX;
+        }
+        return url;
+    }
+
+    public static HttpUrl okHttpUrl(String url) {
+        return HttpUrl.parse(url);
+    }
+
+    public static URL url(String url) {
+        try {
+            return new URL(url);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+/*    public static String getTypeByName(String url){
+        int questionMarkIdx = url.indexOf("?");
+        int dotIdx = url.indexOf(".");
+        if(dotIdx<0){
+            return null;
+        }
+    }*/
+
+}

+ 84 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/UUIDUtils.java

@@ -0,0 +1,84 @@
+package com.usky.common.core.util;
+
+import java.util.UUID;
+import java.util.stream.IntStream;
+
+/**
+ * <p>UUID 工具类</p>
+ *
+ * @author chenpeng
+ * Create at February 11, 2019 at 17:49:24 GMT+8
+ */
+public final class UUIDUtils {
+
+    /**
+     * 16 进制
+     */
+    private static final int HEXADECIMAL = 16;
+    /**
+     * 短 uuid 默认长度
+     */
+    private static final int SHORT_UUID_LENGTH = 8;
+    /**
+     * 字符表
+     */
+    private static final String[] CODES = new String[]{"a", "b", "c", "d", "e", "f",
+            "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
+            "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
+            "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
+            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
+            "W", "X", "Y", "Z"};
+    /**
+     * 字符表长度
+     */
+    private static final int CODES_LENTH = CODES.length;
+    /**
+     * UUID 分隔符
+     */
+    private static final String UUID_SEPARATOR = "-";
+
+    private UUIDUtils() {
+    }
+
+    /**
+     * 获得 uuid,默认不带分隔符 “-”
+     */
+    public static String uuid() {
+        return uuid(true);
+    }
+
+    /**
+     * 通过给定参数指定返回 uuid 是否带有分隔符
+     *
+     * @param withoutSeparator 是否带分隔符
+     * @return String
+     */
+    public static String uuid(boolean withoutSeparator) {
+        String uuid = UUID.randomUUID().toString();
+        if (withoutSeparator) {
+            uuid = uuid.replaceAll(UUID_SEPARATOR, StringUtils.EMPTY);
+        }
+        return uuid;
+    }
+
+    /**
+     * 获取短 uuid
+     *
+     * @return String
+     */
+    public static String shortUUID() {
+
+        StringBuilder shortUUID = new StringBuilder();
+
+        String uuid = uuid();
+        int step = uuid.length() / SHORT_UUID_LENGTH;
+
+        IntStream.range(0, SHORT_UUID_LENGTH).forEach(i -> {
+            String temp = uuid.substring(i * step, i * step + step);
+            int value = Integer.parseInt(temp, HEXADECIMAL);
+            shortUUID.append(CODES[value % CODES_LENTH]);
+        });
+
+        return shortUUID.toString();
+    }
+}

+ 28 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/IdWorker.java

@@ -0,0 +1,28 @@
+package com.usky.common.core.util.id;
+
+/**
+ * <p>ID 派发器</p>
+ *
+ * @author chenpeng
+ * Create at February 12, 2019 at 13:25:58 GMT+8
+ */
+public interface IdWorker {
+
+    /**
+     * <p>获取下一个 id</p>
+     *
+     * @return Long 64 位 id
+     * @author chenpeng
+     * Create at February 12, 2019 at 13:26:41 GMT+8
+     */
+    Long nextId();
+
+    /**
+     * <p>获取当前 idworker 的实现类型</p>
+     *
+     * @return IdWorkerTypeEnum
+     * @author chenpeng
+     * Create at February 12, 2019 at 13:48:51 GMT+8
+     */
+    IdWorkerTypeEnum getType();
+}

+ 77 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/IdWorkerContainer.java

@@ -0,0 +1,77 @@
+package com.usky.common.core.util.id;
+
+import com.google.common.collect.Maps;
+
+import java.util.concurrent.ConcurrentMap;
+
+import static com.usky.common.core.util.id.IdWorkerTypeEnum.SNOWFLAKE;
+
+/**
+ * <p>分布式 ID 生成器容器</p>
+ *
+ * @author chenpeng
+ * Create at February 12, 2019 at 11:56:08 GMT+8
+ */
+public final class IdWorkerContainer {
+
+    private static final String WORKERS_KEY_SEPARATOR = "_";
+
+    private static final ConcurrentMap<String, IdWorker> WORKERS = Maps.newConcurrentMap();
+
+    private Long workerId;
+
+    public IdWorkerContainer(Long workerId) {
+        this.workerId = workerId;
+    }
+
+    /**
+     * <p>获取给定 dataCenterId 对应的派号器,默认雪花</p>
+     *
+     * @param dataCenterId 数据中心 id
+     * @return SnowflakeIdWorker
+     * @author chenpeng
+     * Create at February 12, 2019 at 13:16:50 GMT+8
+     * @see IdWorker
+     */
+    public IdWorker getIdWorker(Long dataCenterId) {
+        return getIdWorker(dataCenterId, SNOWFLAKE);
+    }
+
+    /**
+     * <p>根据给定 dataCenterId 和类型获取对应的派号器,默认雪花</p>
+     *
+     * @param dataCenterId
+     * @param idWorkerType
+     * @return IdWorker
+     * @author chenpeng
+     * Create at February 12, 2019 at 13:38:10 GMT+8
+     * @see IdWorker
+     */
+    public IdWorker getIdWorker(Long dataCenterId, IdWorkerTypeEnum idWorkerType) {
+
+        IdWorker idWorker = null;
+
+        if (idWorkerType == null) {
+            idWorkerType = SNOWFLAKE;
+        }
+
+        if (dataCenterId != null) {
+            IdWorker target = createIdWorker(dataCenterId, idWorkerType);
+            if (target != null) {
+                WORKERS.putIfAbsent(idWorkerType.name() + WORKERS_KEY_SEPARATOR + dataCenterId,
+                        target);
+                idWorker = target;
+            }
+        }
+
+        return idWorker;
+    }
+
+    private IdWorker createIdWorker(Long dataCenterId, IdWorkerTypeEnum idWorkerType) {
+        IdWorker idWorker = null;
+        if (idWorkerType == SNOWFLAKE) {
+            idWorker = new SnowflakeIdWorker(dataCenterId, workerId);
+        }
+        return idWorker;
+    }
+}

+ 15 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/IdWorkerTypeEnum.java

@@ -0,0 +1,15 @@
+package com.usky.common.core.util.id;
+
+/**
+ * <p>分布式 Id 生成器实现类型</p>
+ *
+ * @author chenpeng
+ * Create at February 12, 2019 at 13:29:33 GMT+8
+ */
+public enum IdWorkerTypeEnum {
+
+    /**
+     * 雪花分布式 id
+     */
+    SNOWFLAKE
+}

+ 144 - 0
base-common/usky-common-core/src/main/java/com/usky/common/core/util/id/SnowflakeIdWorker.java

@@ -0,0 +1,144 @@
+package com.usky.common.core.util.id;
+
+import static com.usky.common.core.util.id.IdWorkerTypeEnum.SNOWFLAKE;
+
+/**
+ * <p>雪花分布式 ID 生成器</p>
+ *
+ * @author chenpeng
+ * Create at February 12, 2019 at 11:25:30 GMT+8
+ */
+public final class SnowflakeIdWorker implements IdWorker {
+
+    /**
+     * 纪元 2018-04-20
+     */
+    private final long epoch = 1524153600000L;
+    /**
+     * 机器ID所占的位数
+     */
+    private final long workerIdBits = 6L;
+    /**
+     * 数据标识ID所占的位数
+     */
+    private final long dataCenterIdBits = 6L;
+    /**
+     * 支持的最大机器ID,结果是31
+     */
+    private final long maxWorkerId = ~(-1L << workerIdBits);
+    /**
+     * 支持的最大数据标识ID,结果是31
+     */
+    private final long maxDataCenterId = ~(-1 << dataCenterIdBits);
+    /**
+     * 毫秒内序列在id中所占的位数
+     */
+    private final long sequenceBits = 12L;
+    /**
+     * 机器ID向左移12位
+     */
+    private final long workerIdShift = sequenceBits;
+    /**
+     * 数据标识ID向左移18(12+6)位
+     */
+    private final long dataCenterIdShift = sequenceBits + workerIdBits;
+    /**
+     * 时间戳向左移24(12+6+6)位
+     */
+    private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits;
+    /**
+     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
+     */
+    private final long sequenceMask = ~(-1L << sequenceBits);
+    /**
+     * 数据标识ID(0~63)
+     */
+    private long dataCenterId;
+    /**
+     * 机器ID(0~63)
+     */
+    private long workerId;
+    /**
+     * 毫秒内序列(0~4095)
+     */
+    private long sequence;
+    /**
+     * 上次生成ID的时间戳
+     */
+    private long lastTimestamp = -1L;
+
+    SnowflakeIdWorker(long dataCenterId, long workerId) {
+        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
+            throw new IllegalArgumentException(
+                    String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
+        }
+        if (workerId > maxWorkerId || workerId < 0) {
+            throw new IllegalArgumentException(
+                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
+        }
+        this.dataCenterId = dataCenterId;
+        this.workerId = workerId;
+    }
+
+    /**
+     * 获得下一个ID (该方法是线程安全的)
+     *
+     * @return snowflakeId
+     */
+    @Override
+    public synchronized Long nextId() {
+        long timestamp = generateTimeMills();
+        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,这个时候应当抛出异常
+        if (timestamp < lastTimestamp) {
+            throw new RuntimeException(
+                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
+                            lastTimestamp - timestamp));
+        }
+        //如果是同一时间生成的,则进行毫秒内序列
+        if (timestamp == lastTimestamp) {
+            sequence = (sequence + 1) & sequenceMask;
+            //毫秒内序列溢出
+            if (sequence == 0) {
+                //阻塞到下一个毫秒,获得新的时间戳
+                timestamp = nextMillis(lastTimestamp);
+            }
+        } else {
+            //时间戳改变,毫秒内序列重置
+            sequence = 0L;
+        }
+        lastTimestamp = timestamp;
+        //移位并通过按位或运算拼到一起组成64位的ID
+        return ((timestamp - epoch) << timestampShift)
+                | (dataCenterId << dataCenterIdShift)
+                | (workerId << workerIdShift)
+                | sequence;
+    }
+
+    @Override
+    public IdWorkerTypeEnum getType() {
+        return SNOWFLAKE;
+    }
+
+    /**
+     * 返回以毫秒为单位的当前时间
+     *
+     * @return 当前时间(毫秒)
+     */
+    private long generateTimeMills() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * 阻塞到下一个毫秒,直到获得新的时间戳
+     *
+     * @param lastTimestamp 上次生成ID的时间截
+     * @return 当前时间戳
+     */
+    private long nextMillis(long lastTimestamp) {
+        long timestamp = generateTimeMills();
+        while (timestamp <= lastTimestamp) {
+            timestamp = lastTimestamp;
+        }
+        return timestamp;
+    }
+}

+ 45 - 0
base-common/usky-common-mvc/pom.xml

@@ -0,0 +1,45 @@
+<?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>
+        <artifactId>base-common</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>usky-common-mvc</artifactId>
+
+
+    <dependencies>
+        <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>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+            <version>${parent.version}</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-spring</artifactId>
+            <version>${parent.version}</version>
+        </dependency>
+    </dependencies>
+</project>

+ 100 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/AbstractCrudService.java

@@ -0,0 +1,100 @@
+package com.usky.common.mvc.base;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.core.bean.UnifiedUser;
+
+
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * 基础 service
+ *
+ * @param <M>
+ * @param <T>
+ * @author chenpeng
+ * Create at December 2, 2018 at 14:05:49 GMT+8
+ */
+public abstract class AbstractCrudService<M extends BaseMapper<T>, T>
+        extends ServiceImpl<M, T> implements CrudService<T> {
+
+    private static final String DEFAULT_USER_ID = "0";
+    private static final String DEFAULT_USER_NAME = "system";
+    private static final UnifiedUser DEFAULT_OPERATOR = new UnifiedUser(DEFAULT_USER_ID, DEFAULT_USER_NAME);
+
+    @Override
+    public void fillDefaultOperator(T model) {
+        Optional.ofNullable(model).ifPresent(m -> {
+            if (model instanceof BaseModel) {
+                BaseModel baseModel = (BaseModel) model;
+                fillCreateOperator(baseModel, null);
+                fillUpdateOperator(baseModel, null);
+            }
+        });
+    }
+
+    @Override
+    public UnifiedUser getDefaultOperator() {
+        return DEFAULT_OPERATOR;
+    }
+
+    @Override
+    public void fillNecessaryOperatorProperty(T entity, UnifiedUser operator, boolean isUpdate) {
+        if (entity == null) {
+            return;
+        }
+        if (entity instanceof BaseModel) {
+            BaseModel baseModel = (BaseModel) entity;
+            if (isUpdate) {
+                fillUpdateAt(baseModel);
+                fillUpdateOperator(baseModel, operator);
+            } else {
+                fillCreateAt(baseModel);
+                fillCreateOperator(baseModel, operator);
+                fillUpdateAt(baseModel);
+                fillUpdateOperator(baseModel, operator);
+            }
+        }
+    }
+
+    @Override
+    public <E> CommonPage<E> ToCommonPage(IPage<E> page) {
+        CommonPage<E> commonPage = new CommonPage<>();
+        commonPage.setTotal(page.getTotal());
+        commonPage.setCurrent(page.getCurrent());
+        commonPage.setRecords(page.getRecords());
+        commonPage.setSize(page.getSize());
+        return commonPage;
+    }
+
+    private void fillUpdateOperator(BaseModel baseModel, UnifiedUser operator) {
+        if (baseModel != null) {
+            operator = (operator == null) ? DEFAULT_OPERATOR : operator;
+            baseModel.setUpdateUserId(operator.getId().toString());
+            baseModel.setUpdateUserName(operator.getName());
+        }
+    }
+
+    private void fillCreateOperator(BaseModel baseModel, UnifiedUser operator) {
+        if (baseModel != null) {
+            operator = (operator == null) ? DEFAULT_OPERATOR : operator;
+            baseModel.setCreateUserId(operator.getId().toString());
+            baseModel.setCreateUserName(operator.getName());
+        }
+    }
+
+    private void fillUpdateAt(BaseModel baseModel) {
+        if (baseModel != null) {
+            baseModel.setUpdateTime(new Date());
+        }
+    }
+
+    private void fillCreateAt(BaseModel baseModel) {
+        if (baseModel != null) {
+            baseModel.setCreateTime(new Date());
+        }
+    }
+}

+ 26 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/BaseModel.java

@@ -0,0 +1,26 @@
+package com.usky.common.mvc.base;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * <p>基础模型</p>
+ *
+ * @author chenpeng
+ * Create at January 2, 2019 at 15:35:46 GMT+8
+ */
+@Data
+public abstract class BaseModel implements Serializable {
+
+    private static final long serialVersionUID = 6107459102213712827L;
+
+    private Date createTime;
+    private Date updateTime;
+    private String createUserId;
+    private String updateUserId;
+    private String createUserName;
+    private String updateUserName;
+
+}

+ 13 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/CrudMapper.java

@@ -0,0 +1,13 @@
+package com.usky.common.mvc.base;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * CRUD 基础 mapper
+ *
+ * @param <T>
+ * @author chenpeng
+ * Create at December 2, 2018 at 11:40:59 GMT+8
+ */
+public interface CrudMapper<T> extends BaseMapper<T> {
+}

+ 47 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/base/CrudService.java

@@ -0,0 +1,47 @@
+package com.usky.common.mvc.base;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.core.bean.UnifiedUser;
+
+
+/**
+ * 基础 crud service
+ *
+ * @param <T>
+ * @author chenpeng
+ * Create at December 2, 2018 at 14:37:34 GMT+8
+ * @see AbstractCrudService
+ */
+public interface CrudService<T> extends IService<T> {
+
+    /**
+     * <p>填充默认操作用户</p>
+     *
+     * @author chenpeng
+     * Create at January 2, 2019 at 19:32:38 GMT+8
+     */
+    void fillDefaultOperator(T model);
+
+    /**
+     * <p>获取默认操作用户</p>
+     *
+     * @author chenpeng
+     * Create at January 2, 2019 at 19:42:55 GMT+8
+     */
+    UnifiedUser getDefaultOperator();
+
+    /**
+     * <p>填充必要参数</p>
+     *
+     * @param model
+     * @param operator
+     * @param isUpdate
+     * @author chenpeng
+     * Create at March 13, 2019 at 18:01:52 GMT+8
+     */
+    void fillNecessaryOperatorProperty(T model, UnifiedUser operator, boolean isUpdate);
+
+    <E> CommonPage<E> ToCommonPage(IPage<E> page);
+}

+ 52 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/BaseEnumConverterFactory.java

@@ -0,0 +1,52 @@
+package com.usky.common.mvc.config.convert;
+
+
+import com.usky.common.core.enums.IEnum;
+import com.usky.common.core.util.StringUtils;
+import lombok.SneakyThrows;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+public class BaseEnumConverterFactory implements ConverterFactory<String, IEnum> {
+
+    private static final Map<Class, Converter> converterMap = new WeakHashMap<>();
+
+    private static final String ENUM_CONVERT_METHOD_NAME = "parse";
+
+    @SneakyThrows
+    @Override
+    public <T extends IEnum> Converter<String, T> getConverter(Class<T> targetType) {
+
+        Converter result = converterMap.get(targetType);
+        if (result == null) {
+            result = new StringToIEumConverter<T>(targetType);
+            converterMap.put(targetType, result);
+        }
+        return result;
+    }
+
+    private static class StringToIEumConverter<T extends IEnum> implements Converter<String, T> {
+
+        private final Class<T> targerType;
+
+        public StringToIEumConverter(Class<T> targerType) {
+            this.targerType = targerType;
+        }
+
+        @SneakyThrows
+        @Override
+        public T convert(String source) {
+            if (StringUtils.isEmpty(source)) {
+                return null;
+            }
+            Method method = targerType.getDeclaredMethod(ENUM_CONVERT_METHOD_NAME, String.class);
+            Object o = method.invoke(null, source);
+            return (T) o;
+        }
+    }
+
+}

+ 18 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/DateStringConvert.java

@@ -0,0 +1,18 @@
+package com.usky.common.mvc.config.convert;
+
+
+import com.usky.common.core.util.DateUtils;
+import org.springframework.core.convert.converter.Converter;
+
+import java.util.Date;
+
+/**
+ * @author barry chen
+ * @date 2020/11/19 4:13 下午
+ */
+public class DateStringConvert implements Converter<Date, String> {
+    @Override
+    public String convert(Date source) {
+        return DateUtils.format(source);
+    }
+}

+ 30 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/FormatterConfiguration.java

@@ -0,0 +1,30 @@
+package com.usky.common.mvc.config.convert;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.format.support.FormattingConversionService;
+import org.springframework.http.converter.ObjectToStringHttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author chen
+ */
+@Configuration
+public class FormatterConfiguration implements WebMvcConfigurer {
+
+    @Override
+    public void addFormatters(FormatterRegistry registry) {
+        registry.addConverterFactory(new BaseEnumConverterFactory());
+        registry.addConverter(new StringDateConverter());
+    }
+
+    @Bean
+    public ObjectToStringHttpMessageConverter objectToStringHttpMessageConverter() {
+        FormattingConversionService conversionService = new FormattingConversionService();
+        conversionService.addConverter(new StringDateConverter());
+        conversionService.addConverter(new DateStringConvert());
+        ObjectToStringHttpMessageConverter httpMessageconverter = new ObjectToStringHttpMessageConverter(conversionService);
+        return httpMessageconverter;
+    }
+}

+ 18 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/convert/StringDateConverter.java

@@ -0,0 +1,18 @@
+package com.usky.common.mvc.config.convert;
+
+
+import com.usky.common.core.util.DateUtils;
+import org.springframework.core.convert.converter.Converter;
+
+import java.util.Date;
+
+/**
+ * @author barry chen
+ * @date 2020/9/18 3:02 下午
+ */
+public class StringDateConverter implements Converter<String, Date> {
+    @Override
+    public Date convert(String source) {
+        return DateUtils.parseDate(source);
+    }
+}

+ 22 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/multipartFile/CommonsMultipartResolverConfiguration.java

@@ -0,0 +1,22 @@
+package com.usky.common.mvc.config.multipartFile;
+
+import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.multipart.commons.CommonsMultipartResolver;
+
+@Configuration
+@EnableConfigurationProperties(MultipartProperties.class)
+public class CommonsMultipartResolverConfiguration {
+    @Bean
+    public CommonsMultipartResolver commonsMultipartResolver(MultipartProperties multipartProperties) {
+        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
+        resolver.setDefaultEncoding("UTF-8");
+        resolver.setResolveLazily(true);
+        resolver.setMaxInMemorySize(Long.valueOf(multipartProperties.getFileSizeThreshold().toBytes()).intValue());
+        resolver.setMaxUploadSize(Long.valueOf(multipartProperties.getMaxRequestSize().toBytes()).intValue());
+        resolver.setMaxUploadSizePerFile(Long.valueOf(multipartProperties.getMaxFileSize().toBytes()).intValue());
+        return resolver;
+    }
+}

+ 39 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/config/validator/ValidatorExtensionAutoConfiguration.java

@@ -0,0 +1,39 @@
+//package com.usky.common.mvc.config.validator;
+//
+//
+//
+//import org.hibernate.validator.HibernateValidator;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
+//
+//import javax.validation.Validation;
+//import javax.validation.Validator;
+//import javax.validation.ValidatorFactory;
+//
+///**
+// * <p>Bean 验证器配置</p>
+// *
+// * @author chenpeng
+// * Create at May 30, 2019 at 15:36:05 GMT+8
+// */
+//@Configuration
+//public class ValidatorExtensionAutoConfiguration {
+//
+//    @Bean
+//    public MethodValidationPostProcessor methodValidationPostProcessor() {
+//        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
+//        postProcessor.setValidator(validator());
+//        return postProcessor;
+//    }
+//
+//    @Bean
+//    public Validator validator() {
+//        ValidatorFactory validatorFactory = Validation
+//                .byProvider(HibernateValidator.class)
+//                .configure()
+//                .failFast(false)
+//                .buildValidatorFactory();
+//        return validatorFactory.getValidator();
+//    }
+//}

+ 267 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/handler/UnifiedExceptionHandler.java

@@ -0,0 +1,267 @@
+package com.usky.common.mvc.handler;
+
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.exception.*;
+import com.google.common.base.Throwables;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.util.CollectionUtils;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+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 org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.List;
+import java.util.Set;
+
+import static com.usky.common.core.exception.ErrorMessageConsts.BIZ_ERROR_LOG_MESSAGE_TEMPLATE;
+import static com.usky.common.core.exception.ErrorMessageConsts.SYS_ERROR_LOG_MESSAGE_TEMPLATE;
+
+
+/**
+ * <p>统一异常处理</p>
+ *
+ * @author chenpeng
+ * Create time 2018年11月30日 下午4:41:27
+ * @see ErrorCode
+ * @see com.usky.common.core.exception.BusinessException
+ */
+@ControllerAdvice
+public class UnifiedExceptionHandler {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(UnifiedExceptionHandler.class);
+
+    private static final String EXCEPTION_MESSAGE_SEPARATOR = ":";
+
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(BusinessException.class)
+    public ApiResult<Void> handleBusinessException(BusinessException e) {
+        ErrorCode errorCode = e.getErrorCode();
+        logWarn(errorCode, e, false);
+        String customMessage = getSimpleCustomMessage(e.getCustomMessage());
+        return ApiResult.error(errorCode.getCode(), e.getClass().getName(), errorCode.getDefaultMessage()
+                + (StringUtils.isBlank(customMessage) ? StringUtils.EMPTY : EXCEPTION_MESSAGE_SEPARATOR + customMessage));
+    }
+
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(BusinessCheckException.class)
+    public ApiResult<Void> handleBusinessCheckException(BusinessCheckException e) {
+        ErrorCode errorCode = e.getErrorCode();
+        logError(errorCode, e, false);
+        String customMessage = getSimpleCustomMessage(e.getCustomMessage());
+        return ApiResult.error(errorCode.getCode(), e.getClass().getName(), errorCode.getDefaultMessage()
+                + (StringUtils.isBlank(customMessage) ? StringUtils.EMPTY : EXCEPTION_MESSAGE_SEPARATOR + customMessage));
+    }
+
+    /**
+     * <p>系统异常处理</p>
+     *
+     * @author chenpeng
+     * Create at March 7, 2019 at 13:50:45 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(ApplicationException.class)
+    public ApiResult<Void> handleApplicationException(ApplicationException e) {
+        ErrorCode errorCode = e.getErrorCode();
+        logError(errorCode, e, true);
+        return ApiResult.error(errorCode.getCode(), e.getClass().getName(), errorCode.getDefaultMessage());
+    }
+
+    /**
+     * <p>系统异常处理</p>
+     *
+     * @author chenpeng
+     * Create at March 7, 2019 at 13:50:45 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(ApplicationCheckException.class)
+    public ApiResult<Void> handleApplicationCheckException(ApplicationCheckException e) {
+        ErrorCode errorCode = e.getErrorCode();
+        logError(errorCode, e, true);
+        return ApiResult.error(errorCode.getCode(), e.getClass().getName(), errorCode.getDefaultMessage());
+    }
+
+    /**
+     * <p>最终兜底异常处理</p>
+     *
+     * @author chenpeng
+     * Create at March 7, 2019 at 13:50:22 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(Throwable.class)
+    public ApiResult<Void> handleThrowable(Throwable e) {
+
+        ErrorCode errorCode;
+
+        if (e instanceof OutOfMemoryError) {
+            errorCode = SystemErrorCode.SYS_SYSTEM_OOM_ERROR;
+        } else if (e instanceof StackOverflowError) {
+            errorCode = SystemErrorCode.SYS_SYSTEM_SOF_ERROR;
+        } else {
+            errorCode = SystemErrorCode.SYS_SYSTEM_ERROR;
+        }
+
+        logError(errorCode, e, true);
+        return ApiResult.error(errorCode.getCode(), e.getClass().getName(), String.format("%s-%s-%s", errorCode.getDefaultMessage(),
+                e.getClass().getSimpleName(), e.getMessage()));
+    }
+
+    /**
+     * <p>参数异常处理</p>
+     *
+     * @author chenpeng
+     * Create at March 7, 2019 at 13:51:01 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(ConstraintViolationException.class)
+    public ApiResult<Void> handleConstraintViolationException(ConstraintViolationException ex) {
+        String errorMessage = null;
+        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
+        if (!CollectionUtils.isEmpty(constraintViolations)) {
+            StringBuilder messageBuilder = new StringBuilder();
+            constraintViolations.forEach(cv -> messageBuilder.append(cv.getMessage()).append(","));
+            errorMessage = messageBuilder.toString();
+            if (errorMessage.length() > 1) {
+                errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
+            }
+        }
+        ErrorCode errorCode = ErrorCode.create(BusinessErrorCode.BIZ_INVALID_PARAM_ERROR.getCode(),
+                BusinessErrorCode.BIZ_INVALID_PARAM_ERROR.getDefaultMessage());
+        return handleBusinessException(new BusinessException(errorCode, ex, errorMessage));
+    }
+
+    /**
+     * <p>方法参数异常处理</p>
+     *
+     * @author chenpeng
+     * Create at March 7, 2019 at 13:51:20 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
+    public ApiResult<Void> handleMethodArgumentNotValidException(Exception ex) {
+        String errorMessage = null;
+        List<ObjectError> objectErrors = null;
+        if (ex instanceof MethodArgumentNotValidException) {
+            objectErrors = ((MethodArgumentNotValidException) ex).getBindingResult().getAllErrors();
+        } else if (ex instanceof BindException) {
+            objectErrors = ((BindException) ex).getBindingResult().getAllErrors();
+        }
+        if (!CollectionUtils.isEmpty(objectErrors)) {
+            StringBuilder messageBuilder = new StringBuilder();
+            objectErrors.forEach(o -> {
+                if (o instanceof FieldError) {
+                    messageBuilder.append(((FieldError) o).getField());
+                }
+                messageBuilder.append(o.getDefaultMessage()).append(",");
+            });
+            errorMessage = messageBuilder.toString();
+            if (errorMessage.length() > 1) {
+                errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
+            }
+        }
+        ErrorCode errorCode = ErrorCode.create(BusinessErrorCode.BIZ_INVALID_PARAM_ERROR.getCode(),
+                BusinessErrorCode.BIZ_INVALID_PARAM_ERROR.getDefaultMessage());
+        return handleBusinessException(new BusinessException(errorCode, ex, errorMessage));
+    }
+
+    /**
+     * <p>Servlet 注解的参数异常处理</p>
+     *
+     * @author chenpeng
+     * Create at March 7, 2019 at 13:51:40 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    public ApiResult<Void> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
+        ErrorCode errorCode = ErrorCode.create(BusinessErrorCode.BIZ_LACK_NECESSARY_PARAM_ERROR.getCode(),
+                BusinessErrorCode.BIZ_LACK_NECESSARY_PARAM_ERROR.getDefaultMessage());
+        BusinessException be = new BusinessException(errorCode, e, e.getParameterName() + " is required!");
+        return handleBusinessException(be);
+    }
+
+    /**
+     * <p>Jackson Mapping 异常处理</p>
+     *
+     * @author chenpeng
+     * Create at June 5, 2019 at 10:24:48 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(HttpMessageNotReadableException.class)
+    public ApiResult<Void> handleJsonMappingException(HttpMessageNotReadableException e) {
+        Throwable rootCause = Throwables.getRootCause(e);
+        ErrorCode errorCode = ErrorCode.create(BusinessErrorCode.BIZ_INVALID_PARAM_ERROR.getCode(),
+                BusinessErrorCode.BIZ_INVALID_PARAM_ERROR.getDefaultMessage());
+        BusinessException be = new BusinessException(errorCode, rootCause, rootCause.getMessage());
+        return handleBusinessException(be);
+    }
+
+    /**
+     * <p>Duplicate key 异常处理</p>
+     *
+     * @author chenpeng
+     * Create at June 10, 2019 at 18:27:05 GMT+8
+     */
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(DuplicateKeyException.class)
+    public ApiResult<Void> handleDuplicateKeyException(DuplicateKeyException e) {
+        Throwable rootCause = Throwables.getRootCause(e);
+        ErrorCode errorCode = ErrorCode.create(BusinessErrorCode.BIZ_DUPLICATE_KEY_ERROR.getCode(),
+                BusinessErrorCode.BIZ_DUPLICATE_KEY_ERROR.getDefaultMessage());
+        BusinessException be = new BusinessException(errorCode, rootCause, rootCause.getMessage());
+        return handleBusinessException(be);
+    }
+
+    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+    @ResponseBody
+    @ExceptionHandler(NoHandlerFoundException.class)
+    public ApiResult<Void> handlerNoFoundException(Exception e) {
+        Throwable rootCause = Throwables.getRootCause(e);
+        ErrorCode errorCode = ErrorCode.create(BusinessErrorCode.BIZ_PATH_NOT_FOUND.getCode(),
+                BusinessErrorCode.BIZ_PATH_NOT_FOUND.getDefaultMessage());
+        BusinessException be = new BusinessException(errorCode, rootCause, rootCause.getMessage());
+        return handleBusinessException(be);
+    }
+
+    private void logError(ErrorCode errorCode, Throwable t, boolean isSystemError) {
+        String logTemplate = isSystemError ? SYS_ERROR_LOG_MESSAGE_TEMPLATE : BIZ_ERROR_LOG_MESSAGE_TEMPLATE;
+        LOGGER.error(String.format(logTemplate,
+                errorCode.getCode(),
+                t.getMessage()), t);
+    }
+
+    private void logWarn(ErrorCode errorCode, Throwable t, boolean isSystemError) {
+        String logTemplate = isSystemError ? SYS_ERROR_LOG_MESSAGE_TEMPLATE : BIZ_ERROR_LOG_MESSAGE_TEMPLATE;
+        LOGGER.warn(String.format(logTemplate,
+                errorCode.getCode(),
+                t.getMessage()), t);
+    }
+
+    private String getSimpleCustomMessage(String message) {
+        String customMessage = StringUtils.isBlank(message) ? StringUtils.EMPTY : message;
+        customMessage = (StringUtils.isNotBlank(customMessage) && customMessage.contains(EXCEPTION_MESSAGE_SEPARATOR))
+                ? customMessage.split(EXCEPTION_MESSAGE_SEPARATOR)[0] : customMessage;
+        return customMessage;
+    }
+}

+ 171 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/utils/IpUtils.java

@@ -0,0 +1,171 @@
+package com.usky.common.mvc.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * 获取IP方法
+ *
+ * @author ruoyi
+ */
+public final class IpUtils {
+
+    private IpUtils() {
+    }
+
+    public static String getIpAddr(HttpServletRequest request) {
+        if (request == null) {
+            return "unknown";
+        }
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Forwarded-For");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Real-IP");
+        }
+
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+
+        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+    }
+
+    public static boolean internalIp(String ip) {
+        byte[] addr = textToNumericFormatV4(ip);
+        return internalIp(addr) || "127.0.0.1".equals(ip);
+    }
+
+    private static boolean internalIp(byte[] addr) {
+        if (Objects.isNull(addr) || addr.length < 2) {
+            return true;
+        }
+        final byte b0 = addr[0];
+        final byte b1 = addr[1];
+        // 10.x.x.x/8
+        final byte section1 = 0x0A;
+        // 172.16.x.x/12
+        final byte section2 = (byte) 0xAC;
+        final byte section3 = (byte) 0x10;
+        final byte section4 = (byte) 0x1F;
+        // 192.168.x.x/16
+        final byte section5 = (byte) 0xC0;
+        final byte section6 = (byte) 0xA8;
+        switch (b0) {
+            case section1:
+                return true;
+            case section2:
+                if (b1 >= section3 && b1 <= section4) {
+                    return true;
+                }
+            case section5:
+                switch (b1) {
+                    case section6:
+                        return true;
+                    default:
+                        return false;
+                }
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * 将IPv4地址转换成字节
+     *
+     * @param text IPv4地址
+     * @return byte 字节
+     */
+    public static byte[] textToNumericFormatV4(String text) {
+        if (text.length() == 0) {
+            return null;
+        }
+
+        byte[] bytes = new byte[4];
+        String[] elements = text.split("\\.", -1);
+        try {
+            long l;
+            int i;
+            switch (elements.length) {
+                case 1:
+                    l = Long.parseLong(elements[0]);
+                    if ((l < 0L) || (l > 4294967295L)) {
+                        return null;
+                    }
+                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
+                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
+                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 2:
+                    l = Integer.parseInt(elements[0]);
+                    if ((l < 0L) || (l > 255L)) {
+                        return null;
+                    }
+                    bytes[0] = (byte) (int) (l & 0xFF);
+                    l = Integer.parseInt(elements[1]);
+                    if ((l < 0L) || (l > 16777215L)) {
+                        return null;
+                    }
+                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
+                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 3:
+                    for (i = 0; i < 2; ++i) {
+                        l = Integer.parseInt(elements[i]);
+                        if ((l < 0L) || (l > 255L)) {
+                            return null;
+                        }
+                        bytes[i] = (byte) (int) (l & 0xFF);
+                    }
+                    l = Integer.parseInt(elements[2]);
+                    if ((l < 0L) || (l > 65535L)) {
+                        return null;
+                    }
+                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 4:
+                    for (i = 0; i < 4; ++i) {
+                        l = Integer.parseInt(elements[i]);
+                        if ((l < 0L) || (l > 255L)) {
+                            return null;
+                        }
+                        bytes[i] = (byte) (int) (l & 0xFF);
+                    }
+                    break;
+                default:
+                    return null;
+            }
+        } catch (NumberFormatException e) {
+            return null;
+        }
+        return bytes;
+    }
+
+    public static String getHostIp() {
+        try {
+            return InetAddress.getLocalHost().getHostAddress();
+        } catch (UnknownHostException e) {
+        }
+        return "127.0.0.1";
+    }
+
+    public static String getHostName() {
+        try {
+            return InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException e) {
+        }
+        return "未知";
+    }
+}

+ 31 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/utils/JdbcUtils.java

@@ -0,0 +1,31 @@
+package com.usky.common.mvc.utils;
+
+import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+import java.util.List;
+
+/**
+ * @author barry chen
+ * @date 2020/12/10 4:41 下午
+ */
+public final class JdbcUtils {
+    private JdbcUtils() {
+    }
+
+    /**
+     * 批量执行sql. 可用于insert/update/delete
+     *
+     * @param jdbctemplate
+     * @param sql
+     * @param entityList
+     * @return
+     */
+    public static int[] batchUpdate(NamedParameterJdbcTemplate jdbctemplate, String sql, List<?> entityList) {
+        BeanPropertySqlParameterSource[] notifyParamArray = new BeanPropertySqlParameterSource[entityList.size()];
+        for (int i = 0; i < entityList.size(); i++) {
+            notifyParamArray[i] = new BeanPropertySqlParameterSource(entityList.get(i));
+        }
+        return jdbctemplate.batchUpdate(sql, notifyParamArray);
+    }
+}

+ 120 - 0
base-common/usky-common-mvc/src/main/java/com/usky/common/mvc/utils/ServletUtils.java

@@ -0,0 +1,120 @@
+package com.usky.common.mvc.utils;
+
+import com.usky.common.core.util.Convert;
+import com.usky.common.core.util.StringUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
+/**
+ * 客户端工具类
+ *
+ * @author ruoyi
+ */
+public class ServletUtils {
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name) {
+        return getRequest().getParameter(name);
+    }
+
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name, String defaultValue) {
+        return Convert.toStr(getRequest().getParameter(name), defaultValue);
+    }
+
+    /**
+     * 获取Integer参数
+     */
+    public static Integer getParameterToInt(String name) {
+        return Convert.toInt(getRequest().getParameter(name));
+    }
+
+    /**
+     * 获取Integer参数
+     */
+    public static Integer getParameterToInt(String name, Integer defaultValue) {
+        return Convert.toInt(getRequest().getParameter(name), defaultValue);
+    }
+
+    /**
+     * 获取request
+     */
+    public static HttpServletRequest getRequest() {
+        return getRequestAttributes().getRequest();
+    }
+
+    /**
+     * 获取response
+     */
+    public static HttpServletResponse getResponse() {
+        return getRequestAttributes().getResponse();
+    }
+
+    /**
+     * 获取session
+     */
+    public static HttpSession getSession() {
+        return getRequest().getSession();
+    }
+
+    public static ServletRequestAttributes getRequestAttributes() {
+        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
+        return (ServletRequestAttributes) attributes;
+    }
+
+    /**
+     * 将字符串渲染到客户端
+     *
+     * @param response 渲染对象
+     * @param string   待渲染的字符串
+     * @return null
+     */
+    public static String renderString(HttpServletResponse response, String string) {
+        try {
+            response.setStatus(200);
+            response.setContentType("application/json");
+            response.setCharacterEncoding("utf-8");
+            response.getWriter().print(string);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 是否是Ajax异步请求
+     *
+     * @param request
+     */
+    public static boolean isAjaxRequest(HttpServletRequest request) {
+        String accept = request.getHeader("accept");
+        if (accept != null && accept.indexOf("application/json") != -1) {
+            return true;
+        }
+
+        String xRequestedWith = request.getHeader("X-Requested-With");
+        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
+            return true;
+        }
+
+        String uri = request.getRequestURI();
+        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
+            return true;
+        }
+
+        String ajax = request.getParameter("__ajax");
+        if (StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml")) {
+            return true;
+        }
+        return false;
+    }
+}

+ 60 - 0
base-common/usky-common-spring/pom.xml

@@ -0,0 +1,60 @@
+<?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>
+        <artifactId>base-common</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>usky-common-spring</artifactId>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+            <version>${parent.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <!-- SpringCloud Loadbalancer -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <version>2.10.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+            <version>2.0</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 34 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/annotion/ConditionalOnPropertyNotEmpty.java

@@ -0,0 +1,34 @@
+package com.usky.common.spring.annotion;
+
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Map;
+
+/**
+ * 由于 @ConditionalOnProperty 没有提供"当proper存在且为任意值时,触发条件"的特性, 所以使用该注解实现.
+ * 当 value 对应的proper"被配置且有值不为空"时, 会被触发.
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Conditional(ConditionalOnPropertyNotEmpty.OnPropertyNotEmptyCondition.class)
+public @interface ConditionalOnPropertyNotEmpty {
+    String value();
+
+    class OnPropertyNotEmptyCondition implements Condition {
+
+        @Override
+        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+            Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnPropertyNotEmpty.class.getName());
+            String propertyName = (String) attrs.get("value");
+            String val = context.getEnvironment().getProperty(propertyName);
+            return val != null && !val.trim().isEmpty();
+        }
+    }
+}

+ 26 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/id/IdWorkerAutoConfiguration.java

@@ -0,0 +1,26 @@
+package com.usky.common.spring.config.id;
+
+
+import com.usky.common.core.util.id.IdWorkerContainer;
+import com.usky.common.spring.annotion.ConditionalOnPropertyNotEmpty;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+@ConditionalOnPropertyNotEmpty("id.workerId")
+@Configuration
+@EnableConfigurationProperties(IdWorkerConfigProperties.class)
+public class IdWorkerAutoConfiguration {
+
+    @Autowired
+    private IdWorkerConfigProperties properties;
+
+    @Bean
+    @Primary
+    public IdWorkerContainer idWorkerContainer() {
+        Long workerId = properties.getWorkerId();
+        return new IdWorkerContainer(workerId);
+    }
+}

+ 17 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/id/IdWorkerConfigProperties.java

@@ -0,0 +1,17 @@
+package com.usky.common.spring.config.id;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * <p>分布式 ID 自动配置 properties </p>
+ *
+ * @author chenpeng
+ * Create at February 12, 2019 at 11:20:20 GMT+8
+ */
+@Data
+@ConfigurationProperties(prefix = "id")
+public class IdWorkerConfigProperties {
+
+    private Long workerId;
+}

+ 47 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/jackson/JacksonConfig.java

@@ -0,0 +1,47 @@
+package com.usky.common.spring.config.jackson;
+
+
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.usky.common.core.util.JacksonComponent;
+import com.usky.common.core.util.JsonMapper;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+@Configuration
+public class JacksonConfig {
+
+    @Value("${spring.jackson.data-format:yyyy-MM-dd HH:mm:ss}")
+    private String pattern;
+
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+        return new Jackson2ObjectMapperBuilderCustomizer() {
+            @Override
+            public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
+                jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(JsonMapper.DEFAULT_LOCAL_DATE_TIME_SERIALIZER_PATTERN)));
+                jacksonObjectMapperBuilder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(JsonMapper.DEFAULT_LOCAL_DATE_SERIALIZER_PATTERN)));
+
+                jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(
+                        DateTimeFormatter.ofPattern(JsonMapper.DEFAULT_LOCAL_DATE_TIME_DESERIALIZER_PATTERN + "[" + pattern + "]")
+                                .withZone(ZoneOffset.UTC)));
+                jacksonObjectMapperBuilder.deserializerByType(LocalDate.class, new LocalDateDeserializer(
+                        DateTimeFormatter.ofPattern(JsonMapper.DEFAULT_LOCAL_DATE_DESERIALIZER_PATTERN)
+                                .withZone(ZoneOffset.UTC)));
+                jacksonObjectMapperBuilder.deserializerByType(Date.class, new JacksonComponent.DateJsonDeserializer());
+            }
+        };
+    }
+
+}

+ 45 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/EhCacheCacheConfiguration.java

@@ -0,0 +1,45 @@
+package com.usky.common.spring.config.redis;
+
+import org.springframework.boot.autoconfigure.cache.CacheProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.ehcache.EhCacheCacheManager;
+import org.springframework.cache.ehcache.EhCacheManagerUtils;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+
+/**
+ * @author yangqiang
+ * @date 2020-11-03
+ */
+@ConditionalOnProperty(prefix = "spring.cache.ehcache", value = {"enabled"}, havingValue = "true")
+@Configuration
+@EnableCaching
+@EnableConfigurationProperties(CacheProperties.class)
+public class EhCacheCacheConfiguration {
+    /**
+     * ehCache
+     */
+    public static final String EHCACHE_CACHE_MAANGER = "ehCacheCacheManager";
+
+
+    private final CacheProperties cacheProperties;
+
+    EhCacheCacheConfiguration(CacheProperties cacheProperties) {
+        this.cacheProperties = cacheProperties;
+    }
+
+    /**
+     * 创建ehCacheCacheManager
+     */
+    @Bean
+    public EhCacheCacheManager ehCacheCacheManager() {
+
+        Resource p = this.cacheProperties.getEhcache().getConfig();
+        Resource location = this.cacheProperties
+                .resolveConfigLocation(p);
+        return new EhCacheCacheManager(EhCacheManagerUtils.buildCacheManager(location));
+    }
+}

+ 119 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisExtensionAutoConfiguration.java

@@ -0,0 +1,119 @@
+package com.usky.common.spring.config.redis;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.cache.CacheProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.cache.RedisCacheWriter;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.time.Duration;
+import java.util.Map;
+
+/**
+ * <p>Redis 自动配置</p>
+ *
+ * @author chenpeng
+ * Create at February 18, 2019 at 15:58:59 GMT+8
+ */
+@ConditionalOnProperty(prefix = "spring.cache.redis", value = {"enabled"}, havingValue = "true")
+@Configuration
+@EnableConfigurationProperties(CacheProperties.class)
+public class RedisExtensionAutoConfiguration {
+
+    /**
+     * redis
+     */
+    public static final String REDIS_CACHE_MANAGER = "redisCacheManager";
+
+
+    @Autowired
+    private RedisOverdueTimeProperties redisProperties;
+
+    /**
+     * <p>配置 RedisTemplate</p>
+     *
+     * @author chenpeng
+     * Create at February 18, 2019 at 15:59:19 GMT+8
+     */
+    @Bean(name = "redisTemplate")
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        ObjectMapper objectMapper = new RedisObjectMapper();
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        redisTemplate.setKeySerializer(keySerializer());
+        redisTemplate.setHashKeySerializer(keySerializer());
+        redisTemplate.setValueSerializer(valueSerializer(objectMapper));
+        redisTemplate.setHashValueSerializer(valueSerializer(objectMapper));
+        redisTemplate.setEnableTransactionSupport(true);
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    /**
+     * <p>配置缓存</p>
+     *
+     * @author chenpeng
+     * Create at February 18, 2019 at 15:59:46 GMT+8
+     */
+    @Bean(REDIS_CACHE_MANAGER)
+    @Primary
+    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
+            CacheProperties cacheProperties) {
+
+        ObjectMapper objectMapper = new RedisObjectMapper();
+        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
+
+        if (cacheProperties != null && cacheProperties.getRedis() != null) {
+            Duration timeToLive = cacheProperties.getRedis().getTimeToLive();
+            if (timeToLive != null) {
+                config = config.entryTtl(timeToLive);
+            }
+        }
+
+        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
+                        valueSerializer(objectMapper)));
+
+        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
+
+        ImmutableSet.Builder<String> cacheNames = ImmutableSet.builder();
+        ImmutableMap.Builder<String, RedisCacheConfiguration> cacheConfig = ImmutableMap.builder();
+        for (Map.Entry<String, Duration> entry : redisProperties.getExpires().entrySet()) {
+            cacheNames.add(entry.getKey());
+            cacheConfig.put(entry.getKey(), config.entryTtl(entry.getValue()));
+        }
+
+        return RedisCacheManager.builder(redisCacheWriter)
+                .cacheDefaults(config)
+                .initialCacheNames(cacheNames.build())
+                .withInitialCacheConfigurations(cacheConfig.build())
+                .build();
+    }
+
+    @Bean
+    public RedisHelper redisHelper(RedisTemplate<String, Object> redisTemplate) {
+        return new RedisHelper(redisTemplate);
+    }
+
+    private RedisSerializer<String> keySerializer() {
+        return new StringRedisSerializer();
+    }
+
+    private RedisSerializer<Object> valueSerializer(ObjectMapper mapper) {
+        return new GenericJackson2JsonRedisSerializer(mapper);
+    }
+}

+ 1802 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisHelper.java

@@ -0,0 +1,1802 @@
+package com.usky.common.spring.config.redis;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * <p>Redis helper</p>
+ *
+ * @author chenpeng
+ * Create at February 18, 2019 at 16:04:36 GMT+8
+ */
+public final class RedisHelper {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RedisHelper.class);
+
+    private RedisTemplate<String, Object> redisTemplate;
+
+    RedisHelper(RedisTemplate<String, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    /**
+     * 删除 key
+     *
+     * @param key
+     * @author chenpeng
+     * Create at February 18, 2019 at 16:04:36 GMT+8
+     */
+    public Boolean delete(String key) {
+        return redisTemplate.delete(key);
+    }
+
+    /**
+     * 批量删除 key
+     *
+     * @param keys
+     */
+    public Long delete(Collection<String> keys) {
+        return redisTemplate.delete(keys);
+    }
+
+    /**
+     * 序列化key
+     *
+     * @param key
+     * @return
+     */
+    public byte[] dump(String key) {
+        return redisTemplate.dump(key);
+    }
+
+    /**
+     * 是否存在key
+     *
+     * @param key
+     * @return
+     */
+    public Boolean hasKey(String key) {
+        return redisTemplate.hasKey(key);
+    }
+
+    /**
+     * 设置过期时间
+     *
+     * @param key
+     * @param timeout
+     * @param unit
+     * @return
+     */
+    public Boolean expire(String key, long timeout, TimeUnit unit) {
+        return redisTemplate.expire(key, timeout, unit);
+    }
+
+    /**
+     * 设置过期时间
+     *
+     * @param key
+     * @param date
+     * @return
+     */
+    public Boolean expireAt(String key, Date date) {
+        return redisTemplate.expireAt(key, date);
+    }
+
+    /**
+     * 查找匹配的key
+     *
+     * @param pattern
+     * @return
+     */
+    public Set<String> keys(String pattern) {
+        return redisTemplate.keys(pattern);
+    }
+
+    /**
+     * 将 key 从当前选定的数据库 (请参阅 select 命令) 移动到指定的目标数据库。
+     * 当 key 已存在于目标数据库中, 或者源数据库中不存在时, 它不执行任何操作。
+     * 因此, 可以使用 move 作为锁定原语。
+     *
+     * @param key
+     * @param dbIndex
+     * @return
+     */
+    public Boolean move(String key, int dbIndex) {
+        return redisTemplate.move(key, dbIndex);
+    }
+
+    /**
+     * 删除 key 上的现有超时, 将 key 从可变 (具有过期时间的 key)
+     * 转换为持久 (该 key 永远不会过期, 因为没有关联的超时时间)。
+     *
+     * @param key
+     * @return
+     */
+    public Boolean persist(String key) {
+        return redisTemplate.persist(key);
+    }
+
+    /**
+     * 返回 key 的剩余的过期时间
+     *
+     * @param key
+     * @param unit
+     * @return
+     */
+    public Long getExpire(String key, TimeUnit unit) {
+        return redisTemplate.getExpire(key, unit);
+    }
+
+    /**
+     * 返回 key 的剩余的过期时间
+     *
+     * @param key
+     * @return
+     */
+    public Long getExpire(String key) {
+        return redisTemplate.getExpire(key);
+    }
+
+    /**
+     * 从当前数据库中随机返回一个 key
+     *
+     * @return
+     */
+    public String randomKey() {
+        return redisTemplate.randomKey();
+    }
+
+    /**
+     * 修改 key 的名称
+     *
+     * @param oldKey
+     * @param newKey
+     */
+    public void rename(String oldKey, String newKey) {
+        redisTemplate.rename(oldKey, newKey);
+    }
+
+    /**
+     * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
+     *
+     * @param oldKey
+     * @param newKey
+     * @return
+     */
+    public Boolean renameIfAbsent(String oldKey, String newKey) {
+        return redisTemplate.renameIfAbsent(oldKey, newKey);
+    }
+
+    /**
+     * 返回 key 所储存的值的类型
+     *
+     * @param key
+     * @return
+     */
+    public DataType type(String key) {
+        return redisTemplate.type(key);
+    }
+
+    /** -------------------string相关操作--------------------- */
+
+    /**
+     * 设置指定 key 的值
+     *
+     * @param key
+     * @param value
+     */
+    public void set(String key, Object value) {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 返回 key 中字符串值的子字符
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public String get(String key, long start, long end) {
+        return redisTemplate.opsForValue().get(key, start, end);
+    }
+
+    /**
+     * <p>通过指定 key 和 clazz,获得对应类型的数据</p>
+     *
+     * @author chenpeng
+     * Create at February 20, 2019 at 19:56:38 GMT+8
+     */
+    public <T> T get(String key, Class<T> clazz) {
+        return (T) get(key);
+    }
+
+    /**
+     * <p>通过指定 key 和 clazz,获得对应类型的数据</p>
+     *
+     * @author chenpeng
+     * Create at February 20, 2019 at 19:56:38 GMT+8
+     */
+    public <T> List<T> getList(String key, Class<T> clazz) {
+        final List<T> results = Lists.newArrayList();
+        Object values = get(key);
+        if (values != null) {
+            List r = (List) values;
+            r.forEach(e -> {
+                T t = null;
+                if (e != null) {
+                    t = (T) e;
+                }
+                results.add(t);
+            });
+        }
+        return results;
+    }
+
+    /**
+     * 获取指定 key 的值
+     *
+     * @param key
+     * @return
+     */
+    public Object get(String key) {
+        return redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Object getAndSetObject(String key, Object value) {
+        return redisTemplate.opsForValue().getAndSet(key, value);
+    }
+
+    /**
+     * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public <T> T getAndSet(String key, T value) {
+        Object r = getAndSetObject(key, value);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
+     *
+     * @param key
+     * @param offset
+     * @return
+     */
+    public Boolean getBit(String key, long offset) {
+        return redisTemplate.opsForValue().getBit(key, offset);
+    }
+
+    /**
+     * 批量获取
+     *
+     * @param keys
+     * @return
+     */
+    public List<Object> multiGetObject(Collection<String> keys) {
+        return redisTemplate.opsForValue().multiGet(keys);
+    }
+
+    /**
+     * 批量获取
+     *
+     * @param keys
+     * @return
+     */
+    public <T> List<T> multiGet(Collection<String> keys, Class<T> clazz) {
+        List<Object> objects = multiGetObject(keys);
+        return Optional.ofNullable(objects)
+                .orElse(Lists.newArrayList())
+                .stream()
+                .map(e -> {
+                    T r = apply(e);
+                    return r;
+                }).collect(Collectors.toList());
+    }
+
+    /**
+     * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
+     *
+     * @param key
+     * @param postion 位置
+     * @param value   值,true为1, false为0
+     * @return
+     */
+    public boolean setBit(String key, long offset, boolean value) {
+        return redisTemplate.opsForValue().setBit(key, offset, value);
+    }
+
+    /**
+     * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
+     *
+     * @param key
+     * @param value
+     * @param timeout 过期时间
+     * @param unit    时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
+     *                秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
+     */
+    public void set(String key, Object value, long timeout, TimeUnit unit) {
+        redisTemplate.opsForValue().set(key, value, timeout, unit);
+    }
+
+    /**
+     * 只有在 key 不存在时设置 key 的值
+     *
+     * @param key
+     * @param value
+     * @return 之前已经存在返回false, 不存在返回true
+     */
+    public boolean setIfAbsent(String key, Object value) {
+        return redisTemplate.opsForValue().setIfAbsent(key, value);
+    }
+
+    /**
+     * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
+     *
+     * @param key
+     * @param value
+     * @param offset 从指定位置开始覆写
+     */
+    public void set(String key, Object value, long offset) {
+        redisTemplate.opsForValue().set(key, value, offset);
+    }
+
+    /**
+     * 获取字符串的长度
+     *
+     * @param key
+     * @return
+     */
+    public Long size(String key) {
+        return redisTemplate.opsForValue().size(key);
+    }
+
+    /**
+     * 批量添加
+     *
+     * @param maps
+     */
+    public void multiSet(Map<String, String> maps) {
+        redisTemplate.opsForValue().multiSet(maps);
+    }
+
+    /**
+     * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
+     *
+     * @param maps
+     * @return 之前已经存在返回false, 不存在返回true
+     */
+    public boolean multiSetIfAbsent(Map<String, String> maps) {
+        return redisTemplate.opsForValue().multiSetIfAbsent(maps);
+    }
+
+    /**
+     * 增加(自增长), 负数则为自减
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long increment(String key, long increment) {
+        return redisTemplate.opsForValue().increment(key, increment);
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return
+     */
+    public Double increment(String key, double increment) {
+        return redisTemplate.opsForValue().increment(key, increment);
+    }
+
+    /**
+     * 追加到末尾
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Integer append(String key, String value) {
+        return redisTemplate.opsForValue().append(key, value);
+    }
+
+    /** -------------------hash相关操作------------------------- */
+
+    /**
+     * 获取存储在哈希表中指定字段的值
+     *
+     * @param key
+     * @param field
+     * @return
+     */
+    public Object hGet(String key, String field) {
+        return redisTemplate.opsForHash().get(key, field);
+    }
+
+    /**
+     * 获取存储在哈希表中指定字段的值
+     *
+     * @param key
+     * @param field
+     * @return
+     */
+    public <T> T hGet(String key, String field, Class<T> clazz) {
+        Object r = hGet(key, field);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 获取所有给定字段的值
+     *
+     * @param key
+     * @return
+     */
+    public Map<Object, Object> hGetAll(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * 获取所有给定字段的值
+     *
+     * @param key
+     * @param fields
+     * @return
+     */
+    public List<Object> hMultiGet(String key, Collection<String> fields) {
+        return redisTemplate.opsForHash().multiGet(key, (Collection) fields);
+    }
+
+    /**
+     * 获取所有给定字段的值
+     *
+     * @param key
+     * @param fields
+     * @return
+     */
+    public <T> List<T> hMultiGet(String key, Collection<String> fields, Class<T> clazz) {
+        List<Object> results = hMultiGet(key, fields);
+        return Optional.ofNullable(results)
+                .orElse(Lists.newArrayList())
+                .stream()
+                .map(e -> {
+                    T r = apply(e);
+                    return r;
+                }).collect(Collectors.toList());
+    }
+
+    public void hPut(String key, String hashKey, String value) {
+        redisTemplate.opsForHash().put(key, hashKey, value);
+    }
+
+    public void hPutAll(String key, Map<String, String> maps) {
+        redisTemplate.opsForHash().putAll(key, maps);
+    }
+
+    /**
+     * 仅当hashKey不存在时才设置
+     *
+     * @param key
+     * @param hashKey
+     * @param value
+     * @return
+     */
+    public Boolean hPutIfAbsent(String key, String hashKey, String value) {
+        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
+    }
+
+    /**
+     * 删除一个或多个哈希表字段
+     *
+     * @param key
+     * @param fields
+     * @return
+     */
+    public Long hDelete(String key, Object... fields) {
+        return redisTemplate.opsForHash().delete(key, fields);
+    }
+
+    /**
+     * 查看哈希表 key 中,指定的字段是否存在
+     *
+     * @param key
+     * @param field
+     * @return
+     */
+    public Boolean hExists(String key, String field) {
+        return redisTemplate.opsForHash().hasKey(key, field);
+    }
+
+    /**
+     * 为哈希表 key 中的指定字段的整数值加上增量 increment
+     *
+     * @param key
+     * @param field
+     * @param increment
+     * @return
+     */
+    public Long hIncrBy(String key, Object field, long increment) {
+        return redisTemplate.opsForHash().increment(key, field, increment);
+    }
+
+    /**
+     * 为哈希表 key 中的指定字段的整数值加上增量 increment
+     *
+     * @param key
+     * @param field
+     * @param delta
+     * @return
+     */
+    public Double hIncrByFloat(String key, Object field, double delta) {
+        return redisTemplate.opsForHash().increment(key, field, delta);
+    }
+
+    /**
+     * 获取所有哈希表中的字段
+     *
+     * @param key
+     * @return
+     */
+    public Set<Object> hKeys(String key) {
+        return redisTemplate.opsForHash().keys(key);
+    }
+
+    /**
+     * 获取哈希表中字段的数量
+     *
+     * @param key
+     * @return
+     */
+    public Long hSize(String key) {
+        return redisTemplate.opsForHash().size(key);
+    }
+
+    /**
+     * 获取哈希表中所有值
+     *
+     * @param key
+     * @return
+     */
+    public List<Object> hValues(String key) {
+        return redisTemplate.opsForHash().values(key);
+    }
+
+    /**
+     * 迭代哈希表中的键值对
+     *
+     * @param key
+     * @param options
+     * @return
+     */
+    public Cursor<Map.Entry<Object, Object>> hScan(String key, ScanOptions options) {
+        return redisTemplate.opsForHash().scan(key, options);
+    }
+
+    /** ------------------------list相关操作---------------------------- */
+
+    /**
+     * 通过索引获取列表中的元素
+     *
+     * @param key
+     * @param index
+     * @return
+     */
+    public Object lIndex(String key, long index) {
+        return redisTemplate.opsForList().index(key, index);
+    }
+
+    /**
+     * 通过索引获取列表中的元素
+     *
+     * @param key
+     * @param index
+     * @return
+     */
+    public <T> T lIndex(String key, long index, Class<T> clazz) {
+        Object r = lIndex(key, index);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 获取列表指定范围内的元素
+     *
+     * @param key
+     * @param start 开始位置, 0是开始位置
+     * @param end   结束位置, -1返回所有
+     * @return
+     */
+    public List<Object> lRange(String key, long start, long end) {
+        return redisTemplate.opsForList().range(key, start, end);
+    }
+
+    /**
+     * 获取列表指定范围内的元素
+     *
+     * @param key
+     * @param start 开始位置, 0是开始位置
+     * @param end   结束位置, -1返回所有
+     * @return
+     */
+    public <T> List<T> lRange(String key, long start, long end, Class<T> clazz) {
+        List<Object> results = lRange(key, start, end);
+        return Optional.ofNullable(results)
+                .orElse(Lists.newArrayList())
+                .stream()
+                .map(e -> {
+                    T r = apply(e);
+                    return r;
+                }).collect(Collectors.toList());
+    }
+
+    /**
+     * 存储在list头部
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lLeftPush(String key, Object value) {
+        return redisTemplate.opsForList().leftPush(key, value);
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lLeftPushAll(String key, String... value) {
+        return redisTemplate.opsForList().leftPushAll(key, value);
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lLeftPushAll(String key, Collection<String> value) {
+        return redisTemplate.opsForList().leftPushAll(key, value);
+    }
+
+    /**
+     * 当list存在的时候才加入
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lLeftPushIfPresent(String key, String value) {
+        return redisTemplate.opsForList().leftPushIfPresent(key, value);
+    }
+
+    /**
+     * 如果pivot存在,再pivot前面添加
+     *
+     * @param key
+     * @param pivot
+     * @param value
+     * @return
+     */
+    public Long lLeftPush(String key, String pivot, String value) {
+        return redisTemplate.opsForList().leftPush(key, pivot, value);
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lRightPush(String key, Object value) {
+        return redisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lRightPushAll(String key, String... value) {
+        return redisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lRightPushAll(String key, Collection<String> value) {
+        return redisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * 为已存在的列表添加值
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long lRightPushIfPresent(String key, String value) {
+        return redisTemplate.opsForList().rightPushIfPresent(key, value);
+    }
+
+    /**
+     * 在pivot元素的右边添加值
+     *
+     * @param key
+     * @param pivot
+     * @param value
+     * @return
+     */
+    public Long lRightPush(String key, String pivot, String value) {
+        return redisTemplate.opsForList().rightPush(key, pivot, value);
+    }
+
+    /**
+     * 通过索引设置列表元素的值
+     *
+     * @param key
+     * @param index 位置
+     * @param value
+     */
+    public void lSet(String key, long index, String value) {
+        redisTemplate.opsForList().set(key, index, value);
+    }
+
+    /**
+     * 移出并获取列表的第一个元素
+     *
+     * @param key
+     * @return 删除的元素
+     */
+    public Object lLeftPop(String key) {
+        return redisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 移出并获取列表的第一个元素
+     *
+     * @param key
+     * @return 删除的元素
+     */
+    public <T> T lLeftPop(String key, Class<T> clazz) {
+        Object r = lLeftPop(key);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+     *
+     * @param key
+     * @param timeout 等待时间
+     * @param unit    时间单位
+     * @return
+     */
+    public Object lLeftPopBlock(String key, long timeout, TimeUnit unit) {
+        return redisTemplate.opsForList().leftPop(key, timeout, unit);
+    }
+
+    /**
+     * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+     *
+     * @param key
+     * @param timeout 等待时间
+     * @param unit    时间单位
+     * @return
+     */
+    public <T> T lLeftPopBlock(String key, long timeout, TimeUnit unit, Class<T> clazz) {
+        Object r = lLeftPopBlock(key, timeout, unit);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 移除并获取列表最后一个元素
+     *
+     * @param key
+     * @return 删除的元素
+     */
+    public Object lRightPop(String key) {
+        return redisTemplate.opsForList().rightPop(key);
+    }
+
+    /**
+     * 移除并获取列表最后一个元素
+     *
+     * @param key
+     * @return 删除的元素
+     */
+    public <T> T lRightPop(String key, Class<T> clazz) {
+        Object r = lRightPop(key);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+     *
+     * @param key
+     * @param timeout 等待时间
+     * @param unit    时间单位
+     * @return
+     */
+    public Object lRightPopBlock(String key, long timeout, TimeUnit unit) {
+        return redisTemplate.opsForList().rightPop(key, timeout, unit);
+    }
+
+    /**
+     * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+     *
+     * @param key
+     * @param timeout 等待时间
+     * @param unit    时间单位
+     * @return
+     */
+    public <T> T lRightPopBlock(String key, long timeout, TimeUnit unit, Class<T> clazz) {
+        Object r = lRightPopBlock(key, timeout, unit);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
+     *
+     * @param sourceKey
+     * @param destinationKey
+     * @return
+     */
+    public Object lRightPopAndLeftPush(String sourceKey, String destinationKey) {
+        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
+                destinationKey);
+    }
+
+    /**
+     * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
+     *
+     * @param sourceKey
+     * @param destinationKey
+     * @return
+     */
+    public <T> T lRightPopAndLeftPush(String sourceKey, String destinationKey, Class<T> clazz) {
+        Object r = lRightPopAndLeftPush(sourceKey, destinationKey);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+     *
+     * @param sourceKey
+     * @param destinationKey
+     * @param timeout
+     * @param unit
+     * @return
+     */
+    public Object lRightPopAndLeftPushBlock(String sourceKey, String destinationKey,
+            long timeout, TimeUnit unit) {
+        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
+                destinationKey, timeout, unit);
+    }
+
+    /**
+     * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+     *
+     * @param sourceKey
+     * @param destinationKey
+     * @param timeout
+     * @param unit
+     * @return
+     */
+    public <T> T lRightPopAndLeftPushBlock(String sourceKey, String destinationKey,
+            long timeout, TimeUnit unit, Class<T> clazz) {
+        Object r = lRightPopAndLeftPushBlock(sourceKey, destinationKey, timeout, unit);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 删除集合中值等于value得元素
+     *
+     * @param key
+     * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
+     *              index<0, 从尾部开始删除第一个值等于value的元素;
+     * @param value
+     * @return
+     */
+    public Long lRemove(String key, long index, String value) {
+        return redisTemplate.opsForList().remove(key, index, value);
+    }
+
+    /**
+     * 裁剪list
+     *
+     * @param key
+     * @param start
+     * @param end
+     */
+    public void lTrim(String key, long start, long end) {
+        redisTemplate.opsForList().trim(key, start, end);
+    }
+
+    /**
+     * 获取列表长度
+     *
+     * @param key
+     * @return
+     */
+    public Long lLen(String key) {
+        return redisTemplate.opsForList().size(key);
+    }
+
+    /** --------------------set相关操作-------------------------- */
+
+    /**
+     * set添加元素
+     *
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long sAdd(String key, String... values) {
+        return redisTemplate.opsForSet().add(key, values);
+    }
+
+    /**
+     * set移除元素
+     *
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long sRemove(String key, Object... values) {
+        return redisTemplate.opsForSet().remove(key, values);
+    }
+
+    /**
+     * 移除并返回集合的一个随机元素
+     *
+     * @param key
+     * @return
+     */
+    public Object sPop(String key) {
+        return redisTemplate.opsForSet().pop(key);
+    }
+
+    /**
+     * 移除并返回集合的一个随机元素
+     *
+     * @param key
+     * @return
+     */
+    public <T> T sPop(String key, Class<T> clazz) {
+        Object r = sPop(key);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 将元素value从一个集合移到另一个集合
+     *
+     * @param key
+     * @param value
+     * @param destKey
+     * @return
+     */
+    public Boolean sMove(String key, String value, String destKey) {
+        return redisTemplate.opsForSet().move(key, value, destKey);
+    }
+
+    /**
+     * 获取集合的大小
+     *
+     * @param key
+     * @return
+     */
+    public Long sSize(String key) {
+        return redisTemplate.opsForSet().size(key);
+    }
+
+    /**
+     * 判断集合是否包含value
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Boolean sIsMember(String key, Object value) {
+        return redisTemplate.opsForSet().isMember(key, value);
+    }
+
+    /**
+     * 获取两个集合的交集
+     *
+     * @param key
+     * @param otherKey
+     * @return
+     */
+    public Set<Object> sIntersect(String key, String otherKey) {
+        return redisTemplate.opsForSet().intersect(key, otherKey);
+    }
+
+    /**
+     * 获取两个集合的交集
+     *
+     * @param key
+     * @param otherKey
+     * @return
+     */
+    public <T> Set<T> sIntersect(String key, String otherKey, Class<T> clazz) {
+        return applyToSet(sIntersect(key, otherKey));
+    }
+
+    /**
+     * 获取key集合与多个集合的交集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public Set<Object> sIntersect(String key, Collection<String> otherKeys) {
+        return redisTemplate.opsForSet().intersect(key, otherKeys);
+    }
+
+    /**
+     * 获取key集合与多个集合的交集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public <T> Set<T> sIntersect(String key, Collection<String> otherKeys, Class<T> clazz) {
+        return applyToSet(sIntersect(key, otherKeys));
+    }
+
+    /**
+     * key集合与otherKey集合的交集存储到destKey集合中
+     *
+     * @param key
+     * @param otherKey
+     * @param destKey
+     * @return
+     */
+    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
+        return redisTemplate.opsForSet().intersectAndStore(key, otherKey,
+                destKey);
+    }
+
+    /**
+     * key集合与多个集合的交集存储到destKey集合中
+     *
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public Long sIntersectAndStore(String key, Collection<String> otherKeys,
+            String destKey) {
+        return redisTemplate.opsForSet().intersectAndStore(key, otherKeys,
+                destKey);
+    }
+
+    /**
+     * 获取两个集合的并集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public Set<Object> sUnion(String key, String otherKeys) {
+        return redisTemplate.opsForSet().union(key, otherKeys);
+    }
+
+    /**
+     * 获取两个集合的并集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public <T> Set<T> sUnion(String key, String otherKeys, Class<T> clazz) {
+        return applyToSet(sUnion(key, otherKeys));
+    }
+
+    /**
+     * 获取key集合与多个集合的并集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public Set<Object> sUnion(String key, Collection<String> otherKeys) {
+        return redisTemplate.opsForSet().union(key, otherKeys);
+    }
+
+    /**
+     * 获取key集合与多个集合的并集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public <T> Set<T> sUnion(String key, Collection<String> otherKeys, Class<T> clazz) {
+        return applyToSet(sUnion(key, otherKeys));
+    }
+
+    /**
+     * key集合与otherKey集合的并集存储到destKey中
+     *
+     * @param key
+     * @param otherKey
+     * @param destKey
+     * @return
+     */
+    public Long sUnionAndStore(String key, String otherKey, String destKey) {
+        return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
+    }
+
+    /**
+     * key集合与多个集合的并集存储到destKey中
+     *
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public Long sUnionAndStore(String key, Collection<String> otherKeys,
+            String destKey) {
+        return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
+    }
+
+    /**
+     * 获取两个集合的差集
+     *
+     * @param key
+     * @param otherKey
+     * @return
+     */
+    public Set<Object> sDifference(String key, String otherKey) {
+        return redisTemplate.opsForSet().difference(key, otherKey);
+    }
+
+    /**
+     * 获取两个集合的差集
+     *
+     * @param key
+     * @param otherKey
+     * @return
+     */
+    public <T> Set<T> sDifference(String key, String otherKey, Class<T> clazz) {
+        return applyToSet(sDifference(key, otherKey));
+    }
+
+    /**
+     * 获取key集合与多个集合的差集
+     *
+     * @param key
+     * @param otherKeys
+     * @return
+     */
+    public Set<Object> sDifference(String key, Collection<String> otherKeys) {
+        return redisTemplate.opsForSet().difference(key, otherKeys);
+    }
+
+    /**
+     * 获取两个集合的差集
+     *
+     * @param key
+     * @param otherKey
+     * @return
+     */
+    public <T> Set<T> sDifference(String key, Collection<String> otherKeys, Class<T> clazz) {
+        return applyToSet(sDifference(key, otherKeys));
+    }
+
+    /**
+     * key集合与otherKey集合的差集存储到destKey中
+     *
+     * @param key
+     * @param otherKey
+     * @param destKey
+     * @return
+     */
+    public Long sDifferenceAndStore(String key, String otherKey, String destKey) {
+        return redisTemplate.opsForSet().differenceAndStore(key, otherKey,
+                destKey);
+    }
+
+    /**
+     * key集合与多个集合的差集存储到destKey中
+     *
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public Long sDifferenceAndStore(String key, Collection<String> otherKeys,
+            String destKey) {
+        return redisTemplate.opsForSet().differenceAndStore(key, otherKeys,
+                destKey);
+    }
+
+    /**
+     * 获取集合所有元素
+     *
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public Set<Object> setMembers(String key) {
+        return redisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 获取集合所有元素
+     *
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public <T> Set<T> setMembers(String key, Class<T> clazz) {
+        return applyToSet(setMembers(key));
+    }
+
+    /**
+     * 随机获取集合中的一个元素
+     *
+     * @param key
+     * @return
+     */
+    public Object sRandomMember(String key) {
+        return redisTemplate.opsForSet().randomMember(key);
+    }
+
+    /**
+     * 随机获取集合中的一个元素
+     *
+     * @param key
+     * @return
+     */
+    public <T> T sRandomMember(String key, Class<T> clazz) {
+        Object r = sRandomMember(key);
+        return r == null ? null : (T) r;
+    }
+
+    /**
+     * 随机获取集合中count个元素
+     *
+     * @param key
+     * @param count
+     * @return
+     */
+    public List<Object> sRandomMembers(String key, long count) {
+        return redisTemplate.opsForSet().randomMembers(key, count);
+    }
+
+    /**
+     * 随机获取集合中count个元素
+     *
+     * @param key
+     * @param count
+     * @return
+     */
+    public <T> List<T> sRandomMembers(String key, long count, Class<T> clazz) {
+        List<Object> results = sRandomMembers(key, count);
+        return Optional.ofNullable(results)
+                .orElse(Lists.newArrayList())
+                .stream()
+                .map(e -> {
+                    T r = apply(e);
+                    return r;
+                }).collect(Collectors.toList());
+    }
+
+    /**
+     * 随机获取集合中count个元素并且去除重复的
+     *
+     * @param key
+     * @param count
+     * @return
+     */
+    public Set<Object> sDistinctRandomMembers(String key, long count) {
+        return redisTemplate.opsForSet().distinctRandomMembers(key, count);
+    }
+
+    /**
+     * 随机获取集合中count个元素并且去除重复的
+     *
+     * @param key
+     * @param count
+     * @return
+     */
+    public <T> Set<T> sDistinctRandomMembers(String key, long count, Class<T> clazz) {
+        return applyToSet(sDistinctRandomMembers(key, count));
+    }
+
+    /**
+     * @param key
+     * @param options
+     * @return
+     */
+    public Cursor<Object> sScan(String key, ScanOptions options) {
+        return redisTemplate.opsForSet().scan(key, options);
+    }
+
+    /**------------------zSet相关操作--------------------------------*/
+
+    /**
+     * 添加元素,有序集合是按照元素的score值由小到大排列
+     *
+     * @param key
+     * @param value
+     * @param score
+     * @return
+     */
+    public Boolean zAdd(String key, String value, double score) {
+        return redisTemplate.opsForZSet().add(key, value, score);
+    }
+
+    /**
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long zAdd(String key, Set<ZSetOperations.TypedTuple<Object>> values) {
+        return redisTemplate.opsForZSet().add(key, values);
+    }
+
+    /**
+     * @param key
+     * @param values
+     * @return
+     */
+    public Long zRemove(String key, Object... values) {
+        return redisTemplate.opsForZSet().remove(key, values);
+    }
+
+    /**
+     * 增加元素的score值,并返回增加后的值
+     *
+     * @param key
+     * @param value
+     * @param delta
+     * @return
+     */
+    public Double zIncrementScore(String key, String value, double delta) {
+        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
+    }
+
+    /**
+     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
+     *
+     * @param key
+     * @param value
+     * @return 0表示第一位
+     */
+    public Long zRank(String key, Object value) {
+        return redisTemplate.opsForZSet().rank(key, value);
+    }
+
+    /**
+     * 返回元素在集合的排名,按元素的score值由大到小排列
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Long zReverseRank(String key, Object value) {
+        return redisTemplate.opsForZSet().reverseRank(key, value);
+    }
+
+    /**
+     * 获取集合的元素, 从小到大排序
+     *
+     * @param key
+     * @param start 开始位置
+     * @param end   结束位置, -1查询所有
+     * @return
+     */
+    public Set<Object> zRange(String key, long start, long end) {
+        return redisTemplate.opsForZSet().range(key, start, end);
+    }
+
+    /**
+     * 获取集合的元素, 从小到大排序
+     *
+     * @param key
+     * @param start 开始位置
+     * @param end   结束位置, -1查询所有
+     * @return
+     */
+    public <T> Set<T> zRange(String key, long start, long end, Class<T> clazz) {
+        return applyToSet(zRange(key, start, end));
+    }
+
+    /**
+     * 获取集合元素, 并且把score值也获取
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long start,
+            long end) {
+        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
+    }
+
+    /**
+     * 根据Score值查询集合元素
+     *
+     * @param key
+     * @param min 最小值
+     * @param max 最大值
+     * @return
+     */
+    public Set<Object> zRangeByScore(String key, double min, double max) {
+        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
+    }
+
+    /**
+     * 根据Score值查询集合元素
+     *
+     * @param key
+     * @param min 最小值
+     * @param max 最大值
+     * @return
+     */
+    public <T> Set<T> zRangeByScore(String key, double min, double max, Class<T> clazz) {
+        return applyToSet(zRangeByScore(key, min, max));
+    }
+
+    /**
+     * 根据Score值查询集合元素, 从小到大排序
+     *
+     * @param key
+     * @param min 最小值
+     * @param max 最大值
+     * @return
+     */
+    public Set<ZSetOperations.TypedTuple<Object>> zRangeByScoreWithScores(String key,
+            double min, double max) {
+        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
+    }
+
+    /**
+     * @param key
+     * @param min
+     * @param max
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<ZSetOperations.TypedTuple<Object>> zRangeByScoreWithScores(String key,
+            double min, double max,
+            long start, long end) {
+        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
+                start, end);
+    }
+
+    /**
+     * 获取集合的元素, 从大到小排序
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<Object> zReverseRange(String key, long start, long end) {
+        return redisTemplate.opsForZSet().reverseRange(key, start, end);
+    }
+
+    /**
+     * 获取集合的元素, 从大到小排序
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public <T> Set<T> zReverseRange(String key, long start, long end, Class<T> clazz) {
+        return applyToSet(zReverseRange(key, start, end));
+    }
+
+    /**
+     * 获取集合的元素, 从大到小排序, 并返回score值
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<ZSetOperations.TypedTuple<Object>> zReverseRangeWithScores(String key,
+            long start, long end) {
+        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,
+                end);
+    }
+
+    /**
+     * 根据Score值查询集合元素, 从大到小排序
+     *
+     * @param key
+     * @param min
+     * @param max
+     * @return
+     */
+    public Set<Object> zReverseRangeByScore(String key, double min,
+            double max) {
+        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
+    }
+
+    /**
+     * 根据Score值查询集合元素, 从大到小排序
+     *
+     * @param key
+     * @param min
+     * @param max
+     * @return
+     */
+    public <T> Set<T> zReverseRangeByScore(String key, double min,
+            double max, Class<T> clazz) {
+        return applyToSet(zReverseRangeByScore(key, min, max));
+    }
+
+    /**
+     * 根据Score值查询集合元素, 从大到小排序
+     *
+     * @param key
+     * @param min
+     * @param max
+     * @return
+     */
+    public Set<ZSetOperations.TypedTuple<Object>> zReverseRangeByScoreWithScores(
+            String key, double min, double max) {
+
+
+        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
+                min, max);
+    }
+
+    /**
+     * @param key
+     * @param min
+     * @param max
+     * @param start
+     * @param end
+     * @return
+     */
+    public Set<Object> zReverseRangeByScore(String key, double min,
+            double max, long start, long end) {
+        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
+                start, end);
+    }
+
+    /**
+     * @param key
+     * @param min
+     * @param max
+     * @param start
+     * @param end
+     * @return
+     */
+    public <T> Set<T> zReverseRangeByScore(String key, double min,
+            double max, long start, long end, Class<T> clazz) {
+        return applyToSet(zReverseRangeByScore(key, min, max, start, end));
+    }
+
+    /**
+     * 根据score值获取集合元素数量
+     *
+     * @param key
+     * @param min
+     * @param max
+     * @return
+     */
+    public Long zCount(String key, double min, double max) {
+        return redisTemplate.opsForZSet().count(key, min, max);
+    }
+
+    /**
+     * 获取集合大小
+     *
+     * @param key
+     * @return
+     */
+    public Long zSize(String key) {
+        return redisTemplate.opsForZSet().size(key);
+    }
+
+    /**
+     * 获取集合大小
+     *
+     * @param key
+     * @return
+     */
+    public Long zZCard(String key) {
+        return redisTemplate.opsForZSet().zCard(key);
+    }
+
+    /**
+     * 获取集合中value元素的score值
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public Double zScore(String key, Object value) {
+        return redisTemplate.opsForZSet().score(key, value);
+    }
+
+    /**
+     * 移除指定索引位置的成员
+     *
+     * @param key
+     * @param start
+     * @param end
+     * @return
+     */
+    public Long zRemoveRange(String key, long start, long end) {
+        return redisTemplate.opsForZSet().removeRange(key, start, end);
+    }
+
+    /**
+     * 根据指定的score值的范围来移除成员
+     *
+     * @param key
+     * @param min
+     * @param max
+     * @return
+     */
+    public Long zRemoveRangeByScore(String key, double min, double max) {
+        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
+    }
+
+    /**
+     * 获取key和otherKey的并集并存储在destKey中
+     *
+     * @param key
+     * @param otherKey
+     * @param destKey
+     * @return
+     */
+    public Long zUnionAndStore(String key, String otherKey, String destKey) {
+        return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
+    }
+
+    /**
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public Long zUnionAndStore(String key, Collection<String> otherKeys,
+            String destKey) {
+        return redisTemplate.opsForZSet()
+                .unionAndStore(key, otherKeys, destKey);
+    }
+
+    /**
+     * 交集
+     *
+     * @param key
+     * @param otherKey
+     * @param destKey
+     * @return
+     */
+    public Long zIntersectAndStore(String key, String otherKey,
+            String destKey) {
+        return redisTemplate.opsForZSet().intersectAndStore(key, otherKey,
+                destKey);
+    }
+
+    /**
+     * 交集
+     *
+     * @param key
+     * @param otherKeys
+     * @param destKey
+     * @return
+     */
+    public Long zIntersectAndStore(String key, Collection<String> otherKeys,
+            String destKey) {
+        return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
+                destKey);
+    }
+
+    /**
+     * @param key
+     * @param options
+     * @return
+     */
+    public Cursor<ZSetOperations.TypedTuple<Object>> zScan(String key, ScanOptions options) {
+        return redisTemplate.opsForZSet().scan(key, options);
+    }
+
+    /**
+     * <p>execute</p>
+     *
+     * @author chenpeng
+     * Create at May 28, 2019 at 19:13:26 GMT+8
+     */
+    public <T> T execute(SessionCallback<T> callback) {
+        if (callback != null) {
+            return redisTemplate.execute(callback);
+        }
+        return null;
+    }
+
+    private <T> T apply(Object e) {
+        T r = null;
+        if (e != null) {
+            r = (T) e;
+        }
+        return r;
+    }
+
+    private <T> Set<T> applyToSet(Set<Object> objects) {
+        Set<Object> results = objects;
+        return Optional.ofNullable(results)
+                .orElse(Sets.newHashSet())
+                .stream()
+                .map(e -> {
+                    T r = apply(e);
+                    return r;
+                }).collect(Collectors.toSet());
+    }
+
+    /**
+     * 获取锁:自旋三次
+     *
+     * @param key
+     * @param value
+     * @param value:单位-秒
+     * @return
+     */
+    public boolean lock(String key, Object value, long time) {
+        boolean locked = false;
+        int tryCount = 3;
+        while (!locked && tryCount > 0) {
+            locked = redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
+            tryCount--;
+            if (!locked) {
+                try {
+                    Thread.sleep(300);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return locked;
+
+    }
+
+    /**
+     * 使用lua脚本解锁,不会解除别人锁
+     *
+     * @param
+     * @return
+     */
+    public boolean unlockLua(String key, Object value) {
+        if (key == null || value == null) {
+            return false;
+        }
+        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
+        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
+        Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
+        //没有指定序列化方式,默认使用上面配置的
+        return result.equals(Long.valueOf(1));
+    }
+}

+ 38 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisObjectMapper.java

@@ -0,0 +1,38 @@
+package com.usky.common.spring.config.redis;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+/**
+ * <p>Redis 专用的 ObjectMapper </p>
+ *
+ * @author chenpeng
+ * Create at February 25, 2019 at 19:52:21 GMT+8
+ */
+public class RedisObjectMapper extends ObjectMapper {
+
+    private static final long serialVersionUID = -696530460999764926L;
+
+    public RedisObjectMapper() {
+        super();
+        // 去掉各种@JsonSerialize注解的解析
+        this.configure(MapperFeature.USE_ANNOTATIONS, false);
+        // 只针对非空的值进行序列化
+        this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        // 将类型序列化到属性json字符串中
+        this.enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        // 对于找不到匹配属性的时候忽略报错
+        this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        // 不包含任何属性的bean也不报错
+        this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        // 可以包含转移符等字符
+        this.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+        // 允许单引号
+        this.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+    }
+}

+ 23 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/config/redis/RedisOverdueTimeProperties.java

@@ -0,0 +1,23 @@
+package com.usky.common.spring.config.redis;
+
+import com.google.common.collect.Maps;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.util.Map;
+
+/**
+ * @author yangqiang
+ * @date 2020-11-02
+ */
+@Component
+@ConfigurationProperties(prefix = "spring.cache.redis")
+public class RedisOverdueTimeProperties {
+
+    private final Map<String, Duration> expires = Maps.newHashMap();
+
+    public Map<String, Duration> getExpires() {
+        return expires;
+    }
+}

+ 5 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/injectself/BeanSelfAware.java

@@ -0,0 +1,5 @@
+package com.usky.common.spring.injectself;
+
+public interface BeanSelfAware {
+    void setSelf(Object bean);
+}

+ 25 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/injectself/InjectSelfBeanPostProcessor.java

@@ -0,0 +1,25 @@
+package com.usky.common.spring.injectself;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.stereotype.Component;
+
+@Component
+public class InjectSelfBeanPostProcessor implements BeanPostProcessor {
+
+    @Override
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        return bean;
+    }
+
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        if (bean instanceof BeanSelfAware) {
+            BeanSelfAware myBean = (BeanSelfAware) bean;
+            myBean.setSelf(bean);
+            return myBean;
+        }
+        return bean;
+    }
+
+}

+ 45 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/ApplicationStartupUtils.java

@@ -0,0 +1,45 @@
+package com.usky.common.spring.util;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ApplicationContext;
+
+import java.net.InetAddress;
+
+/**
+ * 应用启动工具类
+ *
+ * @author chenpeng
+ * Create at December 2, 2018 at 21:18:11 GMT+8
+ */
+public final class ApplicationStartupUtils {
+
+
+    // private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationStartupUtils.class);
+
+    private static final String PROP_LOGGING_CONFIG_KEY = "logging.config";
+    private static final String PROFILE_VULE_SEPARATOR = ",";
+
+    private ApplicationStartupUtils() {
+
+    }
+
+    /**
+     * <p>应用启动方法</p>
+     *
+     * @author chenpeng
+     * Create at May 29, 2019 at 15:21:53 GMT+8
+     */
+    public static void startup(Class<?> clazz, String[] args) {
+        // 设置系统参数 本机IP
+        System.setProperty("local-ip", getLocalIp());
+        ApplicationContext context = SpringApplication.run(clazz, args);
+    }
+
+    private static String getLocalIp() {
+        try {
+            return InetAddress.getLocalHost().getHostAddress();
+        } catch (Exception ex) {
+            return "";
+        }
+    }
+}

+ 21 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/GlobalUtils.java

@@ -0,0 +1,21 @@
+package com.usky.common.spring.util;
+
+/**
+ * @author barry chen
+ * @date 2020/12/10 12:19 下午
+ */
+public final class GlobalUtils {
+
+    private GlobalUtils() {
+    }
+
+    /**
+     * 获取缓存目录
+     *
+     * @return
+     */
+    public static String getTempBaseDir() {
+        return SpringEnvUtils.getProper("temp.basedir", "");
+    }
+
+}

+ 54 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/JsonUtils.java

@@ -0,0 +1,54 @@
+package com.usky.common.spring.util;
+
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.usky.common.core.util.JsonMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 通用的Json处理工具.
+ * 注入了spring.jackson的objectMapper, 使得全局使用同一规则进行json处理.
+ * <p>
+ * 如果有特殊需要,对json处理, 可以使用 {@link JsonMapper},传入自定义{@link ObjectMapper}
+ */
+@Component
+public class JsonUtils {
+    private static JsonMapper jsonMapper;
+
+    public static JsonMapper getJsonMapper() {
+        return jsonMapper;
+    }
+
+    public static String toJson(Object object) {
+        return jsonMapper.toJson(object);
+    }
+
+    public static <T> T fromJson(String jsonString, Class<T> clazz) {
+        return jsonMapper.fromJson(jsonString, clazz);
+    }
+
+    public static <T> T fromJson(String jsonString, JavaType javaType) {
+        return jsonMapper.fromJson(jsonString, javaType);
+    }
+
+    public static <T> T fromJson(String jsonString, TypeReference<T> valueTypeRef) {
+        return jsonMapper.fromJson(jsonString, valueTypeRef);
+    }
+
+    public static String toJsonP(String functionName, Object object) {
+        return jsonMapper.toJsonP(functionName, object);
+    }
+
+    @Autowired
+    public void setObjectMapper(ObjectMapper objectMapper) {
+        if (objectMapper != null) {
+            JsonUtils.jsonMapper = new JsonMapper(objectMapper);
+        } else {
+            JsonUtils.jsonMapper = new JsonMapper();
+        }
+    }
+
+}

+ 83 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/MultipartFileUtil.java

@@ -0,0 +1,83 @@
+package com.usky.common.spring.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemFactory;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.io.IOUtils;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author yangqiang
+ * @date 2020-11-06
+ */
+@Slf4j
+public final class MultipartFileUtil {
+
+    private MultipartFileUtil() {
+    }
+
+    public static List<CommonsMultipartFile> getCommonsMultipartFileList(byte[] aByte, String fileName, String name) {
+        List<CommonsMultipartFile> multipartFiles = new ArrayList<>();
+        FileItemFactory factory = new DiskFileItemFactory(16, null);
+        FileItem item = factory.createItem(name, "text/plain", true, fileName);
+        OutputStream os = null;
+        ByteArrayInputStream fis = null;
+        int bytesRead;
+        try {
+            fis = new ByteArrayInputStream(aByte);
+            os = item.getOutputStream();
+            while ((bytesRead = fis.read(aByte, 0, aByte.length)) != -1) {
+                os.write(aByte, 0, bytesRead);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (os != null) {
+                    os.close();
+                }
+                if (fis != null) {
+                    fis.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        CommonsMultipartFile commonsMultipartFile = new CommonsMultipartFile(item);
+        multipartFiles.add(commonsMultipartFile);
+        return multipartFiles;
+    }
+
+    public static CommonsMultipartFile from(File file, String itemName) {
+        FileItemFactory factory = new DiskFileItemFactory(16, null);
+        FileItem item = factory.createItem(itemName, "text/plain", true, file.getName());
+        OutputStream os = null;
+        FileInputStream fis = null;
+        int bytesRead;
+        try {
+            fis = new FileInputStream(file);
+            os = item.getOutputStream();
+            IOUtils.copy(fis, os);
+        } catch (IOException e) {
+            log.error("convert file to multipartFile error. ", e);
+        } finally {
+            try {
+                if (os != null) {
+                    os.close();
+                }
+                if (fis != null) {
+                    fis.close();
+                }
+            } catch (IOException e) {
+                log.error("convert file to multipartFile, occur error when close stream.", e);
+            }
+        }
+        CommonsMultipartFile commonsMultipartFile = new CommonsMultipartFile(item);
+        return commonsMultipartFile;
+    }
+}

+ 114 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/SpringContextUtils.java

@@ -0,0 +1,114 @@
+package com.usky.common.spring.util;
+
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author chenpeng
+ * @date 2019-11-22 17:15
+ */
+@Component
+public class SpringContextUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
+    /**
+     * Spring应用上下文环境
+     */
+    private static ConfigurableListableBeanFactory beanFactory;
+    private static ApplicationContext applicationContext;
+
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringContextUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws BeansException
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws BeansException
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name) {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringContextUtils.beanFactory = beanFactory;
+    }
+
+}

+ 39 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/SpringEnvUtils.java

@@ -0,0 +1,39 @@
+package com.usky.common.spring.util;
+
+import java.util.Optional;
+
+public class SpringEnvUtils {
+
+    /**
+     * 获取当前运行的profile,如果有多个profile,则取第一个
+     *
+     * @return
+     */
+    public static String getProfile() {
+        return Optional.ofNullable(SpringContextUtils.getApplicationContext())
+                .map(c -> c.getEnvironment())
+                .map(m -> m.getActiveProfiles())
+                .filter(p -> p.length > 0)
+                .map(p -> p[0]).orElse(null);
+    }
+
+    /**
+     * 获取当前运行的所有profile
+     *
+     * @return
+     */
+    public static String[] getProfiles() {
+        return Optional.ofNullable(SpringContextUtils.getApplicationContext())
+                .map(c -> c.getEnvironment())
+                .map(m -> m.getActiveProfiles())
+                .filter(p -> p.length > 0)
+                .orElse(null);
+    }
+
+    public static String getProper(String key, String defaultValue) {
+        return Optional.ofNullable(SpringContextUtils.getApplicationContext())
+                .map(c -> c.getEnvironment())
+                .map(m -> m.getProperty(key, defaultValue))
+                .orElse(null);
+    }
+}

+ 24 - 0
base-common/usky-common-spring/src/main/java/com/usky/common/spring/util/ThreadUtils.java

@@ -0,0 +1,24 @@
+package com.usky.common.spring.util;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author barry chen
+ * @date 2020/11/27 4:20 下午
+ */
+public final class ThreadUtils extends org.apache.commons.lang3.ThreadUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(ThreadUtils.class);
+
+    private ThreadUtils() {
+    }
+
+    public static void sleep(Long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            LOG.error("thread sleep error", ExceptionUtils.getStackTrace(e));
+        }
+    }
+}

+ 5 - 0
base-components/gateway/pom.xml

@@ -88,6 +88,11 @@
             <version>${swagger.fox.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 4 - 4
base-components/gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java

@@ -45,10 +45,10 @@ public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
             ServerHttpRequest request = exchange.getRequest();
 
             // 非登录/注册请求或验证码关闭,不处理
-            if (StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled())
-            {
-                return chain.filter(exchange);
-            }
+//            if (StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled())
+//            {
+//                return chain.filter(exchange);
+//            }
 
             try
             {

+ 5 - 0
base-modules/service-job/pom.xml

@@ -77,6 +77,11 @@
             <groupId>com.ruoyi</groupId>
             <artifactId>ruoyi-common-swagger</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
         
     </dependencies>
 

+ 2 - 1
base-modules/service-job/src/main/java/com/ruoyi/job/controller/SysJobController.java

@@ -2,6 +2,8 @@ package com.ruoyi.job.controller;
 
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
+import com.ruoyi.common.core.utils.StringUtils;
 import org.quartz.SchedulerException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -14,7 +16,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.core.constant.Constants;
 import com.ruoyi.common.core.exception.job.TaskException;
-import com.ruoyi.common.core.utils.StringUtils;
 import com.ruoyi.common.core.utils.poi.ExcelUtil;
 import com.ruoyi.common.core.web.controller.BaseController;
 import com.ruoyi.common.core.web.domain.AjaxResult;

+ 0 - 10
base-modules/service-system/service-system-biz/src/main/resources/banner.txt

@@ -1,10 +0,0 @@
-Spring Boot Version: ${spring-boot.version}
-Spring Application Name: ${spring.application.name}
-                            _                           _                    
-                           (_)                         | |                   
- _ __  _   _   ___   _   _  _  ______  ___  _   _  ___ | |_   ___  _ __ ___  
-| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ 
-| |   | |_| || (_) || |_| || |        \__ \| |_| |\__ \| |_ |  __/| | | | | |
-|_|    \__,_| \___/  \__, ||_|        |___/ \__, ||___/ \__| \___||_| |_| |_|
-                      __/ |                  __/ |                           
-                     |___/                  |___/                            

+ 62 - 1
pom.xml

@@ -29,6 +29,7 @@
     </repositories>
 
     <properties>
+        <usky.version>0.0.1</usky.version>
         <ruoyi.version>3.5.0</ruoyi.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -56,6 +57,10 @@
         <commons-collections.version>3.2.2</commons-collections.version>
         <transmittable-thread-local.version>2.12.2</transmittable-thread-local.version>
         <mybatis-plus-boot-starter.version>3.3.0</mybatis-plus-boot-starter.version>
+        <orika-core.version>1.5.4</orika-core.version>
+<!--        <commons-lang3.version>3.11</commons-lang3.version>-->
+        <commons-beanutils.version>1.9.4</commons-beanutils.version>
+        <guava.version>29.0-jre</guava.version>
     </properties>
 
     <!-- 依赖声明 -->
@@ -275,6 +280,62 @@
                 <version>${ruoyi.version}</version>
             </dependency>
 
+            <!-- been转换工具 -->
+            <dependency>
+                <groupId>ma.glasnost.orika</groupId>
+                <artifactId>orika-core</artifactId>
+                <version>${orika-core.version}</version>
+            </dependency>
+
+            <!-- io常用工具类 -->
+<!--            <dependency>-->
+<!--                <groupId>org.apache.commons</groupId>-->
+<!--                <artifactId>commons-lang3</artifactId>-->
+<!--                <version>${commons-lang3.version}</version>-->
+<!--            </dependency>-->
+
+            <dependency>
+                <groupId>com.usky</groupId>
+                <artifactId>usky-common-core</artifactId>
+                <version>${usky.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.usky</groupId>
+                <artifactId>usky-common-mvc</artifactId>
+                <version>${usky.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.usky</groupId>
+                <artifactId>usky-common-spring</artifactId>
+                <version>${usky.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>commons-beanutils</groupId>
+                <artifactId>commons-beanutils</artifactId>
+                <version>${commons-beanutils.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+                <version>${dynamic-ds.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 
@@ -344,4 +405,4 @@
 <!--        </profile>-->
 <!--    </profiles>-->
 
-</project>
+</project>

+ 135 - 135
ruoyi-ui/vue.config.js

@@ -1,135 +1,135 @@
-'use strict'
-const path = require('path')
-
-function resolve(dir) {
-  return path.join(__dirname, dir)
-}
-
-const CompressionPlugin = require('compression-webpack-plugin')
-
-const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
-
-const port = process.env.port || process.env.npm_config_port || 80 // 端口
-
-// vue.config.js 配置说明
-//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
-// 这里只列一部分,具体配置参考文档
-module.exports = {
-  // 部署生产环境和开发环境下的URL。
-  // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
-  // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
-  publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
-  // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
-  outputDir: 'dist',
-  // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
-  assetsDir: 'static',
-  // 是否开启eslint保存检测,有效值:ture | false | 'error'
-  lintOnSave: process.env.NODE_ENV === 'development',
-  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
-  productionSourceMap: false,
-  // webpack-dev-server 相关配置
-  devServer: {
-    host: '0.0.0.0',
-    port: port,
-    open: true,
-    proxy: {
-      // detail: https://cli.vuejs.org/config/#devserver-proxy
-      [process.env.VUE_APP_BASE_API]: {
-        target: `http://localhost:8080`,
-        changeOrigin: true,
-        pathRewrite: {
-          ['^' + process.env.VUE_APP_BASE_API]: ''
-        }
-      }
-    },
-    disableHostCheck: true
-  },
-  css: {
-    loaderOptions: {
-      sass: {
-        sassOptions: { outputStyle: "expanded" }
-      }
-    }
-  },
-  configureWebpack: {
-    name: name,
-    resolve: {
-      alias: {
-        '@': resolve('src')
-      }
-    },
-    plugins: [
-      // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
-      new CompressionPlugin({
-        test: /\.(js|css|html)?$/i,     // 压缩文件格式
-        filename: '[path].gz[query]',   // 压缩后的文件名
-        algorithm: 'gzip',              // 使用gzip压缩
-        minRatio: 0.8                   // 压缩率小于1才会压缩
-      })
-    ],
-  },
-  chainWebpack(config) {
-    config.plugins.delete('preload') // TODO: need test
-    config.plugins.delete('prefetch') // TODO: need test
-
-    // set svg-sprite-loader
-    config.module
-      .rule('svg')
-      .exclude.add(resolve('src/assets/icons'))
-      .end()
-    config.module
-      .rule('icons')
-      .test(/\.svg$/)
-      .include.add(resolve('src/assets/icons'))
-      .end()
-      .use('svg-sprite-loader')
-      .loader('svg-sprite-loader')
-      .options({
-        symbolId: 'icon-[name]'
-      })
-      .end()
-
-    config
-      .when(process.env.NODE_ENV !== 'development',
-        config => {
-          config
-            .plugin('ScriptExtHtmlWebpackPlugin')
-            .after('html')
-            .use('script-ext-html-webpack-plugin', [{
-            // `runtime` must same as runtimeChunk name. default is `runtime`
-              inline: /runtime\..*\.js$/
-            }])
-            .end()
-          config
-            .optimization.splitChunks({
-              chunks: 'all',
-              cacheGroups: {
-                libs: {
-                  name: 'chunk-libs',
-                  test: /[\\/]node_modules[\\/]/,
-                  priority: 10,
-                  chunks: 'initial' // only package third parties that are initially dependent
-                },
-                elementUI: {
-                  name: 'chunk-elementUI', // split elementUI into a single package
-                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
-                  test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
-                },
-                commons: {
-                  name: 'chunk-commons',
-                  test: resolve('src/components'), // can customize your rules
-                  minChunks: 3, //  minimum common number
-                  priority: 5,
-                  reuseExistingChunk: true
-                }
-              }
-            })
-          config.optimization.runtimeChunk('single'),
-          {
-             from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
-             to: './' //到根目录下
-          }
-        }
-      )
-  }
-}
+'use strict'
+const path = require('path')
+
+function resolve(dir) {
+  return path.join(__dirname, dir)
+}
+
+const CompressionPlugin = require('compression-webpack-plugin')
+
+const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
+
+const port = process.env.port || process.env.npm_config_port || 80 // 端口
+
+// vue.config.js 配置说明
+//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
+// 这里只列一部分,具体配置参考文档
+module.exports = {
+  // 部署生产环境和开发环境下的URL。
+  // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
+  // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
+  publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
+  // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
+  outputDir: 'dist',
+  // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
+  assetsDir: 'static',
+  // 是否开启eslint保存检测,有效值:ture | false | 'error'
+  lintOnSave: process.env.NODE_ENV === 'development',
+  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
+  productionSourceMap: false,
+  // webpack-dev-server 相关配置
+  devServer: {
+    host: '0.0.0.0',
+    port: port,
+    open: true,
+    proxy: {
+      // detail: https://cli.vuejs.org/config/#devserver-proxy
+      [process.env.VUE_APP_BASE_API]: {
+        target: `http://localhost:9150`,
+        changeOrigin: true,
+        pathRewrite: {
+          ['^' + process.env.VUE_APP_BASE_API]: ''
+        }
+      }
+    },
+    disableHostCheck: true
+  },
+  css: {
+    loaderOptions: {
+      sass: {
+        sassOptions: { outputStyle: "expanded" }
+      }
+    }
+  },
+  configureWebpack: {
+    name: name,
+    resolve: {
+      alias: {
+        '@': resolve('src')
+      }
+    },
+    plugins: [
+      // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
+      new CompressionPlugin({
+        test: /\.(js|css|html)?$/i,     // 压缩文件格式
+        filename: '[path].gz[query]',   // 压缩后的文件名
+        algorithm: 'gzip',              // 使用gzip压缩
+        minRatio: 0.8                   // 压缩率小于1才会压缩
+      })
+    ],
+  },
+  chainWebpack(config) {
+    config.plugins.delete('preload') // TODO: need test
+    config.plugins.delete('prefetch') // TODO: need test
+
+    // set svg-sprite-loader
+    config.module
+      .rule('svg')
+      .exclude.add(resolve('src/assets/icons'))
+      .end()
+    config.module
+      .rule('icons')
+      .test(/\.svg$/)
+      .include.add(resolve('src/assets/icons'))
+      .end()
+      .use('svg-sprite-loader')
+      .loader('svg-sprite-loader')
+      .options({
+        symbolId: 'icon-[name]'
+      })
+      .end()
+
+    config
+      .when(process.env.NODE_ENV !== 'development',
+        config => {
+          config
+            .plugin('ScriptExtHtmlWebpackPlugin')
+            .after('html')
+            .use('script-ext-html-webpack-plugin', [{
+            // `runtime` must same as runtimeChunk name. default is `runtime`
+              inline: /runtime\..*\.js$/
+            }])
+            .end()
+          config
+            .optimization.splitChunks({
+              chunks: 'all',
+              cacheGroups: {
+                libs: {
+                  name: 'chunk-libs',
+                  test: /[\\/]node_modules[\\/]/,
+                  priority: 10,
+                  chunks: 'initial' // only package third parties that are initially dependent
+                },
+                elementUI: {
+                  name: 'chunk-elementUI', // split elementUI into a single package
+                  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
+                  test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
+                },
+                commons: {
+                  name: 'chunk-commons',
+                  test: resolve('src/components'), // can customize your rules
+                  minChunks: 3, //  minimum common number
+                  priority: 5,
+                  reuseExistingChunk: true
+                }
+              }
+            })
+          config.optimization.runtimeChunk('single'),
+          {
+             from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
+             to: './' //到根目录下
+          }
+        }
+      )
+  }
+}