完善验证码登录
Sync All Branches to GitHub / sync (push) Successful in 2s

This commit is contained in:
2026-01-20 19:01:43 +08:00
parent 31f5a31b9b
commit b87abe0e3e
24 changed files with 249 additions and 110 deletions
@@ -0,0 +1,48 @@
package top.crushtj.xiaoyishu.auth.cache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
/**
* @author ayi
* @version V1.0
* @title CacheLoader
* @date 2026/01/20 18:25
* @description 初始化缓存
*/
@Slf4j
@Component
public class CacheLoader {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private UserMapper userMapper;
/**
* 加载用户自增ID缓存
*/
@PostConstruct
public void loadUserCache() {
log.info("加载用户自增ID缓存开始...");
LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(UserEntity::getXiaoyishuId);
queryWrapper.last("limit 1");
UserEntity user = userMapper.selectOne(queryWrapper);
if (user != null){
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, user.getXiaoyishuId());
}else {
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, 1000000L);
}
log.info("加载用户自增ID缓存结束...");
}
}
@@ -25,6 +25,11 @@ public class RedisKeyConstants {
*/
private static final String USER_ROLES_KEY_PREFIX = "user:roles:";
/**
* 小哈书全局 ID 生成器 KEY
*/
public static final String XIAOYI_ID_GENERATOR_KEY = "xiaoyishu_id_generator";
/**
* 构建验证码 KEY
*
@@ -38,11 +43,11 @@ public class RedisKeyConstants {
/**
* 构建用户角色数据 KEY
*
* @param userId 用户 ID
* @param phone 用户手机号
* @return 用户角色数据 KEY
*/
public static String buildUserRolesKey(Long userId) {
return USER_ROLES_KEY_PREFIX + userId;
public static String buildUserRolesKey(String phone) {
return USER_ROLES_KEY_PREFIX + phone;
}
}
@@ -0,0 +1,13 @@
package top.crushtj.xiaoyishu.auth.constant;
/**
* @author ayi
* @version V1.0
* @title XiaoyiAuthConstants
* @date 2026/01/19 19:40
* @description 小一书常量
*/
public class XiaoyiAuthConstants {
public static final String NICK_NAME_PREFIX = "咿呀_";
}
@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
* @author ayi
* @version V1.0
* @title PermissionEntity
* @date 2026-01-18 21:20:36
* @date 2026-01-19 19:47:27
* @description 权限表(t_permission)表实体类
*/
@@ -27,7 +27,7 @@ import java.time.LocalDateTime;
@TableName("t_permission")
public class PermissionEntity implements Serializable {
@Serial
private static final long serialVersionUID = -18586083527537804L;
private static final long serialVersionUID = -30618877661010662L;
/**
* 主键ID
@@ -99,6 +99,6 @@ public class PermissionEntity implements Serializable {
* 逻辑删除(0:未删除 1:已删除)
*/
@TableField("IS_DELETED")
private Integer isDeleted;
private Boolean isDeleted;
}
@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
* @author ayi
* @version V1.0
* @title RoleEntity
* @date 2026-01-18 21:20:51
* @date 2026-01-19 19:48:23
* @description 角色表(t_role)表实体类
*/
@@ -27,7 +27,7 @@ import java.time.LocalDateTime;
@TableName("t_role")
public class RoleEntity implements Serializable {
@Serial
private static final long serialVersionUID = 294965117543247928L;
private static final long serialVersionUID = -77681371692201000L;
/**
* 主键ID
@@ -81,6 +81,6 @@ public class RoleEntity implements Serializable {
* 逻辑删除(0:未删除 1:已删除)
*/
@TableField("IS_DELETED")
private Integer isDeleted;
private Boolean isDeleted;
}
@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
* @author ayi
* @version V1.0
* @title RolePermissionRelEntity
* @date 2026-01-18 21:21:12
* @date 2026-01-19 19:48:31
* @description 用户权限表(t_role_permission_rel)表实体类
*/
@@ -27,7 +27,7 @@ import java.time.LocalDateTime;
@TableName("t_role_permission_rel")
public class RolePermissionRelEntity implements Serializable {
@Serial
private static final long serialVersionUID = 308157477991435934L;
private static final long serialVersionUID = 345004944667469434L;
/**
* 主键ID
@@ -63,6 +63,6 @@ public class RolePermissionRelEntity implements Serializable {
* 逻辑删除(0:未删除 1:已删除)
*/
@TableField("IS_DELETED")
private Integer isDeleted;
private Boolean isDeleted;
}
@@ -17,8 +17,8 @@ import java.time.LocalDateTime;
* @author ayi
* @version V1.0
* @title UserEntity
* @date 2026/01/18 19:33:00
* @description 用户表
* @date 2026-01-19 19:49:50
* @description 用户表(t_user)表实体类
*/
@Data
@@ -28,7 +28,7 @@ import java.time.LocalDateTime;
@TableName("t_user")
public class UserEntity implements Serializable {
@Serial
private static final long serialVersionUID = -31680834394879938L;
private static final long serialVersionUID = -47473970233354078L;
/**
* 主键ID
@@ -112,6 +112,6 @@ public class UserEntity implements Serializable {
* 逻辑删除(0:未删除 1:已删除)
*/
@TableField("IS_DELETED")
private Integer isDeleted;
private Boolean isDeleted;
}
@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
* @author ayi
* @version V1.0
* @title UserRoleRelEntity
* @date 2026-01-18 21:21:25
* @date 2026-01-19 19:49:59
* @description 用户角色表(t_user_role_rel)表实体类
*/
@@ -27,7 +27,7 @@ import java.time.LocalDateTime;
@TableName("t_user_role_rel")
public class UserRoleRelEntity implements Serializable {
@Serial
private static final long serialVersionUID = 788758889499348222L;
private static final long serialVersionUID = -77498437697772085L;
/**
* 主键ID
@@ -63,6 +63,6 @@ public class UserRoleRelEntity implements Serializable {
* 逻辑删除(0:未删除 1:已删除)
*/
@TableField("IS_DELETED")
private Integer isDeleted;
private Boolean isDeleted;
}
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @author ayi
* @version V1.0
* @title PermissionMapper
* @date 2026-01-18 21:20:37
* @date 2026-01-19 19:47:28
* @description 权限表(t_permission)表数据库访问层
*/
public interface PermissionMapper extends BaseMapper<PermissionEntity> {
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @author ayi
* @version V1.0
* @title RoleMapper
* @date 2026-01-18 21:20:51
* @date 2026-01-19 19:48:24
* @description 角色表(t_role)表数据库访问层
*/
public interface RoleMapper extends BaseMapper<RoleEntity> {
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @author ayi
* @version V1.0
* @title RolePermissionRelMapper
* @date 2026-01-18 21:21:12
* @date 2026-01-19 19:48:32
* @description 用户权限表(t_role_permission_rel)表数据库访问层
*/
public interface RolePermissionRelMapper extends BaseMapper<RolePermissionRelEntity> {
@@ -7,12 +7,17 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author ayi
* @version V1.0
* @title User
* @date 2026/01/18 19:33:00
* @title UserMapper
* @date 2026-01-19 19:49:51
* @description 用户表(t_user)表数据库访问层
*/
public interface UserMapper extends BaseMapper<UserEntity> {
/**
* 根据手机号查询用户
* @param phone 手机号
* @return 用户信息
*/
UserEntity selectByPhone(@Param("phone") String phone);
}
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @author ayi
* @version V1.0
* @title UserRoleRelMapper
* @date 2026-01-18 21:21:25
* @date 2026-01-19 19:50:00
* @description 用户角色表(t_user_role_rel)表数据库访问层
*/
public interface UserRoleRelMapper extends BaseMapper<UserRoleRelEntity> {
@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "top.crushtj.xiaoyishu.auth.domain.mappers.PermissionMapper">
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.PermissionMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity" id = "PermissionMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "parentId" column = "parent_id" jdbcType = "INTEGER"/>
<result property = "name" column = "name" jdbcType = "VARCHAR"/>
<result property = "type" column = "type" jdbcType = "INTEGER"/>
<result property = "menuUrl" column = "menu_url" jdbcType = "VARCHAR"/>
<result property = "menuIcon" column = "menu_icon" jdbcType = "VARCHAR"/>
<result property = "sort" column = "sort" jdbcType = "INTEGER"/>
<result property = "permissionKey" column = "permission_key" jdbcType = "VARCHAR"/>
<result property = "status" column = "status" jdbcType = "INTEGER"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity" id="PermissionMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="parentId" column="parent_id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="type" column="type" jdbcType="INTEGER"/>
<result property="menuUrl" column="menu_url" jdbcType="VARCHAR"/>
<result property="menuIcon" column="menu_icon" jdbcType="VARCHAR"/>
<result property="sort" column="sort" jdbcType="INTEGER"/>
<result property="permissionKey" column="permission_key" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="INTEGER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
</resultMap>
</mapper>
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "top.crushtj.xiaoyishu.auth.domain.mappers.RoleMapper">
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.RoleMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity" id = "RoleMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "roleName" column = "role_name" jdbcType = "VARCHAR"/>
<result property = "roleKey" column = "role_key" jdbcType = "VARCHAR"/>
<result property = "status" column = "status" jdbcType = "INTEGER"/>
<result property = "sort" column = "sort" jdbcType = "INTEGER"/>
<result property = "remark" column = "remark" jdbcType = "VARCHAR"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity" id="RoleMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="roleName" column="role_name" jdbcType="VARCHAR"/>
<result property="roleKey" column="role_key" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="INTEGER"/>
<result property="sort" column="sort" jdbcType="INTEGER"/>
<result property="remark" column="remark" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
</resultMap>
</mapper>
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "top.crushtj.xiaoyishu.auth.domain.mappers.RolePermissionRelMapper">
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.RolePermissionRelMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity" id = "RolePermissionRelMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "roleId" column = "role_id" jdbcType = "INTEGER"/>
<result property = "permissionId" column = "permission_id" jdbcType = "INTEGER"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity" id="RolePermissionRelMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
<result property="permissionId" column="permission_id" jdbcType="INTEGER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
</resultMap>
</mapper>
@@ -1,25 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper">
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.UserEntity" id = "UserMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "xiaoyishuId" column = "xiaoyishu_id" jdbcType = "VARCHAR"/>
<result property = "password" column = "password" jdbcType = "VARCHAR"/>
<result property = "nickname" column = "nickname" jdbcType = "VARCHAR"/>
<result property = "avatar" column = "avatar" jdbcType = "VARCHAR"/>
<result property = "birthday" column = "birthday" jdbcType = "TIMESTAMP"/>
<result property = "backgroundImg" column = "background_img" jdbcType = "VARCHAR"/>
<result property = "phone" column = "phone" jdbcType = "VARCHAR"/>
<result property = "sex" column = "sex" jdbcType = "INTEGER"/>
<result property = "status" column = "status" jdbcType = "INTEGER"/>
<result property = "introduction" column = "introduction" jdbcType = "VARCHAR"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.UserEntity" id="UserMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="xiaoyishuId" column="xiaoyishu_id" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="nickname" column="nickname" jdbcType="VARCHAR"/>
<result property="avatar" column="avatar" jdbcType="VARCHAR"/>
<result property="birthday" column="birthday" jdbcType="TIMESTAMP"/>
<result property="backgroundImg" column="background_img" jdbcType="VARCHAR"/>
<result property="phone" column="phone" jdbcType="VARCHAR"/>
<result property="sex" column="sex" jdbcType="INTEGER"/>
<result property="status" column="status" jdbcType="INTEGER"/>
<result property="introduction" column="introduction" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
</resultMap>
<select id = "selectByPhone" resultType = "top.crushtj.xiaoyishu.auth.domain.entity.UserEntity">
SELECT id, password
SELECT id, password, xiaoyishu_id
FROM t_user
WHERE phone = #{phone}
</select>
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper">
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper">
<resultMap type = "top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity" id = "UserRoleRelMap">
<result property = "id" column = "id" jdbcType = "INTEGER"/>
<result property = "userId" column = "user_id" jdbcType = "INTEGER"/>
<result property = "roleId" column = "role_id" jdbcType = "INTEGER"/>
<result property = "createTime" column = "create_time" jdbcType = "TIMESTAMP"/>
<result property = "updateTime" column = "update_time" jdbcType = "TIMESTAMP"/>
<result property = "isDeleted" column = "is_deleted" jdbcType = "INTEGER"/>
</resultMap>
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity" id="UserRoleRelMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="userId" column="user_id" jdbcType="INTEGER"/>
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
</resultMap>
</mapper>
@@ -1,24 +1,40 @@
package top.crushtj.xiaoyishu.auth.service.impl;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.crushtj.framework.common.enums.DeleteEnum;
import top.crushtj.framework.common.enums.StatusEnum;
import top.crushtj.framework.common.exception.BizException;
import top.crushtj.framework.common.response.Response;
import top.crushtj.framework.common.utils.IdGenerator;
import top.crushtj.framework.common.utils.JsonUtils;
import top.crushtj.framework.common.utils.MaskUtils;
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
import top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity;
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
import top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper;
import top.crushtj.xiaoyishu.auth.enums.LoginTypeEnum;
import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
import top.crushtj.xiaoyishu.auth.service.UserService;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
import static top.crushtj.xiaoyishu.auth.constant.RoleConstants.COMMON_USER_ROLE_ID;
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
/**
* @author ayi
* @version V1.0
@@ -34,6 +50,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
private UserMapper userMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private UserRoleRelMapper userRoleRelMapper;
@Override
public Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO) {
@@ -58,7 +76,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
// 构建验证码 Redis Key
String key = RedisKeyConstants.buildVerificationCodeKey(phone);
// 查询存储在 Redis 中该用户的登录验证码
String sentCode = (String)redisTemplate.opsForValue().get(key);
String sentCode = (String)redisTemplate.opsForValue()
.get(key);
// 判断用户提交的验证码,与 Redis 中的验证码是否一致
if (!StringUtils.equals(verificationCode, sentCode)) {
@@ -68,12 +87,14 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
// 通过手机号查询记录
UserEntity userEntity = userMapper.selectByPhone(phone);
log.info("==> 用户是否注册, phone: {}, userId: {}", MaskUtils.maskMobile(phone), userEntity.getId());
log.info("==> 用户是否注册, phone: {}, userId: {}", MaskUtils.maskMobile(phone),
userEntity == null ? "未注册" : userEntity.getXiaoyishuId());
// 判断是否注册
if (Objects.isNull(userEntity)) {
// 若此用户还没有注册,系统自动注册该用户
// todo 自动注册用户逻辑
userId = registerUser(phone);
} else {
// 已注册,则获取其用户 ID
@@ -81,7 +102,49 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
}
}
}
return Response.success("");
StpUtil.login(userId);
// 获取 Token 令牌
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
// 返回 Token 令牌
return Response.success(tokenInfo.tokenValue);
}
@Transactional(rollbackFor = Exception.class)
public Long registerUser(String phone) {
long userId = IdGenerator.getInstance()
.nextId();
Long xiaoyishuId = redisTemplate.opsForValue()
.increment(XIAOYI_ID_GENERATOR_KEY);
UserEntity userEntity = UserEntity.builder()
.id(userId)
.phone(phone)
.xiaoyishuId(String.valueOf(xiaoyishuId))
.nickname(NICK_NAME_PREFIX + xiaoyishuId)
.status(StatusEnum.ENABLED.getValue())
.createTime(LocalDateTime.now())
.updateTime(LocalDateTime.now())
.isDeleted(DeleteEnum.NO.getValue())
.build();
save(userEntity);
UserRoleRelEntity userRoleRel = UserRoleRelEntity.builder()
.id(IdGenerator.getInstance()
.nextId())
.userId(userId)
.roleId(COMMON_USER_ROLE_ID)
.createTime(LocalDateTime.now())
.updateTime(LocalDateTime.now())
.isDeleted(DeleteEnum.NO.getValue())
.build();
userRoleRelMapper.insert(userRoleRel);
List<Long> roles = new ArrayList<>();
roles.add(COMMON_USER_ROLE_ID);
String userRolesKey = RedisKeyConstants.buildUserRolesKey(phone);
redisTemplate.opsForValue()
.set(userRolesKey, JsonUtils.toJsonString(roles));
return userId;
}
}
@@ -36,7 +36,7 @@ public class VerificationCodeServiceImpl implements VerificationCodeService {
private RedisTemplate<String, Object> redisTemplate;
@Resource(name = "taskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
private ThreadPoolTaskExecutor taskExecutor;
@Resource
private AliyunSmsHelper aliyunSmsHelper;
@@ -56,6 +56,7 @@ public class VerificationCodeServiceImpl implements VerificationCodeService {
// 3. 生成6位随机验证码
String verificationCode = RandomUtil.randomNumbers(6);
//=============== 开发环境不实际调用短信发送接口 ===============
// 4. 异步发送短信(用CompletableFuture跟踪任务状态,捕获异常)
CompletableFuture<Boolean> smsSendFuture = CompletableFuture.supplyAsync(() -> {
// 设置线程名称,便于日志排查
@@ -69,9 +70,9 @@ public class VerificationCodeServiceImpl implements VerificationCodeService {
log.error("==> 手机号: {}, 短信发送接口调用异常", MaskUtils.maskMobile(phoneNumber), e);
return false;
}
}, threadPoolTaskExecutor);
}, taskExecutor);
// 5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
//5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
boolean smsSendSuccess;
try {
smsSendSuccess = smsSendFuture.get(SMS_SEND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -92,11 +93,13 @@ public class VerificationCodeServiceImpl implements VerificationCodeService {
log.error("==> 手机号: {}, 发送验证码失败(第三方接口返回失败)", MaskUtils.maskMobile(phoneNumber));
throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
}
//=============== 开发环境不实际调用短信发送接口 ===============
// 7. 短信发送成功后,记录日志(验证码脱敏,仅保留后2位)+ 存储Redis
log.info("==> 手机号: {}, 已发送验证码:【****{}】", MaskUtils.maskMobile(phoneNumber), verificationCode.substring(4));
redisTemplate.opsForValue().set(key, verificationCode, VERIFICATION_CODE_EXPIRE_TIME, TimeUnit.MINUTES);
return Response.success();
return Response.success(verificationCode);
//return Response.success();
}
}
@@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.TestPropertySource;
/**
* @author ayi
@@ -16,9 +17,10 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Slf4j
@SpringBootTest
@TestPropertySource(properties = {"jasypt.encryptor.password=GhaU7VjZd2b3M4Hbx4SelEXZc"})
public class ThreadPoolTaskExecutorTests {
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Resource(name = "taskExecutor")
private ThreadPoolTaskExecutor taskExecutor;
/**
* 测试线程池
@@ -28,7 +30,7 @@ public class ThreadPoolTaskExecutorTests {
int count = 300;
while (count-- > 0) {
int finalCount = count;
threadPoolTaskExecutor.submit(() -> log.info("异步线程: 这是{}号线程", finalCount));
taskExecutor.submit(() -> log.info("异步线程: 这是{}号线程", finalCount));
}
}
}
@@ -17,5 +17,5 @@ public enum DeleteEnum {
YES(true),
NO(false);
private final Boolean value;
public final Boolean value;
}
@@ -17,5 +17,5 @@ public enum StatusEnum {
ENABLED(1),
DISABLED(0);
private final Integer value;
public final Integer value;
}
@@ -4,7 +4,7 @@ import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
public class SnowflakeIdGenerator {
public class IdGenerator {
// ====================== 配置参数 ======================
/** 开始时间戳 (2024-01-01 00:00:00),可自定义,减少ID长度 */
private static final long START_TIMESTAMP = 1767196800000L;
@@ -36,7 +36,7 @@ public class SnowflakeIdGenerator {
private long lastTimestamp = -1L;
// ====================== 单例实例 ======================
private static volatile SnowflakeIdGenerator INSTANCE;
private static volatile IdGenerator INSTANCE;
/**
* 私有构造器初始化机器ID自动计算也可手动指定
@@ -44,7 +44,7 @@ public class SnowflakeIdGenerator {
* @param workerId 机器ID (0-1023)
* @throws IllegalArgumentException 机器ID超出范围时抛出
*/
private SnowflakeIdGenerator(long workerId) {
private IdGenerator(long workerId) {
// 校验机器ID范围
long maxWorkerId = ~(-1L << WORKER_ID_BITS);
if (workerId < 0 || workerId > maxWorkerId) {
@@ -58,11 +58,11 @@ public class SnowflakeIdGenerator {
*
* @return 雪花算法生成器实例
*/
public static SnowflakeIdGenerator getInstance() {
public static IdGenerator getInstance() {
if (INSTANCE == null) {
synchronized (SnowflakeIdGenerator.class) {
synchronized (IdGenerator.class) {
if (INSTANCE == null) {
INSTANCE = new SnowflakeIdGenerator(calculateWorkerId());
INSTANCE = new IdGenerator(calculateWorkerId());
}
}
}
@@ -75,11 +75,11 @@ public class SnowflakeIdGenerator {
* @param workerId 机器ID (0-1023)
* @return 雪花算法生成器实例
*/
public static SnowflakeIdGenerator getInstance(long workerId) {
public static IdGenerator getInstance(long workerId) {
if (INSTANCE == null) {
synchronized (SnowflakeIdGenerator.class) {
synchronized (IdGenerator.class) {
if (INSTANCE == null) {
INSTANCE = new SnowflakeIdGenerator(workerId);
INSTANCE = new IdGenerator(workerId);
}
}
}
@@ -161,7 +161,7 @@ public class SnowflakeIdGenerator {
// ====================== 测试用例 ======================
public static void main(String[] args) {
// 测试生成10个ID验证有序性和唯一性
SnowflakeIdGenerator generator = SnowflakeIdGenerator.getInstance();
IdGenerator generator = IdGenerator.getInstance();
for (int i = 0; i < 10; i++) {
long id = generator.nextId();
System.out.println("生成的ID" + id);