xiatian 1 week ago
parent
commit
aca47a91a0

+ 10 - 0
pom.xml

@@ -32,6 +32,16 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-aop</artifactId>
         </dependency>
+              <dependency>
+        <groupId>commons-collections</groupId>
+        <artifactId>commons-collections</artifactId>
+        <version>3.2.2</version>
+      </dependency>
+            <dependency>
+        <groupId>javax.validation</groupId>
+        <artifactId>validation-api</artifactId>
+        <version>2.0.1.Final</version>
+      </dependency>
             <dependency>
         <groupId>org.jsoup</groupId>
         <artifactId>jsoup</artifactId>

+ 7 - 1
src/main/java/cn/com/qmth/ac/Application.java

@@ -3,8 +3,14 @@ package cn.com.qmth.ac;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
 
-@SpringBootApplication(scanBasePackages = "com.qmth.*", exclude = { SecurityAutoConfiguration.class })
+@EnableAsync
+@Configuration
+@ComponentScan(basePackages = { "cn.com.qmth" })
+@SpringBootApplication(scanBasePackages = "cn.com.qmth.*", exclude = { SecurityAutoConfiguration.class })
 public class Application {
 
     public static void main(String[] args) {

+ 12 - 0
src/main/java/cn/com/qmth/ac/bean/BaseRequest.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.ac.bean;
+
+/**
+ * 请求体基类
+ *
+ * @author WANGWEI
+ */
+public abstract class BaseRequest extends ExchangeBean {
+
+    private static final long serialVersionUID = 6465330136225230063L;
+
+}

+ 25 - 0
src/main/java/cn/com/qmth/ac/bean/BaseResponse.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.ac.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 响应体基类
+ *
+ * @author WANGWEI
+ */
+public abstract class BaseResponse extends ExchangeBean {
+
+    private static final long serialVersionUID = 1755304211766414171L;
+
+    @ApiModelProperty(value = "耗时(毫秒)", example = "99")
+    private Long cost;
+
+    public Long getCost() {
+        return cost;
+    }
+
+    public void setCost(Long cost) {
+        this.cost = cost;
+    }
+
+}

+ 222 - 0
src/main/java/cn/com/qmth/ac/bean/ControllerAspect.java

@@ -0,0 +1,222 @@
+package cn.com.qmth.ac.bean;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.ac.util.JsonMapper;
+import cn.com.qmth.ac.util.ObjectUtil;
+import cn.com.qmth.ac.util.ServletUtil;
+import cn.com.qmth.ac.util.ThreadLocalUtil;
+
+/**
+ * spring mvc controller aspect.
+ *
+ * @author WANGWEI
+ * @date 2019年1月30日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Component
+@Aspect
+public class ControllerAspect {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ControllerAspect.class);
+
+    /**
+     * 构造函数
+     */
+    public ControllerAspect() {
+        super();
+        LOG.info("ControllerAspect init...");
+    }
+
+    private static String[] excludeFields = new String[] { "password", ".*Password" };
+
+    /**
+     * handlerMethods
+     *
+     * @author WANGWEI
+     */
+    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) "
+            + "|| @annotation(org.springframework.web.bind.annotation.GetMapping) "
+            + "|| @annotation(org.springframework.web.bind.annotation.PostMapping)"
+            + "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)"
+            + "|| @annotation(org.springframework.web.bind.annotation.PutMapping)")
+    public void handlerMethods() {
+
+    }
+
+    /**
+     * 接口处理
+     *
+     * @param joinPoint
+     * @return
+     * @author WANG WEI
+     */
+    @Around("handlerMethods()")
+    public Object doAroundWebRequests(ProceedingJoinPoint joinPoint) {
+        // 获取request对象
+        HttpServletRequest request = ServletUtil.getRequest();
+        String path = request.getServletPath();
+        try {
+            if ("/".equals(request.getServletPath())) {
+                return joinPoint.proceed();
+            }
+        } catch (Throwable e) {
+            LOG.error(e.getMessage(), e);
+            throw new RuntimeException(e);
+        }
+
+        long startTime = System.currentTimeMillis();
+        String method = request.getMethod();
+        Object[] args = joinPoint.getArgs();
+
+        if (LOG.isInfoEnabled()) {
+            StringBuilder params = new StringBuilder();
+
+            // api method params
+            if (ArrayUtils.isNotEmpty(args)) {
+                params.append("params: ");
+                for (int i = 0; i < args.length; i++) {
+                    Object curArg = args[i];
+                    String content = this.parseContent(curArg);
+                    params.append(content);
+
+                    if (i < args.length - 1) {
+                        params.append(";");
+                    }
+                }
+            }
+
+            // api url params
+            Map<String, String[]> paramMaps = request.getParameterMap();
+            if (MapUtils.isNotEmpty(paramMaps)) {
+                params.append(" urlParams: ");
+                for (Map.Entry<String, String[]> entry : paramMaps.entrySet()) {
+                    String name = entry.getKey();
+                    String[] values = entry.getValue();
+                    params.append("&").append(name).append("=");
+
+                    if (ArrayUtils.isNotEmpty(values)) {
+                        if (name.toLowerCase().indexOf(excludeFields[0]) >= 0) {
+                            params.append("***");
+                        } else {
+                            params.append(values[0]);
+                        }
+                    }
+                }
+            }
+
+            if (LOG.isInfoEnabled()) {
+                LOG.info("[HTTP-{}] - {}, {}", method, path, params);
+            }
+        }
+
+        Object ret;
+        try {
+
+            // 执行
+            ret = joinPoint.proceed();
+
+        } catch (Throwable e) {
+
+            LOG.error("[HTTP-fail] - {}, cost {} ms, cause = {}", path, System.currentTimeMillis() - startTime,
+                    e.getMessage());
+            throw new RuntimeException(e);
+        }
+
+        if (null != ret && BaseResponse.class.isAssignableFrom(ret.getClass())) {
+            BaseResponse baseResponse = (BaseResponse) ret;
+            baseResponse.setCost(System.currentTimeMillis() - startTime);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            if (ret == null) {
+                LOG.debug("[HTTP-{}] response status = 200, void", method);
+            } else if (ret instanceof ResponseEntity) {
+                ResponseEntity<?> re = (ResponseEntity<?>) ret;
+                Object body = re.getBody();
+
+                String content = this.parseContent(body);
+                LOG.debug("[HTTP-{}] response status = {}, {}", method, re.getStatusCodeValue(), content);
+            } else {
+                String content = this.parseContent(ret);
+                LOG.debug("[HTTP-{}] response status = 200, {}", method, content);
+            }
+        }
+
+        HttpServletResponse response = ServletUtil.getResponse();
+        response.setHeader("Trace-Id", ThreadLocalUtil.getTraceId());
+        response.setHeader("cost", String.valueOf(System.currentTimeMillis() - startTime));
+
+        if (LOG.isInfoEnabled()) {
+            LOG.info("[HTTP-ok] - {}, cost {} ms", path, System.currentTimeMillis() - startTime);
+        }
+
+        return ret;
+    }
+
+    private String parseContent(Object obj) {
+        if (obj == null) {
+            return "";
+        }
+
+        Class clazz = obj.getClass();
+        if (ObjectUtil.isBaseDataType(clazz) || obj instanceof String) {
+            return this.cutStr(obj.toString());
+        }
+
+        boolean canJson = false;
+        if (obj instanceof Collection || obj instanceof Map) {
+            canJson = true;
+        } else if (obj instanceof Serializable) {
+            String classPath = clazz.getName();
+            if (classPath.startsWith("org.springframework.data.domain.PageImpl") || classPath.startsWith("cn.com.qmth")
+                    || classPath.startsWith("com.qmth")) {
+                canJson = true;
+            }
+        }
+
+        if (!canJson) {
+            // can't to json
+            return clazz.getSimpleName();
+        }
+
+        String content = null;
+        try {
+            content = new JsonMapper().toJson(obj);
+        } catch (Exception e) {
+            LOG.warn("parseContent fail! " + e.getMessage());
+        }
+
+        return this.cutStr(content);
+    }
+
+    private String cutStr(String str) {
+        if (str == null) {
+            return "";
+        }
+
+        if (str.length() > 2000) {
+            // content too large...
+            return str.substring(0, 2000) + "......";
+        }
+
+        return str;
+    }
+
+}

