using Application.Abstractions.Messaging; using Application.Abstractions.Data; using Application.Abstractions.Cache; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Banner.Item.GetByCode; public sealed class Handler(IAppDbContext db, ICacheService cache) : IQueryHandler { private static readonly TimeSpan ActiveCacheTtl = TimeSpan.FromMinutes(10); public async Task Handle(Query request, CancellationToken ct) { var cacheKey = request.OnlyActive ? CacheKeys.BannerByCodeActive(request.Code) : CacheKeys.BannerByCode(request.Code); var cached = await cache.GetAsync(cacheKey, ct); if (cached is not null) { return cached; } var position = await db.BannerPosition.AsNoTracking().FirstOrDefaultAsync(c => c.Code == request.Code, ct); if (position is null) { return new Response(0, []); } var query = db.BannerItem.AsNoTracking().Include(c => c.BannerPosition).Where(c => c.PositionID == position.ID); if (request.OnlyActive) { var now = DateTime.UtcNow; query = query.Where(c => c.IsActive && (c.StartAt == null || c.StartAt <= now) && (c.EndAt == null || c.EndAt > now)); } var total = await query.CountAsync(ct); var list = await query .OrderBy(c => c.Order) .ThenByDescending(c => c.ID) .Select(c => new { c.ID, c.PositionID, PositionCode = c.BannerPosition.Code, PositionSubject = c.BannerPosition.Subject, c.Subject, c.DesktopImage, c.MobileImage, c.Link, c.LinkTarget, c.Order, c.IsActive, c.StartAt, c.EndAt, c.UpdatedAt, c.CreatedAt }) .ToListAsync(ct); var response = new Response( total, [..list.Select(c => new Response.Row( c.ID, c.PositionID, c.PositionCode, c.PositionSubject, c.Subject, c.DesktopImage, c.MobileImage, c.Link, c.LinkTarget, c.Order, c.IsActive, c.StartAt, c.EndAt, c.UpdatedAt, c.CreatedAt ))] ); if (request.OnlyActive) { await cache.SetAsync(cacheKey, response, ActiveCacheTtl, ct); } else { await cache.SetAsync(cacheKey, response, ct); } return response; } }