+3
-2
@@ -10,6 +10,7 @@ import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
|||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
|
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
|
||||||
|
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||||
|
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
@@ -39,9 +40,9 @@ public class CacheLoader {
|
|||||||
queryWrapper.last("limit 1");
|
queryWrapper.last("limit 1");
|
||||||
UserEntity user = userMapper.selectOne(queryWrapper);
|
UserEntity user = userMapper.selectOne(queryWrapper);
|
||||||
if (user != null){
|
if (user != null){
|
||||||
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, user.getXiaoyishuId());
|
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, Long.valueOf(user.getXiaoyishuId()));
|
||||||
}else {
|
}else {
|
||||||
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, 1000000L);
|
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, XIAOYI_ID_INITIAL_VALUE);
|
||||||
}
|
}
|
||||||
log.info("加载用户自增ID缓存结束...");
|
log.info("加载用户自增ID缓存结束...");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ package top.crushtj.xiaoyishu.auth.constant;
|
|||||||
|
|
||||||
public class XiaoyiAuthConstants {
|
public class XiaoyiAuthConstants {
|
||||||
public static final String NICK_NAME_PREFIX = "咿呀_";
|
public static final String NICK_NAME_PREFIX = "咿呀_";
|
||||||
|
public static final Long XIAOYI_ID_INITIAL_VALUE = 1000000L;
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-34
@@ -8,7 +8,7 @@ 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 org.springframework.transaction.support.TransactionTemplate;
|
||||||
import top.crushtj.framework.common.enums.DeleteEnum;
|
import top.crushtj.framework.common.enums.DeleteEnum;
|
||||||
import top.crushtj.framework.common.enums.StatusEnum;
|
import top.crushtj.framework.common.enums.StatusEnum;
|
||||||
import top.crushtj.framework.common.exception.BizException;
|
import top.crushtj.framework.common.exception.BizException;
|
||||||
@@ -34,6 +34,7 @@ import java.util.Objects;
|
|||||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
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.RoleConstants.COMMON_USER_ROLE_ID;
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
|
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
|
||||||
|
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
@@ -53,6 +54,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
@Resource
|
@Resource
|
||||||
private UserRoleRelMapper userRoleRelMapper;
|
private UserRoleRelMapper userRoleRelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO) {
|
public Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO) {
|
||||||
String phone = userLoginReqVO.getPhone();
|
String phone = userLoginReqVO.getPhone();
|
||||||
@@ -111,40 +115,61 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
return Response.success(tokenInfo.tokenValue);
|
return Response.success(tokenInfo.tokenValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
private Long registerUser(String phone) {
|
||||||
public Long registerUser(String phone) {
|
return transactionTemplate.execute(status -> {
|
||||||
long userId = IdGenerator.getInstance()
|
try {
|
||||||
.nextId();
|
long userId = IdGenerator.getInstance()
|
||||||
Long xiaoyishuId = redisTemplate.opsForValue()
|
.nextId();
|
||||||
.increment(XIAOYI_ID_GENERATOR_KEY);
|
// 小壹书用户ID,非数据库主键ID
|
||||||
UserEntity userEntity = UserEntity.builder()
|
Long xiaoyishuId = redisTemplate.opsForValue()
|
||||||
.id(userId)
|
.increment(XIAOYI_ID_GENERATOR_KEY);
|
||||||
.phone(phone)
|
UserEntity userEntity = UserEntity.builder()
|
||||||
.xiaoyishuId(String.valueOf(xiaoyishuId))
|
.id(userId)
|
||||||
.nickname(NICK_NAME_PREFIX + xiaoyishuId)
|
.phone(phone)
|
||||||
.status(StatusEnum.ENABLED.getValue())
|
.xiaoyishuId(String.valueOf(xiaoyishuId))
|
||||||
.createTime(LocalDateTime.now())
|
.nickname(NICK_NAME_PREFIX + xiaoyishuId) // 自动注册的用户昵称默认为"咿呀_" + 用户ID
|
||||||
.updateTime(LocalDateTime.now())
|
.status(StatusEnum.ENABLED.getValue())
|
||||||
.isDeleted(DeleteEnum.NO.getValue())
|
.createTime(LocalDateTime.now())
|
||||||
.build();
|
.updateTime(LocalDateTime.now())
|
||||||
save(userEntity);
|
.isDeleted(DeleteEnum.NO.getValue())
|
||||||
|
.build();
|
||||||
|
save(userEntity);
|
||||||
|
|
||||||
UserRoleRelEntity userRoleRel = UserRoleRelEntity.builder()
|
UserRoleRelEntity userRoleRel = UserRoleRelEntity.builder()
|
||||||
.id(IdGenerator.getInstance()
|
.id(IdGenerator.getInstance()
|
||||||
.nextId())
|
.nextId())
|
||||||
.userId(userId)
|
.userId(userId)
|
||||||
.roleId(COMMON_USER_ROLE_ID)
|
.roleId(COMMON_USER_ROLE_ID) // 自动注册的用户角色为普通用户
|
||||||
.createTime(LocalDateTime.now())
|
.createTime(LocalDateTime.now())
|
||||||
.updateTime(LocalDateTime.now())
|
.updateTime(LocalDateTime.now())
|
||||||
.isDeleted(DeleteEnum.NO.getValue())
|
.isDeleted(DeleteEnum.NO.getValue())
|
||||||
.build();
|
.build();
|
||||||
userRoleRelMapper.insert(userRoleRel);
|
int i = 1 / 0;
|
||||||
List<Long> roles = new ArrayList<>();
|
userRoleRelMapper.insert(userRoleRel);
|
||||||
roles.add(COMMON_USER_ROLE_ID);
|
|
||||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(phone);
|
// 将用户角色信息存储到 Redis 中
|
||||||
redisTemplate.opsForValue()
|
List<Long> roles = new ArrayList<>();
|
||||||
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
roles.add(COMMON_USER_ROLE_ID);
|
||||||
return userId;
|
String userRolesKey = RedisKeyConstants.buildUserRolesKey(phone);
|
||||||
|
redisTemplate.opsForValue()
|
||||||
|
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||||
|
return userId;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 回滚事务
|
||||||
|
log.error("==> 用户注册异常,开始回滚事务: ", e);
|
||||||
|
status.setRollbackOnly();
|
||||||
|
// 回滚 redis 自增ID
|
||||||
|
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
||||||
|
.get(XIAOYI_ID_GENERATOR_KEY))
|
||||||
|
.toString().trim());
|
||||||
|
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
||||||
|
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
||||||
|
redisTemplate.opsForValue()
|
||||||
|
.decrement(XIAOYI_ID_GENERATOR_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-38
@@ -15,10 +15,7 @@ import top.crushtj.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCode
|
|||||||
import top.crushtj.xiaoyishu.auth.service.VerificationCodeService;
|
import top.crushtj.xiaoyishu.auth.service.VerificationCodeService;
|
||||||
import top.crushtj.xiaoyishu.auth.sms.AliyunSmsHelper;
|
import top.crushtj.xiaoyishu.auth.sms.AliyunSmsHelper;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.VERIFICATION_CODE_EXPIRE_TIME;
|
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.VERIFICATION_CODE_EXPIRE_TIME;
|
||||||
|
|
||||||
@@ -58,41 +55,41 @@ public class VerificationCodeServiceImpl implements VerificationCodeService {
|
|||||||
|
|
||||||
//=============== 开发环境不实际调用短信发送接口 ===============
|
//=============== 开发环境不实际调用短信发送接口 ===============
|
||||||
// 4. 异步发送短信(用CompletableFuture跟踪任务状态,捕获异常)
|
// 4. 异步发送短信(用CompletableFuture跟踪任务状态,捕获异常)
|
||||||
CompletableFuture<Boolean> smsSendFuture = CompletableFuture.supplyAsync(() -> {
|
//CompletableFuture<Boolean> smsSendFuture = CompletableFuture.supplyAsync(() -> {
|
||||||
// 设置线程名称,便于日志排查
|
// // 设置线程名称,便于日志排查
|
||||||
Thread.currentThread().setName("sms-send-" + MaskUtils.maskMobile(phoneNumber));
|
// Thread.currentThread().setName("sms-send-" + MaskUtils.maskMobile(phoneNumber));
|
||||||
String signName = "速通互联验证码";
|
// String signName = "速通互联验证码";
|
||||||
String templateCode = "100001";
|
// String templateCode = "100001";
|
||||||
String templateParam = String.format("{\"code\":\"%s\",\"min\":\"%d\"}", verificationCode, VERIFICATION_CODE_EXPIRE_TIME);
|
// String templateParam = String.format("{\"code\":\"%s\",\"min\":\"%d\"}", verificationCode, VERIFICATION_CODE_EXPIRE_TIME);
|
||||||
try {
|
// try {
|
||||||
return aliyunSmsHelper.sendMessage(signName, templateCode, phoneNumber, templateParam);
|
// return aliyunSmsHelper.sendMessage(signName, templateCode, phoneNumber, templateParam);
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
log.error("==> 手机号: {}, 短信发送接口调用异常", MaskUtils.maskMobile(phoneNumber), e);
|
// log.error("==> 手机号: {}, 短信发送接口调用异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}, taskExecutor);
|
//}, taskExecutor);
|
||||||
|
//
|
||||||
//5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
|
// //5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
|
||||||
boolean smsSendSuccess;
|
//boolean smsSendSuccess;
|
||||||
try {
|
//try {
|
||||||
smsSendSuccess = smsSendFuture.get(SMS_SEND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
// smsSendSuccess = smsSendFuture.get(SMS_SEND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException e) {
|
//} catch (InterruptedException e) {
|
||||||
log.error("==> 手机号: {}, 短信发送任务被中断", MaskUtils.maskMobile(phoneNumber), e);
|
// log.error("==> 手机号: {}, 短信发送任务被中断", MaskUtils.maskMobile(phoneNumber), e);
|
||||||
Thread.currentThread().interrupt(); // 恢复中断状态
|
// Thread.currentThread().interrupt(); // 恢复中断状态
|
||||||
throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||||
} catch (ExecutionException e) {
|
//} catch (ExecutionException e) {
|
||||||
log.error("==> 手机号: {}, 短信发送任务执行异常", MaskUtils.maskMobile(phoneNumber), e);
|
// log.error("==> 手机号: {}, 短信发送任务执行异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||||
throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||||
} catch (TimeoutException e) {
|
//} catch (TimeoutException e) {
|
||||||
log.error("==> 手机号: {}, 短信发送任务超时({}秒)", MaskUtils.maskMobile(phoneNumber), SMS_SEND_TIMEOUT_SECONDS, e);
|
// log.error("==> 手机号: {}, 短信发送任务超时({}秒)", MaskUtils.maskMobile(phoneNumber), SMS_SEND_TIMEOUT_SECONDS, e);
|
||||||
throw new BizException(ResponseCodeEnum.SMS_SEND_TIMEOUT);
|
// throw new BizException(ResponseCodeEnum.SMS_SEND_TIMEOUT);
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 6. 短信发送失败则直接抛异常,不存储Redis
|
//// 6. 短信发送失败则直接抛异常,不存储Redis
|
||||||
if (!smsSendSuccess) {
|
//if (!smsSendSuccess) {
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user