[BE-56] facility 운영시간·휴무 VO + Facility 임베드

작업 내용 (설계 의도)

변경 사항

근거 TDD: “Possible Solutions 운영시간 저장 위치 A(임베드)·도메인 모델 OperatingHours/Holiday”. Facility(MongoDB 문서)에 운영시간·휴무를 시설 속성으로 추가한다(FR-7/8). 자동 슬롯 생성(BE-58)이 읽어갈 계약이라 선행 확립.

포함:

  • OperatingHours(VO) — dayOfWeek·openTime·closeTime·breaks(복수 TimeRange)·slotDurationMinutes(default 60)·capacity. slotRangesFor(date): List<TimeRange> — 브레이크 제외 슬롯단위 슬라이스 계산 캡슐화.
  • Holiday(VO) — 휴무 날짜 보유.
  • TimeRange(VO) — HH:mm-HH:mm 표현·겹침 판단.
  • Facility 문서에 operatingHours: List<OperatingHours>·holidays: List<Holiday> 임베드 필드 추가 + registerOperatingHours(...)·addHoliday(date)·removeHoliday(date) 캡슐화 메서드(소유자 검증은 서비스에서). private-mongodb-convention: 임베딩, validator 갱신은 mongodb-implementer.
  • application: RegisterOperatingHoursUseCase·AddHoliday/RemoveHolidayUseCase + Command/Response.
  • FacilityResponse(시설 상세 조회 GET /facilities/{facilityId})에 operatingHours·holidays 임베드 노출 추가(additive — FR-7/8 app 운영시간 카드·web 폼 프리필 FE 지탱, senior-pm 보강). 전용 GET 경로는 불필요(임베드). 기존 시설 조회 UseCase의 Response 매핑에 두 필드 포함(GetFacility/GetMyFacility Response — 읽기 매핑만 확장, 파일 소유 확인).
  • presentation: FacilityScheduleApiController(PUT operating-hours, POST/DELETE holidays) + @ConditionalOnProperty(facility.autoslot.enabled) 아님 — 운영시간 등록 자체는 항상 허용, 슬롯 생성 스케줄러만 플래그. Request→Command 변환.
  • ZonedDateTime·시간 규칙 준수, 브레이크는 data class(no-stringified-json).

롤백

Mongo 문서 필드 추가라 무중단·역호환. 롤백은 배포 되돌림(임베드 필드는 무시됨).

의존

  • 없음

다이어그램

처리 흐름

sequenceDiagram
    participant C as FacilityScheduleApiController
    participant U as RegisterOperatingHoursUseCase
    participant S as FacilityOwnerDomainService
    C->>U: execute(command)
    U->>S: registerOperatingHours(facilityId, ownerId, hours)
    S-->>C: Response

클래스 의존

flowchart LR
    Fac[Facility] --> OH[OperatingHours]
    Fac --> Hol[Holiday]
    OH --> TR[TimeRange]

테스트 케이스

  • 06:0022:00, 브레이크 12:0013:00, 60분 단위 등록 시 slotRangesFor가 브레이크를 제외한 시간대 목록을 만든다
  • 브레이크 경계(12:00·13:00)에 걸친 슬롯이 생성 대상에서 정확히 제외된다
  • 휴무일로 지정된 날짜는 holidays에 포함돼 생성 제외 판단이 true다
  • 소유자가 아닌 사용자의 운영시간 등록은 시설 소유권 예외를 던진다
  • 운영시간이 없는 요일은 slotRangesFor가 빈 목록을 반환한다(에러 아님)
  • 동일 휴무일 중복 추가는 멱등하게 처리된다
  • 시설 상세 조회 응답에 등록된 operatingHours·holidays가 포함된다
  • 운영시간·휴무 미등록 시설의 상세 조회는 빈 배열을 정상 반환한다(에러 아님)