1. 增加了libs子目录, 放置工具项目

2. MVC Starter 中增:
    1) 加统一异常处理
    2) 统一返回: BaseResponse
    3) 框架异常类: BusinessException
    4) 通用异常枚举: ResponseEnum
This commit is contained in:
Administrator
2020-10-22 14:09:50 +08:00
parent b473e31c84
commit b8f1e63cbd
13 changed files with 755 additions and 4 deletions

View File

@ -0,0 +1,66 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chinaunicom.ebtp</groupId>
<artifactId>mall-ebtp-cloud-common</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>mall-ebtp-cloud-common</name>
<properties>
<java.version>1.8</java.version>
<resource.delimiter>@</resource.delimiter>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.12.0-rc1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.12.0-rc1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.0-rc1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,97 @@
package com.chinaunicom.ebtp.mall.cloud.common.utils;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.fasterxml.jackson.databind.JavaType;
import lombok.extern.slf4j.Slf4j;
/**
* json util
*
* @author 付庆吉
* @date 2020-09-16
*/
@Slf4j
public class JsonUtils {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
* <p>Title: pojoToJson</p>
* <p>Description: </p>
*
* @param data
* @return
*/
public static String objectToJson(Object data) {
try {
MAPPER.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
return MAPPER.writeValueAsString(data);
} catch (JsonProcessingException e) {
log.info(ExceptionUtil.stacktraceToString(e));
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData json数据
* @param beanType 对象中的object类型
* @return
*/
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
try {
return MAPPER.readValue(jsonData, beanType);
} catch (Exception e) {
log.info(ExceptionUtil.stacktraceToString(e));
}
return null;
}
/**
* 将json数据转换成pojo对象list
* <p>Title: jsonToList</p>
* <p>Description: </p>
*
* @param jsonData
* @param beanType
* @return
*/
public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
log.info(ExceptionUtil.stacktraceToString(e));
}
return null;
}
/**
* 将List<DTO>转为List<VO>
* <p>Title: jsonToList</p>
* <p>Description: </p>
*
* @param jsonData
* @param beanType
* @return
*/
public static <T> List<T> jsonToList(List jsonData, Class<T> beanType) {
String str = objectToJson(jsonData);
return jsonToList(str,beanType);
}
}

View File

@ -0,0 +1,38 @@
package om.chinaunicom.ebtp.mall.cloud.common;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}

View File

