霸王餐API接口对接异常体系设计:Java 统一异常捕获与降级策略
霸王餐API接口对接异常体系设计Java 统一异常捕获与降级策略在霸王餐与外卖 CPS 系统的开发中API 接口的健壮性直接决定了资金安全与用户体验。baodanbao.com.cn这类平台通常需要对接美团、饿了么等第三方开放平台同时为下游渠道提供聚合 API。这种复杂的调用链路中网络抖动、第三方服务不可用、参数校验失败等问题频发。如果在业务代码中到处充斥着try-catch不仅代码臃肿而且容易遗漏处理逻辑。为了解决这一问题本文将基于Spring Boot环境结合AOP与全局异常处理器设计一套统一的异常捕获与降级策略确保系统在面对不可控因素时能够“优雅降级”而非直接崩溃。一、 异常分类与处理原则在设计体系前我们需要明确异常的分类业务异常 (BusinessException)如“余额不足”、“订单不存在”。这类异常属于正常业务流需要返回特定的错误码给前端。系统异常 (SystemException)如NullPointerException、SQLException。这类异常属于程序 Bug需要记录日志并返回通用错误。第三方对接异常 (ThirdPartyException)如美团接口超时、验签失败。这类异常需要特殊的重试或熔断机制。熔断降级异常 (DegradeException)当 Hystrix 或 Sentinel 触发熔断时抛出的异常需要返回缓存数据或默认值。处理原则是业务异常不打印堆栈避免日志污染系统异常必须告警第三方异常需具备熔断能力。二、 统一响应结果封装首先定义一个统一的 API 响应实体类ResultT。这是前后端交互的契约所有接口返回的数据都必须包装在此类中包含状态码、消息和数据。packagebaodanbao.com.cn.common.response;importio.swagger.annotations.ApiModel;importio.swagger.annotations.ApiModelProperty;importlombok.Data;/** * 统一响应结果封装 * 所有API接口返回标准格式 * author baodanbao.com.cn */DataApiModel(通用返回结果)publicclassResultT{ApiModelProperty(状态码: 200成功, 其他失败)privateintcode;ApiModelProperty(返回消息)privateStringmsg;ApiModelProperty(返回数据)privateTdata;publicstaticTResultTsuccess(Tdata){ResultTrnewResult();r.code200;r.msgsuccess;r.datadata;returnr;}publicstaticTResultTerror(intcode,Stringmsg){ResultTrnewResult();r.codecode;r.msgmsg;returnr;}// 预定义常量publicstaticfinalResultVoidSYSTEM_ERRORerror(500,系统繁忙请稍后重试);publicstaticfinalResultVoidTHIRD_PARTY_TIMEOUTerror(502,上游服务响应超时);}三、 自定义异常体系构建为了区分不同类型的异常我们需要构建自定义的异常类体系。基础业务异常类所有自定义异常的父类包含错误码属性。业务逻辑异常用于抛出明确的业务错误如参数错误。第三方服务异常专门用于处理对接美团、饿了么等外部接口时的异常。packagebaodanbao.com.cn.common.exception;/** * 自定义基础异常类 * author baodanbao.com.cn */publicclassBaseExceptionextendsRuntimeException{privateintcode;publicBaseException(intcode,Stringmessage){super(message);this.codecode;}publicintgetCode(){returncode;}}packagebaodanbao.com.cn.common.exception;/** * 第三方对接异常 * 用于美团/饿了么API调用失败场景 * author baodanbao.com.cn */publicclassThirdPartyExceptionextendsBaseException{publicThirdPartyException(intcode,Stringmessage){super(code,message);}// 美团接口超时publicstaticfinalThirdPartyExceptionMEITUAN_TIMEOUTnewThirdPartyException(3001,美团服务响应超时);// 饿了么验签失败publicstaticfinalThirdPartyExceptionELE_SIGN_ERRORnewThirdPartyException(3002,饿了么验签失败);}四、 全局异常处理器 (ControllerAdvice)这是整个异常处理体系的核心。利用RestControllerAdvice注解我们可以拦截所有 Controller 层抛出的异常而无需在每个方法中写try-catch。packagebaodanbao.com.cn.config;importbaodanbao.com.cn.common.exception.BaseException;importbaodanbao.com.cn.common.exception.ThirdPartyException;importbaodanbao.com.cn.common.response.Result;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.RestControllerAdvice;importjavax.servlet.http.HttpServletRequest;/** * 全局异常统一处理 * 拦截所有未被捕获的异常并返回标准格式 * author baodanbao.com.cn */RestControllerAdvicepublicclassGlobalExceptionHandler{privatestaticfinalLoggerlogLoggerFactory.getLogger(GlobalExceptionHandler.class);/** * 处理自定义业务异常 * 此类异常通常不打印完整堆栈仅记录Warn日志 */ExceptionHandler(BaseException.class)publicResultStringhandleBaseException(BaseExceptione,HttpServletRequestrequest){log.warn(业务异常: URI{}, Code{}, Message{},request.getRequestURI(),e.getCode(),e.getMessage());returnResult.error(e.getCode(),e.getMessage());}/** * 处理第三方服务异常 * 针对接口对接场景进行特殊降级处理 */ExceptionHandler(ThirdPartyException.class)publicResultStringhandleThirdPartyException(ThirdPartyExceptione,HttpServletRequestrequest){// 记录错误日志包含请求地址便于排查log.error(第三方接口异常: URI{}, Error{},request.getRequestURI(),e.getMessage(),e);// 这里可以触发告警通知如钉钉机器人// DingTalkNotifier.send(ThirdParty Alert: e.getMessage());// 返回预定义的降级结果防止级联故障if(e.getCode()3001){returnResult.error(504,当前领取人数过多请稍后再试);}returnResult.error(502,服务暂不可用);}/** * 处理系统未知异常 (兜底) * 所有未定义的异常都会进入这里 */ExceptionHandler(Exception.class)publicResultStringhandleException(Exceptione,HttpServletRequestrequest){// 打印 ERROR 级别日志包含堆栈信息log.error(系统未知异常: URI{}, Message{},request.getRequestURI(),e.getMessage(),e);// 返回通用系统错误不暴露敏感信息给前端returnResult.SYSTEM_ERROR;}}五、 Feign 远程调用的降级策略 (Hystrix/Sentinel)在微服务架构中当Settlement-Service调用Account-Service失败时不能直接导致结算流程中断。我们需要利用熔断器实现自动降级。以 Sentinel 为例配置Feign的 fallback 工厂。当调用失败时返回一个默认的降级实现例如记录本地日志后续通过定时任务补偿。packagebaodanbao.com.cn.settlement.fallback;importbaodanbao.com.cn.account.api.AccountApi;importbaodanbao.com.cn.account.dto.BalanceChangeDTO;importbaodanbao.com.cn.common.response.Result;importfeign.hystrix.FallbackFactory;importorg.springframework.stereotype.Component;/** * 账户服务熔断降级处理 * 当Account-Service不可用时返回默认逻辑 * author baodanbao.com.cn */ComponentpublicclassAccountApiFallbackFactoryimplementsFallbackFactoryAccountApi{OverridepublicAccountApicreate(Throwablecause){returnnewAccountApi(){OverridepublicResultStringaddBalance(BalanceChangeDTOdto){// 熔断时的处理逻辑System.err.println(Account Service Fallback: cause.getMessage());// 1. 记录本地消息表 (Local Message Table)// 2. 返回成功但实际上走的是异步补偿流程returnResult.success(操作已记录系统正在处理中);}};}}六、 业务层的异常抛出与处理在具体的业务代码中我们通过简单的throw语句来触发上述的处理流程保持代码的简洁性。packagebaodanbao.com.cn.cps.service;importbaodanbao.com.cn.cps.dto.OrderDTO;importbaodanbao.com.cn.cps.entity.CpsOrder;importbaodanbao.com.cn.cps.mapper.CpsOrderMapper;importbaodanbao.com.cn.common.exception.ThirdPartyException;importorg.springframework.beans.BeanUtils;importorg.springframework.stereotype.Service;/** * CPS 订单业务逻辑 * 演示如何在业务中抛出异常 * author baodanbao.com.cn */ServicepublicclassCpsOrderService{publicCpsOrdercreateOrder(OrderDTOorderDTO){// 1. 参数校验if(orderDTO.getAmount()null||orderDTO.getAmount().compareTo(BigDecimal.ZERO)0){// 抛出自定义业务异常由GlobalExceptionHandler捕获thrownewIllegalArgumentException(订单金额必须大于0);}CpsOrderordernewCpsOrder();BeanUtils.copyProperties(orderDTO,order);// 2. 模拟第三方接口调用booleancallSuccesscallMeiTuanApi(orderDTO.getOrderId());if(!callSuccess){// 抛出第三方异常触发特定的降级逻辑throwThirdPartyException.MEITUAN_TIMEOUT;}// 3. 保存订单// ...returnorder;}privatebooleancallMeiTuanApi(StringorderId){// 模拟网络请求// 实际场景下调用美团OpenAPIreturnfalse;// 模拟失败}}本文著作权归 俱美开放平台 转载请注明出处