[BE-43] 09:00 KST 수집 SLA snapshot · NOT_STARTED 보충 생성

작업 내용 (설계 의도)

변경 사항

TDD 20260730-확장-안전-공고수집-배치-tdd.md에 따라 job_posting_collection_sla_snapshots와 item을 소유하는 posting domain/application 경로를 구현한다. 00:00 KST의 모든 eligible source NOT_STARTED cycle 생성은 dispatcher(BE-34)가 소유하고, 이 티켓은 같은 시각 snapshot header를 PENDING cutoff gate로 멱등 생성한다. 모든 cycle state-write는 이 daily gate row를 짧게 lock한다. 09:00 KST cutoff transaction은 gate를 보유한 채 활성 company-bound(v2 ON)와 활성 aggregator(v2+aggregator ON) eligibility를 재평가하고 누락 cycle을 NOT_STARTED으로 보충한 뒤, 오늘 cycle을 lock하여 immutable status/count/abnormal/closeGuarded snapshot item을 저장한다. 이 직렬화 규칙으로 item은 later current state가 아닌 09:00 cutoff state다. header (snapshot_date) unique로 다중 인스턴스 중복 캡처를 막고, notification dispatch는 capture 또는 explicit MISSED 기록 완료 뒤에만 시작한다.

09:00 cutoff capture가 수행되지 못한 채 recovery tick가 09:00 이후 발견하면 현재 cycle을 읽어 채우지 않고 header를 MISSED로 기록한다. API는 reason만 보이고 count/status를 꾸며내지 않는다. v2 OFF일 때는 NOT_APPLICABLE; 배포 첫날 이전·기존 이력에는 backfill하지 않고 UNAVAILABLE을 반환한다. 롤백은 snapshot scheduler/notification 선행 호출을 OFF해도 cycle 수집 상태에는 영향을 주지 않는다.

의존

  • BE-33
  • DBA cycles/SLA snapshot schema migration

다이어그램

처리 흐름

sequenceDiagram
    participant S as 09:00 Scheduler
    participant D as Snapshot UseCase
    participant C as Cycle Repository
    participant R as Snapshot Repository
    participant N as Notification Dispatcher
    S->>D: capture cutoff
    D->>C: create missing NOT_STARTED + lock cycles
    D->>R: insert immutable header/items
    D-->>N: CAPTURED only
    N->>N: dispatch notifications

클래스 의존

flowchart LR
    SlaSnapshotScheduler --> SlaSnapshotUseCase
    SlaSnapshotUseCase --> CollectionCycleDomainService
    SlaSnapshotUseCase --> SlaSnapshotRepository
    NotificationScheduler --> SlaSnapshotUseCase

테스트 케이스

  • cycle 상태 변경은 daily SLA gate와 직렬화되어 09:00 capture의 전/후 어느 한쪽으로만 관측된다.
  • 09:00 transaction은 cycle 없는 eligible source를 NOT_STARTED으로 보충하고 해당 상태를 snapshot item에 남긴다.
  • 두 인스턴스가 동시에 09:00 capture해도 snapshot header와 source item은 하나씩만 생긴다.
  • 09:01 SUCCESS로 바뀌어도 CAPTURED item의 09:00 RUNNING/NOT_STARTED 값은 바뀌지 않는다.
  • cutoff를 놓친 recovery는 MISSED header만 기록하고 later current 상태를 item으로 저장하지 않는다.
  • aggregator flag OFF 또는 v2 OFF source는 eligibility와 SLA item에서 제외되고 NOT_APPLICABLE semantics를 유지한다.