@ -14,11 +14,11 @@
<artifactId>mall-ebtp-cloud-mvc-starter</artifactId>
<version>0.0.1</version>
<name>mall-ebtp-cloud-mvc-starter</name>
<organization>
<name>Chinaunicom, Inc.</name>
</organization>
<developers>
<developer>
<name>Ajaxfan</name>
@ -26,11 +26,23 @@
<organization>Chinaunicom, Inc.</organization>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.chinaunicom.ebtp</groupId>
<artifactId>mall-ebtp-cloud-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,254 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.advice;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
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.method.annotation.MethodArgumentTypeMismatchException;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
import com.chinaunicom.ebtp.mall.cloud.common.utils.JsonUtils;
import com.chinaunicom.mall.ebtp.cloud.mvc.starter.base.BaseResponse;
import com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception.BusinessException;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
/**
* 异常处理 拦截BindException异常返回HttpStatus是400的绑定错误信息
* 拦截FrameException异常返回HttpStatus是406的业务处理错误信息(支持自定义状态码)
* 拦截Exception异常返回HttpStatus是500服务器内部异常
*
* @file: com.chinaunicom.mall.ebtp.cloud.mvc.starter.advice.BusinessExceptionHandlerAdvice
* @description:
* @author fqj
* @date 2020年9月3日
* @version: V1.0
* @update
*/
@Slf4j
@ControllerAdvice
@ResponseBody
@ConditionalOnProperty(name = "mconfig.exception-handle-enabled", matchIfMissing = true)
public class BusinessExceptionHandlerAdvice {
/**
* 业务异常处理
*
* @param request 请求
* @param exception ServiceErrorException异常对象
* @return 响应
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> serviceErrorException(HttpServletRequest request, BusinessException exception) {
log.error(ExceptionUtil.stacktraceToString(exception));
Map<String, Object> body = new HashMap<>();
body.put("path", request.getRequestURI());
return BaseResponse.fail(exception.getCode(), exception.getMessage(), Convert.toStr(body));
}
/**
* 参数绑定异常
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(value = BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> methodArgumentNotValidHandler(HttpServletRequest request, BindException exception) {
// 按需重新封装需要返回的错误信息
List<ArgumentInvalidResult> invalidArguments = new ArrayList<>();
// 解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
for (FieldError error : exception.getBindingResult().getFieldErrors()) {
ArgumentInvalidResult invalidArgument = new ArgumentInvalidResult();
invalidArgument.setDefaultMessage(error.getDefaultMessage());
invalidArgument.setField(error.getField());
invalidArgument.setRejectedValue(error.getRejectedValue());
invalidArguments.add(invalidArgument);
}
Map<String, Object> body = new HashMap<>();
body.put("errors", JsonUtils.objectToJson(invalidArguments));
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.BAD_REQUEST.value(), "参数错误", Convert.toStr(body));
}
/**
* 请求方式异常
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public BaseResponse<String> handleMethodNotSupportedException(HttpServletRequest request,
HttpRequestMethodNotSupportedException exception) {
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), "错误的请求方式", Convert.toStr(body));
}
/**
* 参数缺失
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> handleMissingParameterException(HttpServletRequest request,
MissingServletRequestParameterException exception) {
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.BAD_REQUEST.value(), "参数缺失", Convert.toStr(body));
}
// ---------------------------- 数据库操作相关异常 --------------------------------------
/**
* 数据库异常
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(DataAccessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> handlerDataAccessException(HttpServletRequest request, DataAccessException exception) {
log.error(ExceptionUtil.stacktraceToString(exception));
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.BAD_REQUEST.value(), "数据库异常", Convert.toStr(body));
}
/**
* 数据不存在
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(EmptyResultDataAccessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> handleDataEmptyException(HttpServletRequest request,
EmptyResultDataAccessException exception) {
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.BAD_REQUEST.value(), "数据不存在", Convert.toStr(body));
}
/**
* 请求方式异常
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(DuplicateKeyException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> handleDataDualException(HttpServletRequest request, DuplicateKeyException exception) {
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.BAD_REQUEST.value(), "数据重复插入", Convert.toStr(body));
}
/**
* 方法参数类型不匹配异常
*
* @param request 请求
* @param exception BindException异常对象
* @return 响应
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResponse<String> handleMethodArgumentTypeException(HttpServletRequest request,
MethodArgumentTypeMismatchException exception) {
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.BAD_REQUEST.value(), "参数类型不匹配", Convert.toStr(body));
}
/**
* 全局异常处理
*
* @param request 请求
* @param exception Exception异常对象
* @return 响应 MethodArgumentNotValidException
*/
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public BaseResponse<String> handleException(HttpServletRequest request, Exception exception) {
log.error(ExceptionUtil.stacktraceToString(exception));
Map<String, Object> body = new HashMap<>();
body.put("errors", exception.getMessage());
body.put("error", HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
body.put("path", request.getRequestURI());
return BaseResponse.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "网络异常", Convert.toStr(body));
}
/**
* 参数异常
*/
@Getter
@Setter
class ArgumentInvalidResult {
/**
* 字段名
*/
private String field;
/**
* 输入的错误值
*/
private Object rejectedValue;
/**
* 错误信息
*/
private String defaultMessage;
}
}

View File

@ -0,0 +1,65 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.base;
import java.io.Serializable;
import com.chinaunicom.mall.ebtp.cloud.mvc.starter.enums.ResponseEnum;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* 返回类型FrameResponse
*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class BaseResponse<T> implements Serializable {
private static final long serialVersionUID = 6769157532172136264L;
private Integer code;
private String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T data;
public static BaseResponse<String> success() {
return new BaseResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMessage(), "");
}
public static <T> BaseResponse<T> success(T data) {
return new BaseResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMessage(), data);
}
public static <T> BaseResponse<T> success(String message, T data) {
return new BaseResponse<>(ResponseEnum.SUCCESS.getCode(), message, data);
}
public static BaseResponse<String> fail() {
return new BaseResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getMessage(), "");
}
public static BaseResponse<String> fail(String message) {
return new BaseResponse<>(ResponseEnum.ERROR.getCode(), message, "");
}
public static BaseResponse<String> fail(Integer code, String message) {
return new BaseResponse<>(code, message, "");
}
public static <T> BaseResponse<T> fail(T data) {
return new BaseResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getMessage(), data);
}
public static <T> BaseResponse<T> fail(String message, T data) {
return new BaseResponse<>(ResponseEnum.ERROR.getCode(), message, data);
}
public static <T> BaseResponse<T> fail(Integer code, String message, T data) {
return new BaseResponse<T>(code, message, data);
}
}

