ソースを参照

忽略文档提交

yq 3 年 前
コミット
e2771b0bd1
100 ファイル変更10236 行追加0 行削除
  1. 40 0
      .gitignore
  2. 68 0
      mhfire-common/mhfire-common-core/pom.xml
  3. 99 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/ApiResult.java
  4. 25 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/BeanConstraintViolationResult.java
  5. 51 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/CommonPage.java
  6. 25 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/PageRequest.java
  7. 36 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/UnifiedUser.java
  8. 24 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/constants/CommonConst.java
  9. 154 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/constants/SymbolConst.java
  10. 93 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/enums/DatePatternEnum.java
  11. 5 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/enums/IEnum.java
  12. 56 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ApplicationCheckException.java
  13. 59 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ApplicationException.java
  14. 59 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/BusinessCheckException.java
  15. 77 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/BusinessErrorCode.java
  16. 59 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/BusinessException.java
  17. 52 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ErrorCode.java
  18. 24 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ErrorMessageConsts.java
  19. 79 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/SystemErrorCode.java
  20. 216 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/Assert.java
  21. 96 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/BeanMapperUtils.java
  22. 53 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/BeanValidatorUtils.java
  23. 853 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/Convert.java
  24. 722 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/DateUtils.java
  25. 187 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/FileTypeUtils.java
  26. 320 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/FileUtils.java
  27. 342 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/HttpUtils.java
  28. 37 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/JacksonComponent.java
  29. 173 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/JsonMapper.java
  30. 153 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/ShardingTaskExecutorUtils.java
  31. 63 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/StopWatchRecorder.java
  32. 55 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/StringUtils.java
  33. 44 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/URLUtils.java
  34. 86 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/UUIDUtils.java
  35. 61 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/csv/CsvUtil.java
  36. 49 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/csv/CustomMappingStrategy.java
  37. 28 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/IdWorker.java
  38. 77 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/IdWorkerContainer.java
  39. 15 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/IdWorkerTypeEnum.java
  40. 144 0
      mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/SnowflakeIdWorker.java
  41. 57 0
      mhfire-common/mhfire-common-mvc/pom.xml
  42. 99 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/AbstractCrudService.java
  43. 26 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/BaseModel.java
  44. 13 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/CrudMapper.java
  45. 46 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/CrudService.java
  46. 51 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/BaseEnumConverterFactory.java
  47. 17 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/DateStringConvert.java
  48. 30 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/FormatterConfiguration.java
  49. 17 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/StringDateConverter.java
  50. 22 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/multipartFile/CommonsMultipartResolverConfiguration.java
  51. 59 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/DefaultMapperScannerRegistrar.java
  52. 63 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/DefaultMybatisConfiguration.java
  53. 18 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/DefaultMybatisPlusPropertiesCustomizer.java
  54. 25 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisExtensionAutoConfiguration.java
  55. 275 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisMapperRefresh.java
  56. 40 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisMapperRefreshAutoConfiguration.java
  57. 27 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisMapperRefreshProperties.java
  58. 37 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/validator/ValidatorExtensionAutoConfiguration.java
  59. 267 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/handler/UnifiedExceptionHandler.java
  60. 171 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/utils/IpUtils.java
  61. 31 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/utils/JdbcUtils.java
  62. 120 0
      mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/utils/ServletUtils.java
  63. 51 0
      mhfire-common/mhfire-common-spring/pom.xml
  64. 34 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/annotion/ConditionalOnPropertyNotEmpty.java
  65. 47 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/feign/FeignExtensionAutoConfiguration.java
  66. 134 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/feign/FeignModuler.java
  67. 25 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/id/IdWorkerAutoConfiguration.java
  68. 17 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/id/IdWorkerConfigProperties.java
  69. 46 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/jackson/JacksonConfig.java
  70. 45 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/EhCacheCacheConfiguration.java
  71. 119 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisExtensionAutoConfiguration.java
  72. 1802 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisHelper.java
  73. 38 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisObjectMapper.java
  74. 23 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisOverdueTimeProperties.java
  75. 5 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/injectself/BeanSelfAware.java
  76. 25 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/injectself/InjectSelfBeanPostProcessor.java
  77. 45 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/ApplicationStartupUtils.java
  78. 21 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/GlobalUtils.java
  79. 53 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/JsonUtils.java
  80. 83 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/MultipartFileUtil.java
  81. 114 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/SpringContextUtils.java
  82. 39 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/SpringEnvUtils.java
  83. 24 0
      mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/ThreadUtils.java
  84. 54 0
      mhfire-common/pom.xml
  85. 166 0
      mhfire-controller/pom.xml
  86. 19 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/MhfireControllerApplication.java
  87. 47 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/AlFsdAllControllerWeb.java
  88. 86 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/AlertControllerWeb.java
  89. 26 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/AtlasControllerWeb.java
  90. 64 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/FireSiteControllerWeb.java
  91. 127 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/FireStatisticsControllerWeb.java
  92. 68 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/FireWaterControllerWeb.java
  93. 59 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/ReportComplaintControllerWeb.java
  94. 37 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/SiAeAllControllerWeb.java
  95. 64 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/TestController.java
  96. 44 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/UnitBeOnDutyControllerWeb.java
  97. 61 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/UnitControllerWeb.java
  98. 64 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/WeatherControllerWeb.java
  99. 45 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/bulehelp/BmfwEventController.java
  100. 45 0
      mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/bulehelp/BmfwFeedbackController.java

+ 40 - 0
.gitignore

@@ -0,0 +1,40 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Example user template template
+### Example user template
+
+# IntelliJ project files
+.idea
+*.iml
+out
+gen
+target
+### Java template
+# Compiled class file
+*.class
+
+# Log file
+*.log
+log
+logs
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+.DS_Store
+
+/static/doc/

+ 68 - 0
mhfire-common/mhfire-common-core/pom.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.bizmatics</groupId>
+        <artifactId>mhfire-common</artifactId>
+        <version>0.0.1</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>mhfire-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>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.opencsv</groupId>
+            <artifactId>opencsv</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 99 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/ApiResult.java

@@ -0,0 +1,99 @@
+package com.bizmatics.common.core.bean;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.beans.Transient;
+import java.io.Serializable;
+
+import static com.bizmatics.common.core.bean.ApiResult.ResultStatus.ERROR;
+import static com.bizmatics.common.core.bean.ApiResult.ResultStatus.SUCCESS;
+
+/**
+ * <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 = SUCCESS;
+        this.code = SUCCESS_CODE;
+        this.data = content;
+    }
+
+    public ApiResult(String code, String message) {
+        this.status = 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 SUCCESS == status;
+    }
+
+    @Transient
+    public boolean isError() {
+        return !isSuccess();
+    }
+
+    /**
+     * 业务响应状态
+     */
+    public enum ResultStatus {
+        /**
+         * 返回状态:成功
+         */
+        SUCCESS,
+        /**
+         * 返回状态:失败
+         */
+        ERROR
+    }
+}

+ 25 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/BeanConstraintViolationResult.java

@@ -0,0 +1,25 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/CommonPage.java

@@ -0,0 +1,51 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/PageRequest.java

@@ -0,0 +1,25 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/bean/UnifiedUser.java

@@ -0,0 +1,36 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/constants/CommonConst.java

@@ -0,0 +1,24 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/constants/SymbolConst.java

@@ -0,0 +1,154 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/enums/DatePatternEnum.java

@@ -0,0 +1,93 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/enums/IEnum.java

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

+ 56 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ApplicationCheckException.java

@@ -0,0 +1,56 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ApplicationException.java

@@ -0,0 +1,59 @@
+package com.bizmatics.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import static com.bizmatics.common.core.exception.ErrorMessageConsts.EXCEPTION_DEFAULT_CUSTOM_MESSAGE;
+import static com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/BusinessCheckException.java

@@ -0,0 +1,59 @@
+package com.bizmatics.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import static com.bizmatics.common.core.exception.ErrorMessageConsts.BIZ_EXCEPTION_MESSAGE_TEMPLATE;
+import static com.bizmatics.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);
+    }
+}

+ 77 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/BusinessErrorCode.java

@@ -0,0 +1,77 @@
+package com.bizmatics.common.core.exception;
+
+import java.util.stream.Stream;
+
+import static com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/BusinessException.java

@@ -0,0 +1,59 @@
+package com.bizmatics.common.core.exception;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import static com.bizmatics.common.core.exception.ErrorMessageConsts.BIZ_EXCEPTION_MESSAGE_TEMPLATE;
+import static com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ErrorCode.java

