wei 3 týždňov pred
rodič
commit
79be8f5cc4
26 zmenil súbory, kde vykonal 846 pridanie a 189 odobranie
  1. 1 1
      .idea/encodings.xml
  2. 8 1
      base-springframework-starter/pom.xml
  3. 10 3
      base-springframework/base-springframework-core/pom.xml
  4. 80 0
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/config/FastJsonConverter.java
  5. 26 37
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/config/WebConfiguration.java
  6. 19 0
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/AbstractBaseExceptionEnum.java
  7. 34 0
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/ApiServiceException.java
  8. 109 0
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/DefaultExceptionHandler.java
  9. 41 0
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/ServiceException.java
  10. 49 0
      base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/enums/CoreExceptionEnum.java
  11. 2 3
      base-springframework/base-springframework-core/src/main/resources/META-INF/spring.factories
  12. 163 135
      base-springframework/base-springframework-core/src/main/resources/logback-spring.xml
  13. BIN
      base-springframework/base-springframework-mybatis-plus/target/base-springframework-mybatis-plus-0.0.1-SNAPSHOT.jar
  14. 15 3
      base-springframework/base-springframework-redis/pom.xml
  15. 86 0
      base-springframework/base-springframework-redis/src/main/java/com/xxx/base/springframework/redis/hand/DuplicateSubmitAspect.java
  16. 20 0
      base-springframework/base-springframework-redis/src/main/java/com/xxx/base/springframework/redis/interfaces/PreventDuplicateSubmit.java
  17. 1 1
      base-springframework/base-springframework-redis/src/main/resources/META-INF/spring.factories
  18. BIN
      base-springframework/base-springframework-swagger/target/base-springframework-swagger-0.0.1-SNAPSHOT.jar
  19. 12 0
      base-springframework/base-springframework-util/pom.xml
  20. 2 2
      base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/config/PathLocaleResolver.java
  21. 37 0
      base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/util/MessageUtil.java
  22. 21 0
      base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/util/UserUtil.java
  23. 16 0
      base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/vo/UserInfoVO.java
  24. 91 0
      base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/vo/UserVO.java
  25. 2 2
      base-springframework/base-springframework-util/src/main/resources/META-INF/spring.factories
  26. 1 1
      base-springframework/pom.xml

+ 1 - 1
.idea/encodings.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
+  <component name="Encoding" native2AsciiForPropertiesFiles="true" defaultCharsetForPropertiesFiles="UTF-8">
     <file url="file://$PROJECT_DIR$/base-springframework-starter/src/main/java" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/base-springframework-starter/src/main/resources" charset="UTF-8" />
     <file url="file://$PROJECT_DIR$/base-springframework/base-springframework-constant/src/main/java" charset="UTF-8" />

+ 8 - 1
base-springframework-starter/pom.xml

@@ -23,6 +23,8 @@
         <spring.version>5.3.2</spring.version>
         <jackson-annotations.version>2.11.3</jackson-annotations.version>
         <spring-boot-maven-plugin.version>2.4.1</spring-boot-maven-plugin.version>
+        <fastjson.version>1.2.75</fastjson.version>
+        <logback-classic.version>1.2.3</logback-classic.version>
     </properties>
 
     <dependencies>
@@ -88,7 +90,12 @@
             <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>fastjson</artifactId>
