From 6475c0c75389ad34a5b6b1bc4c3e1bc7bbc8d3c0 Mon Sep 17 00:00:00 2001 From: SEAN Date: Tue, 10 Feb 2026 14:07:13 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B4=EC=9A=A9=EC=95=BD=EA=B4=80/?= =?UTF-8?q?=EA=B0=9C=EC=9D=B8=EC=A0=95=EB=B3=B4=EC=B2=98=EB=A6=AC=EB=B0=A9?= =?UTF-8?q?=EC=B9=A8=20API=20=EA=B5=AC=ED=98=84=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SPMS.API/Controllers/TermsController.cs | 44 +++++++++++++++++++ .../DTOs/AppConfig/AppConfigResponseDto.cs | 9 ++++ SPMS.Application/DependencyInjection.cs | 1 + .../Interfaces/IAppConfigService.cs | 9 ++++ SPMS.Application/Services/AppConfigService.cs | 43 ++++++++++++++++++ .../Interfaces/IAppConfigRepository.cs | 8 ++++ SPMS.Infrastructure/DependencyInjection.cs | 1 + .../Repositories/AppConfigRepository.cs | 15 +++++++ 8 files changed, 130 insertions(+) create mode 100644 SPMS.API/Controllers/TermsController.cs create mode 100644 SPMS.Application/DTOs/AppConfig/AppConfigResponseDto.cs create mode 100644 SPMS.Application/Interfaces/IAppConfigService.cs create mode 100644 SPMS.Application/Services/AppConfigService.cs create mode 100644 SPMS.Domain/Interfaces/IAppConfigRepository.cs create mode 100644 SPMS.Infrastructure/Persistence/Repositories/AppConfigRepository.cs diff --git a/SPMS.API/Controllers/TermsController.cs b/SPMS.API/Controllers/TermsController.cs new file mode 100644 index 0000000..af936f5 --- /dev/null +++ b/SPMS.API/Controllers/TermsController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; +using SPMS.Application.DTOs.AppConfig; +using SPMS.Application.Interfaces; +using SPMS.Domain.Common; + +namespace SPMS.API.Controllers; + +[ApiController] +[Route("v1/in/public")] +[ApiExplorerSettings(GroupName = "public")] +public class TermsController : ControllerBase +{ + private readonly IAppConfigService _appConfigService; + + public TermsController(IAppConfigService appConfigService) + { + _appConfigService = appConfigService; + } + + [HttpPost("terms")] + [SwaggerOperation(Summary = "이용약관", Description = "이용약관 URL을 조회합니다.")] + public async Task GetTermsAsync() + { + var serviceCode = Request.Headers["X-Service-Code"].FirstOrDefault(); + if (string.IsNullOrWhiteSpace(serviceCode)) + return BadRequest(ApiResponse.Fail(ErrorCodes.BadRequest, "X-Service-Code 헤더가 필요합니다.")); + + var result = await _appConfigService.GetTermsAsync(serviceCode); + return Ok(ApiResponse.Success(result)); + } + + [HttpPost("privacy")] + [SwaggerOperation(Summary = "개인정보처리방침", Description = "개인정보처리방침 URL을 조회합니다.")] + public async Task GetPrivacyAsync() + { + var serviceCode = Request.Headers["X-Service-Code"].FirstOrDefault(); + if (string.IsNullOrWhiteSpace(serviceCode)) + return BadRequest(ApiResponse.Fail(ErrorCodes.BadRequest, "X-Service-Code 헤더가 필요합니다.")); + + var result = await _appConfigService.GetPrivacyAsync(serviceCode); + return Ok(ApiResponse.Success(result)); + } +} diff --git a/SPMS.Application/DTOs/AppConfig/AppConfigResponseDto.cs b/SPMS.Application/DTOs/AppConfig/AppConfigResponseDto.cs new file mode 100644 index 0000000..48fb218 --- /dev/null +++ b/SPMS.Application/DTOs/AppConfig/AppConfigResponseDto.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace SPMS.Application.DTOs.AppConfig; + +public class AppConfigResponseDto +{ + [JsonPropertyName("url")] + public string? Url { get; set; } +} diff --git a/SPMS.Application/DependencyInjection.cs b/SPMS.Application/DependencyInjection.cs index 79e0283..bd0b784 100644 --- a/SPMS.Application/DependencyInjection.cs +++ b/SPMS.Application/DependencyInjection.cs @@ -15,6 +15,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/SPMS.Application/Interfaces/IAppConfigService.cs b/SPMS.Application/Interfaces/IAppConfigService.cs new file mode 100644 index 0000000..2e65a3f --- /dev/null +++ b/SPMS.Application/Interfaces/IAppConfigService.cs @@ -0,0 +1,9 @@ +using SPMS.Application.DTOs.AppConfig; + +namespace SPMS.Application.Interfaces; + +public interface IAppConfigService +{ + Task GetTermsAsync(string serviceCode); + Task GetPrivacyAsync(string serviceCode); +} diff --git a/SPMS.Application/Services/AppConfigService.cs b/SPMS.Application/Services/AppConfigService.cs new file mode 100644 index 0000000..ba39f3b --- /dev/null +++ b/SPMS.Application/Services/AppConfigService.cs @@ -0,0 +1,43 @@ +using SPMS.Application.DTOs.AppConfig; +using SPMS.Application.Interfaces; +using SPMS.Domain.Common; +using SPMS.Domain.Exceptions; +using SPMS.Domain.Interfaces; + +namespace SPMS.Application.Services; + +public class AppConfigService : IAppConfigService +{ + private readonly IAppConfigRepository _appConfigRepository; + private readonly IServiceRepository _serviceRepository; + + public AppConfigService(IAppConfigRepository appConfigRepository, IServiceRepository serviceRepository) + { + _appConfigRepository = appConfigRepository; + _serviceRepository = serviceRepository; + } + + public async Task GetTermsAsync(string serviceCode) + { + return await GetConfigUrlAsync(serviceCode, "terms_url"); + } + + public async Task GetPrivacyAsync(string serviceCode) + { + return await GetConfigUrlAsync(serviceCode, "privacy_url"); + } + + private async Task GetConfigUrlAsync(string serviceCode, string configKey) + { + var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode); + if (service == null) + throw new SpmsException(ErrorCodes.NotFound, "존재하지 않는 서비스입니다.", 404); + + var config = await _appConfigRepository.GetByKeyAsync(service.Id, configKey); + + return new AppConfigResponseDto + { + Url = config?.ConfigValue + }; + } +} diff --git a/SPMS.Domain/Interfaces/IAppConfigRepository.cs b/SPMS.Domain/Interfaces/IAppConfigRepository.cs new file mode 100644 index 0000000..e61c187 --- /dev/null +++ b/SPMS.Domain/Interfaces/IAppConfigRepository.cs @@ -0,0 +1,8 @@ +using SPMS.Domain.Entities; + +namespace SPMS.Domain.Interfaces; + +public interface IAppConfigRepository : IRepository +{ + Task GetByKeyAsync(long serviceId, string configKey); +} diff --git a/SPMS.Infrastructure/DependencyInjection.cs b/SPMS.Infrastructure/DependencyInjection.cs index ce3a7e0..bdaf8bf 100644 --- a/SPMS.Infrastructure/DependencyInjection.cs +++ b/SPMS.Infrastructure/DependencyInjection.cs @@ -30,6 +30,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // External Services services.AddScoped(); diff --git a/SPMS.Infrastructure/Persistence/Repositories/AppConfigRepository.cs b/SPMS.Infrastructure/Persistence/Repositories/AppConfigRepository.cs new file mode 100644 index 0000000..d4f4a34 --- /dev/null +++ b/SPMS.Infrastructure/Persistence/Repositories/AppConfigRepository.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using SPMS.Domain.Entities; +using SPMS.Domain.Interfaces; + +namespace SPMS.Infrastructure.Persistence.Repositories; + +public class AppConfigRepository : Repository, IAppConfigRepository +{ + public AppConfigRepository(AppDbContext context) : base(context) { } + + public async Task GetByKeyAsync(long serviceId, string configKey) + { + return await _dbSet.FirstOrDefaultAsync(c => c.ServiceId == serviceId && c.ConfigKey == configKey); + } +}