SPMS_API/SPMS.Application/Services/ServiceManagementService.cs
seonkyu.kim cac56761f4 feat: 서비스 관리 API 구현 (#44)
- ServiceController: 서비스 목록/상세/상태변경 엔드포인트
- ServiceManagementService: 비즈니스 로직 구현
- Service DTOs: 요청/응답 DTO 4종
- DI 등록

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-10 00:01:33 +09:00

154 lines
5.0 KiB
C#

using System.Linq.Expressions;
using SPMS.Application.DTOs.Service;
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 ServiceManagementService : IServiceManagementService
{
private readonly IServiceRepository _serviceRepository;
private readonly IUnitOfWork _unitOfWork;
public ServiceManagementService(IServiceRepository serviceRepository, IUnitOfWork unitOfWork)
{
_serviceRepository = serviceRepository;
_unitOfWork = unitOfWork;
}
public async Task<ServiceListResponseDto> GetListAsync(ServiceListRequestDto request)
{
Expression<Func<Service, bool>>? predicate = null;
// 검색어 필터
if (!string.IsNullOrWhiteSpace(request.SearchKeyword))
{
var keyword = request.SearchKeyword;
predicate = s => s.ServiceName.Contains(keyword) || s.ServiceCode.Contains(keyword);
}
// Status 필터
if (request.Status.HasValue)
{
var status = (ServiceStatus)request.Status.Value;
if (predicate != null)
{
var basePredicate = predicate;
predicate = s => basePredicate.Compile()(s) && s.Status == status;
}
else
{
predicate = s => s.Status == status;
}
}
var (items, totalCount) = await _serviceRepository.GetPagedAsync(
request.Page,
request.PageSize,
predicate,
s => s.CreatedAt,
descending: true);
return new ServiceListResponseDto
{
Items = items.Select(MapToSummaryDto).ToList(),
TotalCount = totalCount,
Page = request.Page,
PageSize = request.PageSize
};
}
public async Task<ServiceResponseDto> GetByServiceCodeAsync(string serviceCode)
{
var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode);
if (service is null)
{
throw new SpmsException(
ErrorCodes.NotFound,
"서비스를 찾을 수 없습니다.",
404);
}
// IP 목록 포함해서 다시 조회
var serviceWithIps = await _serviceRepository.GetByIdWithIpsAsync(service.Id);
return MapToDto(serviceWithIps ?? service);
}
public async Task<ServiceResponseDto> ChangeStatusAsync(string serviceCode, ChangeServiceStatusRequestDto request)
{
var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode);
if (service is null)
{
throw new SpmsException(
ErrorCodes.NotFound,
"서비스를 찾을 수 없습니다.",
404);
}
var newStatus = (ServiceStatus)request.Status;
// 이미 같은 상태면 변경 없음
if (service.Status == newStatus)
{
throw new SpmsException(
ErrorCodes.NoChange,
"이미 해당 상태입니다.",
400);
}
service.Status = newStatus;
service.UpdatedAt = DateTime.UtcNow;
_serviceRepository.Update(service);
await _unitOfWork.SaveChangesAsync();
return MapToDto(service);
}
private static ServiceSummaryDto MapToSummaryDto(Service service)
{
return new ServiceSummaryDto
{
ServiceCode = service.ServiceCode,
ServiceName = service.ServiceName,
Description = service.Description,
SubTier = service.SubTier.ToString(),
Status = service.Status.ToString(),
CreatedAt = service.CreatedAt,
DeviceCount = service.Devices?.Count ?? 0
};
}
private static ServiceResponseDto MapToDto(Service service)
{
return new ServiceResponseDto
{
ServiceCode = service.ServiceCode,
ServiceName = service.ServiceName,
Description = service.Description,
ApiKey = service.ApiKey,
ApiKeyCreatedAt = service.ApiKeyCreatedAt,
ApnsBundleId = service.ApnsBundleId,
ApnsKeyId = service.ApnsKeyId,
ApnsTeamId = service.ApnsTeamId,
HasApnsKey = !string.IsNullOrEmpty(service.ApnsPrivateKey),
HasFcmCredentials = !string.IsNullOrEmpty(service.FcmCredentials),
WebhookUrl = service.WebhookUrl,
Tags = service.Tags,
SubTier = service.SubTier.ToString(),
SubStartedAt = service.SubStartedAt,
Status = service.Status.ToString(),
CreatedAt = service.CreatedAt,
CreatedByName = service.CreatedByAdmin?.Name ?? string.Empty,
UpdatedAt = service.UpdatedAt,
DeviceCount = service.Devices?.Count ?? 0,
AllowedIps = service.ServiceIps?.Select(ip => ip.IpAddress).ToList() ?? new List<string>()
};
}
}