-                <version>1.2.68</version>
+                <version>${fastjson.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>${logback-classic.version}</version>
             </dependency>
         </dependencies>
     </dependencyManagement>

+ 10 - 3
base-springframework/base-springframework-core/pom.xml

@@ -21,9 +21,10 @@
         <maven.compiler.target>1.8</maven.compiler.target>
         <spring-boot-maven-plugin.version>2.4.1</spring-boot-maven-plugin.version>
         <springfox-boot-starter.version>3.0.0</springfox-boot-starter.version>
-<!--
-        <mapstruct.version>1.6.3</mapstruct.version>
--->
+        <fastjson.version>1.2.75</fastjson.version>
+        <!--
+                <mapstruct.version>1.6.3</mapstruct.version>
+        -->
     </properties>
 
     <dependencies>
@@ -68,6 +69,12 @@
             <artifactId>mapstruct-processor</artifactId>
             <version>${mapstruct.version}</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 80 - 0
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/config/FastJsonConverter.java

@@ -0,0 +1,80 @@
+package com.xxx.base.springframework.core.web.config;
+
+import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.serializer.SerializeConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.serializer.ToStringSerializer;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+public class FastJsonConverter {
+    @Bean
+    public HttpMessageConverters configureMessageConverters() {
+        // 1、需先定义一个convert 转换消息对象
+        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
+
+        // 2、添加fastJson 的配置信息,比如:是否要格式化返回的json数据
+        FastJsonConfig fastJsonConfig = new FastJsonConfig();
+        fastJsonConfig.setFeatures(Feature.TrimStringFieldValue);
+
+        fastJsonConfig.setSerializerFeatures(
+                //结果是否格式化,默认为false
+                SerializerFeature.PrettyFormat,
+                //是否输出值为null的字段,默认为false
+                SerializerFeature.WriteMapNullValue,
+                //List字段如果为null,输出为[],而非null
+                SerializerFeature.WriteNullListAsEmpty,
+                //字符类型字段如果为null,输出为"",而非null
+                SerializerFeature.WriteNullStringAsEmpty,
+                //数值字段如果为null,输出为0,而非null
+                SerializerFeature.WriteNullNumberAsZero,
+                //Boolean字段如果为null,输出为false,而非null
+                SerializerFeature.WriteNullBooleanAsFalse);
+
+        //解决Long转json精度丢失的问题
+        SerializeConfig serializeConfig = SerializeConfig.globalInstance;
+        serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
+        serializeConfig.put(Long.class, ToStringSerializer.instance);
+        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
+
+        fastJsonConfig.setSerializeConfig(serializeConfig);
+
+        // 2-1、处理中文乱码问题
+        List<MediaType> fastMediaTypes = new ArrayList<>();
+        fastMediaTypes.add(MediaType.APPLICATION_JSON);
+        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
+        fastMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
+        fastMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
+        fastMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
+        fastMediaTypes.add(MediaType.APPLICATION_PDF);
+        fastMediaTypes.add(MediaType.APPLICATION_RSS_XML);
+        fastMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
+        fastMediaTypes.add(MediaType.APPLICATION_XML);
+        fastMediaTypes.add(MediaType.IMAGE_GIF);
+        fastMediaTypes.add(MediaType.IMAGE_JPEG);
+        fastMediaTypes.add(MediaType.IMAGE_PNG);
+        fastMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
+        fastMediaTypes.add(MediaType.TEXT_HTML);
+        fastMediaTypes.add(MediaType.TEXT_MARKDOWN);
+        fastMediaTypes.add(MediaType.TEXT_PLAIN);
+        fastMediaTypes.add(MediaType.TEXT_XML);
+        fastMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
+
+        fastConverter.setSupportedMediaTypes(fastMediaTypes);
+
+        // 3、在convert中添加配置信息
+        fastConverter.setFastJsonConfig(fastJsonConfig);
+        HttpMessageConverter<?> converter = fastConverter;
+        return new HttpMessageConverters(converter);
+    }
+}

+ 26 - 37
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/config/WebConfiguration.java

@@ -1,28 +1,16 @@
 package com.xxx.base.springframework.core.web.config;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.google.common.base.Charsets;
-import com.google.common.collect.Lists;
-import com.xxx.base.springframework.core.converter.StringWithoutSpaceDeserializer;
+import com.xxx.base.springframework.config.PathLocaleResolver;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.web.servlet.LocaleResolver;
 import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
 
-import java.text.SimpleDateFormat;
-import java.util.List;
-
 /**
  * 将MappingJackson2HttpMessageConverter消息转换器排在靠前位置
  *
@@ -38,31 +26,32 @@ public class WebConfiguration implements WebMvcConfigurer {
     @Autowired
     private PathLocaleResolver pathLocaleResolver;
 
-    @Override
-    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
-        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
-        ObjectMapper objectMapper = new ObjectMapper();
-        //设置日期格式
-        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(responseHandlerConfigure.getDateFormat());
-        objectMapper.setDateFormat(simpleDateFormat);
-
-        // null值是否返回,默认为不返回
-        if (!responseHandlerConfigure.getIsWriteMapNullValue()) {
-            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
-        }
-
-        SimpleModule module = new SimpleModule();
-        module.addDeserializer(String.class, new StringWithoutSpaceDeserializer(String.class));
-        objectMapper.registerModule(module);
-
-        // 在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
-        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
-        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
-        mappingJackson2HttpMessageConverter.setDefaultCharset(Charsets.UTF_8);
-        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON));
-        converters.add(0, mappingJackson2HttpMessageConverter);
-    }
+//    @Override
+//    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+//        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
+//        ObjectMapper objectMapper = new ObjectMapper();
+//        //设置日期格式
+////        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(responseHandlerConfigure.getDateFormat());
+////        objectMapper.setDateFormat(simpleDateFormat);
+//
+//        // null值是否返回,默认为不返回
 
+    /// /        if (!responseHandlerConfigure.getIsWriteMapNullValue()) {
+    /// /            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+    /// /        }
+//
+//        SimpleModule module = new SimpleModule();
+//        module.addDeserializer(String.class, new StringWithoutSpaceDeserializer(String.class));
+//        objectMapper.registerModule(module);
+//        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+//
+//        // 在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
+//        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+//        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
+//        mappingJackson2HttpMessageConverter.setDefaultCharset(Charsets.UTF_8);
+//        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON));
+//        converters.add(0, mappingJackson2HttpMessageConverter);
+//    }
     @Bean
     public LocaleChangeInterceptor localeChangeInterceptor() {
         LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();

+ 19 - 0
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/AbstractBaseExceptionEnum.java

@@ -0,0 +1,19 @@
+package com.xxx.base.springframework.core.web.exception;
+
+/**
+ * @description:
+ * @author: huzhiwen
+ * @create: 2019-01-26 17:48
+ **/
+
+public interface AbstractBaseExceptionEnum {
+    /**
+     * 获取异常的状态码
+     */
+    Integer getCode();
+
+    /**
+     * 获取异常的提示信息
+     */
+    String getMessage();
+}

+ 34 - 0
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/ApiServiceException.java

@@ -0,0 +1,34 @@
+package com.xxx.base.springframework.core.web.exception;
+
+import lombok.Data;
+
+/**
+ * @description: 远程接口调用出现的业务异常
+ * @author: huzhiwen
+ * @create: 2019-01-26 17:48
+ **/
+
+@Data
+public abstract class ApiServiceException extends Exception {
+    private static final long serialVersionUID = 2059413292126662216L;
+    /**
+     * 错误编码
+     */
+    private Integer code;
+
+    /**
+     * 错误的提示信息
+     */
+    private String errorMessage;
+
+    public ApiServiceException(AbstractBaseExceptionEnum exception) {
+        super(exception.getMessage());
+        code = exception.getCode();
+        errorMessage = exception.getMessage();
+    }
+
+    /**
+     * 获取异常的类的具体名称
+     */
+    public abstract String getExceptionClassName();
+}

+ 109 - 0
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/DefaultExceptionHandler.java

@@ -0,0 +1,109 @@
+package com.xxx.base.springframework.core.web.exception;
+
+
+import com.xxx.base.springframework.constant.vo.ErrorResponseData;
+import com.xxx.base.springframework.constant.vo.ResponseData;
+import com.xxx.base.springframework.core.web.exception.enums.CoreExceptionEnum;
+import com.xxx.base.springframework.util.MessageUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+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.bind.annotation.RestControllerAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+
+@RestControllerAdvice
+@Slf4j
+public class DefaultExceptionHandler {
+
+    /**
+     * 拦截各个服务的具体异常
+     */
+    @ExceptionHandler(ApiServiceException.class)
+    @ResponseStatus(HttpStatus.OK)
+    public ResponseData apiService(ApiServiceException e) {
+        log.error("服务具体异常:", e);
+        ErrorResponseData errorResponseData = new ErrorResponseData(e.getCode(), e.getErrorMessage());
+        return errorResponseData;
+    }
+
+    /**
+     * 拦截业务异常
+     */
+    @ExceptionHandler(ServiceException.class)
+    @ResponseStatus(HttpStatus.OK)
+    public ResponseData notFount(ServiceException e) {
+        if (e.getCode() != 501 && e.getCode() != 502) {
+            log.info("业务异常:", e);
+        }
+        return new ErrorResponseData(e.getCode(), e.getErrorMessage());
+    }
+
+    /**
+     * 拦截未知的运行时异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.OK)
+    public ResponseData serverError(Exception e) {
+        log.error("运行时异常:", e);
+        return new ErrorResponseData(CoreExceptionEnum.SERVICE_ERROR.getCode(), MessageUtil.get("SERVER_ERROR"));
+    }
+
+
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    @ResponseStatus(HttpStatus.OK)
+    public ResponseData parameterException(Exception e, HttpServletRequest request) {
+        log.error("参数错误,url=" + request.getRequestURI(), e);
+        return new ErrorResponseData(CoreExceptionEnum.PARAM_ERROR.getCode(), MessageUtil.get("PARAM_ERROR"));
+    }
+
+    /**
+     * 用来处理bean validation异常
+     *
+     * @param ex
+     * @return
+     */
+//    @ExceptionHandler(ConstraintViolationException.class)
+//    @ResponseBody
+//    public ResponseData resolveConstraintViolationException(ConstraintViolationException ex) {
+//        ex.printStackTrace();
+//        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
+//        if (constraintViolations.size() > 0) {
+//            StringBuilder msgBuilder = new StringBuilder();
+//            for (ConstraintViolation constraintViolation : constraintViolations) {
+//                msgBuilder.append(constraintViolation.getMessage()).append(",");
+//            }
+//            String errorMessage = msgBuilder.toString();
+//            if (errorMessage.length() > 1) {
+//                errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
+//            }
+//            return ResponseData.error(MessageUtil.get(errorMessage));
+//        }
+//        return ResponseData.error(MessageUtil.get(ex.getMessage()));
+//    }
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseBody
+    public ResponseData resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
+        ex.printStackTrace();
+        List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
+        if (objectErrors.size() > 0) {
+            StringBuilder msgBuilder = new StringBuilder();
+            for (ObjectError objectError : objectErrors) {
+                msgBuilder.append(objectError.getDefaultMessage()).append(",");
+            }
+            String errorMessage = msgBuilder.toString();
+            if (errorMessage.length() > 1) {
+                errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
+            }
+            return ResponseData.error(MessageUtil.get(errorMessage));
+        }
+        return ResponseData.error(MessageUtil.get(ex.getMessage()));
+    }
+}

