+3
-2
@@ -10,6 +10,7 @@ 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;
|
||||
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
@@ -39,9 +40,9 @@ public class CacheLoader {
|
||||
queryWrapper.last("limit 1");
|
||||
UserEntity user = userMapper.selectOne(queryWrapper);
|
||||
if (user != null){
|
||||
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, user.getXiaoyishuId());
|
||||
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, Long.valueOf(user.getXiaoyishuId()));
|
||||
}else {
|
||||
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, 1000000L);
|
||||
redisTemplate.opsForValue().set(XIAOYI_ID_GENERATOR_KEY, XIAOYI_ID_INITIAL_VALUE);
|
||||
}
|
||||
log.info("加载用户自增ID缓存结束...");
|
||||
}
|
||||
|
||||
@@ -10,4 +10,5 @@ package top.crushtj.xiaoyishu.auth.constant;
|
||||
|
||||
public class XiaoyiAuthConstants {
|
||||
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.springframework.data.redis.core.RedisTemplate;
|
||||
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.StatusEnum;
|
||||
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.RoleConstants.COMMON_USER_ROLE_ID;
|
||||
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
|
||||
@@ -53,6 +54,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
@Resource
|
||||
private UserRoleRelMapper userRoleRelMapper;
|
||||
|
||||
@Resource
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
@Override
|
||||
public Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO) {
|
||||
String phone = userLoginReqVO.getPhone();
|
||||
@@ -111,40 +115,61 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
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);
|
||||
private Long registerUser(String phone) {
|
||||
return transactionTemplate.execute(status -> {
|
||||
try {
|
||||
long userId = IdGenerator.getInstance()
|
||||
.nextId();
|
||||
// 小壹书用户ID,非数据库主键ID
|
||||
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) // 自动注册的用户昵称默认为"咿呀_" + 用户ID
|
||||
.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;
|
||||
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();
|
||||
int i = 1 / 0;
|
||||
userRoleRelMapper.insert(userRoleRel);
|
||||
|
||||
// 将用户角色信息存储到 Redis 中
|
||||
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;
|
||||
} 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.sms.AliyunSmsHelper;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.VERIFICATION_CODE_EXPIRE_TIME;
|
||||
|
||||
@@ -58,41 +55,41 @@ public class VerificationCodeServiceImpl implements VerificationCodeService {
|
||||
|
||||
//=============== 开发环境不实际调用短信发送接口 ===============
|
||||
// 4. 异步发送短信(用CompletableFuture跟踪任务状态,捕获异常)
|
||||
CompletableFuture<Boolean> smsSendFuture = CompletableFuture.supplyAsync(() -> {
|
||||
// 设置线程名称,便于日志排查
|
||||
Thread.currentThread().setName("sms-send-" + MaskUtils.maskMobile(phoneNumber));
|
||||
String signName = "速通互联验证码";
|
||||
String templateCode = "100001";
|
||||
String templateParam = String.format("{\"code\":\"%s\",\"min\":\"%d\"}", verificationCode, VERIFICATION_CODE_EXPIRE_TIME);
|
||||
try {
|
||||
return aliyunSmsHelper.sendMessage(signName, templateCode, phoneNumber, templateParam);
|
||||
} catch (Exception e) {
|
||||
log.error("==> 手机号: {}, 短信发送接口调用异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||
return false;
|
||||
}
|
||||
}, taskExecutor);
|
||||
|
||||
//5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
|
||||
boolean smsSendSuccess;
|
||||
try {
|
||||
smsSendSuccess = smsSendFuture.get(SMS_SEND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("==> 手机号: {}, 短信发送任务被中断", MaskUtils.maskMobile(phoneNumber), e);
|
||||
Thread.currentThread().interrupt(); // 恢复中断状态
|
||||
throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
} catch (ExecutionException e) {
|
||||
log.error("==> 手机号: {}, 短信发送任务执行异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||
throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
} catch (TimeoutException e) {
|
||||
log.error("==> 手机号: {}, 短信发送任务超时({}秒)", MaskUtils.maskMobile(phoneNumber), SMS_SEND_TIMEOUT_SECONDS, e);
|
||||
throw new BizException(ResponseCodeEnum.SMS_SEND_TIMEOUT);
|
||||
}
|
||||
|
||||
// 6. 短信发送失败则直接抛异常,不存储Redis
|
||||
if (!smsSendSuccess) {
|
||||
log.error("==> 手机号: {}, 发送验证码失败(第三方接口返回失败)", MaskUtils.maskMobile(phoneNumber));
|
||||
throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
}
|
||||
//CompletableFuture<Boolean> smsSendFuture = CompletableFuture.supplyAsync(() -> {
|
||||
// // 设置线程名称,便于日志排查
|
||||
// Thread.currentThread().setName("sms-send-" + MaskUtils.maskMobile(phoneNumber));
|
||||
// String signName = "速通互联验证码";
|
||||
// String templateCode = "100001";
|
||||
// String templateParam = String.format("{\"code\":\"%s\",\"min\":\"%d\"}", verificationCode, VERIFICATION_CODE_EXPIRE_TIME);
|
||||
// try {
|
||||
// return aliyunSmsHelper.sendMessage(signName, templateCode, phoneNumber, templateParam);
|
||||
// } catch (Exception e) {
|
||||
// log.error("==> 手机号: {}, 短信发送接口调用异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||
// return false;
|
||||
// }
|
||||
//}, taskExecutor);
|
||||
//
|
||||
// //5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
|
||||
//boolean smsSendSuccess;
|
||||
//try {
|
||||
// smsSendSuccess = smsSendFuture.get(SMS_SEND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
//} catch (InterruptedException e) {
|
||||
// log.error("==> 手机号: {}, 短信发送任务被中断", MaskUtils.maskMobile(phoneNumber), e);
|
||||
// Thread.currentThread().interrupt(); // 恢复中断状态
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
//} catch (ExecutionException e) {
|
||||
// log.error("==> 手机号: {}, 短信发送任务执行异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
//} catch (TimeoutException e) {
|
||||
// log.error("==> 手机号: {}, 短信发送任务超时({}秒)", MaskUtils.maskMobile(phoneNumber), SMS_SEND_TIMEOUT_SECONDS, e);
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_TIMEOUT);
|
||||
//}
|
||||
//
|
||||
//// 6. 短信发送失败则直接抛异常,不存储Redis
|
||||
//if (!smsSendSuccess) {
|
||||
// log.error("==> 手机号: {}, 发送验证码失败(第三方接口返回失败)", MaskUtils.maskMobile(phoneNumber));
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
//}
|
||||
//=============== 开发环境不实际调用短信发送接口 ===============
|
||||
|
||||
// 7. 短信发送成功后,记录日志(验证码脱敏,仅保留后2位)+ 存储Redis
|
||||
|
||||
Reference in New Issue
Block a user