@@ -0,0 +1,52 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/ErrorMessageConsts.java

@@ -0,0 +1,24 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/exception/SystemErrorCode.java

@@ -0,0 +1,79 @@
+package com.bizmatics.common.core.exception;
+
+import java.util.stream.Stream;
+
+import static com.bizmatics.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;
+    }
+}

+ 216 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/Assert.java

@@ -0,0 +1,216 @@
+package com.bizmatics.common.core.util;
+
+import com.bizmatics.common.core.exception.BusinessException;
+import com.bizmatics.common.core.exception.ErrorCode;
+import org.apache.commons.collections4.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);
+    }
+
+}

+ 96 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/BeanMapperUtils.java

@@ -0,0 +1,96 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/BeanValidatorUtils.java

@@ -0,0 +1,53 @@
+package com.bizmatics.common.core.util;
+
+import com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/Convert.java

@@ -0,0 +1,853 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/DateUtils.java

@@ -0,0 +1,722 @@
+package com.bizmatics.common.core.util;
+
+import com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/FileTypeUtils.java

@@ -0,0 +1,187 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/FileUtils.java

@@ -0,0 +1,320 @@
+package com.bizmatics.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;
+    }
+
+}

+ 342 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/HttpUtils.java

@@ -0,0 +1,342 @@
+package com.bizmatics.common.core.util;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import okhttp3.*;
+import org.apache.commons.lang3.StringUtils;
+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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/JacksonComponent.java

@@ -0,0 +1,37 @@
+package com.bizmatics.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());
+        }
+    }
+}

+ 173 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/JsonMapper.java

@@ -0,0 +1,173 @@
+package com.bizmatics.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 org.apache.commons.lang3.StringUtils;
+
+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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/ShardingTaskExecutorUtils.java

@@ -0,0 +1,153 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/StopWatchRecorder.java

@@ -0,0 +1,63 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/StringUtils.java

@@ -0,0 +1,55 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/URLUtils.java

@@ -0,0 +1,44 @@
+package com.bizmatics.common.core.util;
+
+import com.bizmatics.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;
+        }
+    }*/
+
+}

+ 86 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/UUIDUtils.java

@@ -0,0 +1,86 @@
+package com.bizmatics.common.core.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+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();
+    }
+}

+ 61 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/csv/CsvUtil.java

@@ -0,0 +1,61 @@
+package com.bizmatics.common.core.util.csv;
+
+import com.opencsv.bean.*;
+
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author yangqiang
+ * @date 2020-12-21
+ */
+public final class CsvUtil {
+    private CsvUtil() {
+    }
+
+    public static <T> MappingStrategy<T> getPositionMapping(Class<T> clazz) {
+        ColumnPositionMappingStrategy<T> mapper = new ColumnPositionMappingStrategy<>();
+        mapper.setType(clazz);
+        return mapper;
+    }
+
+    public static <T> MappingStrategy<T> getNameMapping(Class<T> clazz) {
+        HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
+        strategy.setType(clazz);
+        return strategy;
+    }
+
+    public static <T> MappingStrategy<T> getOrderByMapping(Class<T> clazz) {
+        CustomMappingStrategy<T> strategy = new CustomMappingStrategy<>();
+        strategy.setType(clazz);
+        return strategy;
+    }
+
+    public static <T> MappingStrategy<T> getMapMapping(Class<T> clazz, Map<String, String> columnMapping) {
+        HeaderColumnNameTranslateMappingStrategy<T> strategy = new HeaderColumnNameTranslateMappingStrategy<>();
+        strategy.setType(clazz);
+        strategy.setColumnMapping(columnMapping);
+        return strategy;
+    }
+
+    public static <T> List<T> csvToBean(MappingStrategy<T> mappingStrategy, InputStreamReader inputStream, Character separator, Character quoteChar,
+            Integer skipLines) throws Exception {
+        CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(inputStream)
+                .withSeparator((null == separator) ? ',' : separator)
+                .withQuoteChar((null == quoteChar) ? '\'' : quoteChar)
+                .withMappingStrategy(mappingStrategy)
+                .withSkipLines((null == skipLines) ? 0 : separator)
+                .build();
+        return csvToBean.parse();
+    }
+
+
+    public static <T> void beanToCsv(MappingStrategy<T> mappingStrategy, Writer writer, Character separator, List<T> data) throws Exception {
+        StatefulBeanToCsvBuilder<T> builder = new StatefulBeanToCsvBuilder<>(writer);
+        StatefulBeanToCsv<T> beanToCsv = builder.withMappingStrategy(mappingStrategy)
+                .withSeparator((null == separator) ? ',' : separator).withApplyQuotesToAll(false).build();
+        beanToCsv.write(data);
+    }
+}

+ 49 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/csv/CustomMappingStrategy.java

@@ -0,0 +1,49 @@
+package com.bizmatics.common.core.util.csv;
+
+import com.opencsv.bean.BeanField;
+import com.opencsv.bean.ColumnPositionMappingStrategy;
+import com.opencsv.bean.CsvBindByName;
+import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+
+/**
+ * CSV导出策略类(有序列头)
+ * 导出基础类需添加注解CsvBindByName和CsvBindByPosition
+ * CsvBindByPosition实现排序,CsvBindByName实现列名映射
+ *
+ * @author yangqiang
+ * @date 2020-12-22
+ *
+ * @param <T>
+ */
+public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
+
+    @Override
+    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
+        super.setColumnMapping(new String[FieldUtils.getAllFields(bean.getClass()).length]);
+        final int numColumns = findMaxFieldIndex();
+        if (!isAnnotationDriven() || numColumns == -1) {
+            return super.generateHeader(bean);
+        }
+
+        String[] header = new String[numColumns + 1];
+
+        BeanField beanField;
+        for (int i = 0; i <= numColumns; i++) {
+            beanField = findField(i);
+            String columnHeaderName = extractHeaderName(beanField);
+            header[i] = columnHeaderName;
+        }
+        return header;
+    }
+
+    private String extractHeaderName(final BeanField beanField) {
+        if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
+            return StringUtils.EMPTY;
+        }
+
+        final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
+        return bindByNameAnnotation.column();
+    }
+}

+ 28 - 0
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/IdWorker.java

@@ -0,0 +1,28 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/IdWorkerContainer.java

@@ -0,0 +1,77 @@
+package com.bizmatics.common.core.util.id;
+
+import com.google.common.collect.Maps;
+
+import java.util.concurrent.ConcurrentMap;
+
+import static com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/IdWorkerTypeEnum.java

@@ -0,0 +1,15 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-core/src/main/java/com/bizmatics/common/core/util/id/SnowflakeIdWorker.java

@@ -0,0 +1,144 @@
+package com.bizmatics.common.core.util.id;
+
+import static com.bizmatics.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;
+    }
+}

+ 57 - 0
mhfire-common/mhfire-common-mvc/pom.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.bizmatics</groupId>
+        <artifactId>mhfire-common</artifactId>
+        <version>0.0.1</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>mhfire-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>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
+            <optional>true</optional>
+            <exclusions>
+                <exclusion>
+                    <artifactId>HdrHistogram</artifactId>
+                    <groupId>org.hdrhistogram</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.bizmatics</groupId>
+            <artifactId>mhfire-common-core</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.bizmatics</groupId>
+            <artifactId>mhfire-common-spring</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 99 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/AbstractCrudService.java

@@ -0,0 +1,99 @@
+package com.bizmatics.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.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/BaseModel.java

@@ -0,0 +1,26 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/CrudMapper.java

@@ -0,0 +1,13 @@
+package com.bizmatics.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> {
+}

+ 46 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/base/CrudService.java

@@ -0,0 +1,46 @@
+package com.bizmatics.common.mvc.base;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.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);
+}

+ 51 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/BaseEnumConverterFactory.java

@@ -0,0 +1,51 @@
+package com.bizmatics.common.mvc.config.convert;
+
+import com.bizmatics.common.core.enums.IEnum;
+import com.bizmatics.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;
+        }
+    }
+
+}

+ 17 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/DateStringConvert.java

@@ -0,0 +1,17 @@
+package com.bizmatics.common.mvc.config.convert;
+
+import com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/FormatterConfiguration.java

@@ -0,0 +1,30 @@
+package com.bizmatics.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;
+    }
+}

+ 17 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/convert/StringDateConverter.java

@@ -0,0 +1,17 @@
+package com.bizmatics.common.mvc.config.convert;
+
+import com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/multipartFile/CommonsMultipartResolverConfiguration.java

