From 6f6b958bf8e6c42f019189ae07b04b05ee99a03a Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 01:35:48 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[feat]=20#10=20-=20reservation=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20entity=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kernellabs/kernellabs/domain/Place.java | 57 +++++++++++++++ .../domain/PlaceUnavailableDay.java | 49 +++++++++++++ .../kernellabs/domain/Reservation.java | 69 +++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/domain/Place.java create mode 100644 src/main/java/com/kernellabs/kernellabs/domain/PlaceUnavailableDay.java create mode 100644 src/main/java/com/kernellabs/kernellabs/domain/Reservation.java diff --git a/src/main/java/com/kernellabs/kernellabs/domain/Place.java b/src/main/java/com/kernellabs/kernellabs/domain/Place.java new file mode 100644 index 0000000..85095df --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/domain/Place.java @@ -0,0 +1,57 @@ +package com.kernellabs.kernellabs.domain; + +import com.kernellabs.kernellabs.global.common.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import java.time.LocalTime; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Place extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String name; + + @Column(nullable = false) + private String address; + + @Column(nullable = false) + private LocalTime openTime; + + @Column(nullable = false) + private LocalTime closeTime; + + @Column + private String thumbnailUrl; + + @Lob + private String description; + + @Column + private Integer unitPrice; + + @Builder + public Place(String name, String address, LocalTime openTime, LocalTime closeTime, String thumbnailUrl, String description, Integer unitPrice) { + this.name = name; + this.address = address; + this.openTime = openTime; + this.closeTime = closeTime; + this.thumbnailUrl = thumbnailUrl; + this.description = description; + this.unitPrice = unitPrice; + } + +} diff --git a/src/main/java/com/kernellabs/kernellabs/domain/PlaceUnavailableDay.java b/src/main/java/com/kernellabs/kernellabs/domain/PlaceUnavailableDay.java new file mode 100644 index 0000000..2c89aa5 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/domain/PlaceUnavailableDay.java @@ -0,0 +1,49 @@ +package com.kernellabs.kernellabs.domain; + +import com.kernellabs.kernellabs.global.common.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDate; +import java.time.LocalTime; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PlaceUnavailableDay extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "place_id", nullable = false) + private Place place; + + @Column(nullable = false) + private LocalDate unavailableDate; + + private String reason; + + // 해당 날짜에 특별히 적용할 운영 시간 (null이면 장소의 기본 시간을 따름) + private LocalTime startTimeOverride; + private LocalTime endTimeOverride; + + @Builder + public PlaceUnavailableDay(Place place, LocalDate unavailableDate, String reason, LocalTime startTimeOverride, LocalTime endTimeOverride) { + this.place = place; + this.unavailableDate = unavailableDate; + this.reason = reason; + this.startTimeOverride = startTimeOverride; + this.endTimeOverride = endTimeOverride; + } +} diff --git a/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java b/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java new file mode 100644 index 0000000..7b99624 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java @@ -0,0 +1,69 @@ +package com.kernellabs.kernellabs.domain; + +import com.kernellabs.kernellabs.global.common.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Reservation extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "place_id", nullable = false) + private Place place; + + @Column(nullable = false) + private String password; + + @Column(nullable = false) + private LocalDate reservationDate; + + @Column(nullable = false) + private LocalTime startTime; + + @Column(nullable = false) + private LocalTime endTime; + + @Builder + public Reservation(Place place, String password, LocalDate reservationDate, LocalTime startTime, LocalTime endTime) { + this.place = place; + this.password = password; + this.reservationDate = reservationDate; + this.startTime = startTime; + this.endTime = endTime; + } + + public static Reservation create(Place place, String password, LocalDate date, List timeSlots) { + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); + LocalTime startTime = LocalTime.parse(timeSlots.get(0), timeFormatter); + LocalTime endTime = LocalTime.parse(timeSlots.get(timeSlots.size() - 1), timeFormatter).plusHours(1); + + return Reservation.builder() + .place(place) + .password(password) + .reservationDate(date) + .startTime(startTime) + .endTime(endTime) + .build(); + } + +} From 48cd7ff95c9bca48e8e94f893781d5bd98faccb7 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 01:36:07 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[feat]=20#10=20-=20respotiroy=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 --- .../repository/PlaceRepository.java | 10 +++++++ .../PlaceUnavailableDayRepository.java | 12 ++++++++ .../repository/ReservationRepository.java | 28 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceRepository.java create mode 100644 src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceUnavailableDayRepository.java create mode 100644 src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java diff --git a/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceRepository.java b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceRepository.java new file mode 100644 index 0000000..11d0c2e --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceRepository.java @@ -0,0 +1,10 @@ +package com.kernellabs.kernellabs.infrastructure.repository; + +import com.kernellabs.kernellabs.domain.Place; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PlaceRepository extends JpaRepository { + +} diff --git a/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceUnavailableDayRepository.java b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceUnavailableDayRepository.java new file mode 100644 index 0000000..bd80b65 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/PlaceUnavailableDayRepository.java @@ -0,0 +1,12 @@ +package com.kernellabs.kernellabs.infrastructure.repository; + +import com.kernellabs.kernellabs.domain.PlaceUnavailableDay; +import java.time.LocalDate; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PlaceUnavailableDayRepository extends JpaRepository { + Optional findByPlaceIdAndUnavailableDate(Long placeId, LocalDate date); +} diff --git a/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java new file mode 100644 index 0000000..039098d --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java @@ -0,0 +1,28 @@ +package com.kernellabs.kernellabs.infrastructure.repository; + +import com.kernellabs.kernellabs.domain.Reservation; +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ReservationRepository extends JpaRepository { + List findByPlaceIdAndReservationDateAndStartTimeBeforeAndEndTimeAfter( + Long placeId, + LocalDate date, + LocalTime endTime, + LocalTime startTime + ); + + boolean existsByPlaceIdAndReservationDateAndStartTimeBeforeAndEndTimeAfter( + Long placeId, + LocalDate date, + LocalTime endTime, + LocalTime startTime + ); +} From 083049f1f2954b3f505f02aba255ccdf07a47469 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 01:36:24 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[feat]=20#10=20-=20request,=20response=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/ReservationRequest.java | 32 ++++++++++++++++++ .../dto/response/ReservationResponse.java | 33 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationRequest.java create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/response/ReservationResponse.java diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationRequest.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationRequest.java new file mode 100644 index 0000000..f10a6f1 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationRequest.java @@ -0,0 +1,32 @@ +package com.kernellabs.kernellabs.presentation.dto.request; + +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; +import java.util.List; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ReservationRequest { + + @NotNull(message = "장소 ID는 필수입니다.") + private Long placeId; + + @NotBlank(message = "비밀번호는 필수입니다.") + @Pattern(regexp = "\\d{4}", message = "비밀번호는 4자리 숫자여야 합니다.") + private String password; + + @NotNull(message = "예약 날짜는 필수입니다.") + @FutureOrPresent(message = "과거 날짜는 예약할 수 없습니다.") + private LocalDate reservationDate; + + @NotEmpty(message = "예약 시간은 최소 1개 이상 선택해야 합니다.") + @Size(max = 3, message = "예약은 최대 3시간까지 가능합니다.") + private List timeSlots; +} diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/ReservationResponse.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/ReservationResponse.java new file mode 100644 index 0000000..04aa8cc --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/response/ReservationResponse.java @@ -0,0 +1,33 @@ +package com.kernellabs.kernellabs.presentation.dto.response; + +import com.kernellabs.kernellabs.domain.Reservation; +import java.time.LocalDate; +import java.time.LocalTime; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ReservationResponse { + + private final Long reservationId; + private final Long placeId; + private final String placeName; + private final LocalDate reservationDate; + private final LocalTime startTime; + private final LocalTime endTime; + + public static ReservationResponse from(Reservation reservation) { + return ReservationResponse.builder() + .reservationId(reservation.getId()) + .placeId(reservation.getPlace().getId()) + .placeName(reservation.getPlace().getName()) + .reservationDate(reservation.getReservationDate()) + .startTime(reservation.getStartTime()) + .endTime(reservation.getEndTime()) + .build(); + } +} From 7c18a7aa3ea41e8b71d6be61a072f17953eecf1e Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 01:36:52 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[feat]=20#10=20-=20=EC=98=88=EC=95=BD?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ReservationService.java | 42 +++++++++ .../validator/ReservationValidator.java | 90 +++++++++++++++++++ .../global/exception/ErrorCode.java | 8 +- .../controller/ReservationController.java | 26 ++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/kernellabs/kernellabs/application/ReservationService.java create mode 100644 src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java diff --git a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java new file mode 100644 index 0000000..23906dc --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java @@ -0,0 +1,42 @@ +package com.kernellabs.kernellabs.application; + +import com.kernellabs.kernellabs.application.validator.ReservationValidator; +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.request.ReservationRequest; +import com.kernellabs.kernellabs.presentation.dto.response.ReservationResponse; +import jakarta.transaction.Transactional; +import java.time.format.DateTimeFormatter; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReservationService { + private final ReservationRepository reservationRepository; + private final PlaceRepository placeRepository; + private final ReservationValidator reservationValidator; + + @Transactional + public ReservationResponse createReservation(ReservationRequest request) { + // 1. 장소 조회 + Place place = placeRepository.findById(request.getPlaceId()) + .orElseThrow(() -> new CustomException(ErrorCode.PLACE_NOT_FOUND)); + + // 2. 유효성 검증 + reservationValidator.validate(place, request); + + // 3. 엔티티 생성 + Reservation reservation = Reservation.create(place, request.getPassword(), + request.getReservationDate(), request.getTimeSlots()); + + // 4. 예약 저장 및 응답 반환 + reservationRepository.save(reservation); + return ReservationResponse.from(reservation); + } + +} diff --git a/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java b/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java new file mode 100644 index 0000000..8ba2c90 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java @@ -0,0 +1,90 @@ +package com.kernellabs.kernellabs.application.validator; + +import com.kernellabs.kernellabs.domain.Place; +import com.kernellabs.kernellabs.domain.PlaceUnavailableDay; +import com.kernellabs.kernellabs.global.exception.CustomException; +import com.kernellabs.kernellabs.global.exception.ErrorCode; +import com.kernellabs.kernellabs.infrastructure.repository.PlaceUnavailableDayRepository; +import com.kernellabs.kernellabs.infrastructure.repository.ReservationRepository; +import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ReservationValidator { + + private final ReservationRepository reservationRepository; + private final PlaceUnavailableDayRepository unavailableDayRepository; + private final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + + public void validate(Place place, ReservationRequest request) { + // 1. 시간 슬롯 자체의 유효성 검증 (포맷, 연속성) + validateTimeSlots(request.getTimeSlots()); + + LocalTime requestStartTime = LocalTime.parse(request.getTimeSlots().get(0), TIME_FORMATTER); + LocalTime requestEndTime = LocalTime.parse(request.getTimeSlots().get(request.getTimeSlots().size() - 1), TIME_FORMATTER).plusHours(1); + + // 2. 운영 시간 내의 요청인지 검증 + validateAgainstOperatingHours(place, request.getReservationDate(), requestStartTime, requestEndTime); + + // 3. 중복 예약이 없는지 검증 + validateNoOverlappingReservations(place.getId(), request.getReservationDate(), requestEndTime, requestStartTime); + } + + // 시간 슬롯의 포맷과 연속성 검증 + private void validateTimeSlots(List timeSlots) { + if (timeSlots == null || timeSlots.isEmpty()) { + throw new CustomException(ErrorCode.TIME_SLOTS_EMPTY); + } + Collections.sort(timeSlots); + + for (int i = 0; i < timeSlots.size() - 1; i++) { + LocalTime current = LocalTime.parse(timeSlots.get(i), TIME_FORMATTER); + LocalTime next = LocalTime.parse(timeSlots.get(i + 1), TIME_FORMATTER); + if (!current.plusHours(1).equals(next)) { + throw new CustomException(ErrorCode.INVALID_TIME_SLOT_SEQUENCE); + } + } + } + + // 해당 날짜의 실제 운영 시간 기준으로 요청이 유효한지 검증 + private void validateAgainstOperatingHours(Place place, LocalDate date, LocalTime requestStartTime, LocalTime requestEndTime) { + OperatingHours operatingHours = getOperatingHoursFor(place, date); + + if (requestStartTime.isBefore(operatingHours.openTime()) || requestEndTime.isAfter(operatingHours.closeTime())) { + throw new CustomException(ErrorCode.INVALID_RESERVATION_TIME); + } + } + + // 중복 예약 검증 + private void validateNoOverlappingReservations(Long placeId, LocalDate date, LocalTime endTime, LocalTime startTime) { + if (reservationRepository.existsByPlaceIdAndReservationDateAndStartTimeBeforeAndEndTimeAfter(placeId, date, endTime, startTime)) { + throw new CustomException(ErrorCode.RESERVATION_ALREADY_EXISTS); + } + } + + // 특정 날짜의 실제 운영 시간 계산 + private OperatingHours getOperatingHoursFor(Place place, LocalDate date) { + Optional unavailableDayOpt = unavailableDayRepository.findByPlaceIdAndUnavailableDate(place.getId(), date); + + if (unavailableDayOpt.isPresent()) { + PlaceUnavailableDay unavailableDay = unavailableDayOpt.get(); + if (unavailableDay.getStartTimeOverride() == null) { + throw new CustomException(ErrorCode.RESERVATION_NOT_POSSIBLE_ON_DAY); + } + return new OperatingHours(unavailableDay.getStartTimeOverride(), unavailableDay.getEndTimeOverride()); + } else { + return new OperatingHours(place.getOpenTime(), place.getCloseTime()); + } + } + + // 운영 시간을 담는 간단한 레코드 + private record OperatingHours(LocalTime openTime, LocalTime closeTime) {} +} diff --git a/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java b/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java index e2b1793..58adab4 100644 --- a/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java +++ b/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java @@ -28,7 +28,13 @@ public enum ErrorCode { INTERNAL_SERVER_ERROR("9999", "서버 내부 오류가 발생했습니다.", HttpStatus.INTERNAL_SERVER_ERROR), EXTERNAL_SERVICE_ERROR("9901", "외부 서비스 연동 중 오류가 발생했습니다.", HttpStatus.INTERNAL_SERVER_ERROR), - ; + // custom error + PLACE_NOT_FOUND("2001", "해당 장소를 찾을 수 없습니다.", HttpStatus.NOT_FOUND), + TIME_SLOTS_EMPTY("2002", "예약 시간을 선택해주세요.", HttpStatus.BAD_REQUEST ), + INVALID_TIME_SLOT_SEQUENCE("2003", "예약 시간은 연속되어야 합니다.", HttpStatus.BAD_REQUEST), + RESERVATION_NOT_POSSIBLE_ON_DAY("2004", "선택하신 날짜는 예약이 불가능합니다.", HttpStatus.CONFLICT), + INVALID_RESERVATION_TIME("2005", "예약 요청 시간이 운영 시간 범위를 벗어납니다.", HttpStatus.BAD_REQUEST), + RESERVATION_ALREADY_EXISTS("2006", "해당 시간에 이미 예약이 존재합니다.", HttpStatus.CONFLICT); private final HttpStatus status; private final String code; diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java new file mode 100644 index 0000000..d317ea9 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java @@ -0,0 +1,26 @@ +package com.kernellabs.kernellabs.presentation.controller; + +import com.kernellabs.kernellabs.application.ReservationService; +import com.kernellabs.kernellabs.global.common.ApiResponse; +import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; +import com.kernellabs.kernellabs.presentation.dto.response.ReservationResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class ReservationController { + + private final ReservationService reservationService; + + @PostMapping("/reservations") + public ResponseEntity createReservation(@RequestBody @Valid ReservationRequest request) { + ReservationResponse response = reservationService.createReservation(request); + return ResponseEntity.ok(ApiResponse.success(response)); + } + +} From 88d5c0d3481cc8070920950de06fc9fac698c8ed Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 02:14:27 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[feat]=20#10=20-=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20request=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/ReservationUpdateRequest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationUpdateRequest.java diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationUpdateRequest.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationUpdateRequest.java new file mode 100644 index 0000000..c7a4bc2 --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationUpdateRequest.java @@ -0,0 +1,30 @@ +package com.kernellabs.kernellabs.presentation.dto.request; + +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; +import java.util.List; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ReservationUpdateRequest { + + @NotBlank(message = "비밀번호는 필수입니다.") + @Pattern(regexp = "\\d{4}", message = "비밀번호는 4자리 숫자여야 합니다.") + private String password; + + @NotNull(message = "변경할 날짜를 입력해주세요.") + @FutureOrPresent(message = "과거 날짜로는 변경할 수 없습니다.") + private LocalDate newReservationDate; + + @NotEmpty(message = "변경할 예약 시간은 최소 1개 이상 선택해야 합니다.") + @Size(max = 3, message = "예약은 최대 3시간까지 가능합니다.") + private List newTimeSlots; + +} From d00f11fbc6665180642a3461cfd8a41a07dd4ac5 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 02:15:29 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[feat]=20#10=20-=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/kernellabs/kernellabs/domain/Reservation.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java b/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java index 7b99624..3b49e4b 100644 --- a/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java +++ b/src/main/java/com/kernellabs/kernellabs/domain/Reservation.java @@ -66,4 +66,10 @@ public static Reservation create(Place place, String password, LocalDate date, L .build(); } + public void updateTimes(LocalDate newDate, LocalTime newStartTime, LocalTime newEndTime) { + this.reservationDate = newDate; + this.startTime = newStartTime; + this.endTime = newEndTime; + } + } From 8318e527420ecf098600b17321976a3d9c12d7d8 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 02:15:50 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[feat]=20#10=20-=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ReservationService.java | 43 ++++++++++++++++++- .../validator/ReservationValidator.java | 29 ++++++++++++- .../com/kernellabs/kernellabs/domain/.gitkeep | 0 .../global/exception/ErrorCode.java | 3 +- .../repository/ReservationRepository.java | 6 ++- .../controller/ReservationController.java | 15 ++++++- 6 files changed, 89 insertions(+), 7 deletions(-) delete mode 100644 src/main/java/com/kernellabs/kernellabs/domain/.gitkeep diff --git a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java index 23906dc..a476db9 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java +++ b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java @@ -7,10 +7,18 @@ 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.request.ReservationDeleteRequest; import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; +import com.kernellabs.kernellabs.presentation.dto.request.ReservationUpdateRequest; import com.kernellabs.kernellabs.presentation.dto.response.ReservationResponse; import jakarta.transaction.Transactional; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -28,7 +36,7 @@ public ReservationResponse createReservation(ReservationRequest request) { .orElseThrow(() -> new CustomException(ErrorCode.PLACE_NOT_FOUND)); // 2. 유효성 검증 - reservationValidator.validate(place, request); + reservationValidator.validateForCreate(place, request); // 3. 엔티티 생성 Reservation reservation = Reservation.create(place, request.getPassword(), @@ -39,4 +47,37 @@ public ReservationResponse createReservation(ReservationRequest request) { return ReservationResponse.from(reservation); } + @Transactional + public ReservationResponse updateReservation(Long reservationId, ReservationUpdateRequest request) { + // 1. 예약 조회 및 비밀번호 확인 + Reservation reservation = findReservationById(reservationId); + validatePassword(request.getPassword(), reservation.getPassword()); + + // 2. 변경 요청 유효성 검사 + reservationValidator.validateForUpdate(reservation, request); + + // 3. 엔티티 상태 변경 + reservation.updateTimes(request.getNewReservationDate(), parseStartTime(request.getNewTimeSlots()), parseEndTime(request.getNewTimeSlots())); + return ReservationResponse.from(reservation); + } + + private LocalTime parseStartTime(List timeSlots) { + return LocalTime.parse(timeSlots.get(0), DateTimeFormatter.ofPattern("HH:mm")); + } + + private LocalTime parseEndTime(List timeSlots) { + return LocalTime.parse(timeSlots.get(timeSlots.size() - 1), DateTimeFormatter.ofPattern("HH:mm")).plusHours(1); + } + + private void validatePassword(String rawPassword, String storedPassword) { + if (!rawPassword.equals(storedPassword)) { + throw new CustomException(ErrorCode.INVALID_PASSWORD); + } + } + + private Reservation findReservationById(Long reservationId) { + return reservationRepository.findById(reservationId) + .orElseThrow(() -> new CustomException(ErrorCode.PLACE_NOT_FOUND)); + } + } diff --git a/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java b/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java index 8ba2c90..1cad215 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java +++ b/src/main/java/com/kernellabs/kernellabs/application/validator/ReservationValidator.java @@ -2,11 +2,13 @@ import com.kernellabs.kernellabs.domain.Place; import com.kernellabs.kernellabs.domain.PlaceUnavailableDay; +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.PlaceUnavailableDayRepository; import com.kernellabs.kernellabs.infrastructure.repository.ReservationRepository; import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; +import com.kernellabs.kernellabs.presentation.dto.request.ReservationUpdateRequest; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; @@ -24,7 +26,7 @@ public class ReservationValidator { private final PlaceUnavailableDayRepository unavailableDayRepository; private final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); - public void validate(Place place, ReservationRequest request) { + public void validateForCreate(Place place, ReservationRequest request) { // 1. 시간 슬롯 자체의 유효성 검증 (포맷, 연속성) validateTimeSlots(request.getTimeSlots()); @@ -38,6 +40,18 @@ public void validate(Place place, ReservationRequest request) { validateNoOverlappingReservations(place.getId(), request.getReservationDate(), requestEndTime, requestStartTime); } + public void validateForUpdate(Reservation reservation, ReservationUpdateRequest request) { + LocalDate newDate = request.getNewReservationDate(); + List newTimeSlots = request.getNewTimeSlots(); + validateTimeSlots(newTimeSlots); + + LocalTime newStartTime = LocalTime.parse(request.getNewTimeSlots().get(0), TIME_FORMATTER); + LocalTime newEndTime = LocalTime.parse(request.getNewTimeSlots().get(request.getNewTimeSlots().size() - 1), TIME_FORMATTER).plusHours(1); + + validateAgainstOperatingHours(reservation.getPlace(), newDate, newStartTime, newEndTime); + // 자기 자신 제외하고 중복 검사 + validateNoOverlappingForUpdate(reservation, newDate, newEndTime, newStartTime); } + // 시간 슬롯의 포맷과 연속성 검증 private void validateTimeSlots(List timeSlots) { if (timeSlots == null || timeSlots.isEmpty()) { @@ -87,4 +101,17 @@ private OperatingHours getOperatingHoursFor(Place place, LocalDate date) { // 운영 시간을 담는 간단한 레코드 private record OperatingHours(LocalTime openTime, LocalTime closeTime) {} + + // 자기 자신을 제외하고 중복 예약을 확인하는 메서드 + private void validateNoOverlappingForUpdate(Reservation reservation, LocalDate newDate, LocalTime newEndTime, LocalTime newStartTime) { + if (reservationRepository.existsByPlaceIdAndReservationDateAndIdNotAndStartTimeBeforeAndEndTimeAfter( + reservation.getPlace().getId(), + newDate, + reservation.getId(), + newEndTime, + newStartTime + )) { + throw new CustomException(ErrorCode.RESERVATION_ALREADY_EXISTS); + } + } } diff --git a/src/main/java/com/kernellabs/kernellabs/domain/.gitkeep b/src/main/java/com/kernellabs/kernellabs/domain/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java b/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java index 58adab4..faff81a 100644 --- a/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java +++ b/src/main/java/com/kernellabs/kernellabs/global/exception/ErrorCode.java @@ -34,7 +34,8 @@ public enum ErrorCode { INVALID_TIME_SLOT_SEQUENCE("2003", "예약 시간은 연속되어야 합니다.", HttpStatus.BAD_REQUEST), RESERVATION_NOT_POSSIBLE_ON_DAY("2004", "선택하신 날짜는 예약이 불가능합니다.", HttpStatus.CONFLICT), INVALID_RESERVATION_TIME("2005", "예약 요청 시간이 운영 시간 범위를 벗어납니다.", HttpStatus.BAD_REQUEST), - RESERVATION_ALREADY_EXISTS("2006", "해당 시간에 이미 예약이 존재합니다.", HttpStatus.CONFLICT); + RESERVATION_ALREADY_EXISTS("2006", "해당 시간에 이미 예약이 존재합니다.", HttpStatus.CONFLICT), + INVALID_PASSWORD("2007", "비밀번호가 일치하지 않습니다.", HttpStatus.FORBIDDEN); private final HttpStatus status; private final String code; 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 039098d..7606690 100644 --- a/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java +++ b/src/main/java/com/kernellabs/kernellabs/infrastructure/repository/ReservationRepository.java @@ -12,16 +12,18 @@ @Repository public interface ReservationRepository extends JpaRepository { - List findByPlaceIdAndReservationDateAndStartTimeBeforeAndEndTimeAfter( + boolean existsByPlaceIdAndReservationDateAndStartTimeBeforeAndEndTimeAfter( Long placeId, LocalDate date, LocalTime endTime, LocalTime startTime ); - boolean existsByPlaceIdAndReservationDateAndStartTimeBeforeAndEndTimeAfter( + // 예약 변경 시, 겹치는 예약이 있는지 확인 (자기 자신 제외) + boolean existsByPlaceIdAndReservationDateAndIdNotAndStartTimeBeforeAndEndTimeAfter( Long placeId, LocalDate date, + Long reservationId, LocalTime endTime, LocalTime startTime ); diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java index d317ea9..18d403c 100644 --- a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java @@ -3,24 +3,35 @@ import com.kernellabs.kernellabs.application.ReservationService; import com.kernellabs.kernellabs.global.common.ApiResponse; import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; +import com.kernellabs.kernellabs.presentation.dto.request.ReservationUpdateRequest; import com.kernellabs.kernellabs.presentation.dto.response.ReservationResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@RequestMapping("/reservations") @RequiredArgsConstructor public class ReservationController { private final ReservationService reservationService; - @PostMapping("/reservations") - public ResponseEntity createReservation(@RequestBody @Valid ReservationRequest request) { + @PostMapping("") + public ResponseEntity> createReservation(@RequestBody @Valid ReservationRequest request) { ReservationResponse response = reservationService.createReservation(request); return ResponseEntity.ok(ApiResponse.success(response)); } + @PatchMapping("/{reservationId}") + public ResponseEntity> updateReservation(@PathVariable Long reservationId, + @Valid @RequestBody ReservationUpdateRequest request) { + ReservationResponse response = reservationService.updateReservation(reservationId, request); + return ResponseEntity.ok(ApiResponse.success(response)); + } } From d6c9003affe85b9d9d8d9fe01ae4f927e5055272 Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 02:18:37 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[feat]=20#10=20-=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ReservationService.java | 10 ++++++++++ .../controller/ReservationController.java | 9 +++++++++ .../dto/request/ReservationDeleteRequest.java | 15 +++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationDeleteRequest.java diff --git a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java index a476db9..ca4da13 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java +++ b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java @@ -61,6 +61,16 @@ public ReservationResponse updateReservation(Long reservationId, ReservationUpda return ReservationResponse.from(reservation); } + @Transactional + public void deleteReservation(Long reservationId, ReservationDeleteRequest request) { + // 1. 예약 조회 및 비밀번호 확인 + Reservation reservation = findReservationById(reservationId); + validatePassword(request.getPassword(), reservation.getPassword()); + + // 2. 예약 삭제 + reservationRepository.delete(reservation); + } + private LocalTime parseStartTime(List timeSlots) { return LocalTime.parse(timeSlots.get(0), DateTimeFormatter.ofPattern("HH:mm")); } diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java index 18d403c..f3d321f 100644 --- a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java @@ -2,12 +2,14 @@ import com.kernellabs.kernellabs.application.ReservationService; import com.kernellabs.kernellabs.global.common.ApiResponse; +import com.kernellabs.kernellabs.presentation.dto.request.ReservationDeleteRequest; import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; import com.kernellabs.kernellabs.presentation.dto.request.ReservationUpdateRequest; import com.kernellabs.kernellabs.presentation.dto.response.ReservationResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -34,4 +36,11 @@ public ResponseEntity> updateReservation(@PathV ReservationResponse response = reservationService.updateReservation(reservationId, request); return ResponseEntity.ok(ApiResponse.success(response)); } + + @DeleteMapping("/{reservationId}") + public ResponseEntity deleteReservation(@PathVariable Long reservationId, + @Valid @RequestBody ReservationDeleteRequest request) { + reservationService.deleteReservation(reservationId, request); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationDeleteRequest.java b/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationDeleteRequest.java new file mode 100644 index 0000000..6601a9c --- /dev/null +++ b/src/main/java/com/kernellabs/kernellabs/presentation/dto/request/ReservationDeleteRequest.java @@ -0,0 +1,15 @@ +package com.kernellabs.kernellabs.presentation.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ReservationDeleteRequest { + + @NotBlank(message = "비밀번호는 필수입니다.") + @Pattern(regexp = "\\d{4}", message = "비밀번호는 4자리 숫자여야 합니다.") + private String password; +} \ No newline at end of file From 275506b4fad3f4d21a743162d667ed6546a3892e Mon Sep 17 00:00:00 2001 From: seoyeon-jung Date: Sat, 28 Jun 2025 02:25:02 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[feat]=20#10=20-=20=EC=98=88=EC=95=BD=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/ReservationService.java | 12 +++++++----- .../controller/ReservationController.java | 7 +++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java index ca4da13..d4fad37 100644 --- a/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java +++ b/src/main/java/com/kernellabs/kernellabs/application/ReservationService.java @@ -11,17 +11,14 @@ import com.kernellabs.kernellabs.presentation.dto.request.ReservationRequest; import com.kernellabs.kernellabs.presentation.dto.request.ReservationUpdateRequest; import com.kernellabs.kernellabs.presentation.dto.response.ReservationResponse; -import jakarta.transaction.Transactional; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +@Transactional(readOnly = true) @Service @RequiredArgsConstructor public class ReservationService { @@ -47,6 +44,11 @@ public ReservationResponse createReservation(ReservationRequest request) { return ReservationResponse.from(reservation); } + public ReservationResponse getReservation(Long reservationId) { + Reservation reservation = findReservationById(reservationId); + return ReservationResponse.from(reservation); + } + @Transactional public ReservationResponse updateReservation(Long reservationId, ReservationUpdateRequest request) { // 1. 예약 조회 및 비밀번호 확인 diff --git a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java index f3d321f..51445d0 100644 --- a/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java +++ b/src/main/java/com/kernellabs/kernellabs/presentation/controller/ReservationController.java @@ -10,6 +10,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -30,6 +31,12 @@ public ResponseEntity> createReservation(@Reque return ResponseEntity.ok(ApiResponse.success(response)); } + @GetMapping("/{reservationId}") + public ResponseEntity> getReservation(@PathVariable Long reservationId) { + ReservationResponse response = reservationService.getReservation(reservationId); + return ResponseEntity.ok(ApiResponse.success(response)); + } + @PatchMapping("/{reservationId}") public ResponseEntity> updateReservation(@PathVariable Long reservationId, @Valid @RequestBody ReservationUpdateRequest request) {