From 9b1b548f2c941e4f15a8532fb0e353a485d203e9 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 11:38:23 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat]=20#30=20-=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=20=EC=B6=94=EA=B0=80=ED=95=9C=20response=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/PlaceDetailResponse.java | 35 +++++++++++++++++++ .../dto/response/TimeSlotResponse.java | 13 +++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceDetailResponse.java create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/response/TimeSlotResponse.java diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceDetailResponse.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceDetailResponse.java new file mode 100644 index 0000000..2000b00 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceDetailResponse.java @@ -0,0 +1,35 @@ +package com.kernellabs.kernellabs.presentation.dto.response; + +import com.kernellabs.kernellabs.domain.Place; +import java.time.LocalTime; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PlaceDetailResponse { + private Long id; + private String name; + private String address; + private String thumbnailUrl; + private String description; + private LocalTime openTime; + private LocalTime closeTime; + private Integer unitPrice; + private List timeSlots; // 시간표 정보 + + public static PlaceDetailResponse of(Place place, List timeSlots) { + return PlaceDetailResponse.builder() + .id(place.getId()) + .name(place.getName()) + .address(place.getAddress()) + .thumbnailUrl(place.getThumbnailUrl()) + .description(place.getDescription()) + .openTime(place.getOpenTime()) + .closeTime(place.getCloseTime()) + .unitPrice(place.getUnitPrice()) + .timeSlots(timeSlots) + .build(); + } +} diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/TimeSlotResponse.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/TimeSlotResponse.java new file mode 100644 index 0000000..764ac2f --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/TimeSlotResponse.java @@ -0,0 +1,13 @@ +package com.kernellabs.kernellabs.presentation.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TimeSlotResponse { + + private String time; + private boolean isAvailable; + +} From 9e2c9172714c676df0338c8b5e9eb00176d72ff4 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 11:39:13 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[chore]=20#30=20-=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20response=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/PlaceViewResponse.java | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceViewResponse.java diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceViewResponse.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceViewResponse.java deleted file mode 100644 index c8d04e5..0000000 --- a/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/PlaceViewResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.kernellabs.kernellabs.presentation.dto.response; - -import com.kernellabs.kernellabs.domain.Place; -import java.time.LocalTime; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class PlaceViewResponse { - - private final Long id; - private final String thumbnailUrl; - private final String name; - private final String address; - private final LocalTime openTime; - private final LocalTime closeTime; - private final Integer unitPrice; - private final String description; - - public static PlaceViewResponse from(Place place) { - return PlaceViewResponse.builder() - .id(place.getId()) - .thumbnailUrl(place.getThumbnailUrl()) - .name(place.getName()) - .address(place.getAddress()) - .openTime(place.getOpenTime()) - .closeTime(place.getCloseTime()) - .unitPrice(place.getUnitPrice()) - .description(place.getDescription()) - .build(); - } - -} From 75422b8e11fba44d2ece6ed5abde991f43bfe84f Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 11:39:30 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[feat]=20#30=20-=20=EC=98=88=EC=95=BD?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=ED=8F=AC=ED=95=A8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kernellabs/application/PlaceService.java | 52 +++++++++++++++++-- .../repository/ReservationRepository.java | 2 + .../controller/PlaceController.java | 14 +++-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/kernellabs/kernellabs/application/PlaceService.java b/src/main/java/com/kernellabs/kernellabs/application/PlaceService.java index 936515a..d75ce64 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/PlaceService.java +++ b/src/main/java/com/kernellabs/kernellabs/application/PlaceService.java @@ -1,12 +1,19 @@ package com.kernellabs.kernellabs.application; import com.kernellabs.kernellabs.domain.Place; +import com.kernellabs.kernellabs.domain.Reservation; import com.kernellabs.kernellabs.global.exception.CustomException; import com.kernellabs.kernellabs.global.exception.ErrorCode; import com.kernellabs.kernellabs.infrastructure.repository.PlaceRepository; +import com.kernellabs.kernellabs.infrastructure.repository.ReservationRepository; +import com.kernellabs.kernellabs.presentation.dto.response.PlaceDetailResponse; import com.kernellabs.kernellabs.presentation.dto.response.PlaceListResponse; -import com.kernellabs.kernellabs.presentation.dto.response.PlaceViewResponse; +import com.kernellabs.kernellabs.presentation.dto.response.TimeSlotResponse; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -18,6 +25,7 @@ public class PlaceService { private final PlaceRepository placeRepository; + private final ReservationRepository reservationRepository; public List getAllPlace() { return placeRepository.findAll().stream() @@ -25,10 +33,48 @@ public List getAllPlace() { .collect(Collectors.toList()); } - public PlaceViewResponse getPlaceDetail(Long placeId) { + public PlaceDetailResponse getPlaceDetailWithDate(Long placeId, LocalDate date) { + // 1. 장소 정보 조회 Place place = placeRepository.findById(placeId) .orElseThrow(() -> new CustomException(ErrorCode.PLACE_NOT_FOUND)); - return PlaceViewResponse.from(place); + // 2. 해당 날짜 실제 운영 시간 확인 + LocalTime openTime = place.getOpenTime(); + LocalTime closeTime = place.getCloseTime(); + + // 3. 해당 날짜에 이미 예약된 시간 목록 조회 + List reservations = reservationRepository.findByPlaceIdAndReservationDate(placeId, date); + Set reservedSlots = getReservedSlots(reservations); + + // 4. 전체 시간 슬롯 생성 및 예약 가능 여부 판단 + List timeSlots = generateTimeSlots(openTime, closeTime, reservedSlots); + + // 5. 최종 응답 DTO 생성 및 반환 + return PlaceDetailResponse.of(place, timeSlots); + } + + private List generateTimeSlots(LocalTime openTime, LocalTime closeTime, Set reservedSlots) { + List slots = new ArrayList<>(); + LocalTime currentTime = openTime; + while (!currentTime.isAfter(closeTime.minusHours(1))) { + boolean isAvailable = !reservedSlots.contains(currentTime); + slots.add(new TimeSlotResponse(currentTime.toString(), isAvailable)); + currentTime = currentTime.plusHours(1); + } + return slots; + } + + private Set getReservedSlots(List reservations) { + return reservations.stream() + .flatMap(reservation -> { + List slots = new ArrayList<>(); + LocalTime current = reservation.getStartTime(); + while (current.isBefore(reservation.getEndTime())) { + slots.add(current); + current = current.plusHours(1); + } + return slots.stream(); + }) + .collect(Collectors.toSet()); } } diff --git a/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java index 7606690..a1fba65 100644 --- a/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java +++ b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java @@ -27,4 +27,6 @@ boolean existsByPlaceIdAndReservationDateAndIdNotAndStartTimeBeforeAndEndTimeAft LocalTime endTime, LocalTime startTime ); + + List findByPlaceIdAndReservationDate(Long placeId, LocalDate date); } diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/PlaceController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/PlaceController.java index e25fcc3..b83475b 100644 --- a/src/main/java/com/kernellabs/kernellabs/presentation/controller/PlaceController.java +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/PlaceController.java @@ -2,14 +2,18 @@ import com.kernellabs.kernellabs.application.PlaceService; import com.kernellabs.kernellabs.global.common.ApiResponse; +import com.kernellabs.kernellabs.presentation.dto.response.PlaceDetailResponse; import com.kernellabs.kernellabs.presentation.dto.response.PlaceListResponse; -import com.kernellabs.kernellabs.presentation.dto.response.PlaceViewResponse; +import java.time.LocalDate; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -26,8 +30,12 @@ public ResponseEntity getAllPlaces() { } @GetMapping("/{placeId}") - public ResponseEntity> getPlace(@PathVariable Long placeId) { - PlaceViewResponse response = placeService.getPlaceDetail(placeId); + public ResponseEntity> getPlace(@PathVariable Long placeId, + @RequestParam(required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate date) { + // 날짜 파라미터가 없으면 오늘 날짜 기본값 + LocalDate targetDate = (date == null) ? LocalDate.now() : date; + + PlaceDetailResponse response = placeService.getPlaceDetailWithDate(placeId, targetDate); return ResponseEntity.ok(ApiResponse.success(response)); } }