后端API全面优化:添加参数验证、全局异常处理和金币增减方法
问题修复: 1. 参数验证问题 - 创建DTO类:UpdateVipRequest、UpdateCoinsRequest、UpdatePhoneRequest - 使用@NotBlank、@NotNull、@Min、@Max、@Pattern注解验证参数 - 防止NullPointerException和非法参数 2. 全局异常处理 - 创建GlobalExceptionHandler全局异常处理器 - 统一处理Exception、MethodArgumentNotValidException等异常 - 返回友好的错误信息,避免暴露敏感信息 - 添加日志记录 3. 统一响应格式 - 创建ApiResponse统一响应类 - 包含success、data、error、code、timestamp字段 - 提供success()和error()静态方法 4. 金币增减方法 - UserInfoService新增addCoins方法(增加金币) - UserInfoService新增deductCoins方法(扣除金币) - 添加参数验证和余额检查 - 金币不足时抛出IllegalArgumentException 技术实现: - 使用Spring @RestControllerAdvice实现全局异常处理 - 使用javax.validation注解进行参数验证 - 使用SLF4J记录日志 - 统一HTTP状态码和业务错误码
This commit is contained in:
1 parent
966d7269ba
commit
04e858f6cf
6 files changed
+206
No files matched your search
@@ -0,0 +1,53 @@
|
||||
package xuan.common;
|
||||
|
||||
public class ApiResponse<T> {
|
||||
|
||||
private boolean success;
|
||||
private T data;
|
||||
private String error;
|
||||
private Integer code;
|
||||
private Long timestamp;
|
||||
|
||||
public ApiResponse() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public ApiResponse(boolean success, T data, String error, Integer code, Long timestamp) {
|
||||
this.success = success;
|
||||
this.data = data;
|
||||
this.error = error;
|
||||
this.code = code;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(true, data, null, 200, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> success(String message) {
|
||||
return new ApiResponse<>(true, null, message, 200, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(String message) {
|
||||
return new ApiResponse<>(false, null, message, 500, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(String message, Integer code) {
|
||||
return new ApiResponse<>(false, null, message, code, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public boolean isSuccess() { return success; }
|
||||
public void setSuccess(boolean success) { this.success = success; }
|
||||
|
||||
public T getData() { return data; }
|
||||
public void setData(T data) { this.data = data; }
|
||||
|
||||
public String getError() { return error; }
|
||||
public void setError(String error) { this.error = error; }
|
||||
|
||||
public Integer getCode() { return code; }
|
||||
public void setCode(Integer code) { this.code = code; }
|
||||
|
||||
public Long getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(Long timestamp) { this.timestamp = timestamp; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package xuan.dto;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class UpdateCoinsRequest {
|
||||
|
||||
@NotBlank(message = "userId不能为空")
|
||||
private String userId;
|
||||
|
||||
@NotNull(message = "coins不能为空")
|
||||
@Min(value = 0, message = "金币数不能小于0")
|
||||
private Integer coins;
|
||||
|
||||
public String getUserId() { return userId; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
|
||||
public Integer getCoins() { return coins; }
|
||||
public void setCoins(Integer coins) { this.coins = coins; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package xuan.dto;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
public class UpdatePhoneRequest {
|
||||
|
||||
@NotBlank(message = "userId不能为空")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
public String getUserId() { return userId; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package xuan.dto;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class UpdateVipRequest {
|
||||
|
||||
@NotBlank(message = "userId不能为空")
|
||||
private String userId;
|
||||
|
||||
@NotNull(message = "vipLevel不能为空")
|
||||
@Min(value = 0, message = "VIP等级不能小于0")
|
||||
@Max(value = 10, message = "VIP等级不能大于10")
|
||||
private Integer vipLevel;
|
||||
|
||||
public String getUserId() { return userId; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
|
||||
public Integer getVipLevel() { return vipLevel; }
|
||||
public void setVipLevel(Integer vipLevel) { this.vipLevel = vipLevel; }
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package xuan.exception;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import xuan.common.ApiResponse;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<?>> handleException(Exception e) {
|
||||
logger.error("系统异常", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("系统繁忙,请稍后重试"));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<?>> handleValidation(MethodArgumentNotValidException e) {
|
||||
String message = e.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
logger.warn("参数验证失败: {}", message);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error(message, 400));
|
||||
}
|
||||
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public ResponseEntity<ApiResponse<?>> handleConstraintViolation(ConstraintViolationException e) {
|
||||
String message = e.getConstraintViolations().stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
logger.warn("参数验证失败: {}", message);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error(message, 400));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResponse<?>> handleIllegalArgument(IllegalArgumentException e) {
|
||||
logger.warn("参数错误: {}", e.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error(e.getMessage(), 400));
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<ApiResponse<?>> handleRuntimeException(RuntimeException e) {
|
||||
logger.error("运行时异常", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("操作失败,请稍后重试"));
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,33 @@ public class UserInfoService {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UserInfo addCoins(String userId, Integer amount) {
|
||||
if (amount == null || amount <= 0) {
|
||||
throw new IllegalArgumentException("金币数量必须大于0");
|
||||
}
|
||||
UserInfo user = userInfoRepository.findByUserId(userId);
|
||||
if (user != null) {
|
||||
user.setCoins(user.getCoins() + amount);
|
||||
return userInfoRepository.save(user);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public UserInfo deductCoins(String userId, Integer amount) {
|
||||
if (amount == null || amount <= 0) {
|
||||
throw new IllegalArgumentException("金币数量必须大于0");
|
||||
}
|
||||
UserInfo user = userInfoRepository.findByUserId(userId);
|
||||
if (user != null) {
|
||||
if (user.getCoins() < amount) {
|
||||
throw new IllegalArgumentException("金币不足");
|
||||
}
|
||||
user.setCoins(user.getCoins() - amount);
|
||||
return userInfoRepository.save(user);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public UserInfo updatePhone(String userId, String phone) {
|
||||
UserInfo user = userInfoRepository.findByUserId(userId);
|
||||
if (user != null) {
|
||||
|
||||
Reference in new issue
Block a user