Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -18,17 +25,56 @@
public class PlaceService {

private final PlaceRepository placeRepository;
private final ReservationRepository reservationRepository;

public List<PlaceListResponse> getAllPlace() {
return placeRepository.findAll().stream()
.map(PlaceListResponse::from)
.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<Reservation> reservations = reservationRepository.findByPlaceIdAndReservationDate(placeId, date);
Set<LocalTime> reservedSlots = getReservedSlots(reservations);

// 4. 전체 시간 슬롯 생성 및 예약 가능 여부 판단
List<TimeSlotResponse> timeSlots = generateTimeSlots(openTime, closeTime, reservedSlots);

// 5. 최종 응답 DTO 생성 및 반환
return PlaceDetailResponse.of(place, timeSlots);
}

private List<TimeSlotResponse> generateTimeSlots(LocalTime openTime, LocalTime closeTime, Set<LocalTime> reservedSlots) {
List<TimeSlotResponse> 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<LocalTime> getReservedSlots(List<Reservation> reservations) {
return reservations.stream()
.flatMap(reservation -> {
List<LocalTime> 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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ boolean existsByPlaceIdAndReservationDateAndIdNotAndStartTimeBeforeAndEndTimeAft
LocalTime endTime,
LocalTime startTime
);

List<Reservation> findByPlaceIdAndReservationDate(Long placeId, LocalDate date);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,8 +30,12 @@ public ResponseEntity<?> getAllPlaces() {
}

@GetMapping("/{placeId}")
public ResponseEntity<ApiResponse<PlaceViewResponse>> getPlace(@PathVariable Long placeId) {
PlaceViewResponse response = placeService.getPlaceDetail(placeId);
public ResponseEntity<ApiResponse<PlaceDetailResponse>> 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,34 @@

import com.kernellabs.kernellabs.domain.Place;
import java.time.LocalTime;
import java.util.List;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class PlaceViewResponse {
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<TimeSlotResponse> timeSlots; // 시간표 정보

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()
public static PlaceDetailResponse of(Place place, List<TimeSlotResponse> timeSlots) {
return PlaceDetailResponse.builder()
.id(place.getId())
.thumbnailUrl(place.getThumbnailUrl())
.name(place.getName())
.address(place.getAddress())
.thumbnailUrl(place.getThumbnailUrl())
.description(place.getDescription())
.openTime(place.getOpenTime())
.closeTime(place.getCloseTime())
.unitPrice(place.getUnitPrice())
.description(place.getDescription())
.timeSlots(timeSlots)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -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;

}