using Application.Abstractions.Cache; using Application.Abstractions.Chat; using StackExchange.Redis; using System.Text.Json; namespace Infrastructure.Chat; public class RedisChatConnectionTracker(IConnectionMultiplexer redis) : IChatConnectionTracker { private readonly IDatabase _db = redis.GetDatabase(); private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false }; public async Task AddAsync(string roomKey, ConnectedUser user) { var json = JsonSerializer.Serialize(user, _jsonOptions); await _db.HashSetAsync(CacheKeys.ChatRoomConnections(roomKey), user.ConnectionId, json); } public async Task RemoveAsync(string roomKey, string connectionId) { await _db.HashDeleteAsync(CacheKeys.ChatRoomConnections(roomKey), connectionId); } public async Task> GetByRoomAsync(string roomKey) { var entries = await _db.HashGetAllAsync(CacheKeys.ChatRoomConnections(roomKey)); if (entries.Length <= 0) { return []; } var list = new List(entries.Length); foreach (var entry in entries) { if (!entry.Value.IsNullOrEmpty) { var user = JsonSerializer.Deserialize((string)entry.Value!, _jsonOptions); if (user is not null) { list.Add(user); } } } return list.AsReadOnly(); } public async Task GetCountByRoomAsync(string roomKey) { return (int)await _db.HashLengthAsync(CacheKeys.ChatRoomConnections(roomKey)); } public async Task> GetAllAsync() { // 모든 룸 키를 스캔 후 병합 (관리자 집계용) var server = _db.Multiplexer.GetServer(_db.Multiplexer.GetEndPoints().First()); var list = new List<( string RoomKey, ConnectedUser User )>(); await foreach (var key in server.KeysAsync(pattern: "chat:room:*:connections")) { // 키에서 roomKey 추출 — chat:room:{key}:connections var keyStr = (string)key!; var roomKey = keyStr["chat:room:".Length..^":connections".Length]; var entries = await _db.HashGetAllAsync(key); foreach (var entry in entries) { if (!entry.Value.IsNullOrEmpty) { var user = JsonSerializer.Deserialize((string)entry.Value!, _jsonOptions); if (user is not null) { list.Add((roomKey, user)); } } } } return list.AsReadOnly(); } public async Task ClearAllAsync() { // 서버 시작 시 모든 룸 연결 초기화 (stale 제거) var server = _db.Multiplexer.GetServer(_db.Multiplexer.GetEndPoints().First()); await foreach (var key in server.KeysAsync(pattern: "chat:room:*:connections")) { await _db.KeyDeleteAsync(key); } // 구 채널 키 체계(chat:channel:*) 잔재 정리 — roomKey 전환 이전 배포분 호환 await foreach (var key in server.KeysAsync(pattern: "chat:channel:*:connections")) { await _db.KeyDeleteAsync(key); } } }