Handler.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using Application.Abstractions.Messaging;
  2. using Application.Abstractions.Data;
  3. using Microsoft.EntityFrameworkCore;
  4. namespace Application.Features.Director.LoginLog.Search;
  5. public sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
  6. {
  7. public async Task<Response> Handle(Query request, CancellationToken ct)
  8. {
  9. var query = db.AdminLoginLog.AsNoTracking().AsQueryable();
  10. // 키워드 검색
  11. if (!string.IsNullOrWhiteSpace(request.Keyword))
  12. {
  13. var kw = request.Keyword.Trim();
  14. query = request.Search switch
  15. {
  16. 1 => query.Where(x => x.Account.Contains(kw)),
  17. 2 => query.Where(x => x.IpAddress != null && x.IpAddress.Contains(kw)),
  18. _ => query.Where(x => x.Account.Contains(kw))
  19. };
  20. }
  21. // 성공/실패 필터
  22. if (request.Success.HasValue)
  23. {
  24. query = query.Where(x => x.Success == request.Success.Value);
  25. }
  26. // 날짜 필터
  27. if (!string.IsNullOrWhiteSpace(request.StartAt) && DateTime.TryParse(request.StartAt, out var startAt))
  28. {
  29. query = query.Where(x => x.CreatedAt >= startAt);
  30. }
  31. if (!string.IsNullOrWhiteSpace(request.EndAt) && DateTime.TryParse(request.EndAt, out var endAt))
  32. {
  33. query = query.Where(x => x.CreatedAt <= endAt);
  34. }
  35. var total = await query.CountAsync(ct);
  36. var skip = (request.PageNum - 1) * request.PerPage;
  37. var list = await query
  38. .OrderByDescending(x => x.ID)
  39. .Skip(skip)
  40. .Take(request.PerPage)
  41. .Select(x => new
  42. {
  43. x.ID,
  44. x.Account,
  45. x.Success,
  46. x.Reason,
  47. x.IpAddress,
  48. x.UserAgent,
  49. x.CreatedAt
  50. })
  51. .ToListAsync(ct);
  52. var rows = list.Select((x, idx) => new Response.Row
  53. {
  54. Num = total - skip - idx,
  55. ID = x.ID,
  56. Account = x.Account,
  57. Success = x.Success,
  58. Reason = x.Reason,
  59. IpAddress = x.IpAddress,
  60. UserAgent = x.UserAgent,
  61. Browser = ParseUserAgent(x.UserAgent, "browser"),
  62. OS = ParseUserAgent(x.UserAgent, "os"),
  63. Device = ParseUserAgent(x.UserAgent, "device"),
  64. CreatedAt = x.CreatedAt
  65. }).ToList();
  66. return new Response
  67. {
  68. Total = total,
  69. List = rows
  70. };
  71. }
  72. private static string? ParseUserAgent(string? userAgent, string type)
  73. {
  74. if (string.IsNullOrWhiteSpace(userAgent))
  75. {
  76. return null;
  77. }
  78. return type switch
  79. {
  80. "browser" => ExtractBrowser(userAgent),
  81. "os" => ExtractOS(userAgent),
  82. "device" => ExtractDevice(userAgent),
  83. _ => null
  84. };
  85. }
  86. private static string ExtractBrowser(string ua)
  87. {
  88. if (ua.Contains("Edg/"))
  89. {
  90. return "Edge";
  91. }
  92. if (ua.Contains("Chrome/"))
  93. {
  94. return "Chrome";
  95. }
  96. if (ua.Contains("Firefox/"))
  97. {
  98. return "Firefox";
  99. }
  100. if (ua.Contains("Safari/") && !ua.Contains("Chrome"))
  101. {
  102. return "Safari";
  103. }
  104. if (ua.Contains("MSIE") || ua.Contains("Trident/"))
  105. {
  106. return "IE";
  107. }
  108. return "Unknown";
  109. }
  110. private static string ExtractOS(string ua)
  111. {
  112. if (ua.Contains("Windows NT 10"))
  113. {
  114. return "Windows 10";
  115. }
  116. if (ua.Contains("Windows NT 6.3"))
  117. {
  118. return "Windows 8.1";
  119. }
  120. if (ua.Contains("Windows NT 6.1"))
  121. {
  122. return "Windows 7";
  123. }
  124. if (ua.Contains("Windows"))
  125. {
  126. return "Windows";
  127. }
  128. if (ua.Contains("Mac OS X"))
  129. {
  130. return "macOS";
  131. }
  132. if (ua.Contains("Android"))
  133. {
  134. return "Android";
  135. }
  136. if (ua.Contains("iPhone") || ua.Contains("iPad"))
  137. {
  138. return "iOS";
  139. }
  140. if (ua.Contains("Linux"))
  141. {
  142. return "Linux";
  143. }
  144. return "Unknown";
  145. }
  146. private static string ExtractDevice(string ua)
  147. {
  148. if (ua.Contains("Mobile") || (ua.Contains("Android") && !ua.Contains("Tablet")))
  149. {
  150. return "Mobile";
  151. }
  152. if (ua.Contains("Tablet") || ua.Contains("iPad"))
  153. {
  154. return "Tablet";
  155. }
  156. return "Desktop";
  157. }
  158. }