| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Domain.Entities.Store.ValueObject;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- namespace Application.Features.Api.Store.Games.Detail;
- internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Result<Response>>
- {
- public async Task<Result<Response>> Handle(Query request, CancellationToken ct)
- {
- var now = DateTime.UtcNow;
- var game = await db.Game.AsNoTracking()
- .Where(g => g.ID == request.ID && g.IsActive)
- .Select(g => new
- {
- g.ID,
- g.KorName,
- g.EngName,
- g.Publisher,
- g.Thumbnail,
- g.BigImage,
- g.Description,
- g.Link,
- g.TrailerUrl,
- g.MinSpec,
- g.RecommendedSpec,
- g.CreatedAt,
- g.ReleaseDate
- })
- .FirstOrDefaultAsync(ct);
- if (game is null)
- {
- return Result.Failure<Response>(Error.NotFound("Game.NotFound", "게임을 찾을 수 없습니다."));
- }
- var genres = await db.GameGenreMap.AsNoTracking()
- .Where(m => m.GameID == game.ID && m.GameGenre.IsActive)
- .OrderBy(m => m.GameGenre.Order)
- .Select(m => m.GameGenre.KorName)
- .ToListAsync(ct);
- var categories = await db.GameCategoryMap.AsNoTracking()
- .Where(m => m.GameID == game.ID && m.GameCategory.IsActive)
- .OrderBy(m => m.GameCategory.Order)
- .Select(m => m.GameCategory.KorName)
- .ToListAsync(ct);
- var images = await db.GameImage.AsNoTracking()
- .Where(i => i.GameID == game.ID && !i.IsDisabled)
- .OrderBy(i => i.Order).ThenBy(i => i.ID)
- .Select(i => i.Url)
- .ToListAsync(ct);
- var languages = await db.GameLanguageSupport.AsNoTracking()
- .Where(s => s.GameID == game.ID && s.GameLanguage.IsActive)
- .OrderBy(s => s.GameLanguage.Order).ThenBy(s => s.GameLanguage.KorName)
- .Select(s => new Response.LanguageRow(s.GameLanguage.KorName, s.GameLanguage.EngName, s.Interface, s.FullAudio, s.Subtitles))
- .ToListAsync(ct);
- // 같은 게임 활성 상품 중 무작위 8개 (NEWID 셔플)
- var productsRaw = await db.Product.AsNoTracking()
- .Where(p => p.GameID == game.ID
- && p.IsActive
- && (p.SaleStartAt == null || p.SaleStartAt <= now)
- && (p.SaleEndAt == null || p.SaleEndAt > now))
- .OrderBy(p => Guid.NewGuid())
- .Take(8)
- .Select(p => new
- {
- p.ID,
- p.Name,
- p.Thumbnail,
- p.Price,
- p.DiscountType,
- p.DiscountValue
- })
- .ToListAsync(ct);
- var products = productsRaw.Select(p => new Response.ProductRow(
- p.ID,
- p.Name,
- p.Thumbnail,
- p.Price,
- CalcSalePrice(p.Price, p.DiscountType, p.DiscountValue)
- )).ToList();
- return Result.Success(new Response(
- game.ID,
- game.KorName,
- game.EngName,
- game.Publisher,
- game.Thumbnail,
- game.BigImage,
- game.Description,
- game.Link,
- game.TrailerUrl,
- game.MinSpec,
- game.RecommendedSpec,
- game.CreatedAt,
- game.ReleaseDate,
- genres,
- categories,
- images,
- languages,
- products
- ));
- }
- private static int CalcSalePrice(int price, DiscountType type, int value)
- {
- return type switch
- {
- DiscountType.Percent => Math.Max(0, price - (price * value / 100)),
- DiscountType.Amount => Math.Max(0, price - value),
- _ => price
- };
- }
- }
|