+ 41 - 0
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/ServiceException.java

@@ -0,0 +1,41 @@
+package com.xxx.base.springframework.core.web.exception;
+
+import lombok.Data;
+
+/**
+ * @description: 业务异常的封装
+ * @author: huzhiwen
+ * @create: 2019-01-26 17:50
+ **/
+
+@Data
+public class ServiceException extends RuntimeException {
+    private static final long serialVersionUID = 8075560915449741228L;
+    private Integer code;
+
+    private String errorMessage;
+
+    public ServiceException(Integer code, String errorMessage) {
+        super(errorMessage);
+        this.code = code;
+        this.errorMessage = errorMessage;
+    }
+
+    public ServiceException(AbstractBaseExceptionEnum exception) {
+        super(exception.getMessage());
+        code = exception.getCode();
+        errorMessage = exception.getMessage();
+    }
+
+    public ServiceException(String errorMessage) {
+        super(errorMessage);
+        code = 500;
+        this.errorMessage = errorMessage;
+    }
+
+    public ServiceException(String errorMessage, Object... stringFormat) {
+        super(errorMessage);
+        code = 500;
+        this.errorMessage = String.format(errorMessage, stringFormat);
+    }
+}

+ 49 - 0
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/exception/enums/CoreExceptionEnum.java

