package com.ruoyi.im.service.impl; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.netease.nim.server.sdk.core.BizName; import com.netease.nim.server.sdk.core.YunxinApiHttpClient; import com.netease.nim.server.sdk.core.YunxinApiResponse; import com.netease.nim.server.sdk.core.exception.YunxinSdkException; import com.ruoyi.common.core.domain.R; import com.ruoyi.im.comm.Result; import com.ruoyi.im.config.AppAuthConfig; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.im.service.ImApiServcie; import com.ruoyi.im.dto.RegisterDto; import com.ruoyi.system.service.UserAccountService; import com.ruoyi.im.util.SymmetricCryptoUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.util.CollectionUtils; import javax.annotation.PostConstruct; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; @Service @Slf4j public class ImApiServcieImpl implements ImApiServcie { @Autowired private UserAccountService userAccountService; // 使用单例客户端,避免重复初始化 private YunxinApiHttpClient yunxinClient; // 使用并发安全的集合存储正在生成的账号,防止重复 private final Set generatingAccounts = Collections.newSetFromMap(new ConcurrentHashMap<>()); // 使用原子计数器优化账号生成 private final AtomicLong lastTimestamp = new AtomicLong(0); private final AtomicLong sequence = new AtomicLong(0); @PostConstruct public void init() { // 初始化云信客户端,只执行一次 String appkey = AppAuthConfig.DEFAULT_CONFIG.getAppKey(); String appsecret = AppAuthConfig.DEFAULT_CONFIG.getAppSecret(); int timeoutMillis = 5000; this.yunxinClient = new YunxinApiHttpClient.Builder(BizName.IM, appkey, appsecret) .timeoutMillis(timeoutMillis) .build(); } @Override @Transactional(rollbackFor = Exception.class) // 添加事务注解确保操作原子性 public Result register(RegisterDto dto) { try { // 验证手机号是否已存在(数据库唯一索引提供最终保障) List accounts = userAccountService.list( new LambdaQueryWrapper<>(UserAccount.class) .eq(UserAccount::getAccount, dto.getAccount()) ); if (!CollectionUtils.isEmpty(accounts)) { return Result.error("手机号已注册!"); } // 创建本地用户账户记录 UserAccount userAccount = new UserAccount(); userAccount.setAccount(dto.getAccount()); userAccount.setPhoneNumber(dto.getAccount()); userAccount.setCloudMessageAccount(dto.getAccount()); userAccount.setPassword(SymmetricCryptoUtil.encryptPassword(dto.getPassword())); if (!userAccountService.save(userAccount)) { return Result.error("注册失败,请重试"); } // 注册云信账号(远程调用) String path = "/user/create.action"; Map paramMap = new HashMap<>(); paramMap.put("accid", dto.getAccount()); paramMap.put("token", dto.getPassword()); YunxinApiResponse response = yunxinClient.executeV1Api(path, paramMap); // 处理云信响应 String data = response.getData(); JSONObject json = JSONObject.parseObject(data); int code = json.getIntValue("code"); if (code == 200) { return Result.success("注册成功"); } else if (code == 414) { // 手动触发回滚(事务注解会自动处理) TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return Result.error("账号已被注册!"); } else { // 其他错误码同样触发回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); log.error("云信注册失败,响应: {}, traceId: {}", data, response.getTraceId()); return Result.error("注册失败,错误码: " + code); } } catch (YunxinSdkException e) { // 云信调用异常时回滚事务 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); log.error("云信服务调用异常 traceId: {}", e.getTraceId(), e); return Result.error("注册失败,系统异常"); } catch (Exception e) { // 其他异常同样回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); log.error("注册过程发生未知异常", e); return Result.error("注册失败,请重试"); } } /** * 优化的账号生成方法,使用雪花算法变体提高并发性能 */ public long generateUniqueCloudMessageAccount() { final int maxAttempts = 10; // 减少尝试次数,提高效率 for (int attempt = 0; attempt < maxAttempts; attempt++) { long account = generateSnowflakeLikeId(); // 使用并发集合检查是否正在生成相同账号 if (generatingAccounts.add(account)) { try { // 检查数据库是否存在 boolean exists = userAccountService.lambdaQuery() .eq(UserAccount::getCloudMessageAccount, account) .exists(); if (!exists) { return account; } } finally { // 无论成功与否,都从集合中移除 generatingAccounts.remove(account); } } // 短暂等待后重试 try { Thread.sleep(ThreadLocalRandom.current().nextInt(10, 50)); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); throw new RuntimeException("生成账号被中断", e); } } throw new RuntimeException("无法在" + maxAttempts + "次尝试内生成唯一账号"); } /** * 基于雪花算法的ID生成器,提高并发性能 */ private long generateSnowflakeLikeId() { long currentTime = System.currentTimeMillis(); long timestamp = currentTime % 100_000_000L; // 取时间戳的后8位 // 使用原子操作确保序列号递增 long seq; long lastTime; do { lastTime = lastTimestamp.get(); if (currentTime == lastTime) { seq = sequence.incrementAndGet() % 100; } else { sequence.set(0); lastTimestamp.set(currentTime); seq = 0; } } while (!lastTimestamp.compareAndSet(lastTime, currentTime)); // 组合时间戳和序列号,生成9位ID return timestamp * 100 + seq; } }