接口定义
地址:POST /user/login
入参:
{
"phone": "18011119108", // 手机号
"code": "218603", // 登录验证码,验证码登录时,需要填写
"password": "xx", // 密码登录时,需要填写
"type": 1 // 登录类型,1表示手机号验证码登录;2表示账号密码登录
}
出参:
{
"success": true,
"message": null,
"errorCode": null,
"data": "xxxxx" // 登录成功后,返回 Token 令牌
}
登录请求VO:
public class UserLoginPhoneVOReq {
@NotBlank(message = "手机号不能为空")
@PhoneNumber
private String phone;
private String verificationCode;
private String password;
@NotNull(message = "登录类型不能为空")
private Integer type;
}
登录类型也创建一个枚举记录:
@Getter
@AllArgsConstructor
public enum LoginTypeEnum {
// 验证码 1
VERIFICATION_CODE(1),
// 密码 2
PASSWORD(2);
private final Integer value;
public static LoginTypeEnum getEnumByValue(Integer code) {
for (LoginTypeEnum loginTypeEnum : LoginTypeEnum.values()) {
if (Objects.equals(code, loginTypeEnum.getValue())) {
return loginTypeEnum;
}
}
return null;
}
}
实现
验证码部分之前已经实现过了,所以用户登录时 redis 中已经有了一个 phonNumber:123456
的单值对,直接拿过来比较即可。
UserService
中声明:
Response<String> loginAndRegister(UserLoginPhoneVOReq userLoginPhoneVOReq);
UserServiceImpl
中写逻辑:
@ApiOperationLog(description = "登录与注册")
public Response<String> loginAndRegister(UserLoginPhoneVOReq userLoginPhoneVOReq) {
String phone = userLoginPhoneVOReq.getPhone();
LoginTypeEnum loginTypeEnum = LoginTypeEnum.getEnumByValue(userLoginPhoneVOReq.getType());
switch (loginTypeEnum) {
case VERIFICATION_CODE:
String code = userLoginPhoneVOReq.getVerificationCode();
if(code == null)
return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(), "验证码不能为空,VerificationCode");
// 查之前生成的 redis 验证码
RBucket<String> correctCode = redissonClient.getBucket(RedisKeyConstants.buildVerificationCodeKey(phone));
if (correctCode == null || correctCode.get() == null) {
return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(), "验证码已过期");
}
log.info("correctCode: {}, code: {}", correctCode.get(), code);
if (!code.equals(correctCode.get())) {
return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(), "验证码错误");
}
// 通过手机号查该用户的类
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone, phone);
User user = this.baseMapper.selectOne(queryWrapper);
if (user == null) {
// 注册 TODO
userId = this.userRegisterByPhone(phone);
}else {
userId = user.getId();
}
break;
case PASSWORD:
// TODO
break;
default:
break;
}
// Sa-Token 登录,并返回 Token 令牌存储到 Redis
StpUtil.login(userId);
return Response.success(StpUtil.getTokenValue());
}
其中注册部分实现:
@Resource
private ITUserRoleRelService userRoleRelService;
/**
* 系统自动注册用户
* @param phone
* @return
*/
@Transactional(rollbackFor = Exception.class)
public Long userRegisterByPhone(String phone) {
// 获得全局自增的用户编号
Long ojId = redissonClient.getAtomicLong(RedisKeyConstants.ONLINEJUDGE_ID_GENERATOR).incrementAndGet();
// 新建用户实例
User user = User.builder()
.userAccount(phone)
.userPassword(DigestUtils.md5DigestAsHex((SALT + phone).getBytes()))
.userName("用户" + ojId)
.phone(phone)
.userState(UserStateEnum.LOGOUT.getValue())
.userRole("common_user")
.ojId(ojId)
.build();
// 插入到数据库
this.save(user);
// 获取刚刚插入的用户的数据库id
Long userId = this.baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOjId, ojId)).getId();
// 分配角色
TUserRoleRel userRoleRel = TUserRoleRel.builder()
.userId(userId)
.roleId(RoleConstants.COMMON_USER_ROLE_ID)
.createTime(LocalDateTime.now())
.updateTime(LocalDateTime.now())
.isDeleted(DeletedEnum.NOT_DELETED.getValue())
.build();
userRoleRelService.save(userRoleRel);
// 角色信息存入 redis
List<Long> roles = Lists.newArrayList(RoleConstants.COMMON_USER_ROLE_ID);
String redisRolesKey = RedisKeyConstants.buildUserRoleKey(ojId);
redissonClient.getBucket(redisRolesKey).set(JsonUtil.toJsonString(roles));
return userId;
}
里面的 ONLINEJUDGE_ID_GENERATOR 是 202502231839 Redis 获得全局自增的编号。
数据库操作参考 202502231746 Mybatis-plus。
其中事务处理不能简单使用 @Transactional(rollbackFor = Exception.class)
会出现不完全回滚的问题,解决方法:202502232036 SpringBoot 声明式注解事务失效