+ 207 - 0
src/main/java/cn/com/qmth/ac/bean/CustomExceptionHandler.java

@@ -0,0 +1,207 @@
+package cn.com.qmth.ac.bean;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindingResult;
+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.multipart.support.MissingServletRequestPartException;
+
+/**
+ * 异常转换器
+ *
+ * @author WANGWEI
+ * @date 2019年1月30日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@ControllerAdvice
+@ResponseBody
+public class CustomExceptionHandler {
+
+    /**
+     * 接口日志
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(CustomExceptionHandler.class);
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    @ExceptionHandler(RuntimeException.class)
+    public ResponseEntity<StatusResponse> handleException(RuntimeException e, HttpServletRequest request) {
+        Throwable cause = e.getCause();
+        StatusResponse body = null;
+
+        if (null == cause) {
+            body = new StatusResponse("500", "系统异常");
+            cause = e;
+        } else if (cause instanceof StatusException) {
+            StatusException se = (StatusException) cause;
+            body = new StatusResponse(se.getCode() + "", se.getMessage());
+        } else if (cause instanceof IllegalStateException) {
+            body = new StatusResponse("500", cause.getMessage());
+        } else if (cause instanceof javax.validation.ConstraintViolationException) {
+            javax.validation.ConstraintViolationException cvExcp = (ConstraintViolationException) cause;
+            Set<ConstraintViolation<?>> constraintViolations = cvExcp.getConstraintViolations();
+
+            StringBuffer errorMsg = new StringBuffer();
+            boolean isFirst = true;
+            for (ConstraintViolation<?> cv : constraintViolations) {
+                if (isFirst) {
+                    errorMsg.append(cv.getMessage());
+                    isFirst = false;
+                } else {
+                    errorMsg.append("; ").append(cv.getMessage());
+                }
+            }
+            body = new StatusResponse("500", errorMsg.toString());
+        } else {
+            body = new StatusResponse("500", "系统异常");
+            cause = e;
+        }
+
+        return asResult(cause, body, request);
+    }
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public ResponseEntity<StatusResponse> handleException(MethodArgumentNotValidException e,
+            HttpServletRequest request) {
+        BindingResult result = e.getBindingResult();
+        List<ObjectError> allErrors = result.getAllErrors();
+        StringBuffer errorMsg = new StringBuffer();
+
+        boolean isFirst = true;
+        for (ObjectError err : allErrors) {
+            if (isFirst) {
+                errorMsg.append(err.getDefaultMessage());
+                isFirst = false;
+            } else {
+                errorMsg.append("; ").append(err.getDefaultMessage());
+            }
+        }
+
+        StatusResponse body = new StatusResponse("500", errorMsg.toString());
+        return asResult(e, body, request);
+    }
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    public ResponseEntity<StatusResponse> handleException(MissingServletRequestParameterException e,
+            HttpServletRequest request) {
+        StatusResponse body = new StatusResponse("500", e.getMessage());
+        return asResult(e, body, request);
+    }
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    @ExceptionHandler(MissingServletRequestPartException.class)
+    public ResponseEntity<StatusResponse> handleException(MissingServletRequestPartException e,
+            HttpServletRequest request) {
+        StatusResponse body = new StatusResponse("500", e.getMessage());
+        return asResult(e, body, request);
+    }
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    @ExceptionHandler(HttpMessageNotReadableException.class)
+    public ResponseEntity<StatusResponse> handleException(HttpMessageNotReadableException e,
+            HttpServletRequest request) {
+        StatusResponse body = new StatusResponse("500", "Required request body is missing");
+        return asResult(e, body, request);
+    }
+
+    /**
+     * 异常处理
+     *
+     * @param e
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    @ExceptionHandler(Exception.class)
+    public ResponseEntity<StatusResponse> handleException(Exception e, HttpServletRequest request) {
+        StatusResponse body = new StatusResponse("500", "系统错误");
+        return asResult(e, body, request);
+    }
+
+    /**
+     * 构建响应结果
+     *
+     * @param err
+     * @param body
+     * @param request
+     * @return
+     * @author WANGWEI
+     */
+    private ResponseEntity<StatusResponse> asResult(Throwable err, StatusResponse body, HttpServletRequest request) {
+
+        boolean printStackTrace = true;
+
+        if (!printStackTrace) {
+            String forcePrintStackTrace = System.getProperty("log.forcePrintStackTrace");
+            if (null != forcePrintStackTrace && forcePrintStackTrace.equalsIgnoreCase(Boolean.toString(true))) {
+                printStackTrace = true;
+            }
+        }
+
+        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
+        if (printStackTrace) {
+            LOG.error("[{}] response status = {}, Cause is {}", request.getServletPath(), httpStatus.value(),
+                    err.getMessage(), err);
+        } else {
+            LOG.error("[{}] response status = {}, Cause is {}", request.getServletPath(), httpStatus.value(),
+                    err.getMessage());
+        }
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.add("Content-Type", "application/json;charset=utf-8");
+        return new ResponseEntity<>(body, headers, httpStatus);
+    }
+
+}

