- IDeviceService: AdminDeleteAsync(long deviceId) 추가 - DeviceService: Device 조회 → IsActive=false → 토큰 캐시 무효화 - DeviceController: POST /v1/in/device/admin/delete [Authorize] 엔드포인트 추가 - 기존 SDK 삭제 API와 분리, JWT 인증 기반 관리자 전용 Closes #239
230 lines
8.5 KiB
C#
230 lines
8.5 KiB
C#
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<DeviceRegisterResponseDto> 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<DeviceInfoResponseDto> 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 AdminDeleteAsync(long deviceId)
|
|
{
|
|
var device = await _deviceRepository.GetByIdAsync(deviceId);
|
|
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(device.ServiceId, device.Id);
|
|
}
|
|
|
|
public async Task<DeviceListResponseDto> 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,
|
|
request.Keyword, request.MarketingAgreed);
|
|
|
|
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,
|
|
DeviceToken = d.DeviceToken,
|
|
ServiceName = d.Service?.ServiceName ?? string.Empty,
|
|
ServiceCode = d.Service?.ServiceCode ?? string.Empty,
|
|
OsVersion = d.OsVersion,
|
|
AppVersion = d.AppVersion,
|
|
MarketingAgreed = d.MarketingAgreed,
|
|
IsActive = d.IsActive,
|
|
CreatedAt = d.CreatedAt
|
|
}).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<int>? ParseTags(string? tagsJson)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tagsJson))
|
|
return null;
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<List<int>>(tagsJson);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|