using Application.Abstractions.Cache; using Application.Abstractions.Chat; using Application.Abstractions.Data; using Microsoft.EntityFrameworkCore; namespace Application.Helpers; /// /// ChatRoomConfig 로더 — Redis 캐시(60초 TTL) → MSSQL 폴백. /// 오픈 판정: main 은 row 없어도 기본 오픈, 종목방은 row 존재 + IsActive 필요(단계 오픈 — d0 §5-④), /// 레거시 channel:{sid} 룸은 항상 통과(기존 채널 채팅 동작 유지). /// public static class ChatRoomConfigLoader { public sealed record RoomConfig(bool Exists, bool IsActive, short SlowModeSeconds, string? Notice); private static readonly TimeSpan CacheTtl = TimeSpan.FromSeconds(60); public static async Task GetAsync(ICacheService cache, IAppDbContext db, string roomKey, CancellationToken ct) { var cacheKey = CacheKeys.ChatRoomConfig(roomKey); var cached = await cache.GetAsync(cacheKey); if (cached is not null) { return cached; } var row = await db.ChatRoomConfig.AsNoTracking().Where(c => c.RoomKey == roomKey).Select(c => new { c.IsActive, c.SlowModeSeconds, c.Notice }).FirstOrDefaultAsync(ct); var config = row is null ? new RoomConfig(false, false, 0, null) : new RoomConfig(true, row.IsActive, row.SlowModeSeconds, row.Notice); await cache.SetAsync(cacheKey, config, CacheTtl); return config; } public static bool IsRoomOpen(string roomKey, RoomConfig config) { if (roomKey == ChatRoomKeys.Main) { return !config.Exists || config.IsActive; } if (ChatRoomKeys.IsStockRoom(roomKey)) { return config.Exists && config.IsActive; } return true; } }