@@ -0,0 +1,22 @@
+package com.bizmatics.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;
+    }
+}

+ 59 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/DefaultMapperScannerRegistrar.java

@@ -0,0 +1,59 @@
+package com.bizmatics.common.mvc.config.mybatis;
+
+import com.bizmatics.common.core.exception.ApplicationException;
+import com.bizmatics.common.core.exception.SystemErrorCode;
+import com.bizmatics.common.mvc.base.CrudMapper;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.mybatis.spring.annotation.MapperScannerRegistrar;
+import org.mybatis.spring.mapper.ClassPathMapperScanner;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+/**
+ * <p>Mapper scanner 扩展</p>
+ *
+ * @author chenpeng
+ * Create time Dec 13, 2018 7:38:49 PM
+ */
+public class DefaultMapperScannerRegistrar extends MapperScannerRegistrar {
+
+    private static final String PKG_SEPARATOR = ".";
+    private static final String PKG_DEFAULT_PARTTERN = "%s.**.persistence.mapper";
+
+    private ResourceLoader resourceLoader;
+
+    @Override
+    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+
+        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
+
+        // this check is needed in Spring 3.1
+        if (resourceLoader != null) {
+            scanner.setResourceLoader(resourceLoader);
+        }
+
+        scanner.setMarkerInterface(CrudMapper.class);
+        scanner.registerFilters();
+        scanner.doScan(resolvePackage());
+    }
+
+    @Override
+    public void setResourceLoader(ResourceLoader resourceLoader) {
+        this.resourceLoader = resourceLoader;
+    }
+
+    private String resolvePackage() {
+        List<String> pkgs = Lists.newArrayList(Splitter.on(PKG_SEPARATOR)
+                .split(this.getClass().getPackage().getName()));
+        if (pkgs.size() == 0 || StringUtils.isEmpty(pkgs.get(0))) {
+            throw new ApplicationException(SystemErrorCode.SYS_SYSTEM_PKG_RESOLVE_ERROR,
+                    "根 package 路径未找到,[" + this.getClass().getName() + "] 类需放在包下");
+        }
+        return String.format(PKG_DEFAULT_PARTTERN, pkgs.get(0));
+    }
+}

+ 63 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/DefaultMybatisConfiguration.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.bizmatics.common.mvc.config.mybatis;
+
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
+import com.bizmatics.common.spring.util.SpringEnvUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.mapping.MappedStatement;
+
+/**
+ * 由于mybatis自带的Configuration不允许添加已存在的MappedStatement, 且mybatisplus中的MybatisConfiguration也不允许,
+ * 所以重写addMappedStatement方法
+ *
+ * @author chen
+ * @since 2020-07-22
+ */
+public class DefaultMybatisConfiguration extends MybatisConfiguration {
+    private static final Log logger = LogFactory.getLog(MybatisConfiguration.class);
+
+    /**
+     * MybatisPlus 加载 SQL 顺序:
+     * <p>1、加载XML中的SQL</p>
+     * <p>2、加载sqlProvider中的SQL</p>
+     * <p>3、xmlSql 与 sqlProvider不能包含相同的SQL</p>
+     * <p>调整后的SQL优先级:xmlSql > sqlProvider > curdSql</p>
+     */
+    @Override
+    public void addMappedStatement(MappedStatement ms) {
+        logger.debug("addMappedStatement: " + ms.getId());
+        String profile = SpringEnvUtils.getProfile();
+        if (StringUtils.isNotBlank(profile) && "dev".equals(profile)) {
+            if (super.mappedStatements.containsKey(ms.getId())) {
+                super.mappedStatements.remove(ms.getId());
+            }
+            mappedStatements.put(ms.getId(), ms);
+            return;
+        }
+        if (mappedStatements.containsKey(ms.getId())) {
+            /*
+             * 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
+             */
+            logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");
+            return;
+        }
+        mappedStatements.put(ms.getId(), ms);
+    }
+
+}

+ 18 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/DefaultMybatisPlusPropertiesCustomizer.java

@@ -0,0 +1,18 @@
+package com.bizmatics.common.mvc.config.mybatis;
+
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
+
+/**
+ * 通过MybatisPlusPropertiesCustomizer, 加载自定义的DefaultMybatisPlusPropertiesCustomizer
+ *
+ * @author chen
+ * @since 2020-07-22
+ */
+public class DefaultMybatisPlusPropertiesCustomizer implements MybatisPlusPropertiesCustomizer {
+    @Override
+    public void customize(MybatisPlusProperties properties) {
+        DefaultMybatisConfiguration mybatisConfiguration = new DefaultMybatisConfiguration();
+        properties.setConfiguration(mybatisConfiguration);
+    }
+}

+ 25 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisExtensionAutoConfiguration.java

@@ -0,0 +1,25 @@
+package com.bizmatics.common.mvc.config.mybatis;
+
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * <p>扩展 Mybatis-plus 配置</p>
+ *
+ * @author chenpeng
+ * Create time Dec 13, 2018 8:14:48 PM
+ */
+@Configuration
+@AutoConfigureAfter(MybatisPlusAutoConfiguration.class)
+@Import(DefaultMapperScannerRegistrar.class)
+public class MybatisExtensionAutoConfiguration {
+
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+}

+ 275 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisMapperRefresh.java

