后端功能增强:全局异常处理、API控制器、JSP视图和单元测试
- 添加 GlobalExceptionHandler 全局异常处理 - 添加 ApiController REST API 控制器 - 更新 WebConfig 跨域配置和 ProductRepository 查询方法 - 新增 monitor/product-detail/profile JSP 视图页面 - 添加 FlashSaleServiceTest 秒杀服务单元测试 - 更新 application.yml 配置
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
package com.org.flashsalesystem.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
* 统一处理应用中的各种异常,提供一致的错误响应格式
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 业务异常处理
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e, HttpServletRequest request) {
|
||||
log.warn("业务异常: {} - {}", e.getErrorCode(), e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Business Error")
|
||||
.message(e.getMessage())
|
||||
.errorCode(e.getErrorCode())
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀相关异常处理
|
||||
*/
|
||||
@ExceptionHandler(FlashSaleException.class)
|
||||
public ResponseEntity<ErrorResponse> handleFlashSaleException(FlashSaleException e, HttpServletRequest request) {
|
||||
log.warn("秒杀异常: {} - {}", e.getErrorCode(), e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Flash Sale Error")
|
||||
.message(e.getMessage())
|
||||
.errorCode(e.getErrorCode())
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 限流异常处理
|
||||
*/
|
||||
@ExceptionHandler(RateLimitException.class)
|
||||
public ResponseEntity<ErrorResponse> handleRateLimitException(RateLimitException e, HttpServletRequest request) {
|
||||
log.warn("限流异常: {}", e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.TOO_MANY_REQUESTS.value())
|
||||
.error("Rate Limit Exceeded")
|
||||
.message(e.getMessage())
|
||||
.errorCode("RATE_LIMIT_EXCEEDED")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据验证异常处理
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException e, HttpServletRequest request) {
|
||||
log.warn("数据验证异常: {}", e.getMessage());
|
||||
|
||||
StringBuilder errorMessages = new StringBuilder();
|
||||
for (ObjectError error : e.getBindingResult().getAllErrors()) {
|
||||
if (errorMessages.length() > 0) {
|
||||
errorMessages.append("; ");
|
||||
}
|
||||
errorMessages.append(error.getDefaultMessage());
|
||||
}
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Validation Error")
|
||||
.message(errorMessages.toString())
|
||||
.errorCode("VALIDATION_ERROR")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数绑定异常处理
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public ResponseEntity<ErrorResponse> handleBindException(BindException e, HttpServletRequest request) {
|
||||
log.warn("参数绑定异常: {}", e.getMessage());
|
||||
|
||||
StringBuilder errorMessages = new StringBuilder();
|
||||
for (ObjectError error : e.getBindingResult().getAllErrors()) {
|
||||
if (errorMessages.length() > 0) {
|
||||
errorMessages.append("; ");
|
||||
}
|
||||
errorMessages.append(error.getDefaultMessage());
|
||||
}
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Parameter Binding Error")
|
||||
.message(errorMessages.toString())
|
||||
.errorCode("BINDING_ERROR")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 约束违规异常处理
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
|
||||
log.warn("约束违规异常: {}", e.getMessage());
|
||||
|
||||
StringBuilder errorMessages = new StringBuilder();
|
||||
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
|
||||
for (ConstraintViolation<?> violation : violations) {
|
||||
if (errorMessages.length() > 0) {
|
||||
errorMessages.append("; ");
|
||||
}
|
||||
errorMessages.append(violation.getMessage());
|
||||
}
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Constraint Violation")
|
||||
.message(errorMessages.toString())
|
||||
.errorCode("CONSTRAINT_VIOLATION")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 超时异常处理
|
||||
*/
|
||||
@ExceptionHandler(TimeoutException.class)
|
||||
public ResponseEntity<ErrorResponse> handleTimeoutException(TimeoutException e, HttpServletRequest request) {
|
||||
log.error("超时异常: {}", e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.REQUEST_TIMEOUT.value())
|
||||
.error("Timeout Error")
|
||||
.message("请求超时,请稍后重试")
|
||||
.errorCode("TIMEOUT_ERROR")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 非法参数异常处理
|
||||
*/
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
|
||||
log.warn("非法参数异常: {}", e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.BAD_REQUEST.value())
|
||||
.error("Illegal Argument")
|
||||
.message(e.getMessage())
|
||||
.errorCode("ILLEGAL_ARGUMENT")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.badRequest().body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 空指针异常处理
|
||||
*/
|
||||
@ExceptionHandler(NullPointerException.class)
|
||||
public ResponseEntity<ErrorResponse> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
|
||||
log.error("空指针异常: {}", e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
||||
.error("Null Pointer Error")
|
||||
.message("系统内部错误,请联系管理员")
|
||||
.errorCode("NULL_POINTER_ERROR")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行时异常处理
|
||||
*/
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
|
||||
log.error("运行时异常: {}", e.getMessage(), e);
|
||||
|
||||
// 对于已知的业务异常,使用友好的错误信息
|
||||
String message = e.getMessage();
|
||||
if (message == null || message.trim().isEmpty()) {
|
||||
message = "系统繁忙,请稍后重试";
|
||||
}
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
||||
.error("Runtime Error")
|
||||
.message(message)
|
||||
.errorCode("RUNTIME_ERROR")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用异常处理
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ErrorResponse> handleGenericException(Exception e, HttpServletRequest request) {
|
||||
log.error("系统异常: {}", e.getMessage(), e);
|
||||
|
||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
||||
.timestamp(LocalDateTime.now())
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
||||
.error("System Error")
|
||||
.message("系统异常,请联系管理员")
|
||||
.errorCode("SYSTEM_ERROR")
|
||||
.path(request.getRequestURI())
|
||||
.build();
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一错误响应格式
|
||||
*/
|
||||
public static class ErrorResponse {
|
||||
private LocalDateTime timestamp;
|
||||
private int status;
|
||||
private String error;
|
||||
private String message;
|
||||
private String errorCode;
|
||||
private String path;
|
||||
private Map<String, Object> details;
|
||||
|
||||
public ErrorResponse() {
|
||||
this.details = new HashMap<>();
|
||||
}
|
||||
|
||||
public static ErrorResponseBuilder builder() {
|
||||
return new ErrorResponseBuilder();
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public LocalDateTime getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
|
||||
|
||||
public int getStatus() { return status; }
|
||||
public void setStatus(int status) { this.status = status; }
|
||||
|
||||
public String getError() { return error; }
|
||||
public void setError(String error) { this.error = error; }
|
||||
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
|
||||
public String getErrorCode() { return errorCode; }
|
||||
public void setErrorCode(String errorCode) { this.errorCode = errorCode; }
|
||||
|
||||
public String getPath() { return path; }
|
||||
public void setPath(String path) { this.path = path; }
|
||||
|
||||
public Map<String, Object> getDetails() { return details; }
|
||||
public void setDetails(Map<String, Object> details) { this.details = details; }
|
||||
|
||||
public static class ErrorResponseBuilder {
|
||||
private ErrorResponse errorResponse = new ErrorResponse();
|
||||
|
||||
public ErrorResponseBuilder timestamp(LocalDateTime timestamp) {
|
||||
errorResponse.setTimestamp(timestamp);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponseBuilder status(int status) {
|
||||
errorResponse.setStatus(status);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponseBuilder error(String error) {
|
||||
errorResponse.setError(error);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponseBuilder message(String message) {
|
||||
errorResponse.setMessage(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponseBuilder errorCode(String errorCode) {
|
||||
errorResponse.setErrorCode(errorCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponseBuilder path(String path) {
|
||||
errorResponse.setPath(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponseBuilder detail(String key, Object value) {
|
||||
errorResponse.getDetails().put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorResponse build() {
|
||||
return errorResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务异常类
|
||||
*/
|
||||
public static class BusinessException extends RuntimeException {
|
||||
private final String errorCode;
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
this.errorCode = "BUSINESS_ERROR";
|
||||
}
|
||||
|
||||
public BusinessException(String errorCode, String message) {
|
||||
super(message);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public BusinessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorCode = "BUSINESS_ERROR";
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀异常类
|
||||
*/
|
||||
public static class FlashSaleException extends RuntimeException {
|
||||
private final String errorCode;
|
||||
|
||||
public FlashSaleException(String message) {
|
||||
super(message);
|
||||
this.errorCode = "FLASH_SALE_ERROR";
|
||||
}
|
||||
|
||||
public FlashSaleException(String errorCode, String message) {
|
||||
super(message);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 限流异常类
|
||||
*/
|
||||
public static class RateLimitException extends RuntimeException {
|
||||
public RateLimitException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RateLimitException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user