using LiteCqrs; using Microsoft.EntityFrameworkCore; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Common.Models; namespace TeleWave.Application.Notifications.Subscribers; /// /// Кто подписан и на что. Нужен не ради красоты: рассылка уходит в чужие чаты, и админ должен /// видеть список — иначе «бот кому-то что-то шлёт» проверяется только по логам Telegram. /// public sealed class ListTelegramSubscribersQueryHandler( IAppDbContext dbContext, IIdentityService identity ) : IQueryHandler>> { public async Task>> Handle( ListTelegramSubscribersQuery query, CancellationToken cancellationToken ) { var subscribers = await dbContext .TelegramSubscribers.AsNoTracking() .Include(s => s.Subscriptions) .OrderByDescending(s => s.LastSeenAt ?? s.CreatedAt) .ToListAsync(cancellationToken); var channelNames = await dbContext .Channels.AsNoTracking() .ToDictionaryAsync(c => c.Id, c => c.Name, cancellationToken); // Имена пользователей — через порт: Identity живёт в инфраструктуре, и таблицы его // прикладной слой не видит. var userNames = new Dictionary(); foreach (var userId in subscribers.Select(s => s.UserId).Distinct()) { if (await identity.GetProfileAsync(userId, cancellationToken) is { } profile) userNames[userId] = profile.UserName; } var result = subscribers .Select(s => new TelegramSubscriberDto( s.Id, s.ChatId, s.DisplayName, userNames.GetValueOrDefault(s.UserId), s.IsStopped, s.CreatedAt, s.LastSeenAt, s.Subscriptions.GroupBy(x => x.ChannelId) .Select(g => new TelegramSubscriptionDto( g.Key, // Канал могли удалить — подписки на него остаются висеть, и это надо видеть. channelNames.GetValueOrDefault(g.Key) ?? "—", g.Select(x => x.Kind.ToString()).OrderBy(x => x).ToList() )) .ToList() )) .ToList(); return Result.Success>(result); } }