Handler.cs 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Domain.Entities.Stocks.ValueObject;
  4. using Microsoft.EntityFrameworkCore;
  5. namespace Application.Features.Api.Stocks.GetIndices;
  6. /// <summary>
  7. /// 대표 시장지수 — 최신 거래일의 코스피/코스닥(+KRX 100)만 반환 (헤더 티커 + /market, d0 §3-D).
  8. /// 지수명이 정확히 대표명과 일치하는 행만 취하고, 계열 순서(KOSPI→KOSDAQ→KRX)로 정렬한다.
  9. /// </summary>
  10. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
  11. {
  12. // (계열, 대표 지수명) — KRX OpenAPI IDX_NM 기준
  13. private static readonly (MarketIndexSeries Series, string Name)[] KeyIndices =
  14. [
  15. (MarketIndexSeries.KOSPI, "코스피"),
  16. (MarketIndexSeries.KOSDAQ, "코스닥"),
  17. (MarketIndexSeries.KRX, "KRX 100")
  18. ];
  19. public async Task<Response> Handle(Query request, CancellationToken ct)
  20. {
  21. var latest = await db.IndexDailyPrice.AsNoTracking().OrderByDescending(c => c.TradeDate).Select(c => (DateOnly?)c.TradeDate).FirstOrDefaultAsync(ct);
  22. if (latest is null)
  23. {
  24. return new Response
  25. {
  26. List = []
  27. };
  28. }
  29. var names = KeyIndices.Select(c => c.Name).ToArray();
  30. var rows = await db.IndexDailyPrice.AsNoTracking()
  31. .Where(c => c.TradeDate == latest.Value && names.Contains(c.IndexName))
  32. .Select(c => new Response.Row
  33. {
  34. Series = c.Series,
  35. Name = c.IndexName,
  36. Close = c.Close,
  37. ChangeVal = c.ChangeVal,
  38. FlucRateBp = c.FlucRateBp,
  39. TradeDate = c.TradeDate
  40. })
  41. .ToListAsync(ct);
  42. // 대표 지수명 순서(KOSPI→KOSDAQ→KRX)로 정렬
  43. var ordered = KeyIndices
  44. .Select(k => rows.FirstOrDefault(r => r.Series == k.Series && r.Name == k.Name))
  45. .Where(r => r is not null)
  46. .Select(r => r!)
  47. .ToList();
  48. return new Response
  49. {
  50. List = ordered
  51. };
  52. }
  53. }