[BE-33] 내구성 수집 사이클 도메인 계약 · 상태 머신

작업 내용 (설계 의도)

변경 사항

TDD 20260730-확장-안전-공고수집-배치-tdd.md의 cycle aggregate, budget value object, slice gateway 계약을 posting 도메인에 추가한다. 자정 eligible source creation의 NOT_STARTED→PENDING→RUNNING→PENDING/SUCCESS/BUDGET_EXHAUSTED/FAILED 전이, disable 시 cycle 상태를 바꾸지 않고 future claim만 막는 규칙, lease token, named safe-close predicate SUCCESS && isComplete && fetchedCount>0을 domain에 캡슐화한다. HTTP·JPA·sleep은 이 티켓에 넣지 않는다.

의존

  • DBA의 expand-only cycle/seen/host-throttle 스키마 설계 및 migration

다이어그램

처리 흐름

sequenceDiagram
    participant D as DomainService
    participant C as Cycle
    participant R as CycleRepository
    D->>R: claimReady()
    D->>C: claim(token, until)
    D->>C: recordSlice(result)
    C-->>D: pending/success/exhausted

클래스 의존

flowchart LR
    CycleDomainService --> CollectionCycle
    CycleDomainService --> CycleRepository
    CollectionCycle --> CollectionBudget
    JobSourceGateway --> CollectionSliceDescriptor

테스트 케이스

  • 동일 source/KST 일자 cycle 생성은 멱등이며 하나의 cycle ID만 반환한다.
  • eligible source의 cycle은 00:00 KST 생성 시 NOT_STARTED이고 외부 HTTP 없이 prepare될 때만 PENDING이 된다.
  • 09:00 snapshot 보충 생성은 누락된 eligible source만 NOT_STARTED으로 만들며 기존 cycle 상태를 바꾸지 않는다.
  • NOT_STARTED/PENDING source가 disable되면 상태를 바꾸지 않고 외부 HTTP 없이 claim 대상에서 제외한다.
  • PENDING cycle은 유효한 lease token으로만 RUNNING이 된다.
  • 만료 또는 다른 token의 slice 결과는 상태·카운터를 변경하지 않는다.
  • complete-zero SUCCESS는 abnormal·closeGuarded이고 isComplete=true여도 미발견/마감 델타를 실행하지 않는다.
  • SUCCESS·isComplete·fetchedCount>0을 모두 만족할 때만 미발견/마감 델타 실행 자격을 만든다.
  • 일일 페이지·공고·상세 상한 도달은 BUDGET_EXHAUSTED이며 auto-close 자격을 만들지 않는다.