SPMS_API/SPMS.API/Controllers/StatsController.cs
SEAN f04eb44fff improvement: 서비스 스코프 정책 고정 (#199)
- ErrorCodes.ServiceScopeRequired("133") 추가
- SpmsException.Forbidden 팩토리 추가
- ServiceCodeMiddleware 3-카테고리 라우팅 (SKIP/REQUIRED/OPTIONAL_FOR_ADMIN)
- Swagger 필터 stats/device-list X-Service-Code optional 표시
- StatsController/DeviceController GetOptionalServiceId() 적용
- IStatsService/IDeviceService/레포지토리 시그니처 long? serviceId 변경
- StatsService/DeviceService null serviceId 전체 서비스 모드 처리

Closes #199
2026-02-24 17:11:30 +09:00

101 lines
4.5 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using SPMS.Application.DTOs.Stats;
using SPMS.Application.Interfaces;
using SPMS.Domain.Common;
namespace SPMS.API.Controllers;
[ApiController]
[Route("v1/in/stats")]
[ApiExplorerSettings(GroupName = "stats")]
public class StatsController : ControllerBase
{
private readonly IStatsService _statsService;
public StatsController(IStatsService statsService)
{
_statsService = statsService;
}
[HttpPost("daily")]
[SwaggerOperation(Summary = "일별 통계 조회", Description = "기간별 일별 발송/성공/실패/열람 통계를 조회합니다.")]
public async Task<IActionResult> GetDailyAsync([FromBody] DailyStatRequestDto request)
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetDailyAsync(serviceId, request);
return Ok(ApiResponse<DailyStatResponseDto>.Success(result, "조회 성공"));
}
[HttpPost("summary")]
[SwaggerOperation(Summary = "요약 통계 조회", Description = "대시보드 요약 통계를 조회합니다.")]
public async Task<IActionResult> GetSummaryAsync()
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetSummaryAsync(serviceId);
return Ok(ApiResponse<SummaryStatResponseDto>.Success(result, "조회 성공"));
}
[HttpPost("message")]
[SwaggerOperation(Summary = "메시지별 통계 조회", Description = "특정 메시지의 발송 통계를 조회합니다.")]
public async Task<IActionResult> GetMessageStatAsync([FromBody] MessageStatRequestDto request)
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetMessageStatAsync(serviceId, request);
return Ok(ApiResponse<MessageStatResponseDto>.Success(result, "조회 성공"));
}
[HttpPost("hourly")]
[SwaggerOperation(Summary = "시간대별 통계 조회", Description = "시간대별 발송 추이를 조회합니다.")]
public async Task<IActionResult> GetHourlyAsync([FromBody] HourlyStatRequestDto request)
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetHourlyAsync(serviceId, request);
return Ok(ApiResponse<HourlyStatResponseDto>.Success(result, "조회 성공"));
}
[HttpPost("device")]
[SwaggerOperation(Summary = "디바이스 통계 조회", Description = "플랫폼/모델별 디바이스 분포를 조회합니다.")]
public async Task<IActionResult> GetDeviceStatAsync()
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetDeviceStatAsync(serviceId);
return Ok(ApiResponse<DeviceStatResponseDto>.Success(result, "조회 성공"));
}
[HttpPost("export")]
[SwaggerOperation(Summary = "통계 리포트 다운로드", Description = "일별/시간대별/플랫폼별 통계를 엑셀(.xlsx) 파일로 다운로드합니다.")]
public async Task<IActionResult> ExportReportAsync([FromBody] StatsExportRequestDto request)
{
var serviceId = GetOptionalServiceId();
var fileBytes = await _statsService.ExportReportAsync(serviceId, request);
var fileName = $"stats_report_{request.StartDate}_{request.EndDate}.xlsx";
return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
[HttpPost("failure")]
[SwaggerOperation(Summary = "실패원인 통계 조회", Description = "실패 원인별 집계를 상위 N개로 조회합니다.")]
public async Task<IActionResult> GetFailureStatAsync([FromBody] FailureStatRequestDto request)
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetFailureStatAsync(serviceId, request);
return Ok(ApiResponse<FailureStatResponseDto>.Success(result, "조회 성공"));
}
[HttpPost("send-log")]
[SwaggerOperation(Summary = "발송 상세 로그 조회", Description = "특정 메시지의 개별 디바이스별 발송 상세 로그를 조회합니다.")]
public async Task<IActionResult> GetSendLogDetailAsync([FromBody] SendLogDetailRequestDto request)
{
var serviceId = GetOptionalServiceId();
var result = await _statsService.GetSendLogDetailAsync(serviceId, request);
return Ok(ApiResponse<SendLogDetailResponseDto>.Success(result, "조회 성공"));
}
private long? GetOptionalServiceId()
{
if (HttpContext.Items.TryGetValue("ServiceId", out var obj) && obj is long id)
return id;
return null;
}
}