Handler.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Microsoft.EntityFrameworkCore;
  4. using SharedKernel.Results;
  5. namespace Application.Features.Api.Stocks.GetMeetingCalendar;
  6. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Result<Response>>
  7. {
  8. private const ushort MaxPerPage = 200;
  9. public async Task<Result<Response>> Handle(Query request, CancellationToken ct)
  10. {
  11. var page = request.Page < 1 ? 1 : request.Page;
  12. var perPage = request.PerPage is 0 or > MaxPerPage ? (ushort)30 : request.PerPage;
  13. var query = db.GeneralMeeting.AsNoTracking();
  14. if (request.From.HasValue)
  15. {
  16. query = query.Where(c => c.GmetDt >= request.From.Value);
  17. }
  18. if (request.To.HasValue)
  19. {
  20. query = query.Where(c => c.GmetDt <= request.To.Value);
  21. }
  22. var total = await query.CountAsync(ct);
  23. var meetings = await query
  24. .OrderByDescending(c => c.GmetDt)
  25. .ThenBy(c => c.IssucoCustno)
  26. .ThenBy(c => c.GmetKacd)
  27. .Skip((page - 1) * perPage)
  28. .Take(perPage)
  29. .Select(c => new
  30. {
  31. c.IssucoCustno,
  32. c.RgtStdDt,
  33. c.GmetDt,
  34. c.GmetTtm,
  35. c.GmetKacd,
  36. c.GmetPlace,
  37. c.EvoteYn,
  38. c.DferYn
  39. })
  40. .ToListAsync(ct);
  41. if (meetings.Count == 0)
  42. {
  43. return new Response { Total = total, List = [] };
  44. }
  45. var custnos = meetings.Select(c => c.IssucoCustno).Distinct().ToList();
  46. var stdDts = meetings.Select(c => c.RgtStdDt).Distinct().ToList();
  47. // 종목명·단축코드 (IssucoCustno 조인 — 한 회사에 여러 종목이면 대표 1개)
  48. var stockRows = await db.Stock.AsNoTracking()
  49. .Where(c => c.IssucoCustno != null && custnos.Contains(c.IssucoCustno.Value))
  50. .Select(c => new { Custno = c.IssucoCustno!.Value, c.Name, c.Code })
  51. .ToListAsync(ct);
  52. var stockByCustno = stockRows.GroupBy(c => c.Custno).ToDictionary(g => g.Key, g => g.First());
  53. // 안건수 집계 (회사, 기준일) 단위
  54. var agendaCounts = await db.MeetingAgenda.AsNoTracking()
  55. .Where(c => custnos.Contains(c.IssucoCustno) && stdDts.Contains(c.RgtStdDt))
  56. .GroupBy(c => new { c.IssucoCustno, c.RgtStdDt })
  57. .Select(g => new { g.Key.IssucoCustno, g.Key.RgtStdDt, Count = g.Count() })
  58. .ToListAsync(ct);
  59. var agendaLookup = agendaCounts.ToDictionary(c => (c.IssucoCustno, c.RgtStdDt), c => c.Count);
  60. var list = meetings.Select(c =>
  61. {
  62. stockByCustno.TryGetValue(c.IssucoCustno, out var stock);
  63. agendaLookup.TryGetValue((c.IssucoCustno, c.RgtStdDt), out var agendaCount);
  64. return new Response.Row
  65. {
  66. IssucoCustno = c.IssucoCustno,
  67. Name = stock?.Name,
  68. Code = stock?.Code,
  69. RgtStdDt = c.RgtStdDt,
  70. GmetDt = c.GmetDt,
  71. GmetTtm = c.GmetTtm,
  72. MeetingKind = c.GmetKacd,
  73. Place = c.GmetPlace,
  74. Evote = c.EvoteYn,
  75. Deferred = c.DferYn,
  76. AgendaCount = agendaCount
  77. };
  78. }).ToList();
  79. return new Response
  80. {
  81. Total = total,
  82. List = list
  83. };
  84. }
  85. }