diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/enums/GlobalErrorCodeEnum.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/enums/GlobalErrorCodeEnum.java new file mode 100644 index 000000000..b948cda81 --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/enums/GlobalErrorCodeEnum.java @@ -0,0 +1,51 @@ +package cn.iocoder.common.framework.enums; + +import cn.iocoder.common.framework.util.ServiceExceptionUtil; + +/** + * 全局错误码枚举 + * 1-999 系统异常编码保留 + * + * 一般情况下,{@link GlobalErrorCodeEnum#getCode()} ()} 使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status + * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的 + * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。 + */ +public enum GlobalErrorCodeEnum implements ServiceExceptionUtil.Enumerable { + + SUCCESS(0, "成功"), + + // ========== 客户端错误段 ========== + + BAD_REQUEST(400, "请求参数不正确"), + UNAUTHORIZED(401, "账号未登录"), + FORBIDDEN(403, "没有该操作权限"), + NOT_FOUND(404, "请求未找到"), + METHOD_NOT_ALLOWED(405, "请求方法不正确"), + + // ========== 服务端错误段 ========== + + INTERNAL_SERVER_ERROR(500, "系统异常"), + ; + + private final int code; + private final String message; + + GlobalErrorCodeEnum(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + @Override + public int getGroup() { + return 0; + } + +} diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/enums/SysErrorCodeEnum.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/enums/SysErrorCodeEnum.java deleted file mode 100644 index 82af150b6..000000000 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/enums/SysErrorCodeEnum.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.common.framework.enums; - -/** - * 错误码枚举类 - * - * 系统级异常,使用 2-001-000-000 段 - */ -public enum SysErrorCodeEnum { - - SYS_ERROR(2001001000, "服务端发生异常"), - MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"), - VALIDATION_REQUEST_PARAM_ERROR(2001001002, "参数校验不正确") - ; - - private final int code; - private final String message; - - SysErrorCodeEnum(int code, String message) { - this.code = code; - this.message = message; - } - - public int getCode() { - return code; - } - - public String getMessage() { - return message; - } - ; - -} diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java index 86cbf63cd..2cf042db9 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/vo/CommonResult.java @@ -1,5 +1,6 @@ package cn.iocoder.common.framework.vo; +import cn.iocoder.common.framework.enums.GlobalErrorCodeEnum; import cn.iocoder.common.framework.util.ServiceExceptionUtil; import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.util.Assert; @@ -13,7 +14,7 @@ import java.io.Serializable; */ public final class CommonResult implements Serializable { - private static final Integer CODE_SUCCESS = 0; + private static final Integer CODE_SUCCESS = GlobalErrorCodeEnum.SUCCESS.getCode(); /** * 错误码 diff --git a/common/mall-spring-boot-starter-security-admin/src/main/java/cn/iocoder/mall/security/admin/core/interceptor/AdminSecurityInterceptor.java b/common/mall-spring-boot-starter-security-admin/src/main/java/cn/iocoder/mall/security/admin/core/interceptor/AdminSecurityInterceptor.java index 47af20d13..f9432bebf 100644 --- a/common/mall-spring-boot-starter-security-admin/src/main/java/cn/iocoder/mall/security/admin/core/interceptor/AdminSecurityInterceptor.java +++ b/common/mall-spring-boot-starter-security-admin/src/main/java/cn/iocoder/mall/security/admin/core/interceptor/AdminSecurityInterceptor.java @@ -1,5 +1,6 @@ package cn.iocoder.mall.security.admin.core.interceptor; +import cn.iocoder.common.framework.enums.GlobalErrorCodeEnum; import cn.iocoder.common.framework.enums.UserTypeEnum; import cn.iocoder.common.framework.util.CollectionUtils; import cn.iocoder.common.framework.util.HttpUtil; @@ -7,7 +8,6 @@ import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.security.admin.core.context.AdminSecurityContext; import cn.iocoder.mall.security.admin.core.context.AdminSecurityContextHolder; -import cn.iocoder.mall.systemservice.enums.SystemErrorCodeEnum; import cn.iocoder.mall.systemservice.rpc.oauth.OAuth2Rpc; import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO; import cn.iocoder.mall.systemservice.rpc.permission.PermissionRpc; @@ -21,7 +21,6 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import java.util.Arrays; import static cn.iocoder.mall.systemservice.enums.SystemErrorCodeEnum.OAUTH_USER_TYPE_ERROR; @@ -69,7 +68,7 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter { private void checkAuthentication(HandlerMethod handlerMethod, Integer adminId) { boolean requiresAuthenticate = !handlerMethod.hasMethodAnnotation(RequiresNone.class); // 对于 ADMIN 来说,默认需登录 if (requiresAuthenticate && adminId == null) { - throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_NOT_AUTHENTICATION); + throw ServiceExceptionUtil.exception(GlobalErrorCodeEnum.UNAUTHORIZED); } } diff --git a/common/mall-spring-boot-starter-security-user/src/main/java/cn/iocoder/mall/security/user/core/interceptor/UserSecurityInterceptor.java b/common/mall-spring-boot-starter-security-user/src/main/java/cn/iocoder/mall/security/user/core/interceptor/UserSecurityInterceptor.java index 19db07bfe..190fc42a6 100644 --- a/common/mall-spring-boot-starter-security-user/src/main/java/cn/iocoder/mall/security/user/core/interceptor/UserSecurityInterceptor.java +++ b/common/mall-spring-boot-starter-security-user/src/main/java/cn/iocoder/mall/security/user/core/interceptor/UserSecurityInterceptor.java @@ -1,12 +1,12 @@ package cn.iocoder.mall.security.user.core.interceptor; +import cn.iocoder.common.framework.enums.GlobalErrorCodeEnum; import cn.iocoder.common.framework.enums.UserTypeEnum; import cn.iocoder.common.framework.util.HttpUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.security.user.core.context.UserSecurityContext; import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder; -import cn.iocoder.mall.systemservice.enums.SystemErrorCodeEnum; import cn.iocoder.mall.systemservice.rpc.oauth.OAuth2Rpc; import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO; import cn.iocoder.mall.web.core.util.CommonWebUtil; @@ -64,7 +64,7 @@ public class UserSecurityInterceptor extends HandlerInterceptorAdapter { requiresAuthenticate = true; } if (requiresAuthenticate && userId == null) { - throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_NOT_AUTHENTICATION); + throw ServiceExceptionUtil.exception(GlobalErrorCodeEnum.UNAUTHORIZED); } } diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java index 61c13549f..3d5e4f4f4 100644 --- a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/core/handler/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ package cn.iocoder.mall.web.core.handler; -import cn.iocoder.common.framework.enums.SysErrorCodeEnum; +import cn.iocoder.common.framework.enums.GlobalErrorCodeEnum; import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.util.ExceptionUtil; import cn.iocoder.common.framework.util.HttpUtil; @@ -17,11 +17,18 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.util.Assert; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +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 org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.Date; @@ -47,33 +54,107 @@ public class GlobalExceptionHandler { @Reference(validation = "true", version = "${dubbo.consumer.SystemExceptionLogRpc.version}") private SystemExceptionLogRpc systemExceptionLogRpc; - // 逻辑异常 + /** + * 处理 SpringMVC 请求参数缺失 + * + * 例如说,接口上设置了 @RequestParam("xx") 参数,结果并未传递 xx 参数 + */ + @ExceptionHandler(value = MissingServletRequestParameterException.class) + public CommonResult missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) { + logger.warn("[missingServletRequestParameterExceptionHandler]", ex); + return CommonResult.error(GlobalErrorCodeEnum.BAD_REQUEST.getCode(), + String.format("请求参数缺失:%s", ex.getParameterName())); + } + + /** + * 处理 SpringMVC 请求参数类型错误 + * + * 例如说,接口上设置了 @RequestParam("xx") 参数为 Integer,结果传递 xx 参数类型为 String + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public CommonResult methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) { + logger.warn("[missingServletRequestParameterExceptionHandler]", ex); + return CommonResult.error(GlobalErrorCodeEnum.BAD_REQUEST.getCode(), + String.format("请求参数类型错误:%s", ex.getMessage())); + } + + /** + * 处理 SpringMVC 参数校验不正确 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public CommonResult methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) { + logger.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex); + FieldError fieldError = ex.getBindingResult().getFieldError(); + assert fieldError != null; // 断言,避免告警 + return CommonResult.error(GlobalErrorCodeEnum.BAD_REQUEST.getCode(), + String.format("请求参数不正确:%s", fieldError.getDefaultMessage())); + } + + /** + * 处理 SpringMVC 参数绑定不正确,本质上也是通过 Validator 校验 + */ + @ExceptionHandler(BindException.class) + public CommonResult bindExceptionHandler(BindException ex) { + logger.warn("[handleBindException]", ex); + FieldError fieldError = ex.getFieldError(); + assert fieldError != null; // 断言,避免告警 + return CommonResult.error(GlobalErrorCodeEnum.BAD_REQUEST.getCode(), + String.format("请求参数不正确:%s", fieldError.getDefaultMessage())); + } + + /** + * 处理 Validator 校验不通过产生的异常 + */ + @ExceptionHandler(value = ConstraintViolationException.class) + public CommonResult constraintViolationExceptionHandler(ConstraintViolationException ex) { + logger.warn("[constraintViolationExceptionHandler]", ex); + ConstraintViolation constraintViolation = ex.getConstraintViolations().iterator().next(); + return CommonResult.error(GlobalErrorCodeEnum.BAD_REQUEST.getCode(), + String.format("请求参数不正确:%s", constraintViolation.getMessage())); + } + + /** + * 处理 SpringMVC 请求地址不存在 + * + * 注意,它需要设置如下两个配置项: + * 1. spring.mvc.throw-exception-if-no-handler-found 为 true + * 2. spring.mvc.static-path-pattern 为 /statics/** + */ + @ExceptionHandler(NoHandlerFoundException.class) + public CommonResult noHandlerFoundExceptionHandler(NoHandlerFoundException ex) { + logger.warn("[noHandlerFoundExceptionHandler]", ex); + return CommonResult.error(GlobalErrorCodeEnum.NOT_FOUND.getCode(), + String.format("请求地址不存在:%s", ex.getRequestURL())); + } + + /** + * 处理 SpringMVC 请求方法不正确 + * + * 例如说,A 接口的方法为 GET 方式,结果请求方法为 POST 方式,导致不匹配 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public CommonResult httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) { + logger.warn("[httpRequestMethodNotSupportedExceptionHandler]", ex); + return CommonResult.error(GlobalErrorCodeEnum.METHOD_NOT_ALLOWED.getCode(), + String.format("请求方法不正确:%s", ex.getMessage())); + } + + /** + * 处理业务异常 ServiceException + * + * 例如说,商品库存不足,用户手机号已存在。 + */ @ExceptionHandler(value = ServiceException.class) public CommonResult serviceExceptionHandler(ServiceException ex) { logger.debug("[serviceExceptionHandler]", ex); return CommonResult.error(ex.getCode(), ex.getMessage()); } - // Spring MVC 参数不正确 - @ExceptionHandler(value = MissingServletRequestParameterException.class) - public CommonResult missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) { - logger.warn("[missingServletRequestParameterExceptionHandler]", ex); - return CommonResult.error(SysErrorCodeEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), SysErrorCodeEnum.MISSING_REQUEST_PARAM_ERROR.getMessage() + ":" + ex.getMessage()); - } - - @ExceptionHandler(value = ConstraintViolationException.class) - public CommonResult constraintViolationExceptionHandler(ConstraintViolationException ex) { - logger.info("[constraintViolationExceptionHandler]", ex); - // TODO 芋艿,后续要想一个更好的方式。 - // 拼接详细报错 - StringBuilder detailMessage = new StringBuilder("\n\n详细错误如下:"); - ex.getConstraintViolations().forEach(constraintViolation -> detailMessage.append("\n").append(constraintViolation.getMessage())); - return CommonResult.error(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), - SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getMessage() + detailMessage.toString()); - } - + /** + * 处理系统异常,兜底处理所有的一切 + */ @ExceptionHandler(value = Exception.class) - public CommonResult exceptionHandler(HttpServletRequest req, Exception e) { + public CommonResult exceptionHandler(HttpServletRequest req, Throwable e) { logger.error("[exceptionHandler]", e); // 插入异常日志 SystemExceptionLogCreateDTO exceptionLog = new SystemExceptionLogCreateDTO(); @@ -88,10 +169,10 @@ public class GlobalExceptionHandler { logger.error("[exceptionHandler][插入访问日志({}) 发生异常({})", JSON.toJSONString(exceptionLog), ExceptionUtils.getRootCauseMessage(th)); } // 返回 ERROR CommonResult - return CommonResult.error(SysErrorCodeEnum.SYS_ERROR.getCode(), SysErrorCodeEnum.SYS_ERROR.getMessage()); + return CommonResult.error(GlobalErrorCodeEnum.INTERNAL_SERVER_ERROR.getCode(), GlobalErrorCodeEnum.INTERNAL_SERVER_ERROR.getMessage()); } - private void initExceptionLog(SystemExceptionLogCreateDTO exceptionLog, HttpServletRequest request, Exception e) { + private void initExceptionLog(SystemExceptionLogCreateDTO exceptionLog, HttpServletRequest request, Throwable e) { // 设置账号编号 exceptionLog.setUserId(CommonWebUtil.getUserId(request)); exceptionLog.setUserType(CommonWebUtil.getUserType(request)); diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.http b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.http index 74a498faf..2725d8d4a 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.http +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.http @@ -22,7 +22,20 @@ POST {{baseUrl}}/admin/update-status Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{accessToken}} -id=31&status=1 +adminId=31&status=1 + +### /admin/update-status 失败,参数缺失 +POST {{baseUrl}}/admin/update-status +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{accessToken}} + +adminId=31 + +### admin/update-status 失败,地址不存在 +GET {{baseUrl}}/admin/update-status--- +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{accessToken}} + +adminId=31&status=sss ### - diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.java index da2fa7ab8..fa7f295d7 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/admin/AdminController.java @@ -14,18 +14,21 @@ import cn.iocoder.security.annotations.RequiresPermissions; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; import static cn.iocoder.common.framework.vo.CommonResult.success; @Api("管理员 API") @RestController @RequestMapping("/admin") +@Validated public class AdminController { @Autowired @@ -56,7 +59,7 @@ public class AdminController { @PostMapping("/update-status") @ApiOperation(value = "更新管理员状态") @RequiresPermissions("system:admin:update-status") - public CommonResult updateAdminStatus(AdminUpdateStatusDTO updateStatusDTO) { + public CommonResult updateAdminStatus(@Valid AdminUpdateStatusDTO updateStatusDTO) { adminManager.updateAdminStatus(updateStatusDTO); return success(true); } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemAccessLogController.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemAccessLogController.java index 6c84d6ddf..9ed71e060 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemAccessLogController.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemAccessLogController.java @@ -5,6 +5,7 @@ import cn.iocoder.common.framework.vo.PageResult; import cn.iocoder.mall.managementweb.controller.systemlog.dto.SystemAccessLogPageDTO; import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemAccessLogVO; import cn.iocoder.mall.managementweb.manager.systemlog.SystemAccessLogManager; +import cn.iocoder.security.annotations.RequiresPermissions; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; @@ -29,6 +30,7 @@ public class SystemAccessLogController { @GetMapping("/page") @ApiOperation("获得系统访问日志分页") + @RequiresPermissions("system:system-access-log:page") public CommonResult> pageSystemAccessLog(SystemAccessLogPageDTO pageDTO) { return success(systemAccessLogManager.pageSystemAccessLog(pageDTO)); } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemExceptionLogController.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemExceptionLogController.java index 6c8d5d1ca..cbd43b828 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemExceptionLogController.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/SystemExceptionLogController.java @@ -3,18 +3,18 @@ package cn.iocoder.mall.managementweb.controller.systemlog; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.PageResult; import cn.iocoder.mall.managementweb.controller.systemlog.dto.SystemExceptionLogPageDTO; +import cn.iocoder.mall.managementweb.controller.systemlog.dto.SystemExceptionLogProcessDTO; import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemExceptionLogDetailVO; import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemExceptionLogVO; import cn.iocoder.mall.managementweb.manager.systemlog.SystemExceptionLogManager; +import cn.iocoder.mall.security.admin.core.context.AdminSecurityContextHolder; +import cn.iocoder.security.annotations.RequiresPermissions; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import static cn.iocoder.common.framework.vo.CommonResult.success; @@ -33,14 +33,24 @@ public class SystemExceptionLogController { @GetMapping("/get") @ApiOperation("获得系统异常日志明细") @ApiImplicitParam(name = "logId", value = "系统异常日志编号", required = true) + @RequiresPermissions("system:system-exception-log:page") public CommonResult getSystemExceptionLogDetail(@RequestParam("logId") Integer logId) { return success(systemExceptionLogManager.getSystemExceptionLogDetail(logId)); } @GetMapping("/page") @ApiOperation("获得系统异常日志分页") + @RequiresPermissions("system:system-exception-log:page") public CommonResult> pageSystemExceptionLog(SystemExceptionLogPageDTO pageDTO) { return success(systemExceptionLogManager.pageSystemExceptionLog(pageDTO)); } + @PostMapping("/process") + @ApiOperation("处理系统异常日志") + @RequiresPermissions("system:system-exception-log:process") + public CommonResult processSystemExceptionLog(SystemExceptionLogProcessDTO processDTO) { + systemExceptionLogManager.processSystemExceptionLog(AdminSecurityContextHolder.getAdminId(), processDTO); + return CommonResult.success(true); + } + } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/dto/SystemExceptionLogProcessDTO.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/dto/SystemExceptionLogProcessDTO.java new file mode 100644 index 000000000..e72382573 --- /dev/null +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/dto/SystemExceptionLogProcessDTO.java @@ -0,0 +1,19 @@ +package cn.iocoder.mall.managementweb.controller.systemlog.dto; + +import cn.iocoder.common.framework.vo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@ApiModel("系统异常日志处理 DTO") +@Data +@EqualsAndHashCode(callSuper = true) +public class SystemExceptionLogProcessDTO extends PageParam { + + @ApiModelProperty(value = "系统异常日志编号", required = true, example = "1") + private Integer logId; + @ApiModelProperty(value = "处理状态", required = true, notes = "对应 SystemExceptionLogProcessStatusEnum 枚举类", example = "1") + private Integer processStatus; + +} diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemAccessLogVO.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemAccessLogVO.java index cd998a268..5e8fabf31 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemAccessLogVO.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemAccessLogVO.java @@ -8,7 +8,7 @@ import java.util.*; @Data public class SystemAccessLogVO { - @ApiModelProperty(value = "编号", required = true) + @ApiModelProperty(value = "编号", required = true, example = "1") private Integer id; @ApiModelProperty(value = "用户编号", example = "1024") private Integer userId; diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogDetailVO.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogDetailVO.java index 7a4037e28..7ae71698a 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogDetailVO.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogDetailVO.java @@ -1,4 +1,76 @@ package cn.iocoder.mall.managementweb.controller.systemlog.vo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@ApiModel("系统异常日志明细 DTO") +@Data public class SystemExceptionLogDetailVO { + + @ApiModel("管理员") + @Data + @Accessors(chain = true) + public static class Admin { + + @ApiModelProperty(value = "管理员编号", required = true, example = "1") + private Integer id; + @ApiModelProperty(value = "真实名字", required = true, example = "小王") + private String name; + + } + + @ApiModelProperty(value = "编号", required = true, example = "1") + private Integer id; + @ApiModelProperty(value = "用户编号", example = "1024") + private Integer userId; + @ApiModelProperty(value = "用户类型", example = "1") + private Integer userType; + @ApiModelProperty(value = "链路追踪编号", example = "89aca178-a370-411c-ae02-3f0d672be4ab") + private String traceId; + @ApiModelProperty(value = "应用名", required = true, example = "user-shop-application") + private String applicationName; + @ApiModelProperty(value = "访问地址", required = true, example = "/management-api/system-access-log/page") + private String uri; + @ApiModelProperty(value = "参数", required = true, example = "pageNo=1&pageSize=10") + private String queryString; + @ApiModelProperty(value = "http 方法", required = true, example = "GET") + private String method; + @ApiModelProperty(value = "userAgent", required = true, example = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0") + private String userAgent; + @ApiModelProperty(value = "ip", required = true, example = "127.0.0.1") + private String ip; + @ApiModelProperty(value = "异常发生时间", required = true) + private Date exceptionTime; + @ApiModelProperty(value = "异常名, {@link Throwable#getClass()} 的类全名", required = true) + private String exceptionName; + @ApiModelProperty(value = "异常导致的消息, {@link cn.iocoder.common.framework.util.ExceptionUtil#getMessage(Throwable)}", required = true) + private String exceptionMessage; + @ApiModelProperty(value = "异常导致的根消息, {@link cn.iocoder.common.framework.util.ExceptionUtil#getRootCauseMessage(Throwable)}", required = true) + private String exceptionRootCauseMessage; + @ApiModelProperty(value = "异常的栈轨迹, {@link cn.iocoder.common.framework.util.ExceptionUtil#getServiceException(Exception)}", required = true) + private String exceptionStackTrace; + @ApiModelProperty(value = "异常发生的类全名, {@link StackTraceElement#getClassName()}", required = true) + private String exceptionClassName; + @ApiModelProperty(value = "异常发生的类文件, {@link StackTraceElement#getFileName()}", required = true) + private String exceptionFileName; + @ApiModelProperty(value = "异常发生的方法名, {@link StackTraceElement#getMethodName()}", required = true) + private String exceptionMethodName; + @ApiModelProperty(value = "异常发生的方法所在行, {@link StackTraceElement#getLineNumber()}", required = true) + private Integer exceptionLineNumber; + @ApiModelProperty(value = "处理状态", required = true, notes = "对应 SystemExceptionLogProcessStatusEnum 枚举类", example = "1") + private Integer processStatus; + @ApiModelProperty(value = "处理时间") + private Date processTime; + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + /** + * 处理的管理员信息 + */ + private Admin processAdmin; + } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogVO.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogVO.java index 5c05eca52..49a6a3097 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogVO.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/systemlog/vo/SystemExceptionLogVO.java @@ -8,13 +8,13 @@ import java.util.*; @Data public class SystemExceptionLogVO { - @ApiModelProperty(value = "编号", required = true) + @ApiModelProperty(value = "编号", required = true, example = "1") private Integer id; - @ApiModelProperty(value = "用户编号") + @ApiModelProperty(value = "用户编号", example = "1024") private Integer userId; - @ApiModelProperty(value = "用户类型") + @ApiModelProperty(value = "用户类型", example = "1") private Integer userType; - @ApiModelProperty(value = "链路追踪编", required = true) + @ApiModelProperty(value = "链路追踪编号", example = "89aca178-a370-411c-ae02-3f0d672be4ab") private String traceId; @ApiModelProperty(value = "应用名", required = true, example = "user-shop-application") private String applicationName; diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/systemlog/SystemExceptionLogConvert.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/systemlog/SystemExceptionLogConvert.java index e52d96e83..1076e4481 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/systemlog/SystemExceptionLogConvert.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/systemlog/SystemExceptionLogConvert.java @@ -1,8 +1,11 @@ package cn.iocoder.mall.managementweb.convert.systemlog; import cn.iocoder.common.framework.vo.PageResult; +import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemExceptionLogDetailVO; import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemExceptionLogVO; +import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO; import cn.iocoder.mall.systemservice.rpc.systemlog.dto.SystemExceptionLogPageDTO; +import cn.iocoder.mall.systemservice.rpc.systemlog.dto.SystemExceptionLogProcessDTO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -15,4 +18,10 @@ public interface SystemExceptionLogConvert { PageResult convertPage(PageResult page); + SystemExceptionLogDetailVO convert(cn.iocoder.mall.systemservice.rpc.systemlog.vo.SystemExceptionLogVO bean); + + SystemExceptionLogDetailVO.Admin convert(AdminVO bean); + + SystemExceptionLogProcessDTO convert(cn.iocoder.mall.managementweb.controller.systemlog.dto.SystemExceptionLogProcessDTO bean); + } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java index b6a47ff52..a50fde68c 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java @@ -18,10 +18,13 @@ import cn.iocoder.mall.systemservice.rpc.permission.RoleRpc; import cn.iocoder.mall.systemservice.rpc.permission.vo.RoleVO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.*; @Service +@Validated public class AdminManager { @Reference(version = "${dubbo.consumer.AdminRpc.version}", validation = "false") @@ -104,7 +107,7 @@ public class AdminManager { updateAdminResult.checkError(); } - public void updateAdminStatus(AdminUpdateStatusDTO updateStatusDTO) { + public void updateAdminStatus(@Valid AdminUpdateStatusDTO updateStatusDTO) { CommonResult updateAdminResult = adminRpc.updateAdmin(AdminConvert.INSTANCE.convert(updateStatusDTO)); updateAdminResult.checkError(); } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/systemlog/SystemExceptionLogManager.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/systemlog/SystemExceptionLogManager.java index 05f4bab66..e02d577f3 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/systemlog/SystemExceptionLogManager.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/systemlog/SystemExceptionLogManager.java @@ -3,9 +3,12 @@ package cn.iocoder.mall.managementweb.manager.systemlog; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.PageResult; import cn.iocoder.mall.managementweb.controller.systemlog.dto.SystemExceptionLogPageDTO; +import cn.iocoder.mall.managementweb.controller.systemlog.dto.SystemExceptionLogProcessDTO; import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemExceptionLogDetailVO; import cn.iocoder.mall.managementweb.controller.systemlog.vo.SystemExceptionLogVO; import cn.iocoder.mall.managementweb.convert.systemlog.SystemExceptionLogConvert; +import cn.iocoder.mall.systemservice.rpc.admin.AdminRpc; +import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO; import cn.iocoder.mall.systemservice.rpc.systemlog.SystemExceptionLogRpc; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; @@ -16,8 +19,10 @@ import org.springframework.stereotype.Service; @Service public class SystemExceptionLogManager { - @Reference(version = "$ {dubbo.consumer.SystemExceptionLogRpc.version}", validation = "false") + @Reference(version = "${dubbo.consumer.SystemExceptionLogRpc.version}", validation = "false") private SystemExceptionLogRpc systemExceptionLogRpc; + @Reference(version = "${dubbo.consumer.AdminRpc.version}", validation = "false") + private AdminRpc adminRpc; /** * 获得系统异常日志 @@ -26,11 +31,21 @@ public class SystemExceptionLogManager { * @return 系统异常日志 */ public SystemExceptionLogDetailVO getSystemExceptionLogDetail(Integer systemExceptionLogId) { + // 获得系统异常明细 CommonResult getSystemExceptionLogResult = systemExceptionLogRpc.getSystemExceptionLog(systemExceptionLogId); getSystemExceptionLogResult.checkError(); -// return SystemExceptionLogConvert.INSTANCE.convert(getSystemExceptionLogResult.getData()); - return null; + SystemExceptionLogDetailVO logDetailVO = SystemExceptionLogConvert.INSTANCE.convert(getSystemExceptionLogResult.getData()); + // 拼接处理管理员信息 + if (getSystemExceptionLogResult.getData().getProcessAdminId() != null) { + CommonResult adminVOResult = adminRpc.getAdmin(getSystemExceptionLogResult.getData().getProcessAdminId()); + adminVOResult.checkError(); + if (adminVOResult.getData() != null) { + SystemExceptionLogDetailVO.Admin admin = SystemExceptionLogConvert.INSTANCE.convert(adminVOResult.getData()); + logDetailVO.setProcessAdmin(admin); + } + } + return logDetailVO; } /** @@ -46,4 +61,16 @@ public class SystemExceptionLogManager { return SystemExceptionLogConvert.INSTANCE.convertPage(pageSystemExceptionLogResult.getData()); } + /** + * 处理系统异常日志 + * + * @param processAdminId 处理管理员编号 + * @param processDTO 处理系统异常日志 DTO + */ + public void processSystemExceptionLog(Integer processAdminId, SystemExceptionLogProcessDTO processDTO) { + CommonResult processSystemExceptionLogResult = systemExceptionLogRpc.processSystemExceptionLog( + SystemExceptionLogConvert.INSTANCE.convert(processDTO).setProcessAdminId(processAdminId)); + processSystemExceptionLogResult.checkError(); + } + } diff --git a/management-web-app/src/main/resources/application.yml b/management-web-app/src/main/resources/application.yml index 6dafe1219..dab91438b 100644 --- a/management-web-app/src/main/resources/application.yml +++ b/management-web-app/src/main/resources/application.yml @@ -11,6 +11,10 @@ spring: # Profile 的配置项 profiles: active: local + # SpringMVC 配置项 + mvc: + throw-exception-if-no-handler-found: true # 匹配不到路径时,抛出 NoHandlerFoundException 异常 + static-path-pattern: /statics/** # 静态资源的路径 # Dubbo 配置项 dubbo: diff --git a/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java index 945f25f0e..58aee6ebb 100644 --- a/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java +++ b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java @@ -15,7 +15,6 @@ public enum SystemErrorCodeEnum implements ServiceExceptionUtil.Enumerable convertPage(IPage page); SystemExceptionLogBO convert(SystemExceptionLogDO bean); diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/dal/mysql/mapper/systemlog/SystemExceptionLogMapper.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/dal/mysql/mapper/systemlog/SystemExceptionLogMapper.java index 04aa95616..dd2716d1b 100644 --- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/dal/mysql/mapper/systemlog/SystemExceptionLogMapper.java +++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/dal/mysql/mapper/systemlog/SystemExceptionLogMapper.java @@ -17,8 +17,8 @@ public interface SystemExceptionLogMapper extends BaseMapper roleIds = permissionService.listAdminRoleIds(checkDTO.getAdminId()); if (CollectionUtil.isEmpty(roleIds)) { // 如果没有角色,默认无法访问 - throw ServiceExceptionUtil.exception(PERMISSION_DENY); + throw ServiceExceptionUtil.exception(FORBIDDEN); } // 判断是否为超管。若是超管,默认有所有权限 if (roleService.hasSuperAdmin(roleIds)) { diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/permission/PermissionService.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/permission/PermissionService.java index c054bb57b..7c1f195d5 100644 --- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/permission/PermissionService.java +++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/permission/PermissionService.java @@ -21,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; +import static cn.iocoder.common.framework.enums.GlobalErrorCodeEnum.FORBIDDEN; import static cn.iocoder.mall.systemservice.enums.SystemErrorCodeEnum.*; /** @@ -150,13 +151,13 @@ public class PermissionService { // 权限验证 List roleResourceDOs = roleResourceMapper.selectListByResourceIds(permissionIds); if (CollectionUtil.isEmpty(roleResourceDOs)) { // 资源未授予任何角色,必然权限验证不通过 - throw ServiceExceptionUtil.exception(PERMISSION_DENY); + throw ServiceExceptionUtil.exception(FORBIDDEN); } Map> resourceRoleMap = CollectionUtils.convertMultiMap(roleResourceDOs, RoleResourceDO::getResourceId, RoleResourceDO::getRoleId); for (Map.Entry> entry : resourceRoleMap.entrySet()) { if (!CollectionUtil.containsAny(roleIds, entry.getValue())) { // 所以有任一不满足,就验证失败,抛出异常 - throw ServiceExceptionUtil.exception(PERMISSION_DENY); + throw ServiceExceptionUtil.exception(FORBIDDEN); } } } diff --git a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java index fef5d148c..ab4d4a238 100644 --- a/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java +++ b/user-service-project/user-service-app/src/main/java/cn/iocoder/mall/userservice/service/sms/UserSmsCodeService.java @@ -1,20 +1,21 @@ package cn.iocoder.mall.userservice.service.sms; -import cn.iocoder.common.framework.enums.SysErrorCodeEnum; import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.util.ServiceExceptionUtil; -import cn.iocoder.common.framework.util.ValidationUtil; +import cn.iocoder.common.framework.validator.Mobile; import cn.iocoder.mall.userservice.dal.mysql.dataobject.sms.UserSmsCodeDO; import cn.iocoder.mall.userservice.dal.mysql.mapper.sms.UserSmsCodeMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; import java.util.Date; import static cn.iocoder.mall.userservice.enums.UserErrorCodeEnum.*; @Service +@Validated public class UserSmsCodeService { /** @@ -46,10 +47,7 @@ public class UserSmsCodeService { * @param ip IP * @return 短信验证码 */ - public String createSmsCode(String mobile, Integer scene, String ip) { - if (!ValidationUtil.isMobile(mobile)) { - throw ServiceExceptionUtil.exception(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), "手机格式不正确"); // TODO 有点搓 - } + public String createSmsCode(@Mobile String mobile, Integer scene, String ip) { // 校验是否可以发送验证码,不用筛选场景 UserSmsCodeDO lastUserSmsCodeDO = userSmsCodeMapper.selectLastByMobile(mobile, null); if (lastUserSmsCodeDO != null) {