@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.bizmatics.common.mvc.config.mybatis;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.SystemClock;
+import org.apache.ibatis.binding.MapperRegistry;
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
+import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
+import org.apache.ibatis.executor.ErrorContext;
+import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.parsing.XPathParser;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.util.ResourceUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.*;
+
+/**
+ * 切莫用于生产环境(后果自负)
+ * <p>Mybatis 映射文件热加载(发生变动后自动重新加载).</p>
+ * <p>方便开发时使用,不用每次修改xml文件后都要去重启应用.</p>
+ *
+ * @author nieqiurong
+ * @since 2016-08-25
+ */
+public class MybatisMapperRefresh implements Runnable {
+
+    private static final Log logger = LogFactory.getLog(MybatisMapperRefresh.class);
+    /**
+     * 记录jar包存在的mapper
+     */
+    private static final Map<String, List<Resource>> JAR_MAPPER = new HashMap<>();
+    private final SqlSessionFactory sqlSessionFactory;
+    private final Resource[] mapperLocations;
+    /**
+     * 是否开启刷新mapper
+     */
+    private final boolean enabled;
+    private Long beforeTime = 0L;
+    private Configuration configuration;
+    /**
+     * xml文件目录
+     */
+    private Set<String> fileSet;
+    /**
+     * 延迟加载时间
+     */
+    private int delaySeconds = 10;
+    /**
+     * 刷新间隔时间
+     */
+    private int sleepSeconds = 20;
+
+    public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, int delaySeconds,
+            int sleepSeconds, boolean enabled) {
+        this.mapperLocations = mapperLocations.clone();
+        this.sqlSessionFactory = sqlSessionFactory;
+        this.delaySeconds = delaySeconds;
+        this.enabled = enabled;
+        this.sleepSeconds = sleepSeconds;
+        this.configuration = sqlSessionFactory.getConfiguration();
+        this.run();
+    }
+
+    public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, boolean enabled) {
+        this.mapperLocations = mapperLocations.clone();
+        this.sqlSessionFactory = sqlSessionFactory;
+        this.enabled = enabled;
+        this.configuration = sqlSessionFactory.getConfiguration();
+        this.run();
+    }
+
+    @Override
+    public void run() {
+        if (enabled) {
+            beforeTime = SystemClock.now();
+            final MybatisMapperRefresh runnable = this;
+            new Thread(() -> {
+                if (fileSet == null) {
+                    fileSet = new HashSet<>();
+                    if (mapperLocations != null) {
+                        for (Resource mapperLocation : mapperLocations) {
+                            try {
+                                if (ResourceUtils.isJarURL(mapperLocation.getURL())) {
+                                    String key = new UrlResource(ResourceUtils.extractJarFileURL(mapperLocation.getURL()))
+                                            .getFile().getPath();
+                                    fileSet.add(key);
+                                    if (JAR_MAPPER.get(key) != null) {
+                                        JAR_MAPPER.get(key).add(mapperLocation);
+                                    } else {
+                                        List<Resource> resourcesList = new ArrayList<>();
+                                        resourcesList.add(mapperLocation);
+                                        JAR_MAPPER.put(key, resourcesList);
+                                    }
+                                } else {
+                                    fileSet.add(mapperLocation.getFile().getPath());
+                                }
+                            } catch (IOException ioException) {
+                                ioException.printStackTrace();
+                            }
+                        }
+                    }
+                }
+                try {
+                    Thread.sleep(delaySeconds * 1000);
+                } catch (InterruptedException interruptedException) {
+                    interruptedException.printStackTrace();
+                }
+                do {
+                    try {
+                        for (String filePath : fileSet) {
+                            File file = new File(filePath);
+                            if (file.isFile() && file.lastModified() > beforeTime) {
+                                List<Resource> removeList = JAR_MAPPER.get(filePath);
+                                if (removeList != null && !removeList.isEmpty()) {
+                                    for (Resource resource : removeList) {
+                                        runnable.refresh(resource);
+                                    }
+                                } else {
+                                    runnable.refresh(new FileSystemResource(file));
+                                }
+                            }
+                        }
+                        beforeTime = SystemClock.now();
+                    } catch (Exception exception) {
+                        exception.printStackTrace();
+                    }
+                    try {
+                        Thread.sleep(sleepSeconds * 1000);
+                    } catch (InterruptedException interruptedException) {
+                        interruptedException.printStackTrace();
+                    }
+
+                } while (true);
+            }, "mybatis-plus MapperRefresh").start();
+        }
+    }
+
+    /**
+     * 刷新mapper
+     */
+    @SuppressWarnings("rawtypes")
+    private void refresh(
+            Resource resource) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+        this.configuration = sqlSessionFactory.getConfiguration();
+        boolean isSupper = configuration.getClass().getSuperclass().getSuperclass() == Configuration.class;
+        try {
+            Field loadedResourcesField = isSupper ? configuration.getClass().getSuperclass().getSuperclass().getDeclaredField("loadedResources")
+                    : configuration.getClass().getDeclaredField("loadedResources");
+            loadedResourcesField.setAccessible(true);
+            Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));
+            XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(),
+                    new XMLMapperEntityResolver());
+            XNode context = xPathParser.evalNode("/mapper");
+            String namespace = context.getStringAttribute("namespace");
+            Field field = MapperRegistry.class.getDeclaredField("knownMappers");
+            field.setAccessible(true);
+            Map mapConfig = (Map) field.get(configuration.getMapperRegistry());
+            mapConfig.remove(Resources.classForName(namespace));
+            loadedResourcesSet.remove(resource.toString());
+            configuration.getCacheNames().remove(namespace);
+            cleanParameterMap(context.evalNodes("/mapper/parameterMap"), namespace);
+            cleanResultMap(context.evalNodes("/mapper/resultMap"), namespace);
+            cleanKeyGenerators(context.evalNodes("insert|update"), namespace);
+            cleanSqlElement(context.evalNodes("/mapper/sql"), namespace);
+            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),
+                    sqlSessionFactory.getConfiguration(),
+                    resource.toString(), sqlSessionFactory.getConfiguration().getSqlFragments());
+            xmlMapperBuilder.parse();
+            logger.warn("refresh: '" + resource + "', success!");
+        } catch (IOException e) {
+            logger.error("Refresh IOException :" + e.getMessage());
+        } finally {
+            ErrorContext.instance().reset();
+        }
+    }
+
+    /**
+     * 清理parameterMap
+     *
+     * @param list      ignore
+     * @param namespace ignore
+     */
+    @SuppressWarnings("unlikely-arg-type")
+    private void cleanParameterMap(List<XNode> list, String namespace) {
+        for (XNode parameterMapNode : list) {
+            String id = parameterMapNode.getStringAttribute("id");
+            configuration.getParameterMaps().remove(namespace + StringPool.DOT + id);
+        }
+    }
+
+    /**
+     * 清理resultMap
+     *
+     * @param list      ignore
+     * @param namespace ignore
+     */
+    private void cleanResultMap(List<XNode> list, String namespace) {
+        for (XNode resultMapNode : list) {
+            String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
+            configuration.getResultMapNames().remove(id);
+            configuration.getResultMapNames().remove(namespace + StringPool.DOT + id);
+            clearResultMap(resultMapNode, namespace);
+        }
+    }
+
+    private void clearResultMap(XNode xNode, String namespace) {
+        for (XNode resultChild : xNode.getChildren()) {
+            if ("association".equals(resultChild.getName()) || "collection".equals(resultChild.getName())
+                    || "case".equals(resultChild.getName())) {
+                if (resultChild.getStringAttribute("select") == null) {
+                    configuration.getResultMapNames().remove(
+                            resultChild.getStringAttribute("id", resultChild.getValueBasedIdentifier()));
+                    configuration.getResultMapNames().remove(
+                            namespace + StringPool.DOT + resultChild.getStringAttribute("id", resultChild.getValueBasedIdentifier()));
+                    if (resultChild.getChildren() != null && !resultChild.getChildren().isEmpty()) {
+                        clearResultMap(resultChild, namespace);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 清理selectKey
+     *
+     * @param list      ignore
+     * @param namespace ignore
+     */
+    private void cleanKeyGenerators(List<XNode> list, String namespace) {
+        for (XNode context : list) {
+            String id = context.getStringAttribute("id");
+            configuration.getKeyGeneratorNames().remove(id + SelectKeyGenerator.SELECT_KEY_SUFFIX);
+            configuration.getKeyGeneratorNames().remove(namespace + StringPool.DOT + id + SelectKeyGenerator.SELECT_KEY_SUFFIX);
+        }
+    }
+
+    /**
+     * 清理sql节点缓存
+     *
+     * @param list      ignore
+     * @param namespace ignore
+     */
+    private void cleanSqlElement(List<XNode> list, String namespace) {
+        for (XNode context : list) {
+            String id = context.getStringAttribute("id");
+            configuration.getSqlFragments().remove(id);
+            configuration.getSqlFragments().remove(namespace + StringPool.DOT + id);
+        }
+    }
+}

+ 40 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisMapperRefreshAutoConfiguration.java

@@ -0,0 +1,40 @@
+package com.bizmatics.common.mvc.config.mybatis;
+
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * @author chen
+ * @since 2020-07-22
+ */
+@Configuration
+@AutoConfigureAfter(MybatisPlusAutoConfiguration.class)
+@EnableConfigurationProperties(MybatisMapperRefreshProperties.class)
+@Import(DefaultMybatisPlusPropertiesCustomizer.class)
+public class MybatisMapperRefreshAutoConfiguration {
+    private final MybatisMapperRefreshProperties mybatisMapperRefreshProperties;
+    private final MybatisPlusProperties properties;
+    private final SqlSessionFactory sqlSessionFactory;
+
+    public MybatisMapperRefreshAutoConfiguration(
+            MybatisMapperRefreshProperties mybatisMapperRefreshProperties,
+            MybatisPlusProperties properties, SqlSessionFactory sqlSessionFactory) {
+        this.mybatisMapperRefreshProperties = mybatisMapperRefreshProperties;
+        this.properties = properties;
+        this.sqlSessionFactory = sqlSessionFactory;
+    }
+
+    @Bean
+    public MybatisMapperRefresh enableRefresh() {
+        return new MybatisMapperRefresh(properties.resolveMapperLocations(), sqlSessionFactory,
+                mybatisMapperRefreshProperties.getDelaySeconds(),
+                mybatisMapperRefreshProperties.getSleepSeconds(),
+                mybatisMapperRefreshProperties.isEnabled());
+    }
+}

+ 27 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/mybatis/MybatisMapperRefreshProperties.java

@@ -0,0 +1,27 @@
+package com.bizmatics.common.mvc.config.mybatis;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author chen
+ * @since 2020-07-22
+ */
+@Data
+@ConfigurationProperties("mybatis.refresh")
+public class MybatisMapperRefreshProperties {
+
+    /**
+     * 是否启用刷新
+     */
+    private boolean enabled = false;
+    /**
+     * 延迟加载时间
+     */
+    private int delaySeconds = 10;
+    /**
+     * 刷新间隔时间
+     */
+    private int sleepSeconds = 20;
+
+}

+ 37 - 0
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/config/validator/ValidatorExtensionAutoConfiguration.java

@@ -0,0 +1,37 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/handler/UnifiedExceptionHandler.java

@@ -0,0 +1,267 @@
+package com.bizmatics.common.mvc.handler;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.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.bizmatics.common.core.exception.ErrorMessageConsts.BIZ_ERROR_LOG_MESSAGE_TEMPLATE;
+import static com.bizmatics.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.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/utils/IpUtils.java

@@ -0,0 +1,171 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/utils/JdbcUtils.java

@@ -0,0 +1,31 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-mvc/src/main/java/com/bizmatics/common/mvc/utils/ServletUtils.java

@@ -0,0 +1,120 @@
+package com.bizmatics.common.mvc.utils;
+
+import com.bizmatics.common.core.util.Convert;
+import com.bizmatics.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;
+    }
+}

+ 51 - 0
mhfire-common/mhfire-common-spring/pom.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.bizmatics</groupId>
+        <artifactId>mhfire-common</artifactId>
+        <version>0.0.1</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>mhfire-common-spring</artifactId>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.bizmatics</groupId>
+            <artifactId>mhfire-common-core</artifactId>
+            <version>0.0.1</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>
+
+        <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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/annotion/ConditionalOnPropertyNotEmpty.java

@@ -0,0 +1,34 @@
+package com.bizmatics.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();
+        }
+    }
+}

