diff --git a/src/main/java/com/yunhai/userbase/aspect/OapiLogAspect.java b/src/main/java/com/yunhai/userbase/aspect/OapiLogAspect.java index 965e96e..c204f7b 100644 --- a/src/main/java/com/yunhai/userbase/aspect/OapiLogAspect.java +++ b/src/main/java/com/yunhai/userbase/aspect/OapiLogAspect.java @@ -1,7 +1,9 @@ package com.yunhai.userbase.aspect; -import com.alibaba.fastjson.JSON; +import com.yunhai.platform.apistd.ApiResponse; +import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.appauth.AppAuthFilter; +import com.yunhai.platform.utils.exception.DException; import com.yunhai.userbase.common.annotation.OapiLog; import com.yunhai.userbase.entity.ApplicationInfo; import com.yunhai.userbase.entity.SysOapiLog; @@ -90,6 +92,8 @@ public class OapiLogAspect { } catch (Throwable e) { entity.setStatusCode(500); entity.setErrorMsg(truncate(e.getMessage(), 2000)); + // 业务异常(DException/BizException)同样构造实际返回的响应体落库,便于详情下载 + fillErrorResponse(entity, e, saveResponse, maxResponseSize); throw e; } finally { entity.setCostTime(System.currentTimeMillis() - start); @@ -213,8 +217,42 @@ public class OapiLogAspect { if (!saveResponse || result == null) { return; } + writeResponseData(entity, result, maxResponseSize); + } + + /** + * 异常路径:与 {@code GlobalExceptionHandler} 保持一致的响应体(DException/BizException → ApiResponse), + * 使日志下载内容等于调用方实际收到的 body;其余异常不构造(响应不可预期)。 + */ + private void fillErrorResponse(SysOapiLog entity, Throwable e, boolean saveResponse, int maxResponseSize) { + if (!saveResponse) { + return; + } + ApiResponse response = toApiResponse(e); + if (response == null) { + return; + } + writeResponseData(entity, response, maxResponseSize); + } + + private ApiResponse toApiResponse(Throwable e) { + if (e instanceof DException) { + DException de = (DException) e; + return ApiResponse.fail(de.getCode(), de.getMessage(), de.getData()); + } + if (e instanceof BizException) { + BizException be = (BizException) e; + return ApiResponse.fail(be.getErrorCode(), be.getMessage()); + } + return null; + } + + private void writeResponseData(SysOapiLog entity, Object payload, int maxResponseSize) { + if (payload == null) { + return; + } try { - String resp = SensitiveDataMasker.maskObject(result); + String resp = SensitiveDataMasker.maskObject(payload); if (resp == null) { return; } diff --git a/src/main/java/com/yunhai/userbase/common/GlobalExceptionHandler.java b/src/main/java/com/yunhai/userbase/common/GlobalExceptionHandler.java index 32b2632..2e740f9 100644 --- a/src/main/java/com/yunhai/userbase/common/GlobalExceptionHandler.java +++ b/src/main/java/com/yunhai/userbase/common/GlobalExceptionHandler.java @@ -5,8 +5,10 @@ import org.springframework.core.annotation.Order; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; 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.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; @@ -52,6 +54,18 @@ public class GlobalExceptionHandler { return paramError(msg); } + /** 缺少必需的请求参数(@RequestParam 必填项未传,如 oapi 调用方漏传 groupId)。 */ + @ExceptionHandler(MissingServletRequestParameterException.class) + public Map handleMissingParam(MissingServletRequestParameterException ex) { + return paramError("缺少必需的请求参数: " + ex.getParameterName()); + } + + /** 请求参数类型不匹配(如 Integer 参数收到非数字字符串)。 */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public Map handleTypeMismatch(MethodArgumentTypeMismatchException ex) { + return paramError("参数类型不匹配: " + ex.getName()); + } + private String fieldErrorsMessage(java.util.List fieldErrors) { if (fieldErrors == null || fieldErrors.isEmpty()) { return "参数校验失败";