[BE-57] booking Slot OPEN/CLOSED 상태 + programId 확장

작업 내용 (설계 의도)

변경 사항

근거 TDD: “상태 전이 표 SlotStatus·실패 경로 CLOSED 슬롯 예약·program 회차 예약”. 기존 Slot(MySQL)에 예약 가능 상태와 시설상품 참조를 추가한다(FR-10·FR-12 전제).

포함:

  • SlotStatus(OPEN/CLOSED enum, canTransitTo) 신규.
  • Slot.kt 확장: status: SlotStatus(default OPEN)·programId: Long?(nullable, ID참조) 필드 + close(userId)·open(userId)(소유 검증 후 전이)·requireBookable()(CLOSED면 SlotClosedException 409). create 팩토리에 programId 선택 인자 추가(기존 호출부는 null default 금지 — 명시 전달, no-default-constructor-values 준수 위해 오버로드/명시 인자).
  • BookingDomainService.requestBooking doBookingslot.requireBookable() 게이트 추가 — CLOSED 슬롯 신규 예약 차단(기존 확정 예약 유지, 삭제 아님).
  • SlotDomainService: closeSlot(requesterId, slotId)·openSlot(...) 추가. createSlot에 programId 선택 파라미터 추가(program 회차 생성용, BE-59가 호출). listSlotsprogramId 필터 파라미터 추가(findByFacilityId → programId 조건부, SlotRepository에 필터 조회 시그니처 추가, QueryDSL — @Query 금지).
  • presentation: SlotApiController에 PATCH /slots/{slotId}/close·/open 추가. GET /facilities/{facilityId}/slotsprogramId 쿼리 필터 파라미터 추가(FR-12 program 회차만 노출, design-db B-Q6 인덱스 지원).
  • SlotResponsestatus(OPEN/CLOSED)·programId(nullable) 필드 추가(additive — FR-10 슬롯 상태 배지·FR-12 program 노출 FE 지탱, senior-pm 보강). Controller가 Slot Entity 직접 반환 금지, Response로 변환.
  • 예외 SlotClosedException(409) 신규.

롤백

status는 nullable DEFAULT ‘OPEN’, program_id nullable 컬럼 추가(expand). 롤백은 역방향 DDL(컬럼 DROP) — 코드 되돌림 후. 기존 예약 무영향.

의존

  • 없음

다이어그램

처리 흐름

sequenceDiagram
    participant B as BookingDomainService
    participant S as Slot
    B->>S: requireBookable()
    alt CLOSED
        S-->>B: SlotClosedException(409)
    else OPEN
        B->>B: capacity 검증 후 예약
    end

클래스 의존

flowchart LR
    Slot --> SS[SlotStatus]
    SDS[SlotDomainService] --> Slot
    BDS[BookingDomainService] --> Slot

테스트 케이스

  • OPEN 슬롯 예약은 정상 진행된다
  • CLOSED 슬롯 신규 예약은 SlotClosedException(409)으로 거부된다
  • 소유자가 슬롯을 close하면 status가 CLOSED로 전이한다
  • CLOSED된 슬롯을 open하면 다시 예약 가능해진다
  • 소유자가 아닌 사용자의 close/open은 소유권 예외를 던진다
  • programId를 가진 슬롯도 status·capacity 규칙이 동일하게 적용된다
  • CLOSED 전이 후에도 기존 CONFIRMED 예약 레코드는 그대로 유지된다(삭제 아님)
  • 슬롯 목록 응답에 status·programId 필드가 포함된다
  • programId 필터로 조회 시 해당 program 회차 슬롯만 반환한다
  • programId 필터 없이 조회하면 시설의 전체 슬롯(programId null 포함)을 반환한다