异常处理

master
Harry Yang 2022-12-30 15:45:28 +08:00
parent 7fe28430a9
commit 779a0dc27e
8 changed files with 512 additions and 4 deletions

View File

@ -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("未知错误");
}
}

View File

@ -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());
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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("数据库连接出错");
}
}

View File

@ -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();