1
zj
2025-08-20 1821b2ab068e081e053cf5b4bcc5ee8112a60ea4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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<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
    @Transactional(rollbackFor = Exception.class) // 添加事务注解确保操作原子性
    public Result register(RegisterDto dto) {
        try {
            // 验证手机号是否已存在(数据库唯一索引提供最终保障)
            List<UserAccount> 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<String, String> 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;
    }
}