登录接口完善

This commit is contained in:
dww
2026-02-05 15:47:56 +08:00
parent 3808449d4d
commit 4bc4ca0809
7 changed files with 152 additions and 89 deletions

View File

@@ -0,0 +1,16 @@
package com.bicloud.common.enums;
/**
* 登录类型枚举
*/
public enum LoginType {
/**
* 密码登录
*/
PASSWORD,
/**
* 短信验证码登录
*/
SMS_CODE
}

View File

@@ -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("系统异常,请联系管理员");
// }
//}

View File

@@ -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"
);
}
}

View File

@@ -54,8 +54,7 @@ public class UserController {
@PostMapping("/login")
@Operation(summary = "用户登录")
public Result<LoginVo> login(@Valid @RequestBody LoginDto loginDto) {
LoginVo loginVo = userService.login(loginDto);
return Result.success(loginVo);
return userService.login(loginDto);
}
/**

View File

@@ -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;
}

View File

@@ -16,7 +16,7 @@ public interface UserService {
/**
* 用户登录
*/
LoginVo login(LoginDto loginDto);
Result<LoginVo> login(LoginDto loginDto);
/**
* 用户登出

View File

@@ -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<LoginVo> login(LoginDto loginDto) {
String identifier = loginDto.getIdentifier();
LoginType loginType = loginDto.getLoginType();
// 根据登录类型执行不同的登录逻辑
Result<User> 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<User> 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<User> 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