diff --git a/SPMS.API/Controllers/BannerController.cs b/SPMS.API/Controllers/BannerController.cs new file mode 100644 index 0000000..b68c9ce --- /dev/null +++ b/SPMS.API/Controllers/BannerController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; +using SPMS.Application.DTOs.Banner; +using SPMS.Application.Interfaces; +using SPMS.Domain.Common; + +namespace SPMS.API.Controllers; + +[ApiController] +[Route("v1/in/public/banner")] +[ApiExplorerSettings(GroupName = "public")] +public class BannerController : ControllerBase +{ + private readonly IBannerService _bannerService; + + public BannerController(IBannerService bannerService) + { + _bannerService = bannerService; + } + + [HttpPost("list")] + [SwaggerOperation(Summary = "배너 목록", Description = "활성화된 배너 목록을 조회합니다. position으로 필터링 가능.")] + public async Task GetListAsync([FromBody] BannerListRequestDto request) + { + var serviceCode = Request.Headers["X-Service-Code"].FirstOrDefault(); + if (string.IsNullOrWhiteSpace(serviceCode)) + return BadRequest(ApiResponse.Fail(ErrorCodes.BadRequest, "X-Service-Code 헤더가 필요합니다.")); + + var result = await _bannerService.GetListAsync(serviceCode, request); + return Ok(ApiResponse.Success(result)); + } +} diff --git a/SPMS.Application/DTOs/Banner/BannerListRequestDto.cs b/SPMS.Application/DTOs/Banner/BannerListRequestDto.cs new file mode 100644 index 0000000..25de84b --- /dev/null +++ b/SPMS.Application/DTOs/Banner/BannerListRequestDto.cs @@ -0,0 +1,6 @@ +namespace SPMS.Application.DTOs.Banner; + +public class BannerListRequestDto +{ + public string? Position { get; set; } +} diff --git a/SPMS.Application/DTOs/Banner/BannerListResponseDto.cs b/SPMS.Application/DTOs/Banner/BannerListResponseDto.cs new file mode 100644 index 0000000..0accbf4 --- /dev/null +++ b/SPMS.Application/DTOs/Banner/BannerListResponseDto.cs @@ -0,0 +1,36 @@ +using System.Text.Json.Serialization; + +namespace SPMS.Application.DTOs.Banner; + +public class BannerListResponseDto +{ + [JsonPropertyName("items")] + public List Items { get; set; } = new(); + + [JsonPropertyName("total_count")] + public int TotalCount { get; set; } +} + +public class BannerItemDto +{ + [JsonPropertyName("banner_id")] + public long BannerId { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; + + [JsonPropertyName("image_url")] + public string ImageUrl { get; set; } = string.Empty; + + [JsonPropertyName("link_url")] + public string? LinkUrl { get; set; } + + [JsonPropertyName("link_type")] + public string? LinkType { get; set; } + + [JsonPropertyName("position")] + public string Position { get; set; } = string.Empty; + + [JsonPropertyName("sort_order")] + public int SortOrder { get; set; } +} diff --git a/SPMS.Application/DependencyInjection.cs b/SPMS.Application/DependencyInjection.cs index 0296867..7b8790c 100644 --- a/SPMS.Application/DependencyInjection.cs +++ b/SPMS.Application/DependencyInjection.cs @@ -13,6 +13,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/SPMS.Application/Interfaces/IBannerService.cs b/SPMS.Application/Interfaces/IBannerService.cs new file mode 100644 index 0000000..cf79ae7 --- /dev/null +++ b/SPMS.Application/Interfaces/IBannerService.cs @@ -0,0 +1,8 @@ +using SPMS.Application.DTOs.Banner; + +namespace SPMS.Application.Interfaces; + +public interface IBannerService +{ + Task GetListAsync(string serviceCode, BannerListRequestDto request); +} diff --git a/SPMS.Application/Services/BannerService.cs b/SPMS.Application/Services/BannerService.cs new file mode 100644 index 0000000..bc7bd1f --- /dev/null +++ b/SPMS.Application/Services/BannerService.cs @@ -0,0 +1,43 @@ +using SPMS.Application.DTOs.Banner; +using SPMS.Application.Interfaces; +using SPMS.Domain.Common; +using SPMS.Domain.Exceptions; +using SPMS.Domain.Interfaces; + +namespace SPMS.Application.Services; + +public class BannerService : IBannerService +{ + private readonly IBannerRepository _bannerRepository; + private readonly IServiceRepository _serviceRepository; + + public BannerService(IBannerRepository bannerRepository, IServiceRepository serviceRepository) + { + _bannerRepository = bannerRepository; + _serviceRepository = serviceRepository; + } + + public async Task GetListAsync(string serviceCode, BannerListRequestDto request) + { + var service = await _serviceRepository.GetByServiceCodeAsync(serviceCode); + if (service == null) + throw new SpmsException(ErrorCodes.NotFound, "존재하지 않는 서비스입니다.", 404); + + var items = await _bannerRepository.GetActiveListAsync(service.Id, request.Position); + + return new BannerListResponseDto + { + Items = items.Select(b => new BannerItemDto + { + BannerId = b.Id, + Title = b.Title, + ImageUrl = b.ImageUrl, + LinkUrl = b.LinkUrl, + LinkType = b.LinkType, + Position = b.Position, + SortOrder = b.SortOrder + }).ToList(), + TotalCount = items.Count + }; + } +} diff --git a/SPMS.Domain/Interfaces/IBannerRepository.cs b/SPMS.Domain/Interfaces/IBannerRepository.cs new file mode 100644 index 0000000..e476431 --- /dev/null +++ b/SPMS.Domain/Interfaces/IBannerRepository.cs @@ -0,0 +1,8 @@ +using SPMS.Domain.Entities; + +namespace SPMS.Domain.Interfaces; + +public interface IBannerRepository : IRepository +{ + Task> GetActiveListAsync(long serviceId, string? position = null); +} diff --git a/SPMS.Infrastructure/DependencyInjection.cs b/SPMS.Infrastructure/DependencyInjection.cs index d4aabff..5481114 100644 --- a/SPMS.Infrastructure/DependencyInjection.cs +++ b/SPMS.Infrastructure/DependencyInjection.cs @@ -28,6 +28,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // External Services services.AddScoped(); diff --git a/SPMS.Infrastructure/Persistence/Repositories/BannerRepository.cs b/SPMS.Infrastructure/Persistence/Repositories/BannerRepository.cs new file mode 100644 index 0000000..c28c6fc --- /dev/null +++ b/SPMS.Infrastructure/Persistence/Repositories/BannerRepository.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using SPMS.Domain.Entities; +using SPMS.Domain.Interfaces; + +namespace SPMS.Infrastructure.Persistence.Repositories; + +public class BannerRepository : Repository, IBannerRepository +{ + public BannerRepository(AppDbContext context) : base(context) { } + + public async Task> GetActiveListAsync(long serviceId, string? position = null) + { + var query = _dbSet.Where(b => b.ServiceId == serviceId && b.IsActive); + + if (!string.IsNullOrWhiteSpace(position)) + query = query.Where(b => b.Position == position); + + return await query + .OrderBy(b => b.SortOrder) + .ToListAsync(); + } +}