mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 10:57:13 +08:00 
			
		
		
		
	refactor: 💥 分离 HTTP 状态码和业务状态码
1.传输正常的情况下无论业务是否有异常,HTTP 状态码始终为 200 2.防止非 HTTPS 情况下出现运营商劫持(例如:404)
This commit is contained in:
		| @@ -70,7 +70,7 @@ public class GlobalErrorHandler extends BasicErrorController { | ||||
|         R<Object> result = R.fail(status.value(), (String)errorAttributeMap.get("error")); | ||||
|         result.setData(path); | ||||
|         try { | ||||
|             response.setStatus(status.value()); | ||||
|             response.setStatus(HttpStatus.OK.value()); | ||||
|             response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||||
|             objectMapper.writeValue(response.getWriter(), result); | ||||
|         } catch (IOException e) { | ||||
| @@ -89,6 +89,6 @@ public class GlobalErrorHandler extends BasicErrorController { | ||||
|         R<Object> result = R.fail(status.value(), (String)errorAttributeMap.get("error")); | ||||
|         result.setData(path); | ||||
|         log.error("请求地址 [{}],发生错误,错误信息:{}。", path, JSONUtil.toJsonStr(errorAttributeMap)); | ||||
|         return new ResponseEntity<>(BeanUtil.beanToMap(result), status); | ||||
|         return new ResponseEntity<>(BeanUtil.beanToMap(result), HttpStatus.OK); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,7 +30,6 @@ import org.springframework.validation.BindException; | ||||
| import org.springframework.web.HttpRequestMethodNotSupportedException; | ||||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||||
| import org.springframework.web.bind.annotation.ResponseStatus; | ||||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||||
| import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; | ||||
| import org.springframework.web.multipart.MaxUploadSizeExceededException; | ||||
| @@ -62,7 +61,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截自定义验证异常-错误请求 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.BAD_REQUEST) | ||||
|     @ExceptionHandler(BadRequestException.class) | ||||
|     public R handleBadRequestException(BadRequestException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],自定义验证失败。", request.getRequestURI(), e); | ||||
| @@ -73,7 +71,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截校验异常-违反约束异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.BAD_REQUEST) | ||||
|     @ExceptionHandler(ConstraintViolationException.class) | ||||
|     public R constraintViolationException(ConstraintViolationException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],参数验证失败。", request.getRequestURI(), e); | ||||
| @@ -85,7 +82,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截校验异常-绑定异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.BAD_REQUEST) | ||||
|     @ExceptionHandler(BindException.class) | ||||
|     public R handleBindException(BindException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],参数验证失败。", request.getRequestURI(), e); | ||||
| @@ -97,7 +93,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截校验异常-方法参数无效异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.BAD_REQUEST) | ||||
|     @ExceptionHandler(MethodArgumentNotValidException.class) | ||||
|     public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],参数验证失败。", request.getRequestURI(), e); | ||||
| @@ -110,7 +105,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截校验异常-方法参数类型不匹配异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.BAD_REQUEST) | ||||
|     @ExceptionHandler(MethodArgumentTypeMismatchException.class) | ||||
|     public R handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, | ||||
|         HttpServletRequest request) { | ||||
| @@ -123,7 +117,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截文件上传异常-超过上传大小限制 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.BAD_REQUEST) | ||||
|     @ExceptionHandler(MaxUploadSizeExceededException.class) | ||||
|     public R handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e, HttpServletRequest request) { | ||||
|         log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e); | ||||
| @@ -136,7 +129,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 认证异常-登录认证 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.UNAUTHORIZED) | ||||
|     @ExceptionHandler(NotLoginException.class) | ||||
|     public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],认证失败,无法访问系统资源。", request.getRequestURI(), e); | ||||
| @@ -159,7 +151,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 认证异常-权限认证 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.FORBIDDEN) | ||||
|     @ExceptionHandler(NotPermissionException.class) | ||||
|     public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],权限码校验失败。", request.getRequestURI(), e); | ||||
| @@ -169,7 +160,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 认证异常-角色认证 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.FORBIDDEN) | ||||
|     @ExceptionHandler(NotRoleException.class) | ||||
|     public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],角色权限校验失败。", request.getRequestURI(), e); | ||||
| @@ -179,7 +169,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截校验异常-请求方式不支持异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) | ||||
|     @ExceptionHandler(HttpRequestMethodNotSupportedException.class) | ||||
|     public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { | ||||
|         LogContextHolder.setErrorMsg(e.getMessage()); | ||||
| @@ -190,7 +179,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截业务异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||||
|     @ExceptionHandler(ServiceException.class) | ||||
|     public R handleServiceException(ServiceException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e); | ||||
| @@ -201,7 +189,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截未知的运行时异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||||
|     @ExceptionHandler(RuntimeException.class) | ||||
|     public R handleRuntimeException(RuntimeException e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生系统异常。", request.getRequestURI(), e); | ||||
| @@ -212,7 +199,6 @@ public class GlobalExceptionHandler { | ||||
|     /** | ||||
|      * 拦截未知的系统异常 | ||||
|      */ | ||||
|     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||||
|     @ExceptionHandler(Throwable.class) | ||||
|     public R handleException(Throwable e, HttpServletRequest request) { | ||||
|         log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e); | ||||
|   | ||||
| @@ -45,12 +45,12 @@ public class R<V> implements Serializable { | ||||
|     @Schema(description = "是否成功", example = "true") | ||||
|     private boolean success; | ||||
|  | ||||
|     /** 状态码 */ | ||||
|     @Schema(description = "状态码", example = "200") | ||||
|     /** 业务状态码 */ | ||||
|     @Schema(description = "业务状态码", example = "200") | ||||
|     private int code; | ||||
|  | ||||
|     /** 状态信息 */ | ||||
|     @Schema(description = "状态信息", example = "操作成功") | ||||
|     /** 业务状态信息 */ | ||||
|     @Schema(description = "业务状态信息", example = "操作成功") | ||||
|     private String msg; | ||||
|  | ||||
|     /** 返回数据 */ | ||||
|   | ||||
| @@ -50,6 +50,7 @@ import top.charles7c.cnadmin.auth.model.request.LoginRequest; | ||||
| import top.charles7c.cnadmin.common.constant.StringConsts; | ||||
| import top.charles7c.cnadmin.common.constant.SysConsts; | ||||
| import top.charles7c.cnadmin.common.model.dto.LogContext; | ||||
| import top.charles7c.cnadmin.common.model.vo.R; | ||||
| import top.charles7c.cnadmin.common.util.ExceptionUtils; | ||||
| import top.charles7c.cnadmin.common.util.IpUtils; | ||||
| import top.charles7c.cnadmin.common.util.ServletUtils; | ||||
| @@ -94,7 +95,6 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|         if (null == logDO) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         HandlerMethod handlerMethod = (HandlerMethod)handler; | ||||
|         // 记录所属模块 | ||||
|         this.logModule(logDO, handlerMethod); | ||||
| @@ -104,7 +104,6 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|         this.logRequest(logDO, request); | ||||
|         // 记录响应信息 | ||||
|         this.logResponse(logDO, response); | ||||
|  | ||||
|         // 保存系统日志 | ||||
|         SpringUtil.getApplicationContext().publishEvent(logDO); | ||||
|     } | ||||
| @@ -126,32 +125,30 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|      */ | ||||
|     private LogDO logElapsedTimeAndException() { | ||||
|         LogContext logContext = LogContextHolder.get(); | ||||
|         if (null == logContext) { | ||||
|             return null; | ||||
|         } | ||||
|         try { | ||||
|             if (null != logContext) { | ||||
|                 LogDO logDO = new LogDO(); | ||||
|                 logDO.setCreateTime(logContext.getCreateTime()); | ||||
|                 logDO | ||||
|                     .setElapsedTime(System.currentTimeMillis() - LocalDateTimeUtil.toEpochMilli(logDO.getCreateTime())); | ||||
|                 logDO.setStatus(LogStatusEnum.SUCCESS); | ||||
|  | ||||
|                 // 记录错误信息(非未知异常不记录异常详情,只记录错误信息) | ||||
|                 String errorMsg = logContext.getErrorMsg(); | ||||
|                 if (StrUtil.isNotBlank(errorMsg)) { | ||||
|                     logDO.setStatus(LogStatusEnum.FAILURE); | ||||
|                     logDO.setErrorMsg(errorMsg); | ||||
|                 } | ||||
|                 // 记录异常详情 | ||||
|                 Throwable exception = logContext.getException(); | ||||
|                 if (null != exception) { | ||||
|                     logDO.setStatus(LogStatusEnum.FAILURE); | ||||
|                     logDO.setExceptionDetail(ExceptionUtil.stacktraceToString(exception, -1)); | ||||
|                 } | ||||
|                 return logDO; | ||||
|             LogDO logDO = new LogDO(); | ||||
|             logDO.setCreateTime(logContext.getCreateTime()); | ||||
|             logDO.setElapsedTime(System.currentTimeMillis() - LocalDateTimeUtil.toEpochMilli(logDO.getCreateTime())); | ||||
|             logDO.setStatus(LogStatusEnum.SUCCESS); | ||||
|             // 记录错误信息(非未知异常不记录异常详情,只记录错误信息) | ||||
|             String errorMsg = logContext.getErrorMsg(); | ||||
|             if (StrUtil.isNotBlank(errorMsg)) { | ||||
|                 logDO.setStatus(LogStatusEnum.FAILURE); | ||||
|                 logDO.setErrorMsg(errorMsg); | ||||
|             } | ||||
|             // 记录异常详情 | ||||
|             Throwable exception = logContext.getException(); | ||||
|             if (null != exception) { | ||||
|                 logDO.setStatus(LogStatusEnum.FAILURE); | ||||
|                 logDO.setExceptionDetail(ExceptionUtil.stacktraceToString(exception, -1)); | ||||
|             } | ||||
|             return logDO; | ||||
|         } finally { | ||||
|             LogContextHolder.remove(); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -166,7 +163,6 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|         Tag classTag = handlerMethod.getBeanType().getDeclaredAnnotation(Tag.class); | ||||
|         Log classLog = handlerMethod.getBeanType().getDeclaredAnnotation(Log.class); | ||||
|         Log methodLog = handlerMethod.getMethodAnnotation(Log.class); | ||||
|  | ||||
|         // 例如:@Tag(name = "部门管理") -> 部门管理 | ||||
|         // (本框架代码规范)例如:@Tag(name = "部门管理 API") -> 部门管理 | ||||
|         if (null != classTag) { | ||||
| @@ -194,7 +190,6 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|     private void logDescription(LogDO logDO, HandlerMethod handlerMethod) { | ||||
|         Operation methodOperation = handlerMethod.getMethodAnnotation(Operation.class); | ||||
|         Log methodLog = handlerMethod.getMethodAnnotation(Log.class); | ||||
|  | ||||
|         // 例如:@Operation(summary="新增部门") -> 新增部门 | ||||
|         if (null != methodOperation) { | ||||
|             logDO.setDescription(StrUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述")); | ||||
| @@ -245,14 +240,20 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|     private void logResponse(LogDO logDO, HttpServletResponse response) { | ||||
|         int status = response.getStatus(); | ||||
|         logDO.setStatusCode(status); | ||||
|         logDO.setStatus(status >= HttpStatus.HTTP_BAD_REQUEST ? LogStatusEnum.FAILURE : logDO.getStatus()); | ||||
|         logDO.setResponseHeaders(this.desensitize(ServletUtil.getHeadersMap(response))); | ||||
|         // 响应体(不记录非 JSON 响应数据) | ||||
|         String responseBody = this.getResponseBody(response); | ||||
|         if (StrUtil.isNotBlank(responseBody) && JSONUtil.isTypeJSON(responseBody)) { | ||||
|             logDO.setResponseBody(responseBody); | ||||
|             // 业务状态码优先级高 | ||||
|             try { | ||||
|                 R result = JSONUtil.toBean(responseBody, R.class); | ||||
|                 logDO.setStatusCode(result.getCode()); | ||||
|                 logDO.setStatus(result.isSuccess() ? LogStatusEnum.SUCCESS : LogStatusEnum.FAILURE); | ||||
|             } catch (Exception ignored) { | ||||
|             } | ||||
|         } | ||||
|         // 操作失败:>= 400 | ||||
|         logDO.setStatus(status >= HttpStatus.HTTP_BAD_REQUEST ? LogStatusEnum.FAILURE : logDO.getStatus()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -269,7 +270,6 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|             if (CollUtil.isEmpty(waitDesensitizeData)) { | ||||
|                 return desensitizeDataStr; | ||||
|             } | ||||
|  | ||||
|             for (String desensitizeProperty : operationLogProperties.getDesensitizeFields()) { | ||||
|                 waitDesensitizeData.computeIfPresent(desensitizeProperty, (k, v) -> ENCRYPT_SYMBOL); | ||||
|                 waitDesensitizeData.computeIfPresent(desensitizeProperty.toLowerCase(), (k, v) -> ENCRYPT_SYMBOL); | ||||
| @@ -328,13 +328,11 @@ public class LogInterceptor implements HandlerInterceptor { | ||||
|         if (!(handler instanceof HandlerMethod) || Boolean.FALSE.equals(operationLogProperties.getEnabled())) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // 2、检查是否需要记录内网 IP 操作 | ||||
|         boolean isInnerIp = IpUtils.isInnerIp(ServletUtil.getClientIP(request)); | ||||
|         if (isInnerIp && Boolean.FALSE.equals(operationLogProperties.getIncludeInnerIp())) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // 3、排除不需要记录系统日志的接口 | ||||
|         HandlerMethod handlerMethod = (HandlerMethod)handler; | ||||
|         Log methodLog = handlerMethod.getMethodAnnotation(Log.class); | ||||
|   | ||||
| @@ -46,20 +46,10 @@ axios.interceptors.response.use( | ||||
|     ) { | ||||
|       return response; | ||||
|     } | ||||
|  | ||||
|     const res = response.data; | ||||
|     if (res.success) { | ||||
|       return res; | ||||
|     } | ||||
|     messageErrorWrapper({ | ||||
|       content: res.msg || '网络错误', | ||||
|       duration: 5 * 1000, | ||||
|     }); | ||||
|     return Promise.reject(new Error(res.msg || '网络错误')); | ||||
|   }, | ||||
|   (error) => { | ||||
|     const { response } = error; | ||||
|     const res = response.data; | ||||
|     if ([401].includes(res.code) && response.config.url !== '/auth/user/info') { | ||||
|       modalErrorWrapper({ | ||||
|         title: '确认退出', | ||||
| @@ -68,8 +58,8 @@ axios.interceptors.response.use( | ||||
|         escToClose: false, | ||||
|         okText: '重新登录', | ||||
|         async onOk() { | ||||
|           const userStore = useLoginStore(); | ||||
|           await userStore.logout(); | ||||
|           const loginStore = useLoginStore(); | ||||
|           await loginStore.logout(); | ||||
|           window.location.reload(); | ||||
|         }, | ||||
|       }); | ||||
| @@ -79,6 +69,13 @@ axios.interceptors.response.use( | ||||
|         duration: 5 * 1000, | ||||
|       }); | ||||
|     } | ||||
|     return Promise.reject(new Error(res.msg || '网络错误')); | ||||
|   }, | ||||
|   (error) => { | ||||
|     messageErrorWrapper({ | ||||
|       content: error.msg || '网络错误', | ||||
|       duration: 5 * 1000, | ||||
|     }); | ||||
|     return Promise.reject(error); | ||||
|   } | ||||
| ); | ||||
|   | ||||
| @@ -117,7 +117,7 @@ | ||||
|       <a-drawer | ||||
|         title="日志详情" | ||||
|         :visible="visible" | ||||
|         :width="580" | ||||
|         :width="660" | ||||
|         :footer="false" | ||||
|         unmount-on-close | ||||
|         render-to-body | ||||
|   | ||||
		Reference in New Issue
	
	Block a user