using MediatR; using Web.Api.Common; using Web.Api.Extensions; namespace Web.Api.Endpoints.V1.Stats; internal sealed class ProductSales : IEndpoint { public void MapEndpoint(IEndpointRouteBuilder app) { app.MapGet("v1/stats/products", async ( ISender sender, CancellationToken ct, int gameID = 0 ) => { var query = new Application.Features.Api.V1.Stats.ProductSales.Query(gameID); var result = await sender.Send(query, ct); return result.Match( data => ApiResponse.Ok(data), CustomResults.Problem ); }) .WithTags("통계") .WithGroupName("public") .WithName("GetProductSalesStats") .WithSummary("상품 판매 통계 (게임사별)") .WithDescription(""" 특정 게임사의 **모든 상품별 판매 통계**를 반환합니다. ### 포함 수치 (상품 별) | 필드 | 의미 | |---|---| | `soldQuantity` | 누적 판매 수량 (Pending/Cancelled/Refunded 제외) | | `salesAmount` | 누적 판매 금액 (단가 × 수량) | | `couponGeneratedCount` | 발급된 총 쿠폰 코드 수 (쿠폰 상품만) | | `couponIssuedCount` | 회원 보관함에 발급된 코드 수 (Status=Issued) | | `couponUsedCount` | 사용 완료된 코드 수 (Status=Used) | | `couponAvailableCount` | 미사용 / 미발급 코드 수 (Status=Available) | ### 필수 scope - `read:stats` ### 쿼리 파라미터 - `gameID` (필수) — 게임 ID ### 에러 - `400` — gameID 누락 - `404` — 해당 게임 없음 """) .Produces(StatusCodes.Status200OK) .ProducesProblem(StatusCodes.Status400BadRequest) .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) .RequireAuthorization(policy => policy .AddAuthenticationSchemes("ApiKey", "OAuth2Bearer") .RequireAuthenticatedUser()) .RequireScope("read:stats"); } }