@@ -0,0 +1,49 @@
+package com.xxx.base.springframework.core.web.exception.enums;
+
+
+import com.xxx.base.springframework.core.web.exception.AbstractBaseExceptionEnum;
+
+/**
+ * @description:
+ * @author: huzhiwen
+ * @create: 2019-01-26 17:53
+ **/
+
+public enum CoreExceptionEnum implements AbstractBaseExceptionEnum {
+    /**
+     * 文件上传
+     */
+    FILE_READING_ERROR(400, "FILE_READING_ERROR!"),
+    FILE_NOT_FOUND(400, "FILE_NOT_FOUND!"),
+
+    /**
+     * 错误的请求
+     */
+    PAGE_NULL(404, "请求页面不存在"),
+    IO_ERROR(500, "流读取异常"),
+    SERVICE_ERROR(500, "服务器异常"),
+    REMOTE_SERVICE_NULL(404, "远程服务不存在"),
+
+    /**
+     * 请求参数错误,缺少字段
+     */
+    PARAM_ERROR(1035, "请求参数错误");
+
+    private Integer code;
+    private String message;
+
+    CoreExceptionEnum(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    @Override
+    public Integer getCode() {
+        return code;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+}

+ 2 - 3
base-springframework/base-springframework-core/src/main/resources/META-INF/spring.factories

@@ -1,10 +1,9 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
   com.xxx.base.springframework.core.web.config.ResponseHandlerConfigure,\
-  com.xxx.base.springframework.core.web.handler.BaseExceptionHandler,\
+  com.xxx.base.springframework.core.web.exception.DefaultExceptionHandler,\
   com.xxx.base.springframework.core.web.handler.ResponseHandler,\
   com.xxx.base.springframework.core.web.config.WebConfiguration,\
   com.xxx.base.springframework.core.converter.StringTrimmerEditorInitBinder,\
-  com.xxx.base.springframework.core.web.config.PathLocaleResolver
-
+  com.xxx.base.springframework.core.web.config.FastJsonConverter
 org.springframework.boot.env.EnvironmentPostProcessor=\
   com.xxx.base.springframework.core.env.InitializeCustomConfiguration

+ 163 - 135
base-springframework/base-springframework-core/src/main/resources/logback-spring.xml

@@ -1,185 +1,213 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
-<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
-<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
-<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
-<configuration scan="true" scanPeriod="10 seconds">
-
-    <define name="logPath" class="com.xxx.base.springframework.core.config.LogProperty"/>
-    <property name="log.path" value="/data/release/logs/${logPath}"/>
-    <!--<property name="log.path" value="/data/release/logs"/>-->
-    <property name="log.lever" value="debug"/>
-
-    <!-- 彩色日志 -->
-    <!-- 彩色日志依赖的渲染类 -->
-    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
-    <conversionRule conversionWord="wex"
-                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
-    <conversionRule conversionWord="wEx"
-                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
-    <!-- 彩色日志格式 -->
-    <property name="CONSOLE_LOG_PATTERN"
-              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
-
-    <!--输出到控制台-->
-    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
-        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
-        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
-            <level>info</level>
-        </filter>
+<configuration scan="true">
+    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
+    <property name="LOG_PATH" value="/var/app/logs"/>
+    <!--设置系统日志目录-->
+    <property name="APPDIR" value="play"/>
+    <logger name="io.grpc" level="INFO"/>
+    <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,,,, -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
-            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
-            <!-- 设置字符集 -->
+            <!--<pattern>%d %p (%file:%line\)- %m%n</pattern>-->
+            <!--格式化输出:%d:表示日期    %thread:表示线程名     %-5level:级别从左显示5个字符宽度  %msg:日志消息    %n:是换行符-->
+            <pattern>1-%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger - %msg%n</pattern>
             <charset>UTF-8</charset>
         </encoder>
     </appender>
-
-    <!--输出到文件-->
-    <!-- 时间滚动输出 level为 DEBUG 日志 -->
-    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <!--
+          说明:
+          1、日志级别及文件
+              日志记录采用分级记录,级别与日志文件名相对应,不同级别的日志信息记录到不同的日志文件中
+              例如:error级别记录到log_error_xxx.log或log_error.log(该文件为当前记录的日志文件),而log_error_xxx.log为归档日志,
+              日志文件按日期记录,同一天内,若日志文件大小等于或大于2M,则按0、1、2...顺序分别命名
+              例如log-level-2013-12-21.0.log
+              其它级别的日志也是如此。
+          2、文件路径
+              若开发、测试用,在Eclipse中运行项目,则到Eclipse的安装路径查找logs文件夹,以相对路径../logs。
+              若部署到Tomcat下,则在Tomcat下的logs文件中
+          3、Appender
+              FILEERROR对应error级别,文件名以log-error-xxx.log形式命名
+              FILEWARN对应warn级别,文件名以log-warn-xxx.log形式命名
+              FILEINFO对应info级别,文件名以log-info-xxx.log形式命名
+              FILEDEBUG对应debug级别,文件名以log-debug-xxx.log形式命名
+              CONSOLE将日志信息输出到控制上,为方便开发测试使用
+       -->
+    <!-- 日志记录器,日期滚动记录 -->
+    <appender name="FILEERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
         <!-- 正在记录的日志文件的路径及文件名 -->
-        <file>${log.path}/debug.log</file>
-        <!--日志文件输出格式-->
-        <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-            <charset>UTF-8</charset> <!-- 设置字符集 -->
-        </encoder>
+        <file>${LOG_PATH}/${APPDIR}/log_error.log</file>
         <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            <!-- 日志归档 -->
-            <fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
+            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
+            <fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
+            命名日志文件,例如log-error-2013-12-21.0.log -->
             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
-                <maxFileSize>100MB</maxFileSize>
+                <maxFileSize>2MB</maxFileSize>
             </timeBasedFileNamingAndTriggeringPolicy>
-            <!--日志文件保留天数-->
-            <maxHistory>15</maxHistory>
         </rollingPolicy>
-        <!-- 此日志文件只记录debug级别的 -->
+        <!-- 追加方式记录日志 -->
+        <append>true</append>
+        <!-- 日志文件的格式 -->
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>===%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <!-- 此日志文件只记录error级别的 -->
         <filter class="ch.qos.logback.classic.filter.LevelFilter">
-            <level>debug</level>
+            <level>error</level>
             <onMatch>ACCEPT</onMatch>
             <onMismatch>DENY</onMismatch>
         </filter>
     </appender>
 
-    <!-- 时间滚动输出 level为 INFO 日志 -->
-    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <!-- 日志记录器,日期滚动记录 -->
+    <appender name="FILEWARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
         <!-- 正在记录的日志文件的路径及文件名 -->
-        <file>${log.path}/info.log</file>
-        <!--日志文件输出格式-->
-        <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-            <charset>UTF-8</charset>
-        </encoder>
+        <file>${LOG_PATH}/${APPDIR}/log_warn.log</file>
         <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            <!-- 每天日志归档路径以及格式 -->
-            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
+            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
+            <fileNamePattern>${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
+            命名日志文件,例如log-error-2013-12-21.0.log -->
             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
-                <maxFileSize>100MB</maxFileSize>
+                <maxFileSize>2MB</maxFileSize>
             </timeBasedFileNamingAndTriggeringPolicy>
-            <!--日志文件保留天数-->
-            <maxHistory>15</maxHistory>
         </rollingPolicy>
-        <!-- 此日志文件只记录info级别的 -->
+        <!-- 追加方式记录日志 -->
+        <append>true</append>
+        <!-- 日志文件的格式 -->
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>===%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <!-- 此日志文件只记录warn级别的 -->
         <filter class="ch.qos.logback.classic.filter.LevelFilter">
-            <level>info</level>
+            <level>warn</level>
             <onMatch>ACCEPT</onMatch>
             <onMismatch>DENY</onMismatch>
         </filter>
     </appender>
 
-    <!-- 时间滚动输出 level为 WARN 日志 -->
-    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <!-- 日志记录器,日期滚动记录 -->
+    <appender name="FILEINFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
         <!-- 正在记录的日志文件的路径及文件名 -->
-        <file>${log.path}/warn.log</file>
-        <!--日志文件输出格式-->
-        <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
-        </encoder>
+        <file>${LOG_PATH}/${APPDIR}/log_info.log</file>
         <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
+            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
+            <fileNamePattern>${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
+            命名日志文件,例如log-error-2013-12-21.0.log -->
             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
-                <maxFileSize>100MB</maxFileSize>
+                <maxFileSize>2MB</maxFileSize>
             </timeBasedFileNamingAndTriggeringPolicy>
-            <!--日志文件保留天数-->
-            <maxHistory>15</maxHistory>
         </rollingPolicy>
-        <!-- 此日志文件只记录warn级别的 -->
+        <!-- 追加方式记录日志 -->
+        <append>true</append>
+        <!-- 日志文件的格式 -->
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>===%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <!-- 此日志文件只记录info级别的 -->
         <filter class="ch.qos.logback.classic.filter.LevelFilter">
-            <level>warn</level>
+            <level>info</level>
             <onMatch>ACCEPT</onMatch>
             <onMismatch>DENY</onMismatch>
         </filter>
     </appender>
 
-
-    <!-- 时间滚动输出 level为 ERROR 日志 -->
-    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <!-- 日志记录器,日期滚动记录 -->
+    <appender name="FILELOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
         <!-- 正在记录的日志文件的路径及文件名 -->
-        <file>${log.path}/error.log</file>
-        <!--日志文件输出格式-->
-        <encoder>
-            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
-        </encoder>
+        <file>${LOG_PATH}/${APPDIR}/log_all.log</file>
         <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
+            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
+            <fileNamePattern>${LOG_PATH}/${APPDIR}/all/log-all-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
+            命名日志文件,例如log-error-2013-12-21.0.log -->
             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
-                <maxFileSize>100MB</maxFileSize>
+                <maxFileSize>2MB</maxFileSize>
             </timeBasedFileNamingAndTriggeringPolicy>
-            <!--日志文件保留天数-->
-            <maxHistory>15</maxHistory>
         </rollingPolicy>
-        <!-- 此日志文件只记录ERROR级别的 -->
-        <filter class="ch.qos.logback.classic.filter.LevelFilter">
-            <level>ERROR</level>
-            <onMatch>ACCEPT</onMatch>
-            <onMismatch>DENY</onMismatch>
-        </filter>
+        <!-- 追加方式记录日志 -->
+        <append>true</append>
+        <!-- 日志文件的格式 -->
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>===%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
     </appender>
 
-    <!--
-        <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
-        以及指定<appender>。<logger>仅有一个name属性,
-        一个可选的level和一个可选的addtivity属性。
-        name:用来指定受此logger约束的某一个包或者具体的某一个类。
-        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
-              还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
-              如果未设置此属性,那么当前logger将会继承上级的级别。
-        addtivity:是否向上级logger传递打印信息。默认是true。
-    -->
-    <!--<logger name="org.springframework.web" level="info"/>-->
-    <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
-    <!--
-        使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
-        第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
-        第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
-     -->
-
-
-    <!--
-        root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
-        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
-        不能设置为INHERITED或者同义词NULL。默认是DEBUG
-        可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-    -->
+    <!--日志异步到数据库  -->
+    <!--<appender name="DBAPPENDER" class="ch.qos.logback.classic.db.DBAppender">-->
+    <!--<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">-->
+    <!--<dataSource class="com.zaxxer.hikari.HikariDataSource">-->
+    <!--<driverClassName>com.mysql.jdbc.Driver</driverClassName>-->
+    <!--<jdbcUrl>jdbc:mysql://localhost:3306/albedo-new?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false</jdbcUrl>-->
+    <!--<username>root</username>-->
+    <!--<password>123456</password>-->
+    <!--<poolName>HikariPool-logback</poolName>-->
+    <!--</dataSource>-->
+    <!--</connectionSource>-->
+    <!--&lt;!&ndash; 此日志文件只记录info级别的 &ndash;&gt;-->
+    <!--<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
+    <!--<level>warn</level>-->
+    <!--<onMatch>ACCEPT</onMatch>-->
+    <!--<onMismatch>DENY</onMismatch>-->
+    <!--</filter>-->
+    <!--&lt;!&ndash; 此日志文件只记录info级别的 &ndash;&gt;-->
+    <!--<filter class="ch.qos.logback.classic.filter.LevelFilter">-->
+    <!--<level>error</level>-->
+    <!--<onMatch>ACCEPT</onMatch>-->
+    <!--<onMismatch>DENY</onMismatch>-->
+    <!--</filter>-->
+    <!--</appender>-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>-->
+            <pattern>%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger) -
+                %cyan(%msg%n)
+            </pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+    <!-- 生产环境 -->
+    <springProfile name="pro">
+        <root level="INFO">
+            <appender-ref ref="FILEERROR"/>
+            <appender-ref ref="FILEWARN"/>
+            <appender-ref ref="FILEINFO"/>
+            <appender-ref ref="FILELOG"/>
+            <appender-ref ref="STDOUT"/>
+        </root>
+        <property name="LOG_PATH" value="/var/app/logs/play"/>
+    </springProfile>
 
-    <root level="info">
-        <appender-ref ref="CONSOLE"/>
-        <appender-ref ref="DEBUG_FILE"/>
-        <appender-ref ref="INFO_FILE"/>
-        <appender-ref ref="WARN_FILE"/>
-        <appender-ref ref="ERROR_FILE"/>
-    </root>
-    <root level="">
-        <appender-ref ref="CONSOLE"/>
-        <appender-ref ref="DEBUG_FILE"/>
-        <appender-ref ref="INFO_FILE"/>
-        <appender-ref ref="WARN_FILE"/>
-        <appender-ref ref="ERROR_FILE"/>
-    </root>
-</configuration>
+    <!-- 开发环境 -->
+    <springProfile name="dev">
+        <root level="INFO">
+            <appender-ref ref="FILEERROR"/>
+            <appender-ref ref="FILEWARN"/>
+            <appender-ref ref="FILEINFO"/>
+            <appender-ref ref="FILELOG"/>
+            <appender-ref ref="STDOUT"/>
+        </root>
+    </springProfile>
+    <springProfile name="test">
+        <root level="INFO">
+            <appender-ref ref="FILEERROR"/>
+            <appender-ref ref="FILEWARN"/>
+            <appender-ref ref="FILEINFO"/>
+            <appender-ref ref="FILELOG"/>
+            <appender-ref ref="STDOUT"/>
+        </root>
+    </springProfile>
+</configuration>

BIN
base-springframework/base-springframework-mybatis-plus/target/base-springframework-mybatis-plus-0.0.1-SNAPSHOT.jar


+ 15 - 3
base-springframework/base-springframework-redis/pom.xml

@@ -16,7 +16,7 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <java.version>1.8</java.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <redisson-spring-boot-starter.version>3.16.8</redisson-spring-boot-starter.version>
+        <redisson-spring-boot-starter.version>3.18.0</redisson-spring-boot-starter.version>
         <spring-boot-starter.version>2.4.1</spring-boot-starter.version>
         <jedis.version>3.3.0</jedis.version>
     </properties>
@@ -27,12 +27,18 @@
             <artifactId>base-springframework-util</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.xxx</groupId>
+            <artifactId>base-springframework-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
 
-<!--        <dependency>
+        <dependency>
             <groupId>org.redisson</groupId>
             <artifactId>redisson-spring-boot-starter</artifactId>
             <version>${redisson-spring-boot-starter.version}</version>
-        </dependency>-->
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
@@ -43,6 +49,12 @@
             <artifactId>jedis</artifactId>
             <version>${jedis.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjrt</artifactId>
+            <version>1.9.25.1</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>

+ 86 - 0
base-springframework/base-springframework-redis/src/main/java/com/xxx/base/springframework/redis/hand/DuplicateSubmitAspect.java

@@ -0,0 +1,86 @@
+package com.xxx.base.springframework.redis.hand;
+
+import com.xxx.base.springframework.core.web.exception.ServiceException;
+import com.xxx.base.springframework.redis.interfaces.PreventDuplicateSubmit;
+import com.xxx.base.springframework.util.MessageUtil;
+import com.xxx.base.springframework.util.UserUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.security.MessageDigest;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Aspect
+@Component
+public class DuplicateSubmitAspect {
+
+    @Autowired
+    private RedissonClient redissonClient;
+
+    @Around("@annotation(preventDuplicateSubmit)")
+    public Object around(ProceedingJoinPoint point, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
+
+        // 构建key: 用户ID+URL+参数签名
+        Long userId = UserUtil.getCurrentUser().getUserVO().getId();
+        String url = request.getRequestURI();
+        String key = "DUPLICATE_SUBMIT:" + url + ":" + userId;
+
+        long expireTime = preventDuplicateSubmit.expireTime();
+        long timeout = preventDuplicateSubmit.timeout();
+
+        RLock lock = redissonClient.getLock(key);
+
+        try {
+            // 尝试获取分布式锁
+            boolean isLocked = lock.tryLock(timeout, expireTime, TimeUnit.SECONDS);
+            if (!isLocked) {
+                log.warn("检测到重复提交请求,key={}", key);
+                throw new ServiceException(MessageUtil.get("REPEAT_ERROR"));
+            }
+
+            // 执行业务方法
+            return point.proceed();
+        } finally {
+            // 释放锁
+            if (lock.isHeldByCurrentThread()) {
+                lock.unlock();
+            }
+        }
+    }
+
+    private String getRequestParamSign(HttpServletRequest request) {
+        SortedMap<String, String[]> params = new TreeMap<>(request.getParameterMap());
+        StringBuilder sb = new StringBuilder();
+        for (String key : params.keySet()) {
+            sb.append(key).append("=").append(String.join(",", params.get(key))).append("&");
+        }
+        return md5(sb.toString());
+    }
+
+    private String md5(String content) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] bytes = md.digest(content.getBytes("UTF-8"));
+            StringBuilder result = new StringBuilder();
+            for (byte b : bytes) {
+                result.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);
+            }
+            return result.toString();
+        } catch (Exception e) {
+            throw new RuntimeException("MD5 calculation error", e);
+        }
+    }
+}

