using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using SPMS.Application.Interfaces; using SPMS.Application.Settings; using StackExchange.Redis; namespace SPMS.Infrastructure.Caching; public class TokenCacheService : ITokenCacheService { private readonly RedisConnection _redis; private readonly RedisSettings _settings; private readonly ILogger _logger; private static readonly TimeSpan CacheTtl = TimeSpan.FromHours(1); public TokenCacheService( RedisConnection redis, IOptions settings, ILogger logger) { _redis = redis; _settings = settings.Value; _logger = logger; } public async Task GetDeviceInfoAsync(long serviceId, long deviceId) { try { var db = await _redis.GetDatabaseAsync(); var value = await db.StringGetAsync(BuildKey(serviceId, deviceId)); if (value.IsNullOrEmpty) return null; return JsonSerializer.Deserialize(value!); } catch (Exception ex) { _logger.LogError(ex, "토큰 캐시 조회 실패: serviceId={ServiceId}, deviceId={DeviceId}", serviceId, deviceId); return null; } } public async Task SetDeviceInfoAsync(long serviceId, long deviceId, CachedDeviceInfo info) { try { var db = await _redis.GetDatabaseAsync(); var json = JsonSerializer.Serialize(info); await db.StringSetAsync(BuildKey(serviceId, deviceId), json, CacheTtl); } catch (Exception ex) { _logger.LogError(ex, "토큰 캐시 저장 실패: serviceId={ServiceId}, deviceId={DeviceId}", serviceId, deviceId); } } public async Task InvalidateAsync(long serviceId, long deviceId) { try { var db = await _redis.GetDatabaseAsync(); await db.KeyDeleteAsync(BuildKey(serviceId, deviceId)); } catch (Exception ex) { _logger.LogError(ex, "토큰 캐시 무효화 실패: serviceId={ServiceId}, deviceId={DeviceId}", serviceId, deviceId); } } public async Task InvalidateByServiceAsync(long serviceId) { try { var db = await _redis.GetDatabaseAsync(); var server = _redis.GetServer(); if (server == null) return; var pattern = $"{_settings.InstanceName}device:token:{serviceId}:*"; var keys = server.Keys(pattern: pattern).ToArray(); if (keys.Length > 0) await db.KeyDeleteAsync(keys); _logger.LogInformation("서비스 토큰 캐시 전체 무효화: serviceId={ServiceId}, 삭제={Count}건", serviceId, keys.Length); } catch (Exception ex) { _logger.LogError(ex, "서비스 토큰 캐시 전체 무효화 실패: serviceId={ServiceId}", serviceId); } } private string BuildKey(long serviceId, long deviceId) => $"{_settings.InstanceName}device:token:{serviceId}:{deviceId}"; }