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();
+ }
+}
+
diff --git a/src/main/java/cn/palmte/work/NoStackTraceRuntimeException.java b/src/main/java/cn/palmte/work/NoStackTraceRuntimeException.java
new file mode 100644
index 0000000..e0d7279
--- /dev/null
+++ b/src/main/java/cn/palmte/work/NoStackTraceRuntimeException.java
@@ -0,0 +1,96 @@
+package cn.palmte.work;
+
+import org.springframework.core.NestedExceptionUtils;
+import org.springframework.core.NestedRuntimeException;
+
+/**
+ * 没有堆栈的异常, 降低性能消耗
+ *
+ * @author Harry Yang
+ * @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.
+ * 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;
+ }
+ }
+
+}
+
diff --git a/src/main/java/cn/palmte/work/Result.java b/src/main/java/cn/palmte/work/Result.java
new file mode 100644
index 0000000..1797f5d
--- /dev/null
+++ b/src/main/java/cn/palmte/work/Result.java
@@ -0,0 +1,10 @@
+package cn.palmte.work;
+
+/**
+ * @author Harry Yang
+ * @since 2.0 2022/12/30 15:37
+ */
+public interface Result {
+
+}
+
diff --git a/src/main/java/cn/palmte/work/ValidationError.java b/src/main/java/cn/palmte/work/ValidationError.java
new file mode 100644
index 0000000..14d6206
--- /dev/null
+++ b/src/main/java/cn/palmte/work/ValidationError.java
@@ -0,0 +1,30 @@
+package cn.palmte.work;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author Harry Yang
+ * @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;
+ }
+
+}
diff --git a/src/main/java/cn/palmte/work/config/ApplicationExceptionHandler.java b/src/main/java/cn/palmte/work/config/ApplicationExceptionHandler.java
new file mode 100644
index 0000000..8a2ce26
--- /dev/null
+++ b/src/main/java/cn/palmte/work/config/ApplicationExceptionHandler.java
@@ -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 Harry Yang
+ * @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(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 allErrors = result.getAllErrors();
+ Map 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("数据库连接出错");
+ }
+}
diff --git a/src/main/java/cn/palmte/work/controller/backend/ProcessController.java b/src/main/java/cn/palmte/work/controller/backend/ProcessController.java
index 80c1c00..16e99b7 100644
--- a/src/main/java/cn/palmte/work/controller/backend/ProcessController.java
+++ b/src/main/java/cn/palmte/work/controller/backend/ProcessController.java
@@ -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();