diff --git a/module-auth/src/main/java/com/ether/pms/auth/controller/AuthController.java b/module-auth/src/main/java/com/ether/pms/auth/controller/AuthController.java index 95636f6..65f92bc 100644 --- a/module-auth/src/main/java/com/ether/pms/auth/controller/AuthController.java +++ b/module-auth/src/main/java/com/ether/pms/auth/controller/AuthController.java @@ -2,7 +2,9 @@ package com.ether.pms.auth.controller; import com.ether.pms.auth.service.LoginService; import com.ether.pms.auth.util.JwtTokenProvider; +import com.ether.pms.common.ApiResponse; import jakarta.servlet.http.HttpServletRequest; +import lombok.Data; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -19,56 +21,55 @@ public class AuthController { private final JwtTokenProvider jwtTokenProvider; @PostMapping("/login") - public ResponseEntity> login( - @RequestParam String username, - @RequestParam String password, - HttpServletRequest request) { + public ResponseEntity>> login( + @RequestBody LoginRequest request, + HttpServletRequest httpRequest) { - String ip = getClientIp(request); - Map result = loginService.login(username, password, ip); - return ResponseEntity.ok(result); + String ip = getClientIp(httpRequest); + Map result = loginService.login(request.getUsername(), request.getPassword(), ip); + return ResponseEntity.ok(ApiResponse.success(result)); } @PostMapping("/logout") - public ResponseEntity logout(@RequestHeader(value = "Authorization", required = false) String token) { - return ResponseEntity.ok().build(); + public ResponseEntity> logout(@RequestHeader(value = "Authorization", required = false) String token) { + return ResponseEntity.ok(ApiResponse.success()); } @GetMapping("/me") - public ResponseEntity> getCurrentUser( + public ResponseEntity>> getCurrentUser( @RequestHeader(value = "Authorization", required = false) String token) { if (token == null || !token.startsWith("Bearer ")) { - return ResponseEntity.status(401).build(); + return ResponseEntity.ok(ApiResponse.error(401, "未授权")); } String jwt = token.substring(7); if (!jwtTokenProvider.validateToken(jwt)) { - return ResponseEntity.status(401).build(); + return ResponseEntity.ok(ApiResponse.error(401, "Token无效")); } String username = jwtTokenProvider.getUsernameFromToken(jwt); Map result = new HashMap<>(); result.put("username", username); - return ResponseEntity.ok(result); + return ResponseEntity.ok(ApiResponse.success(result)); } @PostMapping("/refresh") - public ResponseEntity> refreshToken( + public ResponseEntity>> refreshToken( @RequestHeader(value = "Authorization", required = false) String token) { if (token == null || !token.startsWith("Bearer ")) { - return ResponseEntity.status(401).build(); + return ResponseEntity.ok(ApiResponse.error(401, "未授权")); } String jwt = token.substring(7); if (!jwtTokenProvider.validateToken(jwt)) { - return ResponseEntity.status(401).build(); + return ResponseEntity.ok(ApiResponse.error(401, "Token无效")); } if (jwtTokenProvider.isTokenExpired(jwt)) { - return ResponseEntity.status(401).build(); + return ResponseEntity.ok(ApiResponse.error(401, "Token已过期")); } String username = jwtTokenProvider.getUsernameFromToken(jwt); @@ -78,7 +79,7 @@ public class AuthController { Map result = new HashMap<>(); result.put("token", newToken); - return ResponseEntity.ok(result); + return ResponseEntity.ok(ApiResponse.success(result)); } private String getClientIp(HttpServletRequest request) { @@ -91,4 +92,10 @@ public class AuthController { } return ip; } + + @Data + public static class LoginRequest { + private String username; + private String password; + } } diff --git a/module-auth/src/main/java/com/ether/pms/auth/service/LoginService.java b/module-auth/src/main/java/com/ether/pms/auth/service/LoginService.java index d807d0c..6bf1140 100644 --- a/module-auth/src/main/java/com/ether/pms/auth/service/LoginService.java +++ b/module-auth/src/main/java/com/ether/pms/auth/service/LoginService.java @@ -3,6 +3,8 @@ package com.ether.pms.auth.service; import com.ether.pms.auth.entity.User; import com.ether.pms.auth.repository.UserRepository; import com.ether.pms.auth.util.JwtTokenProvider; +import com.ether.pms.common.BusinessException; +import com.ether.pms.common.ErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -18,34 +20,25 @@ public class LoginService { private final PasswordService passwordService; private final LoginAttemptService loginAttemptService; - private static final int MAX_ATTEMPTS = 5; - public Map login(String username, String password, String ip) { if (loginAttemptService.isLockedOut(username)) { - int remaining = loginAttemptService.getRemainingAttempts(username); - throw new RuntimeException("账号已被锁定,请" + 10 + "分钟后再试"); + throw new BusinessException(ErrorCode.AUTH_002); } - User user = userRepository.findByUsername(username) - .orElseThrow(() -> { + User user = userRepository.findByUsername(username).orElse(null); + + if (user == null || !passwordService.matches(password, user.getPassword())) { + if (user != null) { loginAttemptService.recordFailure(username); - return new RuntimeException("用户名或密码错误"); - }); + } + throw new BusinessException(ErrorCode.AUTH_001); + } if (user.getStatus() == User.UserStatus.LOCKED) { - throw new RuntimeException("账号已被锁定"); + throw new BusinessException(ErrorCode.AUTH_002); } if (user.getStatus() == User.UserStatus.DISABLED) { - throw new RuntimeException("账号已被禁用"); - } - - if (!passwordService.matches(password, user.getPassword())) { - loginAttemptService.recordFailure(username); - int remaining = loginAttemptService.getRemainingAttempts(username); - if (remaining <= 0) { - throw new RuntimeException("账号已被锁定,请10分钟后再试"); - } - throw new RuntimeException("用户名或密码错误,剩余尝试次数: " + remaining); + throw new BusinessException(ErrorCode.AUTH_003); } loginAttemptService.recordSuccess(username); diff --git a/module-auth/src/main/java/com/ether/pms/auth/service/UserService.java b/module-auth/src/main/java/com/ether/pms/auth/service/UserService.java index ff24b65..764e9f3 100644 --- a/module-auth/src/main/java/com/ether/pms/auth/service/UserService.java +++ b/module-auth/src/main/java/com/ether/pms/auth/service/UserService.java @@ -4,6 +4,8 @@ import com.ether.pms.auth.entity.User; import com.ether.pms.auth.entity.Role; import com.ether.pms.auth.repository.UserRepository; import com.ether.pms.auth.repository.RoleRepository; +import com.ether.pms.common.BusinessException; +import com.ether.pms.common.ErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,27 +28,31 @@ public class UserService { public User findById(UUID id) { return userRepository.findById(id) - .orElseThrow(() -> new RuntimeException("用户不存在")); + .orElseThrow(() -> new BusinessException(ErrorCode.USER_003)); } public User findByUsername(String username) { return userRepository.findByUsername(username) - .orElseThrow(() -> new RuntimeException("用户不存在")); + .orElseThrow(() -> new BusinessException(ErrorCode.USER_003)); } @Transactional public User create(User user) { if (userRepository.existsByUsername(user.getUsername())) { - throw new RuntimeException("用户名已存在"); + throw new BusinessException(ErrorCode.USER_001); } if (user.getPhone() != null && userRepository.existsByPhone(user.getPhone())) { - throw new RuntimeException("手机号已存在"); + throw new BusinessException(ErrorCode.USER_002); } - passwordService.validatePassword(user.getPassword()); + try { + passwordService.validatePassword(user.getPassword()); + } catch (IllegalArgumentException e) { + throw new BusinessException(ErrorCode.BAD_REQUEST, e.getMessage()); + } if (passwordService.isPasswordWeak(user.getPassword())) { - throw new RuntimeException("密码太弱,请使用更复杂的密码"); + throw new BusinessException(ErrorCode.BAD_REQUEST, "密码太弱,请使用更复杂的密码"); } user.setPassword(passwordService.encode(user.getPassword())); @@ -63,7 +69,7 @@ public class UserService { } if (user.getPhone() != null) { if (!user.getPhone().equals(existing.getPhone()) && userRepository.existsByPhone(user.getPhone())) { - throw new RuntimeException("手机号已存在"); + throw new BusinessException(ErrorCode.USER_002); } existing.setPhone(user.getPhone()); } @@ -85,13 +91,17 @@ public class UserService { User user = findById(id); if (!passwordService.matches(oldPassword, user.getPassword())) { - throw new RuntimeException("原密码错误"); + throw new BusinessException(ErrorCode.USER_004); } - passwordService.validatePassword(newPassword); + try { + passwordService.validatePassword(newPassword); + } catch (IllegalArgumentException e) { + throw new BusinessException(ErrorCode.BAD_REQUEST, e.getMessage()); + } if (passwordService.isPasswordWeak(newPassword)) { - throw new RuntimeException("新密码太弱,请使用更复杂的密码"); + throw new BusinessException(ErrorCode.BAD_REQUEST, "新密码太弱,请使用更复杂的密码"); } user.setPassword(passwordService.encode(newPassword)); @@ -102,10 +112,14 @@ public class UserService { public void resetPassword(UUID id, String newPassword) { User user = findById(id); - passwordService.validatePassword(newPassword); + try { + passwordService.validatePassword(newPassword); + } catch (IllegalArgumentException e) { + throw new BusinessException(ErrorCode.BAD_REQUEST, e.getMessage()); + } if (passwordService.isPasswordWeak(newPassword)) { - throw new RuntimeException("密码太弱,请使用更复杂的密码"); + throw new BusinessException(ErrorCode.BAD_REQUEST, "密码太弱,请使用更复杂的密码"); } user.setPassword(passwordService.encode(newPassword)); diff --git a/module-common/src/main/java/com/ether/pms/common/ApiResponse.java b/module-common/src/main/java/com/ether/pms/common/ApiResponse.java new file mode 100644 index 0000000..5229493 --- /dev/null +++ b/module-common/src/main/java/com/ether/pms/common/ApiResponse.java @@ -0,0 +1,45 @@ +package com.ether.pms.common; + +import lombok.Data; + +@Data +public class ApiResponse { + + private int code; + private String message; + private T data; + + public ApiResponse() { + } + + public ApiResponse(int code, String message) { + this.code = code; + this.message = message; + } + + public ApiResponse(int code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static ApiResponse success() { + return new ApiResponse<>(200, "success"); + } + + public static ApiResponse success(T data) { + return new ApiResponse<>(200, "success", data); + } + + public static ApiResponse success(String message, T data) { + return new ApiResponse<>(200, message, data); + } + + public static ApiResponse error(int code, String message) { + return new ApiResponse<>(code, message); + } + + public static ApiResponse error(String message) { + return new ApiResponse<>(500, message); + } +} diff --git a/module-common/src/main/java/com/ether/pms/common/BusinessException.java b/module-common/src/main/java/com/ether/pms/common/BusinessException.java new file mode 100644 index 0000000..804dc7d --- /dev/null +++ b/module-common/src/main/java/com/ether/pms/common/BusinessException.java @@ -0,0 +1,28 @@ +package com.ether.pms.common; + +import lombok.Getter; + +@Getter +public class BusinessException extends RuntimeException { + + private final int code; + private final String message; + + public BusinessException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.code = errorCode.getCode(); + this.message = errorCode.getMessage(); + } + + public BusinessException(ErrorCode errorCode, String customMessage) { + super(customMessage); + this.code = errorCode.getCode(); + this.message = customMessage; + } + + public BusinessException(int code, String message) { + super(message); + this.code = code; + this.message = message; + } +} diff --git a/module-common/src/main/java/com/ether/pms/common/ErrorCode.java b/module-common/src/main/java/com/ether/pms/common/ErrorCode.java new file mode 100644 index 0000000..de5d673 --- /dev/null +++ b/module-common/src/main/java/com/ether/pms/common/ErrorCode.java @@ -0,0 +1,48 @@ +package com.ether.pms.common; + +public enum ErrorCode { + + SUCCESS(200, "success"), + + BAD_REQUEST(400, "请求参数错误"), + UNAUTHORIZED(401, "未授权"), + FORBIDDEN(403, "禁止访问"), + NOT_FOUND(404, "资源不存在"), + + AUTH_001(1001, "用户名或密码错误"), + AUTH_002(1002, "账号已被锁定"), + AUTH_003(1003, "账号已被禁用"), + AUTH_004(1004, "Token已过期"), + AUTH_005(1005, "Token无效"), + + USER_001(2001, "用户名已存在"), + USER_002(2002, "手机号已存在"), + USER_003(2003, "用户不存在"), + USER_004(2004, "原密码错误"), + + ROLE_001(3001, "角色编码已存在"), + ROLE_002(3002, "角色不存在"), + + PERMISSION_001(4001, "权限编码已存在"), + + PROJECT_001(5001, "项目编码已存在"), + PROJECT_002(5002, "项目不存在"), + + SYSTEM_ERROR(9999, "系统错误"); + + private final int code; + private final String message; + + ErrorCode(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/module-common/src/main/java/com/ether/pms/common/GlobalExceptionHandler.java b/module-common/src/main/java/com/ether/pms/common/GlobalExceptionHandler.java new file mode 100644 index 0000000..f78f07d --- /dev/null +++ b/module-common/src/main/java/com/ether/pms/common/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package com.ether.pms.common; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + @ExceptionHandler(BusinessException.class) + public ResponseEntity> handleBusinessException(BusinessException e) { + log.warn("业务异常: code={}, message={}", e.getCode(), e.getMessage()); + return ResponseEntity + .status(HttpStatus.OK) + .body(ApiResponse.error(e.getCode(), e.getMessage())); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgumentException(IllegalArgumentException e) { + log.warn("参数异常: {}", e.getMessage()); + return ResponseEntity + .status(HttpStatus.OK) + .body(ApiResponse.error(400, e.getMessage())); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception e) { + log.error("系统异常", e); + return ResponseEntity + .status(HttpStatus.OK) + .body(ApiResponse.error(ErrorCode.SYSTEM_ERROR.getCode(), "系统错误,请稍后重试")); + } +}