+ 47 - 0
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/feign/FeignExtensionAutoConfiguration.java

@@ -0,0 +1,47 @@
+package com.bizmatics.common.spring.config.feign;
+
+import feign.Feign;
+import feign.codec.Decoder;
+import feign.codec.Encoder;
+import feign.codec.ErrorDecoder;
+import feign.form.spring.SpringFormEncoder;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
+import org.springframework.cloud.openfeign.support.SpringEncoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * <p>对 Feign 默认行为的扩展</p>
+ *
+ * @author chenpeng
+ * Create time 2018年12月3日 下午1:38:25
+ */
+@Configuration
+@ConditionalOnClass(Feign.class)
+public class FeignExtensionAutoConfiguration {
+
+    @Autowired
+    private HttpMessageConverters messageConverters;
+
+    @Bean
+    public Decoder feignDecoder() {
+        return new ResponseEntityDecoder(new FeignModuler.ResultDecoder());
+    }
+
+    @Bean
+    public ErrorDecoder feignErrorDecoder() {
+        return new FeignModuler.ExceptionDecoder();
+    }
+
+    @Bean
+    public Encoder feignFormEncoder() {
+        ObjectFactory<HttpMessageConverters> httpMessageConvertsObjectFactory = () -> {
+            return messageConverters;
+        };
+        return new SpringFormEncoder(new SpringEncoder(httpMessageConvertsObjectFactory));
+    }
+}

+ 134 - 0
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/feign/FeignModuler.java

@@ -0,0 +1,134 @@
+package com.bizmatics.common.spring.config.feign;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.exception.ApplicationException;
+import com.bizmatics.common.core.exception.BusinessException;
+import com.bizmatics.common.core.exception.ErrorCode;
+import com.bizmatics.common.core.util.StringUtils;
+import com.bizmatics.common.spring.util.JsonUtils;
+import feign.FeignException;
+import feign.Response;
+import feign.Util;
+import feign.codec.Decoder;
+import feign.codec.ErrorDecoder;
+import lombok.SneakyThrows;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import static com.bizmatics.common.core.bean.ApiResult.ResultStatus.ERROR;
+
+/**
+ * <p>Feign 组件</p>
+ *
+ * @author chenpeng
+ * Create at February 21, 2019 at 16:38:07 GMT+8
+ */
+public class FeignModuler {
+
+    private static final String STRING_BODY_TYPE_NAME = "String";
+    private static final String CALL_RESULT_STATUS_FLAG = "status";
+
+    private static RuntimeException processException(String bodyText) {
+        RuntimeException ex = null;
+        if (!hasError(bodyText)) {
+            return ex;
+        }
+        ApiResult<Void> errorResult = JsonUtils.fromJson(bodyText, ApiResult.class);
+        if (errorResult == null || StringUtils.isBlank(errorResult.getCode())) {
+            return ex;
+        }
+        String code = errorResult.getCode();
+        String message = errorResult.getMsg();
+        String exception = errorResult.getException();
+
+        if (StringUtils.isBlank(exception) || exception.equals(BusinessException.class.getName())) {
+            return new BusinessException(buildErrorCode(code, message));
+        } else if (exception.equals(ApplicationException.class.getName())) {
+            return new ApplicationException(buildErrorCode(code, message));
+        }
+
+        //实例化真正的Exception
+        Class<? extends RuntimeException> exClazz;
+        try {
+            exClazz = (Class<? extends RuntimeException>) Class.forName(exception);
+            ex = (RuntimeException) exClazz.getDeclaredConstructor(String.class)
+                    .newInstance(String.format("%s - %s", code, message));
+        } catch (Exception e) {
+            return new BusinessException(buildErrorCode(code, message));
+        }
+        return ex;
+    }
+
+    private static boolean hasError(String bodyText) {
+        return bodyText.contains(CALL_RESULT_STATUS_FLAG) && bodyText.contains(ERROR.name());
+    }
+
+    private static boolean isStringReturnType(Type type) {
+        String typeName = type.getTypeName();
+        return (STRING_BODY_TYPE_NAME.equals(typeName)
+                || String.class.getName().equals(typeName));
+    }
+
+    private static ErrorCode buildErrorCode(String code, String message) {
+        return new ErrorCode() {
+            @Override
+            public String getCode() {
+                return code;
+            }
+
+            @Override
+            public String getDefaultMessage() {
+                return message;
+            }
+        };
+    }
+
+    /**
+     * 解码器
+     */
+    static class ResultDecoder implements Decoder {
+        @Override
+        public Object decode(Response response, Type type) throws IOException, FeignException {
+            Response.Body body = response.body();
+            if (Objects.isNull(body)) {
+                return null;
+            }
+            String bodyText = Util.toString(body.asReader());
+            if (StringUtils.isBlank(bodyText)) {
+                return null;
+            }
+
+            RuntimeException e = processException(bodyText);
+            if (e != null) {
+                throw e;
+            }
+
+            if (isStringReturnType(type)) {
+                return bodyText;
+            }
+            return JsonUtils.fromJson(bodyText, JsonUtils.getJsonMapper().getTypeFactory().constructType(type));
+        }
+
+    }
+
+    /**
+     * 异常处理解码器
+     */
+    static class ExceptionDecoder implements ErrorDecoder {
+        @SneakyThrows
+        @Override
+        public Exception decode(String s, Response response) {
+            Response.Body body = response.body();
+            if (Objects.isNull(body)) {
+                return null;
+            }
+            String bodyText = Util.toString(body.asReader());
+            if (StringUtils.isBlank(bodyText)) {
+                return null;
+            }
+            return processException(bodyText);
+        }
+    }
+}

+ 25 - 0
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/id/IdWorkerAutoConfiguration.java

@@ -0,0 +1,25 @@
+package com.bizmatics.common.spring.config.id;
+
+import com.bizmatics.common.core.util.id.IdWorkerContainer;
+import com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/id/IdWorkerConfigProperties.java

@@ -0,0 +1,17 @@
+package com.bizmatics.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;
+}

+ 46 - 0
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/jackson/JacksonConfig.java

@@ -0,0 +1,46 @@
+package com.bizmatics.common.spring.config.jackson;
+
+import com.bizmatics.common.core.util.JacksonComponent;
+import com.bizmatics.common.core.util.JsonMapper;
+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 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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/EhCacheCacheConfiguration.java

@@ -0,0 +1,45 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisExtensionAutoConfiguration.java

@@ -0,0 +1,119 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisHelper.java

@@ -0,0 +1,1802 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisObjectMapper.java

@@ -0,0 +1,38 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/config/redis/RedisOverdueTimeProperties.java

@@ -0,0 +1,23 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/injectself/BeanSelfAware.java

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

+ 25 - 0
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/injectself/InjectSelfBeanPostProcessor.java

@@ -0,0 +1,25 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/ApplicationStartupUtils.java

@@ -0,0 +1,45 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/GlobalUtils.java

@@ -0,0 +1,21 @@
+package com.bizmatics.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", "");
+    }
+
+}

+ 53 - 0
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/JsonUtils.java

@@ -0,0 +1,53 @@
+package com.bizmatics.common.spring.util;
+
+import com.bizmatics.common.core.util.JsonMapper;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/MultipartFileUtil.java

@@ -0,0 +1,83 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/SpringContextUtils.java

@@ -0,0 +1,114 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/SpringEnvUtils.java

@@ -0,0 +1,39 @@
+package com.bizmatics.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
mhfire-common/mhfire-common-spring/src/main/java/com/bizmatics/common/spring/util/ThreadUtils.java

@@ -0,0 +1,24 @@
+package com.bizmatics.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));
+        }
+    }
+}

