using System.Text.Json; using SPMS.Application.DTOs.Device; using SPMS.Application.Interfaces; using SPMS.Domain.Common; using SPMS.Domain.Entities; using SPMS.Domain.Enums; using SPMS.Domain.Exceptions; using SPMS.Domain.Interfaces; namespace SPMS.Application.Services; public class DeviceService : IDeviceService { private readonly IDeviceRepository _deviceRepository; private readonly IUnitOfWork _unitOfWork; private readonly ITokenCacheService _tokenCache; public DeviceService( IDeviceRepository deviceRepository, IUnitOfWork unitOfWork, ITokenCacheService tokenCache) { _deviceRepository = deviceRepository; _unitOfWork = unitOfWork; _tokenCache = tokenCache; } public async Task RegisterAsync(long serviceId, DeviceRegisterRequestDto request) { var platform = ParsePlatform(request.Platform); var existing = await _deviceRepository.GetByServiceAndTokenAsync(serviceId, request.DeviceToken); if (existing != null) { existing.Platform = platform; existing.OsVersion = request.OsVersion; existing.AppVersion = request.AppVersion; existing.DeviceModel = request.Model; existing.IsActive = true; existing.UpdatedAt = DateTime.UtcNow; _deviceRepository.Update(existing); await _unitOfWork.SaveChangesAsync(); await _tokenCache.InvalidateAsync(serviceId, existing.Id); return new DeviceRegisterResponseDto { DeviceId = existing.Id, IsNew = false }; } var device = new Device { ServiceId = serviceId, DeviceToken = request.DeviceToken, Platform = platform, OsVersion = request.OsVersion, AppVersion = request.AppVersion, DeviceModel = request.Model, PushAgreed = true, MarketingAgreed = false, IsActive = true, CreatedAt = DateTime.UtcNow }; await _deviceRepository.AddAsync(device); await _unitOfWork.SaveChangesAsync(); return new DeviceRegisterResponseDto { DeviceId = device.Id, IsNew = true }; } public async Task GetInfoAsync(long serviceId, DeviceInfoRequestDto request) { var device = await _deviceRepository.GetByIdAndServiceAsync(request.DeviceId, serviceId); if (device == null) throw new SpmsException(ErrorCodes.DeviceNotFound, "존재하지 않는 디바이스입니다.", 404); return new DeviceInfoResponseDto { DeviceId = device.Id, DeviceToken = device.DeviceToken, Platform = device.Platform.ToString().ToLowerInvariant(), OsVersion = device.OsVersion, AppVersion = device.AppVersion, Model = device.DeviceModel, PushAgreed = device.PushAgreed, MarketingAgreed = device.MarketingAgreed, Tags = ParseTags(device.Tags), IsActive = device.IsActive, LastActiveAt = device.UpdatedAt, CreatedAt = device.CreatedAt }; } public async Task UpdateAsync(long serviceId, DeviceUpdateRequestDto request) { var device = await _deviceRepository.GetByIdAndServiceAsync(request.DeviceId, serviceId); if (device == null) throw new SpmsException(ErrorCodes.DeviceNotFound, "존재하지 않는 디바이스입니다.", 404); if (request.DeviceToken != null) device.DeviceToken = request.DeviceToken; if (request.OsVersion != null) device.OsVersion = request.OsVersion; if (request.AppVersion != null) device.AppVersion = request.AppVersion; device.UpdatedAt = DateTime.UtcNow; _deviceRepository.Update(device); await _unitOfWork.SaveChangesAsync(); await _tokenCache.InvalidateAsync(serviceId, device.Id); } public async Task DeleteAsync(long serviceId, DeviceDeleteRequestDto request) { var device = await _deviceRepository.GetByIdAndServiceAsync(request.DeviceId, serviceId); if (device == null) throw new SpmsException(ErrorCodes.DeviceNotFound, "존재하지 않는 디바이스입니다.", 404); device.IsActive = false; device.UpdatedAt = DateTime.UtcNow; _deviceRepository.Update(device); await _unitOfWork.SaveChangesAsync(); await _tokenCache.InvalidateAsync(serviceId, device.Id); } public async Task GetListAsync(long? serviceId, DeviceListRequestDto request) { Platform? platform = null; if (!string.IsNullOrWhiteSpace(request.Platform)) platform = ParsePlatform(request.Platform); var (items, totalCount) = await _deviceRepository.GetPagedAsync( serviceId, request.Page, request.Size, platform, request.PushAgreed, request.IsActive, request.Tags); var totalPages = (int)Math.Ceiling((double)totalCount / request.Size); return new DeviceListResponseDto { Items = items.Select(d => new DeviceSummaryDto { DeviceId = d.Id, Platform = d.Platform.ToString().ToLowerInvariant(), Model = d.DeviceModel, PushAgreed = d.PushAgreed, Tags = ParseTags(d.Tags), LastActiveAt = d.UpdatedAt }).ToList(), Pagination = new DTOs.Notice.PaginationDto { Page = request.Page, Size = request.Size, TotalCount = totalCount, TotalPages = totalPages } }; } public async Task SetTagsAsync(long serviceId, DeviceTagsRequestDto request) { var device = await _deviceRepository.GetByIdAndServiceAsync(request.DeviceId, serviceId); if (device == null) throw new SpmsException(ErrorCodes.DeviceNotFound, "존재하지 않는 디바이스입니다.", 404); device.Tags = JsonSerializer.Serialize(request.Tags); device.UpdatedAt = DateTime.UtcNow; _deviceRepository.Update(device); await _unitOfWork.SaveChangesAsync(); } public async Task SetAgreeAsync(long serviceId, DeviceAgreeRequestDto request) { var device = await _deviceRepository.GetByIdAndServiceAsync(request.DeviceId, serviceId); if (device == null) throw new SpmsException(ErrorCodes.DeviceNotFound, "존재하지 않는 디바이스입니다.", 404); device.PushAgreed = request.PushAgreed; device.MarketingAgreed = request.MarketingAgreed; device.AgreeUpdatedAt = DateTime.UtcNow; if (request.MarketingAgreed != device.MarketingAgreed) device.MktAgreeUpdatedAt = DateTime.UtcNow; _deviceRepository.Update(device); await _unitOfWork.SaveChangesAsync(); await _tokenCache.InvalidateAsync(serviceId, device.Id); } private static Platform ParsePlatform(string platform) { return platform.ToLowerInvariant() switch { "ios" => Platform.iOS, "android" => Platform.Android, "web" => Platform.Web, _ => throw new SpmsException(ErrorCodes.BadRequest, "유효하지 않은 플랫폼입니다.", 400) }; } private static List? ParseTags(string? tagsJson) { if (string.IsNullOrWhiteSpace(tagsJson)) return null; try { return JsonSerializer.Deserialize>(tagsJson); } catch { return null; } } }