应定义继承RuntimeException的CouponReceiveException类,在领取失败时主动抛出并携带错误码,再通过@ControllerAdvice统一处理为标准JSON响应。

在优惠券领取失败时触发自定义异常,核心是:定义一个业务异常类(如 CouponReceiveException),在领取逻辑中根据失败原因主动 throw 它,而不是用通用异常或返回码“掩盖”问题。
定义自定义异常类
继承 RuntimeException(非检查异常,调用方无需强制 try-catch)更符合业务异常场景:
public class CouponReceiveException extends RuntimeException {
private final int errorCode;
public CouponReceiveException(String message) {
this(message, 500);
}
public CouponReceiveException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
这样既保留语义(是领取失败),又携带可识别的错误码,便于前端或日志处理。
在领取服务中按条件抛出
不要等系统报空指针或数据库异常才响应;要在业务规则校验不通过时立即抛出自定义异常:
立即学习“Java免费学习笔记(深入)”;
- 用户已领过该券 →
throw new CouponReceiveException("您已领取过此优惠券", 409); - 库存不足 →
throw new CouponReceiveException("优惠券已抢光", 404); - 用户不符合领取条件(如新用户限定)→
throw new CouponReceiveException("当前用户不满足领取条件", 403); - 活动未开始/已结束 →
throw new CouponReceiveException("活动暂未开启或已结束", 410);
统一异常处理(让异常真正“生效”)
仅抛出还不够,需配合 Spring 的 @ControllerAdvice 拦截并转为标准 HTTP 响应:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CouponReceiveException.class)
public ResponseEntity<ApiResponse> handleCouponReceiveException(CouponReceiveException e) {
return ResponseEntity.status(400).body(
ApiResponse.fail(e.getErrorCode(), e.getMessage())
);
}
}
这样前端收到的是明确的 JSON 错误体(如 {"code":409,"msg":"您已领取过此优惠券"}),而非 500 或空白响应。
避免常见误区
- ❌ 不要用
return new Result(false, "xxx")替代异常 —— 会模糊错误语义,链路追踪困难 - ❌ 不要捕获后吞掉异常(
catch (Exception e) { })—— 自定义异常失去意义 - ✅ 在日志中记录异常(建议用
log.error("Coupon receive failed", e)),方便排查 - ✅ 若需补偿或通知,可在异常处理器中触发事件(如发消息、写失败流水)


















