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();
}
}
}
}