DailyBriefingPublisher.cs 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. using Application.Abstractions.Data;
  2. using Application.Helpers;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using Microsoft.Extensions.Logging;
  5. namespace Infrastructure.StockData;
  6. /// <summary>
  7. /// 장전 브리핑 자동 발행 배치 (D2 M3, d2 §③·§⑥) — 매 영업일 07:30 KST.
  8. /// 전일 지수(IndexDailyPrice)·등락 상위/하위 종목(StockDailyPrice)·주요 공시(Disclosure)를 취합해
  9. /// briefing 보드에 시스템 계정(MemberID=null)으로 IsNotice Post 를 발행하고 DailyBriefing 기록을 남긴다.
  10. /// BriefingDate 로 멱등 — 같은 날 이미 발행됐으면 skip.
  11. ///
  12. /// BackgroundJobs:DailyBriefing 플래그로 토글(기본 false), Web.Api 전용(중복 실행 방지).
  13. /// 조립/발행 로직은 순수 엔진(DailyBriefingComposer)에 위임 — 여기서는 스케줄만 담당한다.
  14. /// </summary>
  15. internal sealed class DailyBriefingPublisher(
  16. IServiceScopeFactory scopeFactory,
  17. ILogger<DailyBriefingPublisher> logger
  18. ) : DailyScheduledService(logger)
  19. {
  20. /// <summary>KST 실행 시각 — 장 시작(09:00) 전.</summary>
  21. private static readonly TimeOnly RunTime = new(7, 30);
  22. protected override string JobName => "DailyBriefing";
  23. protected override TimeOnly TargetTime => RunTime;
  24. protected override async Task<bool> RunOnceAsync(DateOnly todayKst, CancellationToken ct)
  25. {
  26. using var scope = scopeFactory.CreateScope();
  27. var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
  28. var result = await DailyBriefingComposer.PublishAsync(db, todayKst, ct);
  29. if (result.AlreadyExisted)
  30. {
  31. Logger.LogInformation("[{Job}] 이미 발행됨 — skip. date={Date}, postID={PostID}", JobName, todayKst, result.PostID);
  32. }
  33. else
  34. {
  35. Logger.LogInformation("[{Job}] 발행 완료 — date={Date}, postID={PostID}", JobName, todayKst, result.PostID);
  36. }
  37. return true;
  38. }
  39. }