+ 54 - 0
mhfire-common/pom.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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>mhfire</artifactId>
+        <groupId>com.bizmatics</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>mhfire-common</artifactId>
+    <packaging>pom</packaging>
+    <version>0.0.1</version>
+
+    <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>mhfire-common-mvc</module>
+        <module>mhfire-common-spring</module>
+        <module>mhfire-common-core</module>
+    </modules>
+</project>

+ 166 - 0
mhfire-controller/pom.xml

@@ -0,0 +1,166 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>mhfire-controller</artifactId>
+    <version>0.0.1</version>
+    <name>mhfire-controller</name>
+
+    <properties>
+        <project.name>${parent.name}</project.name>
+    </properties>
+    <parent>
+        <artifactId>mhfire</artifactId>
+        <groupId>com.bizmatics</groupId>
+        <version>0.0.1</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.bizmatics</groupId>
+            <artifactId>mhfire-service</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-starter-actuator</artifactId>-->
+<!--            <exclusions>-->
+<!--                <exclusion>-->
+<!--                    <groupId>org.springframework.boot</groupId>-->
+<!--                    <artifactId>spring-boot-starter-logging</artifactId>-->
+<!--                </exclusion>-->
+<!--            </exclusions>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <artifactId>asm</artifactId>
+                    <groupId>org.ow2.asm</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>5.2.7.RELEASE</version>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.cloud</groupId>-->
+<!--            <artifactId>spring-cloud-starter</artifactId>-->
+<!--        </dependency>-->
+
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.cloud</groupId>-->
+<!--            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
+<!--        </dependency>-->
+
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.cloud</groupId>-->
+<!--            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
+<!--            <exclusions>-->
+<!--                <exclusion>-->
+<!--                    <artifactId>HdrHistogram</artifactId>-->
+<!--                    <groupId>org.hdrhistogram</groupId>-->
+<!--                </exclusion>-->
+<!--            </exclusions>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.cloud</groupId>-->
+<!--            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>-->
+<!--            <exclusions>-->
+<!--                <exclusion>-->
+<!--                    <artifactId>HdrHistogram</artifactId>-->
+<!--                    <groupId>org.hdrhistogram</groupId>-->
+<!--                </exclusion>-->
+<!--            </exclusions>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>HdrHistogram</artifactId>
+                    <groupId>org.hdrhistogram</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${parent.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <testFailureIgnore>true</testFailureIgnore>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+        <resources>
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.xml</include>
+                </includes>
+                <filtering>false</filtering>
+            </resource>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.xml</include>
+                    <include>**/*.properties</include>
+                    <include>**/*.yml</include>
+                </includes>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+    </build>
+
+</project>

+ 19 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/MhfireControllerApplication.java

@@ -0,0 +1,19 @@
+package com.bizmatics.mhfire.controller;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@ComponentScan("com.bizmatics")
+@SpringBootApplication
+@MapperScan("com.bizmatics.mhfire.persistence")
+@EnableScheduling
+public class MhfireControllerApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(MhfireControllerApplication.class, args);
+    }
+
+}

+ 47 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/AlFsdAllControllerWeb.java

@@ -0,0 +1,47 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.mhfire.service.AlFsdAllService;
+import com.bizmatics.mhfire.service.vo.AlFsdAllCsVO;
+import com.bizmatics.mhfire.service.vo.AlFsdAllVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+
+/**
+ * 行政许可-消防安检申报总
+ * @author yq
+ * @date 2021/5/25 17:14
+ */
+@RestController
+@RequestMapping("/alFsdAll")
+public class AlFsdAllControllerWeb {
+
+    @Autowired
+    private AlFsdAllService alFsdAllService;
+
+
+    /**
+     * 获取行政许可的检查,受理,出文数量
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    @GetMapping("/alFsdAllNumber")
+    public ApiResult<AlFsdAllVO> alFsdAllNumber(@RequestParam(required = false) Date startTime,
+                                                 @RequestParam(required = false) Date endTime){
+        return ApiResult.success(alFsdAllService.getAlFsdAllNumber(startTime,endTime));
+    }
+    /**
+     * 获取开业检查记录的汇总
+     * @return
+     */
+    @GetMapping("/csCount")
+    public ApiResult<AlFsdAllCsVO> getCsCount(){
+        return ApiResult.success(alFsdAllService.getCsCount());
+    }
+}

+ 86 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/AlertControllerWeb.java

@@ -0,0 +1,86 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.model.Alert;
+import com.bizmatics.mhfire.service.AlertService;
+import com.bizmatics.mhfire.service.vo.AlertStatisticsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 警情模块
+ * @author yq
+ * @date 2021/5/31 10:05
+ */
+@RestController
+@RequestMapping("/alert")
+public class AlertControllerWeb {
+
+    @Autowired
+    private AlertService alertService;
+
+
+    /**
+     * 查询当天24小时警情
+     *
+     * @return
+     */
+    @GetMapping("/alertStatisticsByHouse")
+    public ApiResult<Map<String, List<AlertStatisticsVO>>> getAlertStatisticsByHouse(@RequestParam Date startTime,
+                                                                                     @RequestParam Date endTime) {
+        return ApiResult.success(alertService.getAlertStatisticsByHouse(startTime,endTime));
+    }
+
+
+    /**
+     * 查询每年12个月警情
+     *
+     * @return
+     */
+    @GetMapping("/alertStatisticsByMonth")
+    public ApiResult<Map<String, List<AlertStatisticsVO>>> getAlertStatisticsByMonth(@RequestParam Date startTime,
+                                                                                     @RequestParam Date endTime) {
+        return ApiResult.success(alertService.getAlertStatisticsByMonth(startTime,endTime));
+    }
+
+
+    /**
+     * 警情详细信息
+     *
+     * @param current   页数
+     * @param size      条数
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return
+     */
+    @GetMapping("/page")
+    public ApiResult<CommonPage<Alert>> page(@RequestParam Integer current,
+                                             @RequestParam Integer size,
+                                             @RequestParam(required = false) Date startTime,
+                                             @RequestParam(required = false) Date endTime) {
+        return ApiResult.success(alertService.page(current, size, startTime, endTime));
+    }
+
+    /**
+     * 查询不同警情(社会救助,警情,火灾)的占比
+     *
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return
+     */
+    @GetMapping("/alertStatistics")
+    public ApiResult<List<AlertStatisticsVO>> getAlertStatistics(@RequestParam(required = false) Date startTime,
+                                                                 @RequestParam(required = false) Date endTime) {
+        return ApiResult.success(alertService.getAlertStatistics(startTime, endTime));
+    }
+
+    @PostMapping("/add")
+    public void add(@RequestBody List<Alert> alert){
+        alertService.saveBatch(alert);
+    }
+}

+ 26 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/AtlasControllerWeb.java

@@ -0,0 +1,26 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.mhfire.service.AtlasService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.print.DocFlavor;
+
+/**
+ * @author yq
+ * @date 2021/7/13 14:06
+ */
+@RestController
+@RequestMapping("atlas")
+public class AtlasControllerWeb {
+
+    @Autowired
+    public AtlasService atlasService;
+
+    @RequestMapping("encrypt")
+    public ApiResult<String> encrypt(){
+        return ApiResult.success(atlasService.encrypt());
+    }
+}

+ 64 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/FireSiteControllerWeb.java

@@ -0,0 +1,64 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.model.FireSite;
+import com.bizmatics.mhfire.service.FireSiteService;
+import com.bizmatics.mhfire.service.vo.FireSiteDutyVO;
+import com.bizmatics.mhfire.service.vo.FireSiteVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 消防站点
+ * @author yq
+ * @date 2021/5/31 10:13
+ */
+@RestController
+@RequestMapping("/fireSite")
+public class FireSiteControllerWeb {
+
+    @Autowired
+    private FireSiteService fireSiteService;
+
+
+    /**
+     * 分页
+     * @param current 页数
+     * @param size 条数
+     * @return
+     */
+    @GetMapping("/page")
+    public ApiResult<CommonPage<FireSite>> page(@RequestParam Integer current,
+                                                @RequestParam Integer size){
+        return ApiResult.success(fireSiteService.page(current, size));
+    }
+
+
+    /**
+     * get one
+     * @param id id
+     * @return
+     */
+    @GetMapping("/one")
+    public ApiResult<FireSiteVO> getOne(@RequestParam String id){
+        return ApiResult.success(fireSiteService.getOne(id));
+    }
+
+    /**
+     * 站点执勤实力
+     * @param fireSiteId
+     * @return
+     */
+    @GetMapping("/fireSiteDuty")
+    public ApiResult<List<FireSiteDutyVO>> getFireSiteDuty(@RequestParam String fireSiteId){
+        return ApiResult.success(fireSiteService.getFireSiteDuty(fireSiteId));
+    }
+
+    @GetMapping("/addList")
+    public void addList(@RequestBody List<FireSite> fireSite){
+        fireSiteService.saveBatch(fireSite);
+    }
+}

