using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using SPMS.Application.DTOs.Push; using SPMS.Application.Interfaces; using SPMS.Domain.Common; namespace SPMS.API.Controllers; [ApiController] [Route("v1/in/push")] [ApiExplorerSettings(GroupName = "push")] public class PushController : ControllerBase { private readonly IPushService _pushService; public PushController(IPushService pushService) { _pushService = pushService; } [HttpPost("send")] [SwaggerOperation(Summary = "단건 발송", Description = "특정 디바이스에 푸시 메시지를 즉시 발송합니다.")] public async Task SendAsync([FromBody] PushSendRequestDto request) { var serviceId = GetServiceId(); var result = await _pushService.SendAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("send/tag")] [SwaggerOperation(Summary = "태그 발송", Description = "태그 조건에 해당하는 디바이스에 푸시 메시지를 발송합니다.")] public async Task SendByTagAsync([FromBody] PushSendTagRequestDto request) { var serviceId = GetServiceId(); var result = await _pushService.SendByTagAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("schedule")] [SwaggerOperation(Summary = "예약 발송", Description = "지정 시간에 푸시 메시지 발송을 예약합니다.")] public async Task ScheduleAsync([FromBody] PushScheduleRequestDto request) { var serviceId = GetServiceId(); var result = await _pushService.ScheduleAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("schedule/cancel")] [SwaggerOperation(Summary = "예약 취소", Description = "예약된 발송을 취소합니다.")] public async Task CancelScheduleAsync([FromBody] PushScheduleCancelRequestDto request) { var serviceId = GetServiceId(); await _pushService.CancelScheduleAsync(request); return Ok(ApiResponse.Success()); } [HttpPost("log")] [SwaggerOperation(Summary = "발송 로그 조회", Description = "푸시 발송 이력을 페이지 단위로 조회합니다.")] public async Task GetLogAsync([FromBody] PushLogRequestDto request) { var serviceId = GetServiceId(); var result = await _pushService.GetLogAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("send/bulk")] [SwaggerOperation(Summary = "대용량 발송", Description = "CSV 파일로 대량 푸시 발송을 요청합니다.")] public async Task SendBulkAsync(IFormFile file, [FromForm(Name = "message_code")] string messageCode) { var serviceId = GetServiceId(); if (file == null || file.Length == 0) throw new Domain.Exceptions.SpmsException(ErrorCodes.BadRequest, "CSV 파일은 필수입니다.", 400); if (string.IsNullOrWhiteSpace(messageCode)) throw new Domain.Exceptions.SpmsException(ErrorCodes.BadRequest, "message_code는 필수입니다.", 400); await using var stream = file.OpenReadStream(); var result = await _pushService.SendBulkAsync(serviceId, stream, messageCode); return Ok(ApiResponse.Success(result, "발송 요청이 접수되었습니다.")); } [HttpPost("job/status")] [SwaggerOperation(Summary = "발송 상태 조회", Description = "대용량/태그 발송 작업의 상태를 조회합니다.")] public async Task GetJobStatusAsync([FromBody] JobStatusRequestDto request) { var serviceId = GetServiceId(); var result = await _pushService.GetJobStatusAsync(serviceId, request); return Ok(ApiResponse.Success(result, "조회 성공")); } [HttpPost("job/cancel")] [SwaggerOperation(Summary = "발송 취소", Description = "대기 중이거나 처리 중인 작업을 취소합니다.")] public async Task CancelJobAsync([FromBody] JobCancelRequestDto request) { var serviceId = GetServiceId(); var result = await _pushService.CancelJobAsync(serviceId, request); return Ok(ApiResponse.Success(result, "발송이 취소되었습니다.")); } private long GetServiceId() { if (HttpContext.Items.TryGetValue("ServiceId", out var serviceIdObj) && serviceIdObj is long serviceId) return serviceId; throw new Domain.Exceptions.SpmsException(ErrorCodes.BadRequest, "서비스 식별 정보가 없습니다.", 400); } }