using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace VueWebCoreApi.SignalR { /// /// 用户连接ID与用户编码映射存储(线程安全版) /// 解决多线程并发读写导致的字典数据错乱问题 /// public static class UserIdsStore { // 替换为线程安全的并发字典 private static readonly Dictionary _ids = new Dictionary(); // 加锁保证字典操作的原子性(比ConcurrentDictionary更灵活控制批量操作) private static readonly object _lockObj = new object(); /// /// 对外暴露的连接映射(只读,避免外部直接修改) /// public static IReadOnlyDictionary Ids { get { lock (_lockObj) { // 返回新字典,避免外部拿到引用后修改 return new Dictionary(_ids); } } } /// /// 添加/更新用户连接(原子操作) /// /// SignalR连接ID /// 用户编码 public static void AddOrUpdateUser(string connectionId, string userCode) { if (string.IsNullOrEmpty(connectionId) || string.IsNullOrEmpty(userCode)) throw new ArgumentNullException("连接ID和用户编码不能为空"); lock (_lockObj) { // 先移除该用户的旧连接(一个用户只保留最新的连接) var oldConnIds = _ids.Where(kv => kv.Value == userCode).Select(kv => kv.Key).ToList(); foreach (var oldConnId in oldConnIds) { _ids.Remove(oldConnId); } // 添加新连接(覆盖同名connectionId,避免重复) if (_ids.ContainsKey(connectionId)) { _ids[connectionId] = userCode; } else { _ids.Add(connectionId, userCode); } } } /// /// 移除指定连接ID的映射 /// /// SignalR连接ID public static void RemoveUser(string connectionId) { if (string.IsNullOrEmpty(connectionId)) return; lock (_lockObj) { if (_ids.ContainsKey(connectionId)) { _ids.Remove(connectionId); } } } /// /// 根据用户编码获取所有在线连接ID(一个用户可能多端登录时返回多个) /// /// 用户编码列表 /// 连接ID列表 public static List GetConnectionIdsByUserCodes(List userCodes) { if (userCodes == null || userCodes.Count == 0) return new List(); lock (_lockObj) { return _ids.Where(kv => userCodes.Contains(kv.Value)) .Select(kv => kv.Key) .Distinct() // 去重,避免重复推送 .ToList(); } } } }