diff --git a/src/main/java/com/bicloud/common/enums/LoginType.java b/src/main/java/com/bicloud/common/enums/LoginType.java new file mode 100644 index 0000000..88a756e --- /dev/null +++ b/src/main/java/com/bicloud/common/enums/LoginType.java @@ -0,0 +1,16 @@ +package com.bicloud.common.enums; + +/** + * 登录类型枚举 + */ +public enum LoginType { + /** + * 密码登录 + */ + PASSWORD, + + /** + * 短信验证码登录 + */ + SMS_CODE +} diff --git a/src/main/java/com/bicloud/common/exception/GlobalExceptionHandler.java b/src/main/java/com/bicloud/common/exception/GlobalExceptionHandler.java deleted file mode 100644 index 31ec136..0000000 --- a/src/main/java/com/bicloud/common/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -//package com.bicloud.common.exception; -// -//import com.bicloud.common.result.Result; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.validation.BindException; -//import org.springframework.validation.FieldError; -//import org.springframework.web.bind.MethodArgumentNotValidException; -//import org.springframework.web.bind.annotation.ExceptionHandler; -//import org.springframework.web.bind.annotation.RestControllerAdvice; -// -///** -// * 全局异常处理器 -// * 统一处理各类异常,返回友好的错误提示 -// */ -//@Slf4j -//@RestControllerAdvice(basePackages = "com.bicloud.controller") -//public class GlobalExceptionHandler { -// -// /** -// * 处理业务异常 -// */ -// @ExceptionHandler(BusinessException.class) -// public Result handleBusinessException(BusinessException e) { -// log.warn("业务异常:{}", e.getMessage()); -// return Result.error(e.getMessage()); -// } -// -// /** -// * 处理 @Valid 参数校验异常(用于 @RequestBody) -// * 当 RegisterDto 等 DTO 的字段验证失败时触发 -// */ -// @ExceptionHandler(MethodArgumentNotValidException.class) -// public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { -// // 获取第一个错误信息 -// String errorMessage = e.getBindingResult().getFieldErrors() -// .stream() -// .map(FieldError::getDefaultMessage) -// .findFirst() -// .orElse("参数校验失败"); -// -// log.warn("参数校验失败:{}", errorMessage); -// return Result.error(errorMessage); -// } -// -// /** -// * 处理 @Validated 参数校验异常(用于 @RequestParam, @PathVariable) -// */ -// @ExceptionHandler(BindException.class) -// public Result handleBindException(BindException e) { -// String errorMessage = e.getFieldErrors() -// .stream() -// .map(FieldError::getDefaultMessage) -// .findFirst() -// .orElse("参数校验失败"); -// -// log.warn("参数绑定失败:{}", errorMessage); -// return Result.error(errorMessage); -// } -// -// /** -// * 处理其他未捕获的异常 -// */ -// @ExceptionHandler(Exception.class) -// public Result handleException(Exception e) { -// log.error("系统异常", e); -// return Result.error("系统异常,请联系管理员"); -// } -//} diff --git a/src/main/java/com/bicloud/config/WebMvcConfig.java b/src/main/java/com/bicloud/config/WebMvcConfig.java index f625790..abd90c6 100644 --- a/src/main/java/com/bicloud/config/WebMvcConfig.java +++ b/src/main/java/com/bicloud/config/WebMvcConfig.java @@ -27,10 +27,19 @@ public class WebMvcConfig implements WebMvcConfigurer { "/v3/api-docs/**", "/webjars/**", "/favicon.ico", + // 静态资源路径放行(新增) + "/img/**", + "/video/**", // 用户相关放行路径 "/user/code", "/user/register", - "/user/login" + "/user/login", + "/course/cats", + "/course/page", + "/document/page", + "/document/types" + + ); } } diff --git a/src/main/java/com/bicloud/controller/UserController.java b/src/main/java/com/bicloud/controller/UserController.java index 6e66446..8f1508d 100644 --- a/src/main/java/com/bicloud/controller/UserController.java +++ b/src/main/java/com/bicloud/controller/UserController.java @@ -54,8 +54,7 @@ public class UserController { @PostMapping("/login") @Operation(summary = "用户登录") public Result login(@Valid @RequestBody LoginDto loginDto) { - LoginVo loginVo = userService.login(loginDto); - return Result.success(loginVo); + return userService.login(loginDto); } /** diff --git a/src/main/java/com/bicloud/pojo/dto/LoginDto.java b/src/main/java/com/bicloud/pojo/dto/LoginDto.java index c969810..75abb6a 100644 --- a/src/main/java/com/bicloud/pojo/dto/LoginDto.java +++ b/src/main/java/com/bicloud/pojo/dto/LoginDto.java @@ -1,7 +1,9 @@ package com.bicloud.pojo.dto; +import com.bicloud.common.enums.LoginType; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; /** @@ -15,7 +17,13 @@ public class LoginDto { @NotBlank(message = "登录标识不能为空") private String identifier; - @Schema(description = "密码", example = "Password123") - @NotBlank(message = "密码不能为空") + @Schema(description = "密码(密码登录时必填)", example = "Password123") private String password; + + @Schema(description = "验证码(验证码登录时必填)", example = "123123") + private String code; + + @Schema(description = "登录类型:PASSWORD-密码登录,SMS_CODE-短信验证码登录", example = "PASSWORD") + @NotNull(message = "登录类型不能为空") + private LoginType loginType; } diff --git a/src/main/java/com/bicloud/service/UserService.java b/src/main/java/com/bicloud/service/UserService.java index 5fc3c42..943dfd9 100644 --- a/src/main/java/com/bicloud/service/UserService.java +++ b/src/main/java/com/bicloud/service/UserService.java @@ -16,7 +16,7 @@ public interface UserService { /** * 用户登录 */ - LoginVo login(LoginDto loginDto); + Result login(LoginDto loginDto); /** * 用户登出 diff --git a/src/main/java/com/bicloud/service/impl/UserServiceImpl.java b/src/main/java/com/bicloud/service/impl/UserServiceImpl.java index f5a5ad4..cb76d0a 100644 --- a/src/main/java/com/bicloud/service/impl/UserServiceImpl.java +++ b/src/main/java/com/bicloud/service/impl/UserServiceImpl.java @@ -1,6 +1,7 @@ package com.bicloud.service.impl; import com.bicloud.common.context.UserContext; +import com.bicloud.common.enums.LoginType; import com.bicloud.common.exception.BusinessException; import com.bicloud.common.result.Result; import com.bicloud.common.utils.JwtUtils; @@ -20,6 +21,7 @@ import jakarta.servlet.http.HttpSession; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.util.concurrent.TimeUnit; @@ -161,11 +163,57 @@ public class UserServiceImpl implements UserService { } @Override - public LoginVo login(LoginDto loginDto) { + public Result login(LoginDto loginDto) { + String identifier = loginDto.getIdentifier(); + LoginType loginType = loginDto.getLoginType(); + + // 根据登录类型执行不同的登录逻辑 + Result userResult = null; + + if (loginType == LoginType.PASSWORD) { + // 密码登录 + userResult = loginByPassword(loginDto); + } else if (loginType == LoginType.SMS_CODE) { + // 验证码登录 + userResult = loginBySmsCode(loginDto); + } else { + return Result.error("不支持的登录类型!"); + } + + // 检查登录结果(code != 200 表示失败) + if (userResult.getCode() != 200) { + return Result.error(userResult.getMsg()); + } + + User user = userResult.getData(); + + // 生成Token + String token = jwtUtils.generateToken(user.getId(), user.getUsername()); + + // 构建返回结果 + LoginVo loginVo = LoginVo.builder() + .token(token) + .expiresIn(jwtProperties.getTokenExpiration()) + .userId(user.getId()) + .username(user.getUsername()) + .build(); + + return Result.success(loginVo); + } + + /** + * 密码登录 + */ + private Result loginByPassword(LoginDto loginDto) { String identifier = loginDto.getIdentifier(); String password = loginDto.getPassword(); - // 1. 根据登录标识类型查询用户 + // 1. 校验密码不能为空 + if (!StringUtils.hasText(password)) { + return Result.error("密码不能为空!"); + } + + // 2. 根据登录标识类型查询用户 User user = null; if (RegexUtils.isPhoneValid(identifier)) { // 手机号登录 @@ -178,26 +226,77 @@ public class UserServiceImpl implements UserService { user = userMapper.selectByUsername(identifier); } - // 2. 验证用户是否存在 + // 3. 验证用户是否存在 if (user == null) { - throw new BusinessException("用户不存在!"); + return Result.error("用户不存在!"); } - // 3. 验证密码 + // 4. 验证密码 if (!PasswordUtils.verifyPassword(password, user.getSalt(), user.getPwd())) { - throw new BusinessException("密码错误!"); + return Result.error("密码错误!"); } - // 4. 生成Token - String token = jwtUtils.generateToken(user.getId(), user.getUsername()); + return Result.success(user); + } - // 5. 构建返回结果 - return LoginVo.builder() - .token(token) - .expiresIn(jwtProperties.getTokenExpiration()) - .userId(user.getId()) - .username(user.getUsername()) - .build(); + /** + * 短信验证码登录 + */ + private Result loginBySmsCode(LoginDto loginDto) { + String identifier = loginDto.getIdentifier(); + String code = loginDto.getCode(); + + // 1. 校验验证码不能为空 + if (!StringUtils.hasText(code)) { + return Result.error("验证码不能为空!"); + } + + // 2. 验证码登录只支持手机号 + if (!RegexUtils.isPhoneValid(identifier)) { + return Result.error("验证码登录仅支持手机号!"); + } + + // 3. 从Redis获取验证码 + String cacheCode = null; + try { + cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + identifier); + } catch (Exception e) { + log.error("从Redis获取验证码失败,手机号:{},错误信息:{}", identifier, e.getMessage()); + return Result.error("验证码获取失败,请稍后重试!"); + } + + // 4. 验证码不存在或已过期 + if (cacheCode == null) { + return Result.error("验证码已过期,请重新获取!"); + } + + // 5. 验证验证码是否正确 + if (!cacheCode.equals(code)) { + return Result.error("验证码错误!"); + } + + // 6. 根据手机号查询用户 + User user = null; + try { + user = userMapper.selectByMobile(identifier); + } catch (Exception e) { + log.error("查询用户失败,手机号:{},错误信息:{}", identifier, e.getMessage()); + return Result.error("查询用户失败,请稍后重试!"); + } + + if (user == null) { + return Result.error("该手机号未注册!"); + } + + // 7. 验证码验证成功后删除Redis中的验证码(防止重复使用) + try { + stringRedisTemplate.delete(LOGIN_CODE_KEY + identifier); + } catch (Exception e) { + log.error("删除Redis验证码失败,手机号:{},错误信息:{}", identifier, e.getMessage()); + // 删除失败不影响登录,只记录日志 + } + + return Result.success(user); } @Override