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