+ 20 - 0
base-springframework/base-springframework-redis/src/main/java/com/xxx/base/springframework/redis/interfaces/PreventDuplicateSubmit.java

@@ -0,0 +1,20 @@
+package com.xxx.base.springframework.redis.interfaces;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PreventDuplicateSubmit {
+    /**
+     * 防重复操作过期时间(秒)
+     */
+    long expireTime() default 5;
+
+    /**
+     * 获取锁超时时间(ms)
+     */
+    long timeout() default 1000;
+}

+ 1 - 1
base-springframework/base-springframework-redis/src/main/resources/META-INF/spring.factories

@@ -1,6 +1,6 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
   com.xxx.base.springframework.redis.config.JRedisConfig,\
+  com.xxx.base.springframework.redis.hand.DuplicateSubmitAspect,\
   com.xxx.base.springframework.redis.utils.RedisUtil
-
 org.springframework.boot.env.EnvironmentPostProcessor=\
   com.xxx.base.springframework.redis.env.InitializeCustomConfiguration

BIN
base-springframework/base-springframework-swagger/target/base-springframework-swagger-0.0.1-SNAPSHOT.jar


+ 12 - 0
base-springframework/base-springframework-util/pom.xml

@@ -20,6 +20,7 @@
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <kaptcha.version>2.3.0</kaptcha.version>
+        <transmittable.version>2.14.5</transmittable.version>
     </properties>
 
     <dependencies>
