using Microsoft.EntityFrameworkCore; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; namespace PnvPanel.Application.Admin.Billing; public sealed class ListPaymentRequestsQueryHandler( IAppDbContext dbContext, IIdentityService identityService ) : IQueryHandler>> { public async Task>> Handle( ListPaymentRequestsQuery query, CancellationToken cancellationToken ) { var page = query.Page <= 0 ? 1 : query.Page; var pageSize = query.PageSize is <= 0 or > 100 ? 20 : query.PageSize; var requestsQuery = dbContext.PaymentRequests.AsNoTracking(); if (query.StatusFilter is { } status) requestsQuery = requestsQuery.Where(r => r.Status == status); if (query.KindFilter is { } kind) requestsQuery = requestsQuery.Where(r => r.Kind == kind); if (!string.IsNullOrWhiteSpace(query.Search)) { // PaymentRequest хранит только UserId — резолвим совпадающих пользователей ДО пагинации // (иначе "поиск по имени" фильтровал бы уже отобранную страницу, а не весь набор). var matchingUserIds = await identityService.FindUserIdsByUserNameAsync( query.Search.Trim(), cancellationToken ); requestsQuery = requestsQuery.Where(r => matchingUserIds.Contains(r.UserId)); } var page1 = await requestsQuery .OrderByDescending(r => r.CreatedAt) .ToPagedListAsync(page, pageSize, cancellationToken); var userNames = await identityService.GetUserNamesAsync( page1.Items.Select(r => r.UserId).Distinct().ToList(), cancellationToken ); var items = page1 .Items.Select(r => new AdminPaymentRequestDto( r.Id, r.UserId, userNames.GetValueOrDefault(r.UserId, "?"), r.Kind, r.Period, r.AmountSnapshot, r.Status, r.CreatedAt )) .ToList(); return Result.Success( new PagedList(items, page1.Total, page1.Page, page1.PageSize) ); } }