+ 127 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/FireStatisticsControllerWeb.java

@@ -0,0 +1,127 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.persistence.mapper.po.FireStatisticsPO;
+import com.bizmatics.mhfire.service.FireStatisticsService;
+import com.bizmatics.mhfire.service.vo.FireBubbleVO;
+import com.bizmatics.mhfire.service.vo.FireLevelRatioVO;
+import com.bizmatics.mhfire.service.vo.FireUnitPoliceVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.xml.crypto.Data;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 火灾详情
+ * @author yq
+ * @date 2021/5/31 10:20
+ */
+@RestController
+@RequestMapping("/fireStatistics")
+public class FireStatisticsControllerWeb {
+
+
+    @Autowired
+    private FireStatisticsService fireStatisticsService;
+
+
+    /**
+     * 根据年份和地址查看12月份的火灾统计
+     * @param startTime 开始时间
+     * @param endTime 开始时间
+     * @param address 地址
+     * @param fireType 火灾类别
+     * @return
+     */
+    @GetMapping("fireCountByData")
+    public ApiResult<List<Object>> getFireCountByData(@RequestParam Date startTime,
+                                                       @RequestParam Date endTime,
+                                                       @RequestParam(required = false)String address,
+                                                       @RequestParam(required = false)String fireType){
+        return ApiResult.success(fireStatisticsService.getFireCountByData(startTime,endTime,address,fireType));
+    }
+
+
+    /**
+     * 分页
+     * @param current 页数
+     * @param size 条数
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @param address 地址
+     * @return
+     */
+    @GetMapping("page")
+    public ApiResult<CommonPage<FireStatisticsPO>> page(@RequestParam Integer current,
+                                                        @RequestParam Integer size,
+                                                        @RequestParam(required = false) Date startTime,
+                                                        @RequestParam(required = false) Date endTime,
+                                                        @RequestParam(required = false)String address){
+        return ApiResult.success(fireStatisticsService.page(current, size, startTime, endTime, address));
+    }
+
+
+    /**
+     * getOne
+     * @param id
+     * @return
+     */
+    @GetMapping("one")
+    public ApiResult<FireStatisticsPO> getOne(@RequestParam String id){
+        return ApiResult.success(fireStatisticsService.getOne(id));
+    }
+
+
+    /**
+     * 火灾-单位-出警信息
+     * @param id
+     * @return
+     */
+    @GetMapping("fireUnitPolice")
+    public ApiResult<FireUnitPoliceVO> getFireUnitPolice(@RequestParam String id){
+        return ApiResult.success(fireStatisticsService.getFireUnitPolice(id));
+    }
+
+
+    /**
+     * 火灾原因占比
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @param address   地址
+     * @return
+     */
+    @GetMapping("fireCauseRatio")
+    public ApiResult<List<FireLevelRatioVO>> getGroupByLevel(@RequestParam(required = false) Date startTime,
+                                                             @RequestParam(required = false)Date endTime,
+                                                             @RequestParam(required = false)String address){
+        return ApiResult.success(fireStatisticsService.getGroupByLevel(startTime,endTime,address));
+    }
+
+    /**
+     * 火灾类型
+     * @return
+     */
+    @GetMapping("fireType")
+    public ApiResult<List<String>> getFireType(){
+        return ApiResult.success(fireStatisticsService.getFireType());
+    }
+
+    @GetMapping("fireAddress")
+    public ApiResult<List<String>> getAddress(){
+        return ApiResult.success(fireStatisticsService.getAddress());
+    }
+
+    @GetMapping("fireBubble")
+    public ApiResult<List<FireBubbleVO>> getFireBubble(@RequestParam(required = false)Date startTime,
+                                                       @RequestParam(required = false)Date endTime,
+                                                       @RequestParam(required = false)String fireType,
+                                                       @RequestParam(required = false)String unitId){
+        return ApiResult.success(fireStatisticsService.getAvgAndSum(startTime, endTime, fireType, unitId));
+    }
+}

+ 68 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/FireWaterControllerWeb.java

@@ -0,0 +1,68 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.persistence.mapper.po.FireWaterPO;
+import com.bizmatics.mhfire.service.FireWaterService;
+import com.bizmatics.mhfire.service.vo.FireWaterStatisticsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+
+/**
+ * 水源模块
+ * @author yq
+ * @date 2021/5/31 9:35
+ */
+@RestController
+@RequestMapping("/fireWater")
+public class FireWaterControllerWeb {
+    @Autowired
+    private FireWaterService fireWaterService;
+
+
+
+
+    /**
+     * 获取水源详细信息
+     * @param id 水源id
+     * @return
+     */
+    @GetMapping("/one")
+    public ApiResult<FireWaterPO> getOne(@RequestParam String id){
+        return ApiResult.success(fireWaterService.getOne(id));
+    }
+
+
+    /**
+     * 分页
+     * @param current 页数
+     * @param size  条数
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    @GetMapping("/page")
+    public ApiResult<CommonPage<FireWaterPO>> page(@RequestParam Integer current,
+                                                   @RequestParam Integer size,
+                                                   @RequestParam(required = false)Date startTime,
+                                                   @RequestParam(required = false)Date endTime){
+        return ApiResult.success(fireWaterService.page(current,size,startTime,endTime));
+    }
+
+    /**
+     * 水源统计
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return
+     */
+    @GetMapping("/fireWaterStatistics")
+    public ApiResult<FireWaterStatisticsVO> getFireWaterStatistics(@RequestParam(required = false) Date startTime,
+                                                                   @RequestParam(required = false) Date endTime){
+        return ApiResult.success(fireWaterService.getFireWaterStatistics(startTime,endTime));
+    }
+}

+ 59 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/ReportComplaintControllerWeb.java

@@ -0,0 +1,59 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.mhfire.service.ReportComplaintService;
+import com.bizmatics.mhfire.service.vo.ReportComplaintVO;
+import com.bizmatics.mhfire.service.vo.ReportStatisticsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 举报投诉
+ * @author yq
+ * @date 2021/5/31 9:21
+ */
+@RestController
+@RequestMapping("/reportComplaint")
+public class ReportComplaintControllerWeb {
+
+
+    @Autowired
+    private ReportComplaintService reportComplaintService;
+
+
+
+    /**
+     * 举报投诉分类汇总
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return
+     */
+    @GetMapping("/reportStatistics")
+    public ApiResult<ReportStatisticsVO> getReportStatistics(@RequestParam(required = false) Date startTime,
+                                                             @RequestParam(required = false) Date endTime){
+        return ApiResult.success(reportComplaintService.getReportStatistics(startTime, endTime));
+    }
+
+    /**
+     * 举报情况信息
+     * @return
+     */
+    /**
+     * 举报情况信息
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return
+     */
+    @GetMapping("/reportComplaint")
+    public ApiResult<List<ReportComplaintVO>> getReportComplaint(@RequestParam(required = false) Date startTime,
+                                                                @RequestParam(required = false) Date endTime){
+        return ApiResult.success(reportComplaintService.getReportComplaint(startTime, endTime));
+    }
+
+}

+ 37 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/SiAeAllControllerWeb.java