@@ -29,11 +30,22 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>transmittable-thread-local</artifactId>
+            <version>${transmittable.version}</version>
+        </dependency>
         <!--<dependency>
             <groupId>com.google.code</groupId>
             <artifactId>kaptcha</artifactId>
             <version>${kaptcha.version}</version>
         </dependency>-->
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.24</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 2 - 2
base-springframework/base-springframework-core/src/main/java/com/xxx/base/springframework/core/web/config/PathLocaleResolver.java → base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/config/PathLocaleResolver.java

@@ -1,4 +1,4 @@
-package com.xxx.base.springframework.core.web.config;
+package com.xxx.base.springframework.config;
 
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -32,7 +32,7 @@ public class PathLocaleResolver implements LocaleResolver {
             if (StringUtils.equals(lang, ADMIN_DEFAULT_LANGUAGE)) {
                 return Locale.SIMPLIFIED_CHINESE;
             }
-            
+
             return new Locale(lang);
         }
 

+ 37 - 0
base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/util/MessageUtil.java

@@ -0,0 +1,37 @@
+package com.xxx.base.springframework.util;
+
+import com.xxx.base.springframework.config.PathLocaleResolver;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.MessageSource;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description
+ * @Author huzhiwen
+ * @Date 2020/10/21 13:28
+ **/
+@Component
+@Slf4j
+public class MessageUtil {
+
+    private static MessageSource messageSource;
+
+    private static PathLocaleResolver pathLocaleResolver;
+
+    public MessageUtil(MessageSource messageSource, PathLocaleResolver pathLocaleResolver) {
+        this.messageSource = messageSource;
+        this.pathLocaleResolver = pathLocaleResolver;
+    }
+
+    /**
+     * 获取单个国际化翻译值
+     */
+    public static String get(String msgKey) {
+        try {
+            return messageSource.getMessage(msgKey, null, pathLocaleResolver.getLocal());
+        } catch (Exception e) {
+            log.error("国际化发生异常", e);
+            return msgKey;
+        }
+    }
+}

