feat: 서비스 태그 목록/수정 API 구현 (#70)
All checks were successful
SPMS_API/pipeline/head This commit looks good

Reviewed-on: https://git.ipstein.myds.me/SPMS/SPMS_API/pulls/71
This commit is contained in:
김선규 2026-02-10 02:30:56 +00:00
commit e288f28123
6 changed files with 152 additions and 0 deletions

View File

@ -178,6 +178,35 @@ public class ServiceController : ControllerBase
return Ok(ApiResponse<CredentialsResponseDto>.Success(result));
}
[HttpPost("tags/list")]
[SwaggerOperation(
Summary = "태그 목록 조회",
Description = "서비스에 등록된 태그 목록을 조회합니다.")]
[SwaggerResponse(200, "조회 성공", typeof(ApiResponse<ServiceTagsResponseDto>))]
[SwaggerResponse(401, "인증되지 않은 요청")]
[SwaggerResponse(403, "권한 없음")]
[SwaggerResponse(404, "서비스를 찾을 수 없음")]
public async Task<IActionResult> GetTagsAsync([FromBody] ServiceTagsRequestDto request)
{
var result = await _serviceManagementService.GetTagsAsync(request);
return Ok(ApiResponse<ServiceTagsResponseDto>.Success(result));
}
[HttpPost("tags/update")]
[SwaggerOperation(
Summary = "태그 수정",
Description = "서비스의 태그 목록을 수정합니다. 최대 10개까지 등록 가능합니다.")]
[SwaggerResponse(200, "수정 성공", typeof(ApiResponse<ServiceTagsResponseDto>))]
[SwaggerResponse(400, "잘못된 요청 또는 변경 없음")]
[SwaggerResponse(401, "인증되지 않은 요청")]
[SwaggerResponse(403, "권한 없음")]
[SwaggerResponse(404, "서비스를 찾을 수 없음")]
public async Task<IActionResult> UpdateTagsAsync([FromBody] UpdateServiceTagsRequestDto request)
{
var result = await _serviceManagementService.UpdateTagsAsync(request);
return Ok(ApiResponse<ServiceTagsResponseDto>.Success(result));
}
[HttpPost("{serviceCode}/ip/list")]
[SwaggerOperation(
Summary = "IP 화이트리스트 조회",

View File

@ -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;
}

View File

@ -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<TagItemDto> 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;
}

View File

@ -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<string> Tags { get; set; } = new();
}

View File

@ -15,6 +15,10 @@ public interface IServiceManagementService
Task RegisterFcmCredentialsAsync(string serviceCode, FcmCredentialsRequestDto request);
Task<CredentialsResponseDto> GetCredentialsAsync(string serviceCode);
// Tags
Task<ServiceTagsResponseDto> GetTagsAsync(ServiceTagsRequestDto request);
Task<ServiceTagsResponseDto> UpdateTagsAsync(UpdateServiceTagsRequestDto request);
// IP Whitelist
Task<IpListResponseDto> GetIpListAsync(string serviceCode);
Task<ServiceIpDto> AddIpAsync(string serviceCode, AddIpRequestDto request);

View File

@ -454,6 +454,76 @@ public class ServiceManagementService : IServiceManagementService
return response;
}
public async Task<ServiceTagsResponseDto> 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<ServiceTagsResponseDto> 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<string>();
if (!string.IsNullOrEmpty(service.Tags))
{
try
{
tagNames = JsonSerializer.Deserialize<List<string>>(service.Tags) ?? new List<string>();
}
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<IpListResponseDto> GetIpListAsync(string serviceCode)
{
var service = await _serviceRepository.GetByServiceCodeWithIpsAsync(serviceCode);