1
zj
2025-08-19 0b10f87268bd511c7d8dddad30060abefa49aab2
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
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;
    }
}