异常处理
parent
7fe28430a9
commit
779a0dc27e
|
@ -0,0 +1,33 @@
|
|||
package cn.palmte.work;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:26
|
||||
*/
|
||||
public class ErrorMessage implements Result {
|
||||
|
||||
private String message;
|
||||
|
||||
public ErrorMessage() { }
|
||||
|
||||
public ErrorMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public static ErrorMessage failed(String message) {
|
||||
return new ErrorMessage(message);
|
||||
}
|
||||
|
||||
public static ErrorMessage failed() {
|
||||
return new ErrorMessage("未知错误");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package cn.palmte.work;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:25
|
||||
*/
|
||||
public class ErrorMessageException extends NoStackTraceRuntimeException {
|
||||
|
||||
private final HttpStatus status;
|
||||
|
||||
public ErrorMessageException(/*@Nullable*/ String msg) {
|
||||
this(msg, null, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
public ErrorMessageException(/*@Nullable*/ String msg, /*@Nullable*/ Throwable cause, HttpStatus status) {
|
||||
super(msg, cause);
|
||||
Assert.notNull(status, "http status is required");
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public HttpStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public static ErrorMessageException failed(String message) {
|
||||
return new ErrorMessageException(message);
|
||||
}
|
||||
|
||||
public static ErrorMessageException failed(String message, HttpStatus status) {
|
||||
return new ErrorMessageException(message, null, status);
|
||||
}
|
||||
|
||||
public static void notNull(Object obj, String message) {
|
||||
if (obj == null) {
|
||||
throw ErrorMessageException.failed(message, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notNull(Object obj, Supplier<String> supplier) {
|
||||
if (obj == null) {
|
||||
throw new ErrorMessageException(supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package cn.palmte.work;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:38
|
||||
*/
|
||||
|
||||
public class Json implements Result {
|
||||
|
||||
private Object data;
|
||||
private String message;
|
||||
private boolean success;
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Json data(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Json message(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Json success(boolean success) {
|
||||
this.success = success;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the common {@link Json} result
|
||||
*
|
||||
* @param func the {@link Function}
|
||||
* @param param parameter
|
||||
*/
|
||||
public static <T> Json apply(Function<T, Boolean> func, T param) {
|
||||
if (func.apply(param)) {
|
||||
return Json.ok();
|
||||
}
|
||||
return Json.failed();
|
||||
}
|
||||
|
||||
public static <T> Json apply(boolean success) {
|
||||
if (success) {
|
||||
return Json.ok();
|
||||
}
|
||||
return Json.failed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param success if success
|
||||
* @param message the message of the response
|
||||
* @param data response data
|
||||
*/
|
||||
public static Json create(boolean success, String message, Object data) {
|
||||
return new Json()
|
||||
.data(data)
|
||||
.message(message)
|
||||
.success(success);
|
||||
}
|
||||
|
||||
public static Json ok() {
|
||||
return create(true, "ok", null);
|
||||
}
|
||||
|
||||
public static Json ok(String message, Object data) {
|
||||
return create(true, message, data);
|
||||
}
|
||||
|
||||
public static Json ok(Object data) {
|
||||
return create(true, "ok", data);
|
||||
}
|
||||
|
||||
public static Json ok(String message) {
|
||||
return create(true, message, null);
|
||||
}
|
||||
|
||||
public static Json failed() {
|
||||
return create(false, "失败", null);
|
||||
}
|
||||
|
||||
public static Json failed(String message) {
|
||||
return create(false, message, null);
|
||||
}
|
||||
|
||||
public static Json failed(String message, Object data) {
|
||||
return create(false, message, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder()//
|
||||
.append("{\"message\":\"").append(message)//
|
||||
.append("\",\"data\":\"").append(data)//
|
||||
.append("\",\"success\":\"").append(success)//
|
||||
.append("\"}")//
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package cn.palmte.work;
|
||||
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* 没有堆栈的异常, 降低性能消耗
|
||||
*
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:25
|
||||
*/
|
||||
public class NoStackTraceRuntimeException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NoStackTraceRuntimeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NoStackTraceRuntimeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public NoStackTraceRuntimeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public NoStackTraceRuntimeException(/*@Nullable*/ String msg, /*@Nullable */Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the innermost cause of this exception, if any.
|
||||
*
|
||||
* @return the innermost exception, or {@code null} if none
|
||||
*/
|
||||
// @Nullable
|
||||
public Throwable getRootCause() {
|
||||
return NestedExceptionUtils.getRootCause(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the most specific cause of this exception, that is,
|
||||
* either the innermost cause (root cause) or this exception itself.
|
||||
* <p>Differs from {@link #getRootCause()} in that it falls back
|
||||
* to the present exception if there is no root cause.
|
||||
*
|
||||
* @return the most specific cause (never {@code null})
|
||||
*/
|
||||
public Throwable getMostSpecificCause() {
|
||||
Throwable rootCause = getRootCause();
|
||||
return (rootCause != null ? rootCause : this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this exception contains an exception of the given type:
|
||||
* either it is of the given class itself or it contains a nested cause
|
||||
* of the given type.
|
||||
*
|
||||
* @param exType the exception type to look for
|
||||
* @return whether there is a nested exception of the specified type
|
||||
*/
|
||||
public boolean contains(Class<?> exType) {
|
||||
if (exType == null) {
|
||||
return false;
|
||||
}
|
||||
if (exType.isInstance(this)) {
|
||||
return true;
|
||||
}
|
||||
Throwable cause = getCause();
|
||||
if (cause == this) {
|
||||
return false;
|
||||
}
|
||||
if (cause instanceof NestedRuntimeException) {
|
||||
return ((NestedRuntimeException) cause).contains(exType);
|
||||
}
|
||||
else {
|
||||
while (cause != null) {
|
||||
if (exType.isInstance(cause)) {
|
||||
return true;
|
||||
}
|
||||
if (cause.getCause() == cause) {
|
||||
break;
|
||||
}
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package cn.palmte.work;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:37
|
||||
*/
|
||||
public interface Result {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package cn.palmte.work;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:39
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class ValidationError implements Result {
|
||||
|
||||
private Object validation;
|
||||
|
||||
public static ValidationError failed(Object validation) {
|
||||
return new ValidationError(validation);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Object getData() {
|
||||
return validation;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package cn.palmte.work.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
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.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.palmte.work.ErrorMessage;
|
||||
import cn.palmte.work.ErrorMessageException;
|
||||
import cn.palmte.work.Json;
|
||||
import cn.palmte.work.Result;
|
||||
import cn.palmte.work.ValidationError;
|
||||
|
||||
/**
|
||||
* 异常处理
|
||||
*
|
||||
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
|
||||
* @since 2.0 2022/12/30 15:24
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class ApplicationExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(ApplicationExceptionHandler.class);
|
||||
|
||||
public static final ErrorMessage argsErrorMessage = ErrorMessage.failed("参数错误");
|
||||
public static final ErrorMessage sizeExceeded = ErrorMessage.failed("上传文件大小超出限制");
|
||||
public static final ErrorMessage methodNotSupported = ErrorMessage.failed("请求方式不支持");
|
||||
public static final ErrorMessage internalServerError = ErrorMessage.failed("服务器内部异常");
|
||||
public static final ErrorMessage notWritableError = ErrorMessage.failed("数据无法正常返回到客户端");
|
||||
|
||||
@ExceptionHandler(ErrorMessageException.class)
|
||||
public ResponseEntity<ErrorMessage> errorMessage(ErrorMessageException errorMessage) {
|
||||
HttpStatus httpStatus = errorMessage.getStatus();
|
||||
return ResponseEntity.status(httpStatus)
|
||||
.body(ErrorMessage.failed(errorMessage.getMessage()));
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ErrorMessage badRequest(IllegalArgumentException exception) {
|
||||
return ErrorMessage.failed(exception.getMessage());
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler({ MaxUploadSizeExceededException.class })
|
||||
public ErrorMessage badRequest() {
|
||||
return sizeExceeded;
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ErrorMessage methodNotSupported() {
|
||||
return methodNotSupported;
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorMessage error(Exception exception) {
|
||||
log.error("An Exception occurred", exception);
|
||||
if (exception instanceof SQLException) {
|
||||
return internalServerError;
|
||||
}
|
||||
if (exception instanceof HttpMessageNotWritableException) {
|
||||
return notWritableError;
|
||||
}
|
||||
if (exception instanceof TransactionSystemException) {
|
||||
return ErrorMessage.failed("数据库出错");
|
||||
}
|
||||
return ErrorMessage.failed(exception.getMessage());
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public ErrorMessage typeMismatch(MethodArgumentTypeMismatchException mismatch) {
|
||||
return ErrorMessage.failed("参数'" + mismatch.getName() + "'不能转换到对应类型");
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ErrorMessage parameterError(MissingServletRequestParameterException e) {
|
||||
return ErrorMessage.failed("缺少参数'" + e.getParameterName() + "'");
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler({
|
||||
MultipartException.class,
|
||||
HttpMessageNotReadableException.class,
|
||||
HttpMediaTypeNotSupportedException.class
|
||||
})
|
||||
public ErrorMessage messageNotReadable() {
|
||||
return argsErrorMessage;
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler({ BindException.class, MethodArgumentNotValidException.class })
|
||||
public Result validExceptionHandler(Exception e) {
|
||||
|
||||
BindingResult result;
|
||||
if (e instanceof MethodArgumentNotValidException) {
|
||||
result = ((MethodArgumentNotValidException) e).getBindingResult();
|
||||
}
|
||||
else if (e instanceof BindException) {
|
||||
result = (BindingResult) e;
|
||||
}
|
||||
else {
|
||||
return ErrorMessage.failed();
|
||||
}
|
||||
List<ObjectError> allErrors = result.getAllErrors();
|
||||
Map<String, String> model = new HashMap<>(16);
|
||||
|
||||
for (ObjectError error : allErrors) {
|
||||
if (error instanceof FieldError) {
|
||||
FieldError fieldError = (FieldError) error;
|
||||
String field = fieldError.getField();
|
||||
String defaultMessage = error.getDefaultMessage();
|
||||
model.put(field, defaultMessage);
|
||||
// log.error("[{}] -> [{}]", field, defaultMessage);
|
||||
}
|
||||
}
|
||||
return ValidationError.failed(model);
|
||||
}
|
||||
|
||||
@ExceptionHandler(NullPointerException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Json nullPointer(NullPointerException exception) {
|
||||
final StackTraceElement[] stackTrace = exception.getStackTrace();
|
||||
if (ObjectUtils.isEmpty(stackTrace)) {
|
||||
return Json.failed("空指针", "暂无堆栈信息");
|
||||
}
|
||||
return Json.failed("空指针", stackTrace[0]);
|
||||
}
|
||||
|
||||
@ExceptionHandler(DataAccessException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorMessage dataAccessException(DataAccessException accessException) {
|
||||
String message = getDataAccessMessage(accessException.getCause());
|
||||
log.error(message, accessException);
|
||||
return ErrorMessage.failed(message);
|
||||
}
|
||||
|
||||
String getDataAccessMessage(Throwable cause) {
|
||||
return "数据库出错";
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(DataAccessResourceFailureException.class)
|
||||
public ErrorMessage dataAccessException(DataAccessResourceFailureException accessException) {
|
||||
log.error("数据库连接出错", accessException);
|
||||
return ErrorMessage.failed("数据库连接出错");
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ import javax.persistence.TypedQuery;
|
|||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import cn.palmte.work.ErrorMessageException;
|
||||
import cn.palmte.work.config.activiti.ActApproveTypeEnum;
|
||||
import cn.palmte.work.config.activiti.ActProjectTypeEnum;
|
||||
import cn.palmte.work.model.Admin;
|
||||
|
@ -58,19 +59,19 @@ import cn.palmte.work.model.enums.ProcessType;
|
|||
import cn.palmte.work.model.enums.ProcurementMode;
|
||||
import cn.palmte.work.model.enums.ProjectType;
|
||||
import cn.palmte.work.model.enums.SealType;
|
||||
import cn.palmte.work.model.process.ProcurementDetail;
|
||||
import cn.palmte.work.model.process.BudgetPurchaseAmount;
|
||||
import cn.palmte.work.model.process.BudgetPurchaseAmountModel;
|
||||
import cn.palmte.work.model.process.BudgetPurchaseDetail;
|
||||
import cn.palmte.work.model.process.BudgetPurchaseDetailModel;
|
||||
import cn.palmte.work.model.process.ProcessAttachment;
|
||||
import cn.palmte.work.model.process.ProcurementContract;
|
||||
import cn.palmte.work.model.process.ProcurementDetail;
|
||||
import cn.palmte.work.model.process.ProjectProcess;
|
||||
import cn.palmte.work.model.process.ProjectProcessDetail;
|
||||
import cn.palmte.work.model.process.ProjectProcessRepository;
|
||||
import cn.palmte.work.model.process.SaleContract;
|
||||
import cn.palmte.work.model.process.SealTypeArray;
|
||||
import cn.palmte.work.model.process.SupplierMaterial;
|
||||
import cn.palmte.work.model.process.BudgetPurchaseAmountModel;
|
||||
import cn.palmte.work.model.process.BudgetPurchaseDetailModel;
|
||||
import cn.palmte.work.model.process.form.ProcessCreationForm;
|
||||
import cn.palmte.work.model.process.form.ProcessQueryForm;
|
||||
import cn.palmte.work.model.process.form.ProcessUpdateForm;
|
||||
|
@ -326,7 +327,7 @@ public class ProcessController {
|
|||
public ProjectProcessDetail get(@PathVariable int id) {
|
||||
ProjectProcess process = processService.getById(id);
|
||||
if (process == null) {
|
||||
throw new RuntimeException("流程不存在");
|
||||
throw ErrorMessageException.failed("流程不存在");
|
||||
}
|
||||
|
||||
ProjectProcessDetail detail = new ProjectProcessDetail();
|
||||
|
|
Loading…
Reference in New Issue