diff --git a/SPMS.API/Controllers/ServiceController.cs b/SPMS.API/Controllers/ServiceController.cs index 8af1835..6be0163 100644 --- a/SPMS.API/Controllers/ServiceController.cs +++ b/SPMS.API/Controllers/ServiceController.cs @@ -178,6 +178,35 @@ public class ServiceController : ControllerBase return Ok(ApiResponse.Success(result)); } + [HttpPost("tags/list")] + [SwaggerOperation( + Summary = "태그 목록 조회", + Description = "서비스에 등록된 태그 목록을 조회합니다.")] + [SwaggerResponse(200, "조회 성공", typeof(ApiResponse))] + [SwaggerResponse(401, "인증되지 않은 요청")] + [SwaggerResponse(403, "권한 없음")] + [SwaggerResponse(404, "서비스를 찾을 수 없음")] + public async Task GetTagsAsync([FromBody] ServiceTagsRequestDto request) + { + var result = await _serviceManagementService.GetTagsAsync(request); + return Ok(ApiResponse.Success(result)); + } + + [HttpPost("tags/update")] + [SwaggerOperation( + Summary = "태그 수정", + Description = "서비스의 태그 목록을 수정합니다. 최대 10개까지 등록 가능합니다.")] + [SwaggerResponse(200, "수정 성공", typeof(ApiResponse))] + [SwaggerResponse(400, "잘못된 요청 또는 변경 없음")] + [SwaggerResponse(401, "인증되지 않은 요청")] + [SwaggerResponse(403, "권한 없음")] + [SwaggerResponse(404, "서비스를 찾을 수 없음")] + public async Task UpdateTagsAsync([FromBody] UpdateServiceTagsRequestDto request) + { + var result = await _serviceManagementService.UpdateTagsAsync(request); + return Ok(ApiResponse.Success(result)); + } + [HttpPost("{serviceCode}/ip/list")] [SwaggerOperation( Summary = "IP 화이트리스트 조회", diff --git a/SPMS.Application/DTOs/Service/ServiceTagsRequestDto.cs b/SPMS.Application/DTOs/Service/ServiceTagsRequestDto.cs new file mode 100644 index 0000000..04ecdbd --- /dev/null +++ b/SPMS.Application/DTOs/Service/ServiceTagsRequestDto.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace SPMS.Application.DTOs.Service; + +public class ServiceTagsRequestDto +{ + [Required(ErrorMessage = "서비스 코드는 필수입니다.")] + [JsonPropertyName("service_code")] + public string ServiceCode { get; set; } = string.Empty; +} diff --git a/SPMS.Application/DTOs/Service/ServiceTagsResponseDto.cs b/SPMS.Application/DTOs/Service/ServiceTagsResponseDto.cs new file mode 100644 index 0000000..8346db5 --- /dev/null +++ b/SPMS.Application/DTOs/Service/ServiceTagsResponseDto.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace SPMS.Application.DTOs.Service; + +public class ServiceTagsResponseDto +{ + [JsonPropertyName("service_code")] + public string ServiceCode { get; set; } = string.Empty; + + public List Tags { get; set; } = new(); + + [JsonPropertyName("total_count")] + public int TotalCount { get; set; } +} + +public class TagItemDto +{ + [JsonPropertyName("tag_index")] + public int TagIndex { get; set; } + + [JsonPropertyName("tag_name")] + public string TagName { get; set; } = string.Empty; +} diff --git a/SPMS.Application/DTOs/Service/UpdateServiceTagsRequestDto.cs b/SPMS.Application/DTOs/Service/UpdateServiceTagsRequestDto.cs new file mode 100644 index 0000000..5743503 --- /dev/null +++ b/SPMS.Application/DTOs/Service/UpdateServiceTagsRequestDto.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace SPMS.Application.DTOs.Service; + +public class UpdateServiceTagsRequestDto +{ + [Required(ErrorMessage = "서비스 코드는 필수입니다.")] + [JsonPropertyName("service_code")] + public string ServiceCode { get; set; } = string.Empty; + + [Required(ErrorMessage = "태그 목록은 필수입니다.")] + [MaxLength(10, ErrorMessage = "태그는 최대 10개까지 등록할 수 있습니다.")] + public List Tags { get; set; } = new(); +} diff --git a/SPMS.Application/Interfaces/IServiceManagementService.cs b/SPMS.Application/Interfaces/IServiceManagementService.cs index 19afd94..007a9e4 100644 --- a/SPMS.Application/Interfaces/IServiceManagementService.cs +++ b/SPMS.Application/Interfaces/IServiceManagementService.cs @@ -15,6 +15,10 @@ public interface IServiceManagementService Task RegisterFcmCredentialsAsync(string serviceCode, FcmCredentialsRequestDto request); Task GetCredentialsAsync(string serviceCode); + // Tags + Task GetTagsAsync(ServiceTagsRequestDto request); + Task UpdateTagsAsync(UpdateServiceTagsRequestDto request); + // IP Whitelist Task GetIpListAsync(string serviceCode); Task AddIpAsync(string serviceCode, AddIpRequestDto request); diff --git a/SPMS.Application/Services/ServiceManagementService.cs b/SPMS.Application/Services/ServiceManagementService.cs index b70989b..1d8858a 100644 --- a/SPMS.Application/Services/ServiceManagementService.cs +++ b/SPMS.Application/Services/ServiceManagementService.cs @@ -454,6 +454,76 @@ public class ServiceManagementService : IServiceManagementService return response; } + public async Task GetTagsAsync(ServiceTagsRequestDto request) + { + var service = await _serviceRepository.GetByServiceCodeAsync(request.ServiceCode); + if (service is null) + { + throw new SpmsException( + ErrorCodes.NotFound, + "서비스를 찾을 수 없습니다.", + 404); + } + + return BuildTagsResponse(service); + } + + public async Task UpdateTagsAsync(UpdateServiceTagsRequestDto request) + { + var service = await _serviceRepository.GetByServiceCodeAsync(request.ServiceCode); + if (service is null) + { + throw new SpmsException( + ErrorCodes.NotFound, + "서비스를 찾을 수 없습니다.", + 404); + } + + var newTagsJson = JsonSerializer.Serialize(request.Tags); + if (newTagsJson == service.Tags) + { + throw new SpmsException( + ErrorCodes.NoChange, + "변경된 내용이 없습니다.", + 400); + } + + service.Tags = newTagsJson; + service.UpdatedAt = DateTime.UtcNow; + + _serviceRepository.Update(service); + await _unitOfWork.SaveChangesAsync(); + + return BuildTagsResponse(service); + } + + private static ServiceTagsResponseDto BuildTagsResponse(Service service) + { + var tagNames = new List(); + if (!string.IsNullOrEmpty(service.Tags)) + { + try + { + tagNames = JsonSerializer.Deserialize>(service.Tags) ?? new List(); + } + catch + { + // Invalid JSON, return empty + } + } + + return new ServiceTagsResponseDto + { + ServiceCode = service.ServiceCode, + Tags = tagNames.Select((name, index) => new TagItemDto + { + TagIndex = index, + TagName = name + }).ToList(), + TotalCount = tagNames.Count + }; + } + public async Task GetIpListAsync(string serviceCode) { var service = await _serviceRepository.GetByServiceCodeWithIpsAsync(serviceCode);