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.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 org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
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
|
public class ImApiServcieImpl implements ImApiServcie {
|
@Autowired
|
private UserAccountService userAccountService;
|
|
// 使用单例客户端,避免重复初始化
|
private YunxinApiHttpClient yunxinClient;
|
|
// 使用并发安全的集合存储正在生成的账号,防止重复
|
private final Set<Long> 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
|
public Result register(RegisterDto dto) {
|
// 短信验证 TODO
|
|
// 手机号是否存在 - 添加分布式锁或数据库唯一索引确保并发安全
|
List<UserAccount> accounts = userAccountService.list(
|
new LambdaQueryWrapper<>(UserAccount.class)
|
.eq(UserAccount::getAccount, dto.getAccount())
|
);
|
|
if(!CollectionUtils.isEmpty(accounts)){
|
return Result.error("手机号已注册!");
|
}
|
|
// // 生成云信账号
|
// long cloudMessageAccount;
|
// try {
|
// cloudMessageAccount = generateUniqueCloudMessageAccount();
|
// } catch (RuntimeException e) {
|
// return Result.error("系统繁忙,请稍后重试");
|
// }
|
|
// 注册云信
|
String path = "/user/create.action";
|
Map<String, String> paramMap = new HashMap<>();
|
paramMap.put("accid", String.valueOf(dto.getAccount()));
|
paramMap.put("token", dto.getPassword());
|
|
YunxinApiResponse response;
|
try {
|
response = yunxinClient.executeV1Api(path, paramMap);
|
} catch (YunxinSdkException e) {
|
System.err.println("register error, traceId = " + e.getTraceId());
|
return Result.error("注册失败,系统异常");
|
}
|
|
// 获取结果
|
String data = response.getData();
|
JSONObject json = JSONObject.parseObject(data);
|
int code = json.getIntValue("code");
|
|
if (code == 200) {
|
// 注册成功
|
UserAccount userAccount = new UserAccount();
|
userAccount.setAccount(dto.getAccount());
|
userAccount.setPhoneNumber(dto.getAccount());
|
userAccount.setCloudMessageAccount(dto.getAccount());
|
userAccount.setPassword(SymmetricCryptoUtil.encryptPassword(dto.getPassword()));
|
|
try {
|
userAccountService.save(userAccount);
|
return Result.success("注册成功");
|
} catch (Exception e) {
|
// 处理数据库插入异常,可能需要回滚云信账号
|
return Result.error("注册失败,请重试");
|
}
|
} else if (code == 414) {
|
return Result.error("账号已被注册!");
|
} else {
|
System.err.println("register fail, response = " + data + ", traceId=" + response.getTraceId());
|
return Result.error("注册失败,错误码: " + code);
|
}
|
}
|
|
/**
|
* 优化的账号生成方法,使用雪花算法变体提高并发性能
|
*/
|
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) {
|
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;
|
}
|
}
|