+ 12 - 0
src/main/java/cn/com/qmth/ac/bean/ExchangeBean.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.ac.bean;
+
+/**
+ * bean 基类
+ *
+ * @author WANGWEI
+ */
+public abstract class ExchangeBean implements JsonSerializable {
+
+    private static final long serialVersionUID = 3913250969569367810L;
+
+}

+ 15 - 0
src/main/java/cn/com/qmth/ac/bean/JsonSerializable.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.ac.bean;
+
+import java.io.Serializable;
+
+/**
+ * 可序列化为JSON<br>
+ * <p>
+ * 严重警告: 此接口为标识接口,禁止添加属性和方法. by wangwei
+ * </p>
+ *
+ * @author WANGWEI
+ */
+public interface JsonSerializable extends Serializable {
+
+}

+ 1 - 1
src/main/java/cn/com/qmth/ac/bean/StatusException.java

@@ -7,7 +7,7 @@ public class StatusException extends RuntimeException implements CodeNameExcepti
 
     private static final long serialVersionUID = -2411329525159341065L;
 
-    private Integer code;
+    private Integer code = 500;
 
     private String name;
 

+ 57 - 0
src/main/java/cn/com/qmth/ac/bean/StatusResponse.java

@@ -0,0 +1,57 @@
+package cn.com.qmth.ac.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * 状态响应实体
+ *
+ * @author WANGWEI
+ * @date 2018年5月24日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class StatusResponse implements JsonSerializable {
+
+    private static final long serialVersionUID = 8393074113722405560L;
+
+    @ApiModelProperty(value = "响应编码", example = "xxx", required = true)
+    private String code;
+
+    @ApiModelProperty(value = "响应描述", example = "具体描述信息", required = true)
+    private String desc;
+
+    /**
+     * 构造函数
+     */
+    public StatusResponse() {
+        super();
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param code
+     * @param desc
+     */
+    public StatusResponse(String code, String desc) {
+        super();
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+}

+ 4 - 5
src/main/java/cn/com/qmth/ac/controller/OpenApiController.java → src/main/java/cn/com/qmth/ac/controller/AuthController.java

@@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import cn.com.qmth.ac.bean.RedirectParam;
-import cn.com.qmth.ac.bean.SystemConstant;
 import cn.com.qmth.ac.service.CommonService;
 import cn.com.qmth.ac.util.Result;
 import io.swagger.annotations.ApiOperation;
@@ -19,23 +18,23 @@ import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
 @RestController
-@RequestMapping("api" + SystemConstant.PREFIX_URL_OPEN)
+@RequestMapping("api/auth")
 @Validated
-public class OpenApiController {
+public class AuthController {
 
     @Resource
     private CommonService commonService;
 
     @ApiOperation(value = "cas鉴权接口")
     @ApiResponses({ @ApiResponse(code = 200, message = "返回消息", response = Result.class) })
-    @RequestMapping(value = "auth/login/{schoolCode}", method = RequestMethod.GET)
+    @RequestMapping(value = "login/{schoolCode}", method = RequestMethod.GET)
     public void authentication(@ApiParam(value = "学校编码") @PathVariable("schoolCode") String schoolCode) {
         RedirectParam redirectParams = commonService.authentication(schoolCode);
         commonService.redirect(redirectParams);
     }
 
     @ApiOperation(value = "cas鉴权退出接口")
-    @RequestMapping(value = "auth/logout/{schoolCode}", method = RequestMethod.GET)
+    @RequestMapping(value = "logout/{schoolCode}", method = RequestMethod.GET)
     @ApiResponses({ @ApiResponse(code = 200, message = "返回消息", response = Result.class) })
     public void logout(@ApiParam(value = "学校编码") @PathVariable("schoolCode") String schoolCode,
             @ApiParam(value = "sessionId", required = false) @RequestParam(required = false) String gSessionId) {

+ 27 - 0
src/main/java/cn/com/qmth/ac/controller/IndexController.java

@@ -0,0 +1,27 @@
+package cn.com.qmth.ac.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.ac.util.Result;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@RestController
+@RequestMapping()
+@Validated
+public class IndexController {
+
+    @ApiResponses({ @ApiResponse(code = 200, message = "返回消息", response = Result.class) })
+    @RequestMapping(method = RequestMethod.GET)
+    public void index(HttpServletResponse response) throws IOException {
+        response.getWriter().print(System.currentTimeMillis());
+    }
+
+}

+ 306 - 0
src/main/java/cn/com/qmth/ac/util/JsonMapper.java

@@ -0,0 +1,306 @@
+package cn.com.qmth.ac.util;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.util.JSONPObject;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * 简单封装Jackson,实现JSON 与 Java Object互相转换的Mapper 封装不同的输出风格, 使用不同的builder函数创建实例
+ */
+@SuppressWarnings("unchecked")
+public class JsonMapper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JsonMapper.class);
+
+    private ObjectMapper mapper;
+
+    /**
+     * 默认构造JsonMapper
+     */
+    public JsonMapper() {
+        this(null);
+    }
+
+    /**
+     * 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式
+     */
+    public static JsonMapper nonDefaultMapper() {
+        return new JsonMapper(Include.NON_DEFAULT);
+    }
+
+    /**
+     * 创建只输出非Null且非Empty的属性到Json字符串的Mapper
+     */
+    public static JsonMapper nonEmptyMapper() {
+        return new JsonMapper(Include.NON_EMPTY);
+    }
+
+    /**
+     * 创建只输出非Null的属性到Json字符串的Mapper
+     */
+    public static JsonMapper nonNullMapper() {
+        return new JsonMapper(Include.NON_NULL);
+    }
+
+    /**
+     * 构造JsonMapper
+     */
+    public JsonMapper(Include include) {
+        mapper = new ObjectMapper();
+
+        SimpleModule module = new SimpleModule();
+        module.addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
+
+            @Override
+            public String deserialize(JsonParser jsonParser, DeserializationContext ctx)
+                    throws IOException, JsonProcessingException {
+                // 去掉头尾空格
+                return jsonParser.getValueAsString() != null ? jsonParser.getValueAsString().trim() : null;
+            }
+        });
+        mapper.registerModule(module);
+
+        // 设置输出时包含属性的风格
+        if (include != null) {
+            mapper.setSerializationInclusion(include);
+        }
+
+        // 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
+        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+
+        // 忽略无法转换的对象
+        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+
+        // 设置默认日期格式
+        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+    }
+
+    /**
+     * Object可以是POJO,也可以是Collection或数组 如果对象为Null, 返回"null" 如果集合为空集合, 返回"[]"
+     */
+    public String toJson(Object object) {
+        if (object == null) {
+            return null;
+        }
+
+        try {
+            return mapper.writeValueAsString(object);
+        } catch (IOException e) {
+            LOG.error("toJson error!" + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * Object 转换为 Json(美化版) 若对象为 null 或 转换异常时,返回null 若集合为空集合,返回[]
+     */
+    public String toPrettyJson(Object object) {
+        if (object == null) {
+            return null;
+        }
+
+        try {
+            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+        } catch (IOException e) {
+            LOG.error("toPrettyJson error! " + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化POJO或简单Collection如List<String> 如果JSON字符串为Null或"null"字符串, 返回Null
+     * 如果JSON字符串为"[]", 返回空集合 如需反序列化复杂Collection如List<MyBean>,
+     * 请使用parseJson(String, JavaType)
+     */
+    public <T> T parseJson(String jsonStr, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            return mapper.readValue(jsonStr, clazz);
+        } catch (IOException e) {
+            LOG.error("parseJson error!" + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化复杂Collection如List<Bean>,
+     * 先使用createCollectionType()或constructMapType()构造类型, 然后调用本函数
+     */
+    public <T> T parseJson(String jsonStr, JavaType javaType) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            return (T) mapper.readValue(jsonStr, javaType);
+        } catch (IOException e) {
+            LOG.error("parseJson error!" + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化复杂的对象,如Page<Bean>
+     */
+    public <T> T parseJson(String jsonStr, TypeReference javaType) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            return (T) mapper.readValue(jsonStr, javaType);
+        } catch (IOException e) {
+            LOG.error("parseJson error!" + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * Json 转换为 T[] 若Json字符串为 null 或 empty 或 转换异常时,返回null
+     */
+    public <T> T[] toArray(String jsonStr, Class<T> clazz) {
+        JavaType javaType = this.constructArrayType(clazz);
+        return this.parseJson(jsonStr, javaType);
+    }
+
+    /**
+     * Json to List
+     */
+    public <T> List<T> toList(String jsonStr, Class<T> bean) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            JavaType javaType = constructCollectionType(List.class, bean);
+            return mapper.readValue(jsonStr, javaType);
+        } catch (IOException e) {
+            LOG.error("parseJson error!" + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * Json 转换为 Set<T> 若Json字符串为 null 或 empty 或 转换异常时,返回null
+     */
+    public <T> Set<T> toSet(String jsonStr, Class<T> clazz) {
+        JavaType javaType = this.constructCollectionType(Set.class, clazz);
+        return this.parseJson(jsonStr, javaType);
+    }
+
+    /**
+     * Json to HashMap
+     */
+    public <T> Map<String, T> toMap(String jsonStr, Class<T> bean) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            JavaType javaType = constructMapType(HashMap.class, String.class, bean);
+            return mapper.readValue(jsonStr, javaType);
+        } catch (IOException e) {
+            LOG.error("parseJson error!" + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 构造Array类型
+     */
+    public JavaType constructArrayType(Class<?> elementClass) {
+        return mapper.getTypeFactory().constructArrayType(elementClass);
+    }
+
+    /**
+     * 构造Collection类型
+     */
+    public JavaType constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
+        return mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
+    }
+
+    /**
+     * 构造Map类型
+     */
+    public JavaType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
+        return mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
+    }
+
+    /**
+     * 当JSON里只含有Bean的部分属性時,更新一個已存在Bean,只覆盖該部分的属性
+     */
+    public void update(String jsonStr, Object object) {
+        if (jsonStr == null) {
+            return;
+        }
+
+        try {
+            mapper.readerForUpdating(object).readValue(jsonStr);
+        } catch (JsonProcessingException e) {
+            LOG.error("updateJson error!" + e.getMessage(), e);
+        } catch (IOException e) {
+            LOG.error("updateJson error!" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 輸出JSONP格式数据
+     */
+    public String toJsonP(String functionName, Object object) {
+        if (object == null) {
+            return null;
+        }
+
+        return toJson(new JSONPObject(functionName, object));
+    }
+
+    /**
+     * 設定是否使用Enum的toString函数來读写Enum 为False時使用Enum的name()函数來读写Enum, 默认为False
+     * 注意本函数一定要在Mapper創建後, 所有的读写動作之前調用
+     */
+    public void enableEnumUseToString() {
+        mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+        mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+    }
+
+    /**
+     * 取出Mapper做进一步的设置或使用其他序列化API
+     */
+    public ObjectMapper getMapper() {
+        return mapper;
+    }
+
+    /***
+     * 把Json字符串转换成Node对象
+     */
+    public JsonNode getNode(String jsonStr) {
+        if (jsonStr == null) {
+            return null;
+        }
+
+        try {
+            // mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS,
+            // true);
+            return mapper.readTree(jsonStr);
+        } catch (IOException e) {
+            LOG.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+}

+ 69 - 0
src/main/java/cn/com/qmth/ac/util/ObjectUtil.java

@@ -0,0 +1,69 @@
+package cn.com.qmth.ac.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 对象工具
+ *
+ * @author WANGWEI
+ */
+public final class ObjectUtil {
+
+    /**
+     * 判断对象或对象数组中每一个对象是否为空: 对象为null,字符序列长度为0,集合类、Map为empty
+     *
+     * @param obj
+     * @return
+     * @author WANGWEI
+     */
+    public static boolean isNullOrEmpty(Object obj) {
+        if (obj == null)
+            return true;
+
+        if (obj instanceof CharSequence)
+            return ((CharSequence) obj).length() == 0;
+
+        if (obj instanceof Collection)
+            return ((Collection<?>) obj).isEmpty();
+
+        if (obj instanceof Map)
+            return ((Map<?, ?>) obj).isEmpty();
+
+        if (obj instanceof Object[]) {
+            Object[] object = (Object[]) obj;
+            if (object.length == 0) {
+                return true;
+            }
+            boolean empty = true;
+            for (int i = 0; i < object.length; i++) {
+                if (!isNullOrEmpty(object[i])) {
+                    empty = false;
+                    break;
+                }
+            }
+            return empty;
+        }
+        return false;
+    }
+
+    /**
+     * 判断一个类是否为基本数据类型
+     *
+     * @param clazz
+     * @return
+     * @author WANGWEI
+     */
+    public static boolean isBaseDataType(Class<?> clazz) {
+        Boolean isBaseType = (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class)
+                || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class)
+                || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class)
+                || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class)
+                || clazz.isPrimitive());
+        return isBaseType;
+    }
+
+}

+ 124 - 0
src/main/java/cn/com/qmth/ac/util/ThreadLocalUtil.java

@@ -0,0 +1,124 @@
+package cn.com.qmth.ac.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 线程本地化工具
+ *
+ * @author WANGWEI
+ */
+public class ThreadLocalUtil {
+
+    /**
+     * trace ID
+     */
+    private static final ThreadLocal<String> LOCAL_TRACE_ID = new ThreadLocal<String>() {
+
+        @Override
+        public String initialValue() {
+            return null;
+        }
+    };
+
+    /**
+     * map
+     */
+    private static final ThreadLocal<Map<String, Object>> LOCAL_TRACE_MAP = new ThreadLocal<Map<String, Object>>() {
+
+        @Override
+        public Map<String, Object> initialValue() {
+            return null;
+        }
+    };
+
+    /**
+     * 重新初始化
+     *
+     * @return
+     * @author WANGWEI
+     */
+    public static String next() {
+        LOCAL_TRACE_MAP.set(null);
+        String traceID = UUID.randomUUID().toString().replace("-", "");
+        setTraceId(traceID);
+        return traceID;
+    }
+
+    /**
+     * 获取 trace ID
+     *
+     * @return
+     * @author WANGWEI
+     */
+    public static String getTraceId() {
+        String traceID = LOCAL_TRACE_ID.get();
+        if (traceID == null) {
+            traceID = next();
+        } else {
+            return traceID;
+        }
+
+        return traceID;
+    }
+
+    /**
+     * 设置 trace ID
+     *
+     * @param traceId
+     * @author WANGWEI
+     */
+    public static void setTraceId(String traceId) {
+        if (!(StringUtils.isNotBlank(traceId)))
+            return;
+        LOCAL_TRACE_ID.set(traceId);
+    }
+
+    /**
+     * 设置属性
+     *
+     * @param key
+     * @param value
+     * @author WANGWEI
+     */
+    public static void set(String key, Object value) {
+        Map<String, Object> map = LOCAL_TRACE_MAP.get();
+
+        if (null == map) {
+            map = new HashMap<>();
+            LOCAL_TRACE_MAP.set(map);
+        }
+        map.put(key, value);
+    }
+
+    /**
+     * 获取属性
+     *
+     * @param key
+     * @return
+     * @author WANGWEI
+     */
+    public static Object get(String key) {
+        Map<String, Object> map = LOCAL_TRACE_MAP.get();
+        if (null == map) {
+            return null;
+        }
+        return map.get(key);
+    }
+
+    /**
+     * 清理属性
+     *
+     * @author WANGWEI
+     */
+    public static void clearAll() {
+        Map<String, Object> map = LOCAL_TRACE_MAP.get();
+        if (null != map) {
+            map.clear();
+        }
+    }
+
+}