| | |
| | | namespace VueWebCoreApi.SignalR |
| | | { |
| | | /// <summary> |
| | | /// 用户id集合 |
| | | /// 用户连接ID与用户编码映射存储(线程安全版) |
| | | /// 解决多线程并发读写导致的字典数据错乱问题 |
| | | /// </summary> |
| | | public static class UserIdsStore |
| | | { |
| | | public static Dictionary<string, string> Ids = new Dictionary<string, string>(); |
| | | // 替换为线程安全的并发字典 |
| | | private static readonly Dictionary<string, string> _ids = new Dictionary<string, string>(); |
| | | // 加锁保证字典操作的原子性(比ConcurrentDictionary更灵活控制批量操作) |
| | | private static readonly object _lockObj = new object(); |
| | | |
| | | /// <summary> |
| | | /// 对外暴露的连接映射(只读,避免外部直接修改) |
| | | /// </summary> |
| | | public static IReadOnlyDictionary<string, string> Ids |
| | | { |
| | | get |
| | | { |
| | | lock (_lockObj) |
| | | { |
| | | // 返回新字典,避免外部拿到引用后修改 |
| | | return new Dictionary<string, string>(_ids); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加/更新用户连接(原子操作) |
| | | /// </summary> |
| | | /// <param name="connectionId">SignalR连接ID</param> |
| | | /// <param name="userCode">用户编码</param> |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 移除指定连接ID的映射 |
| | | /// </summary> |
| | | /// <param name="connectionId">SignalR连接ID</param> |
| | | public static void RemoveUser(string connectionId) |
| | | { |
| | | if (string.IsNullOrEmpty(connectionId)) |
| | | return; |
| | | |
| | | lock (_lockObj) |
| | | { |
| | | if (_ids.ContainsKey(connectionId)) |
| | | { |
| | | _ids.Remove(connectionId); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据用户编码获取所有在线连接ID(一个用户可能多端登录时返回多个) |
| | | /// </summary> |
| | | /// <param name="userCodes">用户编码列表</param> |
| | | /// <returns>连接ID列表</returns> |
| | | public static List<string> GetConnectionIdsByUserCodes(List<string> userCodes) |
| | | { |
| | | if (userCodes == null || userCodes.Count == 0) |
| | | return new List<string>(); |
| | | |
| | | lock (_lockObj) |
| | | { |
| | | return _ids.Where(kv => userCodes.Contains(kv.Value)) |
| | | .Select(kv => kv.Key) |
| | | .Distinct() // 去重,避免重复推送 |
| | | .ToList(); |
| | | } |
| | | } |
| | | } |
| | | } |