+ 21 - 0
base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/util/UserUtil.java

@@ -0,0 +1,21 @@
+package com.xxx.base.springframework.util;
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+import com.xxx.base.springframework.vo.UserInfoVO;
+
+public class UserUtil {
+
+    private static TransmittableThreadLocal<UserInfoVO> userInfo = new TransmittableThreadLocal<>();
+
+    private UserUtil() {
+    }
+
+    public static UserInfoVO getCurrentUser() {
+        return userInfo.get();
+    }
+
+    public static void setCurrentUser(UserInfoVO user) {
+        userInfo.set(user);
+    }
+
+}

+ 16 - 0
base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/vo/UserInfoVO.java

@@ -0,0 +1,16 @@
+package com.xxx.base.springframework.vo;
+
+import lombok.Data;
+
+
+@Data
+public class UserInfoVO {
+
+    private String token;
+
+    private UserVO userVO;
+
+    private String type;
+
+    private String ip;
+}

+ 91 - 0
base-springframework/base-springframework-util/src/main/java/com/xxx/base/springframework/vo/UserVO.java

@@ -0,0 +1,91 @@
+package com.xxx.base.springframework.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+public class UserVO {
+
+    //    @ApiModelProperty(value = "用户id")
+    private Long id;
+
+    //    @ApiModelProperty(value = "用户名")
+    private String username;
+
+    //    @JSONField(serialize = false)
+    private String password;
+
+    //    @ApiModelProperty(value = "等级")
+    private String level;
+
+    //    @ApiModelProperty(value = "等级昵称")
+    private String levelNickName;
+
+    //    @ApiModelProperty(value = "vip过期时间")
+//    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime vipExpire;
+
+    //    @ApiModelProperty(value = "邀请码")
+    private String invitationCode;
+
+    private Long ascriptionUserId;
+
+    private String ascriptionUserName;
+
+    //    @ApiModelProperty(value = "注册时间")
+//    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    //    @ApiModelProperty(value = "注册ip")
+    private String registerIp;
+
+    //    @ApiModelProperty(value = "登录ip")
+    private String loginIp;
+
+    //    @ApiModelProperty(value = "登录时间")
+//    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime loginTime;
+
+    //    @ApiModelProperty(value = "状态")
+    private String status;
+
+    //    @ApiModelProperty(value = "余额")
+    private BigDecimal balance;
+
+    //    @ApiModelProperty(value = "优惠卷金额")
+    private BigDecimal vouchereAmount;
+
+    //    @ApiModelProperty("活跃奖励金额")
+    private BigDecimal activeAmount;
+
+    //    @ApiModelProperty(value = "代理人")
+    private String agent;
+
+    private Long agentId;
+
+    //    @ApiModelProperty(value = "是否假人 1.否 2.是")
+    private String dummy;
+
+    //    @ApiModelProperty(value = "是否不能提款,true:是,false:不是")
+    private Boolean isCantWithdrawal;
+
+    //    @ApiModelProperty(value = "更新管理人ID")
+    private Long updateUserId;
+
+    //    @ApiModelProperty(value = "是否无限制提款")
+    private Boolean isUnlimitedWithdrawals;
+
+    //    @ApiModelProperty(value = "更新时间")
+    private LocalDateTime updateTime;
+
+    //    @ApiModelProperty(value = "可购买分红权数量")
+    private Integer copyrightCount;
+
+    //    @ApiModelProperty(value = "可用免费提款次数")
+    private Integer availableFreeWithdrawalsCount;
+
+    //    @ApiModelProperty(value = "今日是否可以提款")
+    private Boolean isWithdrawMoneyToday;
+}

+ 2 - 2
base-springframework/base-springframework-util/src/main/resources/META-INF/spring.factories

@@ -1,5 +1,5 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-	com.xxx.base.springframework.util.SpringBeanUtil
-	
+    com.xxx.base.springframework.config.PathLocaleResolver,\
+    com.xxx.base.springframework.util.SpringBeanUtil
 #org.springframework.boot.env.EnvironmentPostProcessor=\
 #	org.springframework.core.web.config.Test

+ 1 - 1
base-springframework/pom.xml

@@ -28,7 +28,7 @@
         <lombok.version>1.18.12</lombok.version>
         <spring-boot-starter.version>2.4.1</spring-boot-starter.version>
         <spring-boot-starter-web.version>2.4.1</spring-boot-starter-web.version>
-        <guava.version>29.0-jre</guava.version>
+        <guava.version>20.0</guava.version>
         <commons-lang3.version>3.11</commons-lang3.version>
         <commons-collections4.version>4.4</commons-collections4.version>
         <commons-io.version>1.3.2</commons-io.version>