错误码梳理

This commit is contained in:
waner
2026-04-17 16:59:18 +08:00
parent a805480d01
commit a02c9959b7
17 changed files with 250 additions and 147 deletions
@@ -3,9 +3,13 @@ package com.cisd.tms.common.enums;
public enum ErrorCode {
BAD_REQUEST(400, "bad request"),
UNAUTHORIZED(4010, "unauthorized"),
SESSION_INVALID(4011, "session invalid"),
FORBIDDEN(4030, "forbidden"),
VALIDATE_FAILED(4001, "parameter validation failed"),
CONFLICT(4090, "conflict"),
BIZ_ERROR(5001, "business exception"),
CRYPTO_CARD_ERROR(5201, "crypto card adapter error"),
SERVICE_UNAVAILABLE(5030, "service unavailable"),
INTERNAL_ERROR(5000, "internal server error");
private final int code;
@@ -8,6 +8,8 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -20,7 +22,7 @@ public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<Void> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpServletRequest request) {
public ResponseEntity<ApiResponse<Void>> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpServletRequest request) {
String msg = ex.getBindingResult().getAllErrors().stream()
.findFirst()
.map(error -> {
@@ -30,53 +32,84 @@ public class GlobalExceptionHandler {
return error.getDefaultMessage();
})
.orElse(ErrorCode.VALIDATE_FAILED.getMessage());
return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), msg).withPath(request.getRequestURI());
return response(HttpStatus.BAD_REQUEST, ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), msg).withPath(request.getRequestURI()));
}
@ExceptionHandler(ConstraintViolationException.class)
public ApiResponse<Void> handleConstraintViolation(ConstraintViolationException ex, HttpServletRequest request) {
return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), ex.getMessage()).withPath(request.getRequestURI());
public ResponseEntity<ApiResponse<Void>> handleConstraintViolation(ConstraintViolationException ex, HttpServletRequest request) {
return response(HttpStatus.BAD_REQUEST,
ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), ex.getMessage()).withPath(request.getRequestURI()));
}
@ExceptionHandler(BizException.class)
public ApiResponse<Void> handleBizException(BizException ex, HttpServletRequest request) {
return ApiResponse.fail(ex.getCode(), ex.getMessage()).withPath(request.getRequestURI());
public ResponseEntity<ApiResponse<Void>> handleBizException(BizException ex, HttpServletRequest request) {
return response(resolveHttpStatus(ex.getCode()),
ApiResponse.fail(ex.getCode(), ex.getMessage()).withPath(request.getRequestURI()));
}
@ExceptionHandler(IllegalArgumentException.class)
public ApiResponse<Void> handleIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), ex.getMessage()).withPath(request.getRequestURI());
public ResponseEntity<ApiResponse<Void>> handleIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
return response(HttpStatus.BAD_REQUEST,
ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), ex.getMessage()).withPath(request.getRequestURI()));
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ApiResponse<Void> handleMaxUploadSizeExceeded(MaxUploadSizeExceededException ex, HttpServletRequest request) {
return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "upload file size exceeds limit")
.withPath(request.getRequestURI());
public ResponseEntity<ApiResponse<Void>> handleMaxUploadSizeExceeded(MaxUploadSizeExceededException ex, HttpServletRequest request) {
return response(HttpStatus.BAD_REQUEST, ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "upload file size exceeds limit")
.withPath(request.getRequestURI()));
}
@ExceptionHandler(MultipartException.class)
public ApiResponse<Void> handleMultipartException(MultipartException ex, HttpServletRequest request) {
return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "failed to parse multipart request")
.withPath(request.getRequestURI());
public ResponseEntity<ApiResponse<Void>> handleMultipartException(MultipartException ex, HttpServletRequest request) {
return response(HttpStatus.BAD_REQUEST, ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "failed to parse multipart request")
.withPath(request.getRequestURI()));
}
@ExceptionHandler(PcieCryptoException.class)
public ApiResponse<Void> handleCryptoCardException(PcieCryptoException ex, HttpServletRequest request) {
return ApiResponse.fail(ErrorCode.CRYPTO_CARD_ERROR.getCode(), ex.getMessage())
.withPath(request.getRequestURI());
public ResponseEntity<ApiResponse<Void>> handleCryptoCardException(PcieCryptoException ex, HttpServletRequest request) {
return response(HttpStatus.INTERNAL_SERVER_ERROR, ApiResponse.fail(ErrorCode.CRYPTO_CARD_ERROR.getCode(), ex.getMessage())
.withPath(request.getRequestURI()));
}
@ExceptionHandler(ReplayProtectionException.class)
public ApiResponse<Void> handleReplayProtectionException(ReplayProtectionException ex, HttpServletRequest request) {
public ResponseEntity<ApiResponse<Void>> handleReplayProtectionException(ReplayProtectionException ex, HttpServletRequest request) {
log.warn("Replay protection failed", ex);
return ApiResponse.fail(ErrorCode.INTERNAL_ERROR.getCode(), "replay protection unavailable")
.withPath(request.getRequestURI());
return response(HttpStatus.SERVICE_UNAVAILABLE,
ApiResponse.fail(ErrorCode.SERVICE_UNAVAILABLE.getCode(), "replay protection unavailable")
.withPath(request.getRequestURI()));
}
@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception ex, HttpServletRequest request) {
public ResponseEntity<ApiResponse<Void>> handleException(Exception ex, HttpServletRequest request) {
log.error("Unhandled exception", ex);
return ApiResponse.fail(ErrorCode.INTERNAL_ERROR.getCode(), ErrorCode.INTERNAL_ERROR.getMessage())
.withPath(request.getRequestURI());
return response(HttpStatus.INTERNAL_SERVER_ERROR,
ApiResponse.fail(ErrorCode.INTERNAL_ERROR.getCode(), ErrorCode.INTERNAL_ERROR.getMessage())
.withPath(request.getRequestURI()));
}
private ResponseEntity<ApiResponse<Void>> response(HttpStatus status, ApiResponse<Void> body) {
return ResponseEntity.status(status).body(body);
}
private HttpStatus resolveHttpStatus(int code) {
if (code == ErrorCode.BAD_REQUEST.getCode() || code == ErrorCode.VALIDATE_FAILED.getCode()) {
return HttpStatus.BAD_REQUEST;
}
if (code == ErrorCode.UNAUTHORIZED.getCode()) {
return HttpStatus.UNAUTHORIZED;
}
if (code == ErrorCode.SESSION_INVALID.getCode()) {
return HttpStatus.UNAUTHORIZED;
}
if (code == ErrorCode.FORBIDDEN.getCode()) {
return HttpStatus.FORBIDDEN;
}
if (code == ErrorCode.CONFLICT.getCode()) {
return HttpStatus.CONFLICT;
}
if (code == ErrorCode.SERVICE_UNAVAILABLE.getCode()) {
return HttpStatus.SERVICE_UNAVAILABLE;
}
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
@@ -63,7 +63,7 @@ public class InternalAuthorizationInterceptor implements HandlerInterceptor {
HttpResponseUtil.writeJson(
response,
HttpServletResponse.SC_FORBIDDEN,
ApiResponse.fail(ErrorCode.UNAUTHORIZED.getCode(), message),
ApiResponse.fail(ErrorCode.FORBIDDEN.getCode(), message),
objectMapper
);
}
@@ -461,9 +461,9 @@ public class AuthServiceImpl implements AuthService {
private AuthSessionEntity requireActiveSession(String sessionToken) {
AuthSessionEntity session = authSessionRepository.findBySessionToken(sessionToken)
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "session not found"));
.orElseThrow(() -> new BizException(ErrorCode.SESSION_INVALID.getCode(), "session not found"));
if (session.getLogoutAt() != null || session.getExpiresAt() == null || !session.getExpiresAt().isAfter(now())) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "session expired");
throw new BizException(ErrorCode.SESSION_INVALID.getCode(), "session expired");
}
return session;
}
@@ -2,6 +2,8 @@ package com.cisd.tms.modules.device.controller;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.modules.device.dto.network.*;
import com.cisd.tms.modules.device.service.NetworkConfigService;
import com.cisd.tms.modules.log.annotation.AuditedOperation;
@@ -122,7 +124,7 @@ public class NetworkConfigController {
String mode = networkConfigService.getBondMode(bondName);
if (mode == null) {
return ApiResponse.fail(400, "未找到该连接的 Bond 模式", null);
throw new BizException(ErrorCode.BAD_REQUEST.getCode(), "未找到该连接的 Bond 模式");
}
return ApiResponse.success(mode);
@@ -1,6 +1,10 @@
package com.cisd.tms.modules.device.filter;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.util.HttpResponseUtil;
import com.cisd.tms.modules.device.service.IpWhitelistService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,6 +21,9 @@ public class IpWhitelistFilter implements Filter {
@Autowired
private IpWhitelistService ipWhitelistService;
@Autowired
private ObjectMapper objectMapper;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
@@ -39,9 +46,7 @@ public class IpWhitelistFilter implements Filter {
try {
clientIpInt = ipToInt(clientIp);
} catch (IllegalArgumentException e) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\": 403, \"msg\": \"非法的客户端 IP 格式,拒绝访问\"}");
writeForbidden(response, "非法的客户端 IP 格式,拒绝访问");
return;
}
@@ -72,9 +77,7 @@ public class IpWhitelistFilter implements Filter {
// 默认拒绝
if (!isAllowed) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\": 403, \"msg\": \"非法访问:您的IP不在允许的白名单内\"}");
writeForbidden(response, "非法访问:您的IP不在允许的白名单内");
return;
}
@@ -136,4 +139,13 @@ public class IpWhitelistFilter implements Filter {
return result;
}
private void writeForbidden(HttpServletResponse response, String message) throws IOException {
HttpResponseUtil.writeJson(
response,
HttpServletResponse.SC_FORBIDDEN,
ApiResponse.fail(ErrorCode.FORBIDDEN.getCode(), message),
objectMapper
);
}
}
@@ -2,6 +2,8 @@ package com.cisd.tms.modules.log.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.modules.log.dto.AuditLogReviewRequest;
import com.cisd.tms.modules.log.dto.OperationAuditLogPageRequest;
import com.cisd.tms.modules.log.dto.OperationAuditLogPageResponse;
@@ -50,7 +52,7 @@ public class OperationAuditController {
// 权限校验:仅审计管理员可操作
if (roleObj == null || !roleObj.toString().equals("AUDIT_ADMIN")) {
return ApiResponse.fail(403, "权限不足:仅审计管理员可执行此操作");
throw new BizException(ErrorCode.FORBIDDEN.getCode(), "权限不足:仅审计管理员可执行此操作");
}
auditorUser = roleObj.toString();
@@ -59,4 +61,4 @@ public class OperationAuditController {
auditService.reviewLog(logId, req, auditorUser);
return ApiResponse.success();
}
}
}
@@ -51,17 +51,17 @@ public class InternalApiAuthInterceptor implements HandlerInterceptor {
String sessionToken = extractSessionToken(request);
if (sessionToken == null || sessionToken.isBlank()) {
writeUnauthorized(response, "session token missing");
writeSessionInvalid(response, "session token missing");
return false;
}
AuthSessionEntity session = authSessionRepository.findBySessionToken(sessionToken).orElse(null);
if (session == null || session.getLogoutAt() != null) {
writeUnauthorized(response, "session not found");
writeSessionInvalid(response, "session not found");
return false;
}
if (session.getExpiresAt() == null || !session.getExpiresAt().isAfter(now())) {
writeUnauthorized(response, "session expired");
writeSessionInvalid(response, "session expired");
return false;
}
@@ -99,4 +99,13 @@ public class InternalApiAuthInterceptor implements HandlerInterceptor {
objectMapper
);
}
private void writeSessionInvalid(HttpServletResponse response, String message) throws IOException {
HttpResponseUtil.writeJson(
response,
HttpServletResponse.SC_UNAUTHORIZED,
ApiResponse.fail(ErrorCode.SESSION_INVALID.getCode(), message),
objectMapper
);
}
}
@@ -69,11 +69,11 @@ public class InternalApiReplayInterceptor implements HandlerInterceptor {
try {
ReplayCheckResult result = replayProtectionService.check(replayRequest);
if (result.isReplayed()) {
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.UNAUTHORIZED.getCode(), "replayed nonce");
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.CONFLICT.getCode(), "replayed nonce");
return false;
}
} catch (ReplayProtectionException ex) {
writeJson(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE, ErrorCode.INTERNAL_ERROR.getCode(),
writeJson(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE, ErrorCode.SERVICE_UNAVAILABLE.getCode(),
"replay protection unavailable");
return false;
}
@@ -89,11 +89,12 @@ public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
// 先完成签名校验,再占用 nonce,避免伪造请求提前消耗合法 nonce。
ReplayCheckResult result = replayProtectionService.check(buildReplayRequest(request, appId, nonce, requestEpoch));
if (result.isReplayed()) {
writeUnauthorized(response, "replayed nonce");
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.CONFLICT.getCode(), "replayed nonce");
return false;
}
} catch (ReplayProtectionException ex) {
writeUnauthorized(response, "replay protection unavailable");
writeJson(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE,
ErrorCode.SERVICE_UNAVAILABLE.getCode(), "replay protection unavailable");
return false;
}
@@ -136,10 +137,14 @@ public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
}
private void writeUnauthorized(HttpServletResponse response, String message) throws IOException {
writeJson(response, HttpServletResponse.SC_UNAUTHORIZED, ErrorCode.UNAUTHORIZED.getCode(), message);
}
private void writeJson(HttpServletResponse response, int status, int code, String message) throws IOException {
HttpResponseUtil.writeJson(
response,
HttpServletResponse.SC_UNAUTHORIZED,
ApiResponse.fail(ErrorCode.UNAUTHORIZED.getCode(), message),
status,
ApiResponse.fail(code, message),
objectMapper
);
}