diff --git a/SPMS.API/Controllers/StatsController.cs b/SPMS.API/Controllers/StatsController.cs index 7fe6391..544d566 100644 --- a/SPMS.API/Controllers/StatsController.cs +++ b/SPMS.API/Controllers/StatsController.cs @@ -19,7 +19,7 @@ public class StatsController : ControllerBase } [HttpPost("daily")] - [SwaggerOperation(Summary = "일별 통계 조회", Description = "기간별 일별 발송/성공/실패/열람 통계를 조회합니다.")] + [SwaggerOperation(Summary = "일별 통계 조회", Description = "기간별 일별 발송/성공/실패/열람 통계를 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetDailyAsync([FromBody] DailyStatRequestDto request) { var serviceId = GetOptionalServiceId(); @@ -28,7 +28,7 @@ public class StatsController : ControllerBase } [HttpPost("summary")] - [SwaggerOperation(Summary = "요약 통계 조회", Description = "대시보드 요약 통계를 조회합니다.")] + [SwaggerOperation(Summary = "요약 통계 조회", Description = "대시보드 요약 통계를 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetSummaryAsync() { var serviceId = GetOptionalServiceId(); @@ -37,7 +37,7 @@ public class StatsController : ControllerBase } [HttpPost("message")] - [SwaggerOperation(Summary = "메시지별 통계 조회", Description = "특정 메시지의 발송 통계를 조회합니다.")] + [SwaggerOperation(Summary = "메시지별 통계 조회", Description = "특정 메시지의 발송 통계를 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetMessageStatAsync([FromBody] MessageStatRequestDto request) { var serviceId = GetOptionalServiceId(); @@ -46,7 +46,7 @@ public class StatsController : ControllerBase } [HttpPost("hourly")] - [SwaggerOperation(Summary = "시간대별 통계 조회", Description = "시간대별 발송 추이를 조회합니다.")] + [SwaggerOperation(Summary = "시간대별 통계 조회", Description = "시간대별 발송 추이를 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetHourlyAsync([FromBody] HourlyStatRequestDto request) { var serviceId = GetOptionalServiceId(); @@ -55,7 +55,7 @@ public class StatsController : ControllerBase } [HttpPost("device")] - [SwaggerOperation(Summary = "디바이스 통계 조회", Description = "플랫폼/모델별 디바이스 분포를 조회합니다.")] + [SwaggerOperation(Summary = "디바이스 통계 조회", Description = "플랫폼/모델별 디바이스 분포를 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetDeviceStatAsync() { var serviceId = GetOptionalServiceId(); @@ -64,7 +64,7 @@ public class StatsController : ControllerBase } [HttpPost("export")] - [SwaggerOperation(Summary = "통계 리포트 다운로드", Description = "일별/시간대별/플랫폼별 통계를 엑셀(.xlsx) 파일로 다운로드합니다.")] + [SwaggerOperation(Summary = "통계 리포트 다운로드", Description = "일별/시간대별/플랫폼별 통계를 엑셀(.xlsx) 파일로 다운로드합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task ExportReportAsync([FromBody] StatsExportRequestDto request) { var serviceId = GetOptionalServiceId(); @@ -74,7 +74,7 @@ public class StatsController : ControllerBase } [HttpPost("failure")] - [SwaggerOperation(Summary = "실패원인 통계 조회", Description = "실패 원인별 집계를 상위 N개로 조회합니다.")] + [SwaggerOperation(Summary = "실패원인 통계 조회", Description = "실패 원인별 집계를 상위 N개로 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetFailureStatAsync([FromBody] FailureStatRequestDto request) { var serviceId = GetOptionalServiceId(); @@ -83,7 +83,7 @@ public class StatsController : ControllerBase } [HttpPost("send-log")] - [SwaggerOperation(Summary = "발송 상세 로그 조회", Description = "특정 메시지의 개별 디바이스별 발송 상세 로그를 조회합니다.")] + [SwaggerOperation(Summary = "발송 상세 로그 조회", Description = "특정 메시지의 개별 디바이스별 발송 상세 로그를 조회합니다. X-Service-Code 헤더 미지정 시 전체 서비스 통계를 조회합니다.")] public async Task GetSendLogDetailAsync([FromBody] SendLogDetailRequestDto request) { var serviceId = GetOptionalServiceId(); diff --git a/SPMS.API/Filters/SpmsHeaderOperationFilter.cs b/SPMS.API/Filters/SpmsHeaderOperationFilter.cs index 45fc3c4..fbe4da5 100644 --- a/SPMS.API/Filters/SpmsHeaderOperationFilter.cs +++ b/SPMS.API/Filters/SpmsHeaderOperationFilter.cs @@ -16,14 +16,15 @@ public class SpmsHeaderOperationFilter : IOperationFilter operation.Parameters ??= new List(); // v1/in/* 중 X-Service-Code 대상 경로 판별 - var isStatsOrDeviceList = relativePath.StartsWith("v1/in/stats") || - relativePath == "v1/in/device/list"; - var isRequired = relativePath.StartsWith("v1/in/message") || + var isOptional = relativePath.StartsWith("v1/in/stats") || + relativePath == "v1/in/device/list" || + relativePath == "v1/in/message/list"; + var isRequired = (relativePath.StartsWith("v1/in/message") && !isOptional) || relativePath.StartsWith("v1/in/push") || relativePath.StartsWith("v1/in/file"); - var isDeviceNonList = relativePath.StartsWith("v1/in/device") && !isStatsOrDeviceList; + var isDeviceNonList = relativePath.StartsWith("v1/in/device") && !isOptional; - if (isStatsOrDeviceList) + if (isOptional) { operation.Parameters.Add(new OpenApiParameter { diff --git a/SPMS.Application/Services/StatsService.cs b/SPMS.Application/Services/StatsService.cs index 8837b1e..6fb144b 100644 --- a/SPMS.Application/Services/StatsService.cs +++ b/SPMS.Application/Services/StatsService.cs @@ -443,13 +443,13 @@ public class StatsService : IStatsService private static (DateOnly Start, DateOnly End) ParseDateRange(string startStr, string endStr) { if (!DateOnly.TryParseExact(startStr, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var start)) - throw new SpmsException(ErrorCodes.BadRequest, "start_date 형식이 올바르지 않습니다. (yyyy-MM-dd)", 400); + throw new SpmsException(ErrorCodes.StatsDateRangeInvalid, "start_date 형식이 올바르지 않습니다. (yyyy-MM-dd)", 400); if (!DateOnly.TryParseExact(endStr, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var end)) - throw new SpmsException(ErrorCodes.BadRequest, "end_date 형식이 올바르지 않습니다. (yyyy-MM-dd)", 400); + throw new SpmsException(ErrorCodes.StatsDateRangeInvalid, "end_date 형식이 올바르지 않습니다. (yyyy-MM-dd)", 400); if (start > end) - throw new SpmsException(ErrorCodes.BadRequest, "start_date가 end_date보다 클 수 없습니다.", 400); + throw new SpmsException(ErrorCodes.StatsDateRangeInvalid, "start_date가 end_date보다 클 수 없습니다.", 400); return (start, end); } diff --git a/SPMS.Domain/Common/ErrorCodes.cs b/SPMS.Domain/Common/ErrorCodes.cs index 76ee6fc..96bc07d 100644 --- a/SPMS.Domain/Common/ErrorCodes.cs +++ b/SPMS.Domain/Common/ErrorCodes.cs @@ -51,6 +51,10 @@ public static class ErrorCodes public const string JobNotFound = "163"; public const string JobAlreadyCompleted = "164"; + // === Stats (7) === + public const string StatsDateRangeInvalid = "171"; + public const string StatsServiceScopeInvalid = "172"; + // === File (8) === public const string FileNotFound = "181"; public const string FileTypeNotAllowed = "182";