Handler.cs 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Microsoft.EntityFrameworkCore;
  4. namespace Application.Features.Api.Forum.StockBoard.ListPosts;
  5. /// <summary>
  6. /// 종목 가상보드 글 목록 (d2 §④ M2) — Post.StockCode 파티션 필터(최신순), StockBoardStats + 종목명 헤더 포함.
  7. /// 삭제글 제외. 익명 열람.
  8. /// </summary>
  9. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
  10. {
  11. public async Task<Response> Handle(Query request, CancellationToken ct)
  12. {
  13. var stockCode = request.StockCode?.Trim() ?? string.Empty;
  14. var page = request.Page < 1 ? 1 : request.Page;
  15. var perPage = request.PerPage == 0 ? (ushort)20 : request.PerPage;
  16. var stockName = await db.Stock.AsNoTracking().Where(c => c.Code == stockCode).Select(c => c.Name).FirstOrDefaultAsync(ct);
  17. var stats = await db.StockBoardStats.AsNoTracking().Where(c => c.StockCode == stockCode)
  18. .Select(c => new { c.Posts, c.Comments, c.LastPostAt })
  19. .FirstOrDefaultAsync(ct);
  20. var header = new Response.StatsHeader(
  21. stockCode,
  22. stockName,
  23. stats?.Posts ?? 0,
  24. stats?.Comments ?? 0,
  25. stats?.LastPostAt);
  26. var baseQuery = db.Post.AsNoTracking().Where(c => c.StockCode == stockCode && !c.IsDeleted);
  27. var total = await baseQuery.CountAsync(ct);
  28. var predictionPostIDs = await db.PostPrediction.AsNoTracking()
  29. .Where(c => c.StockCode == stockCode)
  30. .Select(c => c.PostID)
  31. .ToListAsync(ct);
  32. var predictionSet = predictionPostIDs.ToHashSet();
  33. var list = await baseQuery
  34. .OrderByDescending(c => c.ID)
  35. .Skip((page - 1) * perPage)
  36. .Take(perPage)
  37. .Select(c => new
  38. {
  39. c.ID,
  40. c.StockCode,
  41. c.Subject,
  42. c.Thumbnail,
  43. c.Name,
  44. c.SID,
  45. c.Views,
  46. c.Likes,
  47. c.Comments,
  48. c.Images,
  49. c.UpdatedAt,
  50. c.CreatedAt
  51. })
  52. .ToListAsync(ct);
  53. var startNum = total - ((page - 1) * perPage);
  54. return new Response(
  55. total,
  56. header,
  57. [..list.Select((c, i) => new Response.Row(
  58. Num: startNum - i,
  59. c.ID,
  60. c.StockCode ?? stockCode,
  61. c.Subject,
  62. c.Thumbnail,
  63. c.Name,
  64. c.SID,
  65. predictionSet.Contains(c.ID),
  66. c.Views,
  67. c.Likes,
  68. c.Comments,
  69. c.Images,
  70. c.UpdatedAt,
  71. c.CreatedAt
  72. ))]
  73. );
  74. }
  75. }