@@ -0,0 +1,37 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.mhfire.service.SiAeAllService;
+import com.bizmatics.mhfire.service.vo.SiAeAllVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 监督检查行政执法总记录
+ * @author yq
+ * @date 2021/5/25 16:19
+ */
+@RestController
+@RequestMapping("/siaeall")
+public class SiAeAllControllerWeb {
+
+
+    @Autowired
+    public SiAeAllService aeAllService;
+
+
+    /**
+     * 行政执法总记录汇总
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    @GetMapping("/siAeAllCollect")
+    public ApiResult<List<SiAeAllVO>> aeAllCollect(@RequestParam Date startTime,
+                                                   @RequestParam Date endTime){
+        return ApiResult.success(aeAllService.getAeAllCollect(startTime,endTime));
+    }
+}

+ 64 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/TestController.java

@@ -0,0 +1,64 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.mhfire.model.Alert;
+import com.bizmatics.mhfire.model.FireSite;
+import com.bizmatics.mhfire.service.api.AlertAndSiteApi;
+import com.bizmatics.mhfire.service.job.AlertJob;
+import com.bizmatics.mhfire.service.job.FireSiteJob;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author yq
+ * @date 2021/8/12 11:05
+ */
+@Log4j2
+@RestController
+public class TestController {
+
+    @Autowired
+    private AlertJob alertJob;
+
+    @Autowired
+    private FireSiteJob fireSiteJob;
+
+    @GetMapping("login")
+    public String login(){
+        return AlertAndSiteApi.login(AlertAndSiteApi.USER_NAME,AlertAndSiteApi.USER_PASSWORD);
+    }
+
+    @GetMapping("alert")
+    public List<Alert> alert(@RequestParam(required = false) String ajlx,
+                             @RequestParam(required = false) String searchValue,
+                             @RequestParam Integer page,
+                             @RequestParam Integer size,
+                             @RequestParam String token){
+        return AlertAndSiteApi.alertPage(ajlx,searchValue,page,size,token);
+    }
+
+    @GetMapping("fireSite")
+    public List<FireSite> fireSite(@RequestParam(required = false) String dwxz,
+                                   @RequestParam(required = false) String xfzlx,
+                                   @RequestParam(required = false) String rang,
+                                   @RequestParam(required = false) String addr,
+                                   @RequestParam String token){
+        return AlertAndSiteApi.fireSiteList(dwxz, xfzlx, rang, addr, token);
+    }
+
+    @GetMapping("alertJob")
+    public void alertJob(){
+        alertJob.execute();
+    }
+
+
+    @GetMapping("fireSiteJob")
+    public void fireSiteJob(){
+        fireSiteJob.execute();
+    }
+
+}

+ 44 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/UnitBeOnDutyControllerWeb.java

@@ -0,0 +1,44 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.mhfire.persistence.mapper.po.UnitBeOnDutyPO;
+import com.bizmatics.mhfire.service.UnitBeOnDutyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 支队值班
+ * @author yq
+ * @date 2021/5/31 9:28
+ */
+@RestController
+@RequestMapping("/unitBeOnDuty")
+public class UnitBeOnDutyControllerWeb {
+
+    @Autowired
+    private UnitBeOnDutyService unitBeOnDutyService;
+
+
+    /**
+     * 今日值班表
+     * @return
+     */
+    @GetMapping("/one")
+    public ApiResult<List<UnitBeOnDutyPO>> getOneByToday(){
+        return ApiResult.success(unitBeOnDutyService.getOneByToday());
+    }
+
+
+    @GetMapping("/list")
+    public ApiResult<List<List<UnitBeOnDutyPO>>> list(@RequestParam(required = false) Date startTime,
+                                                @RequestParam(required = false) Date endTime){
+        return ApiResult.success(unitBeOnDutyService.list(startTime,endTime));
+    }
+
+}

+ 61 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/UnitControllerWeb.java

@@ -0,0 +1,61 @@
+package com.bizmatics.mhfire.controller.web;
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.persistence.mapper.po.UnitPO;
+import com.bizmatics.mhfire.service.UnitService;
+import com.bizmatics.mhfire.service.vo.UnitAlCheckVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+
+/**
+ * 单位信息
+ * @author yq
+ * @date 2021/5/28 17:44
+ */
+@RestController
+@RequestMapping("/unit")
+public class UnitControllerWeb {
+
+    @Autowired
+    private UnitService unitService;
+
+
+    /**
+     * 获取单个单位信息
+     * @return
+     */
+    @GetMapping("/one")
+    public ApiResult<UnitPO> getOne(@RequestBody UnitPO unitPo){
+        return ApiResult.success(unitService.getOne(unitPo));
+    }
+
+    /**
+     * 分页
+     * @param current 页数
+     * @param size 条数
+     * @return
+     */
+    @GetMapping("/page")
+    public ApiResult<CommonPage<UnitPO>> page(@RequestParam Integer current,
+                                              @RequestParam Integer size){
+        return ApiResult.success(unitService.page(current,size));
+    }
+
+
+    /**
+     * 单位信息及执法情况和历史检查记录
+     * @param unitId  单位ID
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    @GetMapping("/unitAlCheck")
+    public ApiResult<UnitAlCheckVO> getUnitAlCheck(@RequestParam String unitId,
+                                                   @RequestParam(required = false) Date startTime,
+                                                   @RequestParam(required = false) Date endTime){
+        return ApiResult.success(unitService.getUnitAlCheck(unitId,startTime,endTime));
+    }
+}

+ 64 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/WeatherControllerWeb.java

@@ -0,0 +1,64 @@
+package com.bizmatics.mhfire.controller.web;
+
+
+
+
+import com.bizmatics.common.core.exception.BusinessException;
+import com.bizmatics.common.core.util.HttpUtils;
+
+
+import com.bizmatics.common.core.util.StringUtils;
+import com.bizmatics.mhfire.service.vo.AlertStatisticsVO;
+import okhttp3.*;
+
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author yq
+ * @date 2021/6/16 18:02
+ */
+@RestController
+@RequestMapping("aliWeather")
+public class WeatherControllerWeb {
+
+
+    private static final String ALI_WEATHER_API_URL = "https://weather01.market.alicloudapi.com/area-to-weather?area=闵行区";
+    private static final String ALI_WEATHER_HEADER_KEY = "Authorization";
+    private static final String ALI_WEATHER_APPCODE = "0f2b7fce6e104ba8835358b7b59b4fb6";
+    private static final String ALI_WEATHER_HEADER_VALUE = "APPCODE " + ALI_WEATHER_APPCODE;
+
+    private String weather = "";
+
+    private Date date = null;
+
+    @GetMapping()
+    public String get() {
+        if (StringUtils.isBlank(weather)){
+            weather = getWeatherApi();
+            date = new Date();
+        }else {
+            if ((System.currentTimeMillis() - date.getTime()) >= (1000 * 60 * 60)){
+                weather = getWeatherApi();
+                date = new Date();
+            }
+        }
+        return weather;
+    }
+    public String getWeatherApi(){
+        try {
+            Map<String,String> headerMap = new HashMap<>();
+            headerMap.put(ALI_WEATHER_HEADER_KEY,ALI_WEATHER_HEADER_VALUE);
+            return HttpUtils.get(ALI_WEATHER_API_URL,headerMap);
+        } catch (IOException e) {
+            throw new BusinessException(e.getMessage());
+        }
+    }
+}
+

+ 45 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/bulehelp/BmfwEventController.java

@@ -0,0 +1,45 @@
+package com.bizmatics.mhfire.controller.web.bulehelp;
+
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.model.bulehelp.BmfwEvent;
+import com.bizmatics.mhfire.service.bulehelp.BmfwEventService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ *  项目申报记录
+ * </p>
+ *
+ * @author yq
+ * @since 2021-06-15
+ */
+@RestController
+@RequestMapping("/bule/bmfwEvent")
+public class BmfwEventController {
+
+    @Autowired
+    private BmfwEventService bmfwEventService;
+
+    /**
+     * 分页
+     * @param current 页数
+     * @param size 条数
+     * @param unit 单位信息
+     * @return
+     */
+    @GetMapping("/page")
+    public ApiResult<CommonPage<BmfwEvent>> page(@RequestParam Integer current,
+                                                 @RequestParam Integer size,
+                                                 @RequestParam(required = false) String unit){
+        return ApiResult.success(bmfwEventService.page(current, size, unit));
+    }
+}
+

+ 45 - 0
mhfire-controller/src/main/java/com/bizmatics/mhfire/controller/web/bulehelp/BmfwFeedbackController.java

@@ -0,0 +1,45 @@
+package com.bizmatics.mhfire.controller.web.bulehelp;
+
+
+import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
+import com.bizmatics.mhfire.model.bulehelp.BmfwEvent;
+import com.bizmatics.mhfire.model.bulehelp.BmfwFeedback;
+import com.bizmatics.mhfire.service.bulehelp.BmfwFeedbackService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ *  用户反馈记录
+ * </p>
+ *
+ * @author yq
+ * @since 2021-06-15
+ */
+@RestController
+@RequestMapping("/bule/bmfwFeedback")
+public class BmfwFeedbackController {
+
+    @Autowired
+    private BmfwFeedbackService bmfwFeedbackService;
+
+
+    /**
+     * 分页
+     * @param current 页数
+     * @param size 条数
+     * @return
+     */
+    @GetMapping("/page")
+    public ApiResult<CommonPage<BmfwFeedback>> page(@RequestParam Integer current,
+                                                    @RequestParam Integer size){
+        return ApiResult.success(bmfwFeedbackService.page(current, size));
+    }
+}
+

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません