using Application.Abstractions.Messaging; using SharedKernel.Extensions; using Application.Abstractions.Data; using Microsoft.EntityFrameworkCore; namespace Application.Features.Admin.Member.Logs.Login.Search; public sealed class Handler(IAppDbContext db) : IQueryHandler { public async Task Handle(Query request, CancellationToken ct) { var query = db.MemberLoginLog.AsNoTracking().Include(x => x.Member).AsQueryable(); if (!string.IsNullOrWhiteSpace(request.Keyword)) { query = request.Search switch { 1 => query.Where(x => x.MemberID.HasValue && x.MemberID.Value.ToString().Contains(request.Keyword)), 2 => query.Where(x => x.Member != null && x.Member.Email.Contains(request.Keyword)), 3 => query.Where(x => x.Member != null && x.Member.Name != null && x.Member.Name.Contains(request.Keyword)), _ => query }; } if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt)) { query = query.Where(x => x.CreatedAt >= startAt); } if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt)) { query = query.Where(x => x.CreatedAt <= endAt); } var total = await query.CountAsync(ct); var skip = (request.PageNum - 1) * request.PerPage; var list = await query .OrderByDescending(x => x.ID) .Skip(skip) .Take(request.PerPage) .Select(x => new { x.ID, x.MemberID, MemberName = x.Member != null ? x.Member.Name : null, x.Account, x.Success, x.Reason, x.IpAddress, x.UserAgent, x.CreatedAt }) .ToListAsync(ct); var rows = list.Select((x, idx) => new Response.Row { Num = total - skip - idx, ID = x.ID, MemberID = x.MemberID, MemberName = x.MemberName, Account = x.Account, Success = x.Success, Reason = x.Reason, IpAddress = x.IpAddress, UserAgent = x.UserAgent, Browser = ParseUserAgent(x.UserAgent, "browser"), OS = ParseUserAgent(x.UserAgent, "os"), Device = ParseUserAgent(x.UserAgent, "device"), CreatedAt = x.CreatedAt }).ToList(); return new Response { Total = total, List = rows }; } private static string? ParseUserAgent(string? userAgent, string type) { if (string.IsNullOrWhiteSpace(userAgent)) { return null; } return type switch { "browser" => ExtractBrowser(userAgent), "os" => ExtractOS(userAgent), "device" => ExtractDevice(userAgent), _ => null }; } private static string ExtractBrowser(string ua) { if (ua.Contains("Edg/")) return "Edge"; if (ua.Contains("Chrome/")) return "Chrome"; if (ua.Contains("Firefox/")) return "Firefox"; if (ua.Contains("Safari/") && !ua.Contains("Chrome")) return "Safari"; if (ua.Contains("MSIE") || ua.Contains("Trident/")) return "IE"; return "Unknown"; } private static string ExtractOS(string ua) { if (ua.Contains("Windows NT 10")) return "Windows 10"; if (ua.Contains("Windows NT 6.3")) return "Windows 8.1"; if (ua.Contains("Windows NT 6.1")) return "Windows 7"; if (ua.Contains("Windows")) return "Windows"; if (ua.Contains("Mac OS X")) return "macOS"; if (ua.Contains("Android")) return "Android"; if (ua.Contains("iPhone") || ua.Contains("iPad")) return "iOS"; if (ua.Contains("Linux")) return "Linux"; return "Unknown"; } private static string ExtractDevice(string ua) { if (ua.Contains("Mobile") || ua.Contains("Android") && !ua.Contains("Tablet")) return "Mobile"; if (ua.Contains("Tablet") || ua.Contains("iPad")) return "Tablet"; return "Desktop"; } }