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); + } +}