View File

@ -0,0 +1,48 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.enums;
import com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception.BusinessExceptionAssert;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通用异常Enum
*
* @author 付庆吉
* @date 2020-09-14
*/
@Getter
@AllArgsConstructor
public enum ResponseEnum implements BusinessExceptionAssert {
/**
* 成功
*/
SUCCESS(0, "success"),
/**
* 失败
*/
ERROR(90000, "操作失败"),
/**
* token not find
*/
TOKEN_NOT_FIND(30001, "认证不存在!"),
/**
* connect timeout
*/
CONNECT_TIMEOUT(30002, "连接超时!");
/**
* 返回码
*/
private int code;
/**
* 返回消息
*/
private String message;
}

View File

@ -0,0 +1,75 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception;
/**
* 通用断言接口
*
* @file: com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception.Assert
* @description:
* @author 付庆吉
* @date 2020-09-14
* @version: V1.0
* @update
*/
public interface Assert {
/**
* 创建异常
*
* @param args
* @return
*/
BusinessException newException(Object... args);
/**
* 创建异常
*
* @param t
* @param args
* @return
*/
BusinessException newException(Throwable t, Object... args);
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
*
* @param obj 待判断对象
*/
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
}
/**
* <p>断言对象<code>args</code>非空。如果对象<code>args</code>为null或空字符串则抛出异常
*
* @param args 待判断字符串
*/
default void assertStrNotNull(String... args) {
this.assertNotNull(args);
for (String arg : args) {
if (arg == null || arg.isEmpty()) {
throw newException(arg);
}
}
}
/**
* 根据传参抛出异常,则抛出异常
*
* @param t 验证结果
*/
default void customValid(boolean t) {
if (t) {
throw newException();
}
}
/**
* 抛出异常
*/
default void throwException() {
throw newException();
}
}

View File

@ -0,0 +1,36 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception;
import lombok.Getter;
import lombok.Setter;
/**
* 平台自定义异常类
*
* @file: com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception.BusinessException
* @description: 平台自定义异常类
* @author: ajaxfan
* @date: 2020-10-22
* @version: V1.0
* @update
*/
@Getter
@Setter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
public BusinessException(IResponseEnum iResponseEnum, String message) {
super(message);
this.code = iResponseEnum.getCode();
this.message = message;
}
public BusinessException(IResponseEnum iResponseEnum, String message, Throwable cause) {
super(message, cause);
this.code = iResponseEnum.getCode();
this.message = message;
}
}

View File

@ -0,0 +1,33 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception;
import java.text.MessageFormat;
/**
* 初始化异常及断言接口所有自定义异常Enum均需实现此接口
* <p>
* 自定义异常枚举中年必须有code、message
*
* @file: com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception.BusinessExceptionAssert
* @description:
* @author 付庆吉
* @date 2020-09-14
* @version: V1.0
* @update
*/
public interface BusinessExceptionAssert extends IResponseEnum, Assert {
@Override
default BusinessException newException(Object... args) {
String msg = MessageFormat.format(this.getMessage(), args);
return new BusinessException(this, msg);
}
@Override
default BusinessException newException(Throwable t, Object... args) {
String msg = MessageFormat.format(this.getMessage(), args);
return new BusinessException(this, msg, t);
}
}

View File

@ -0,0 +1,19 @@
package com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception;
/**
* 异常编码和消息
*
* @file: com.chinaunicom.mall.ebtp.cloud.mvc.starter.exception.IResponseEnum
* @description:
* @author 付庆吉
* @date 2020-09-14
* @version: V1.0
* @update
*/
public interface IResponseEnum {
int getCode();
String getMessage();
}

View File

@ -337,7 +337,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.chinaunicom.ebtp</groupId>
<artifactId>mall-ebtp-cloud-common</artifactId>
</dependency>
</dependencies>
<repositories>

View File

@ -97,6 +97,11 @@
<artifactId>mall-ebtp-cloud-feign-starter</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.chinaunicom.ebtp</groupId>
<artifactId>mall-ebtp-cloud-common</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</dependencyManagement>