diff --git a/README.md b/README.md index bf97d5b92a..1a72b0fb3a 100644 --- a/README.md +++ b/README.md @@ -197,10 +197,10 @@ - 예약/대기 추가 - Http Method: POST - URL: /reservations - - Request + - Request Header: `Member-Id: {memberId}` + - Request Body ```text { - "name": "fizz", "date": "2026-05-02", "timeId": 1, "themeId": 1 @@ -211,7 +211,10 @@ ```text { "id": 1, - "name": "fizz", + "member": { + "id": 1, + "name": "fizz" + }, "date": "2026-05-02", "time": { "id": 1, @@ -231,7 +234,10 @@ ```text { "id": 1, - "name": "fizz", + "member": { + "id": 1, + "name": "fizz" + }, "date": "2026-05-02", "time": { "id": 1, @@ -303,6 +309,65 @@ } ``` +- 내 예약/대기 조회 + - Http Method: GET + - URL: /reservations/mine + - Request Header: `Member-Id: {memberId}` + - Response + - [x] 정상적으로 조회된 경우: `Http Status: 200 OK` + ```text + { + "reservations": { + "items": [ + { + "id": 1, + "member": { + "id": 1, + "name": "예약자01" + }, + "date": "2026-05-01", + "time": { + "id": 1, + "startAt": "10:00:00" + }, + "theme": { + "id": 1, + "name": "잃어버린 왕국", + "description": "사라진 고대 왕국의 비밀을 추적하는 모험 테마", + "thumbnailUrl": "https://example.com/images/lost-kingdom.jpg" + }, + "status": "CONFIRMED" + } + ] + }, + "waits": { + "items": [ + { + "id": 1, + "member": { + "id": 1, + "name": "예약자01" + }, + "date": "2026-05-02", + "time": { + "id": 2, + "startAt": "11:00:00" + }, + "theme": { + "id": 1, + "name": "잃어버린 왕국", + "description": "사라진 고대 왕국의 비밀을 추적하는 모험 테마", + "thumbnailUrl": "https://example.com/images/lost-kingdom.jpg" + }, + "status": "WAITING", + "order": 1, + "createdAt": "2026-05-01T09:00:00" + } + ] + } + } + ``` + - 사용자 이름으로 예약/대기 조회 - Http Method: GET - URL: /reservations?name={name} @@ -314,7 +379,10 @@ "items": [ { "id": 1, - "name": "예약자01", + "member": { + "id": 1, + "name": "예약자01" + }, "date": "2026-05-01", "time": { "id": 1, @@ -334,7 +402,10 @@ "items": [ { "id": 1, - "name": "예약자01", + "member": { + "id": 1, + "name": "예약자01" + }, "date": "2026-05-02", "time": { "id": 2, @@ -366,7 +437,10 @@ "items": [ { "id": 1, - "name": "예약자01", + "member": { + "id": 1, + "name": "예약자01" + }, "date": "2026-05-01", "time": { "id": 1, @@ -386,7 +460,10 @@ "items": [ { "id": 1, - "name": "예약자02", + "member": { + "id": 2, + "name": "예약자02" + }, "date": "2026-05-02", "time": { "id": 2, diff --git a/build.gradle b/build.gradle index c175ebcfca..6e23898f11 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/roomescape/controller/ReservationController.java b/src/main/java/roomescape/controller/ReservationController.java index 6022e938fe..3c59c06094 100644 --- a/src/main/java/roomescape/controller/ReservationController.java +++ b/src/main/java/roomescape/controller/ReservationController.java @@ -7,6 +7,7 @@ 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.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -27,13 +28,22 @@ public ReservationController(ReservationFacade reservationFacade) { @PostMapping public ResponseEntity save( + @RequestHeader("Member-Id") Long memberId, @RequestBody ReservationCreateRequest request) { - ReservationWaitResponse response = reservationFacade.save(request); + ReservationWaitResponse response = reservationFacade.save(request, memberId); return ResponseEntity. status(HttpStatus.CREATED) .body(response); } + @GetMapping("/mine") + public ResponseEntity findByMemberId( + @RequestHeader("Member-Id") Long memberId + ) { + ReservationWaitListResponse response = reservationFacade.findByMemberId(memberId); + return ResponseEntity.ok(response); + } + @GetMapping(params = "name") public ResponseEntity findByName( @RequestParam("name") String name diff --git a/src/main/java/roomescape/controller/dto/request/ReservationCreateRequest.java b/src/main/java/roomescape/controller/dto/request/ReservationCreateRequest.java index 3f384f82d1..22303cc3ef 100644 --- a/src/main/java/roomescape/controller/dto/request/ReservationCreateRequest.java +++ b/src/main/java/roomescape/controller/dto/request/ReservationCreateRequest.java @@ -2,6 +2,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import roomescape.domain.Member; import roomescape.domain.Reservation; import roomescape.domain.ReservationTime; import roomescape.domain.Slot; @@ -10,28 +11,24 @@ import roomescape.exception.custom.InvalidRequestArgumentException; public record ReservationCreateRequest( - String name, LocalDate date, Long timeId, Long themeId ) { public ReservationCreateRequest { - validate(name, date, timeId, themeId); + validate(date, timeId, themeId); } - public Reservation toReservation(ReservationTime reservationTime, Theme theme) { - return new Reservation(name, new Slot(date, reservationTime, theme)); + public Reservation toReservation(Member member, ReservationTime reservationTime, Theme theme) { + return new Reservation(member, new Slot(date, reservationTime, theme)); } - public Wait toWait(LocalDateTime createdAt, ReservationTime reservationTime, Theme theme) { - return new Wait(createdAt, name, new Slot(date, reservationTime, theme)); + public Wait toWait(LocalDateTime createdAt, Member member, ReservationTime reservationTime, Theme theme) { + return new Wait(createdAt, member, new Slot(date, reservationTime, theme)); } - private void validate(String name, LocalDate date, Long timeId, Long themeId) { - if (name == null || name.isBlank()) { - throw new InvalidRequestArgumentException("예약자 이름은 비어 있을 수 없습니다."); - } + private void validate(LocalDate date, Long timeId, Long themeId) { if (date == null) { throw new InvalidRequestArgumentException("예약 날짜는 비어 있을 수 없습니다."); } diff --git a/src/main/java/roomescape/controller/dto/response/MemberResponse.java b/src/main/java/roomescape/controller/dto/response/MemberResponse.java new file mode 100644 index 0000000000..9f8cdd8589 --- /dev/null +++ b/src/main/java/roomescape/controller/dto/response/MemberResponse.java @@ -0,0 +1,15 @@ +package roomescape.controller.dto.response; + +import roomescape.domain.Member; + +public record MemberResponse( + Long id, + String name +) { + public static MemberResponse from(Member member) { + return new MemberResponse( + member.getId(), + member.getName() + ); + } +} diff --git a/src/main/java/roomescape/controller/dto/response/ReservationResponse.java b/src/main/java/roomescape/controller/dto/response/ReservationResponse.java index 5abd28d41b..1bdfd36498 100644 --- a/src/main/java/roomescape/controller/dto/response/ReservationResponse.java +++ b/src/main/java/roomescape/controller/dto/response/ReservationResponse.java @@ -6,7 +6,7 @@ public record ReservationResponse( Long id, - String name, + MemberResponse member, LocalDate date, ReservationTimeResponse time, ThemeResponse theme, @@ -16,7 +16,7 @@ public record ReservationResponse( public static ReservationResponse from(Reservation reservation) { return new ReservationResponse( reservation.getId(), - reservation.getName(), + MemberResponse.from(reservation.getMember()), reservation.getDate(), ReservationTimeResponse.from(reservation.getTime()), ThemeResponse.from(reservation.getTheme()), diff --git a/src/main/java/roomescape/controller/dto/response/WaitResponse.java b/src/main/java/roomescape/controller/dto/response/WaitResponse.java index 7ebe8e0621..00a916603c 100644 --- a/src/main/java/roomescape/controller/dto/response/WaitResponse.java +++ b/src/main/java/roomescape/controller/dto/response/WaitResponse.java @@ -7,7 +7,7 @@ public record WaitResponse( Long id, - String name, + MemberResponse member, LocalDate date, ReservationTimeResponse time, ThemeResponse theme, @@ -19,7 +19,7 @@ public record WaitResponse( public static WaitResponse of(Wait wait, Long order) { return new WaitResponse( wait.getId(), - wait.getName(), + MemberResponse.from(wait.getMember()), wait.getReservationDate(), ReservationTimeResponse.from(wait.getTime()), ThemeResponse.from(wait.getTheme()), diff --git a/src/main/java/roomescape/domain/Member.java b/src/main/java/roomescape/domain/Member.java new file mode 100644 index 0000000000..216164d7f8 --- /dev/null +++ b/src/main/java/roomescape/domain/Member.java @@ -0,0 +1,59 @@ +package roomescape.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import java.util.Objects; +import roomescape.exception.custom.InvalidDomainValueException; + +@Entity +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + public Member() { + } + + public Member(Long id, String name) { + validate(name); + this.id = id; + this.name = name; + } + + public Member(String name) { + this(null, name); + } + + private void validate(String name) { + if (name == null || name.isBlank()) { + throw new InvalidDomainValueException("회원 이름은 비어 있을 수 없습니다."); + } + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) { + return false; + } + Member member = (Member) object; + return Objects.equals(id, member.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } +} diff --git a/src/main/java/roomescape/domain/Reservation.java b/src/main/java/roomescape/domain/Reservation.java index 29118b3159..aea1bccd03 100644 --- a/src/main/java/roomescape/domain/Reservation.java +++ b/src/main/java/roomescape/domain/Reservation.java @@ -1,33 +1,52 @@ package roomescape.domain; +import jakarta.persistence.Embedded; +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.LocalDateTime; import java.util.Objects; import roomescape.exception.custom.InvalidDomainValueException; +@Entity public class Reservation { - private final Long id; - private final String name; - private final Slot slot; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public Reservation(Long id, String name, Slot slot) { - validate(name, slot); + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Embedded + private Slot slot; + + public Reservation() { + } + + public Reservation(Long id, Member member, Slot slot) { + validate(member, slot); this.id = id; - this.name = name; + this.member = member; this.slot = slot; } - public Reservation(String name, Slot slot) { - this(null, name, slot); + public Reservation(Member member, Slot slot) { + this(null, member, slot); } public Reservation withId(Long id) { - return new Reservation(id, name, slot); + return new Reservation(id, member, slot); } - public boolean isSameUser(String name) { - return this.name.equals(name); + public boolean isSameUser(Member other) { + return this.member.getId().equals(other.getId()); } public boolean isPast(LocalDateTime now) { @@ -38,8 +57,12 @@ public Long getId() { return id; } + public Member getMember() { + return member; + } + public String getName() { - return name; + return member.getName(); } public Slot getSlot() { @@ -47,7 +70,7 @@ public Slot getSlot() { } public LocalDate getDate() { - return slot.getDate(); + return slot.getReservationDate(); } public ReservationTime getTime() { @@ -64,17 +87,17 @@ public boolean equals(Object object) { return false; } Reservation that = (Reservation) object; - return Objects.equals(name, that.name) && Objects.equals(slot, that.slot); + return Objects.equals(member, that.member) && Objects.equals(slot, that.slot); } @Override public int hashCode() { - return Objects.hash(name, slot); + return Objects.hash(member, slot); } - private void validate(String name, Slot slot) { - if (name == null || name.isBlank()) { - throw new InvalidDomainValueException("예약자 이름은 비어 있을 수 없습니다."); + private void validate(Member member, Slot slot) { + if (member == null) { + throw new InvalidDomainValueException("예약자는 비어 있을 수 없습니다."); } if (slot == null) { throw new InvalidDomainValueException("예약 슬롯은 비어 있을 수 없습니다."); diff --git a/src/main/java/roomescape/domain/ReservationTime.java b/src/main/java/roomescape/domain/ReservationTime.java index 55b990a7d3..17094649e4 100644 --- a/src/main/java/roomescape/domain/ReservationTime.java +++ b/src/main/java/roomescape/domain/ReservationTime.java @@ -1,13 +1,24 @@ package roomescape.domain; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import java.time.LocalTime; import java.util.Objects; import roomescape.exception.custom.InvalidDomainValueException; +@Entity public class ReservationTime { - private final Long id; - private final LocalTime startAt; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private LocalTime startAt; + + public ReservationTime() { + } public ReservationTime(Long id, LocalTime startAt) { validate(startAt); diff --git a/src/main/java/roomescape/domain/Slot.java b/src/main/java/roomescape/domain/Slot.java index f608f2101e..6f9b8faeb6 100644 --- a/src/main/java/roomescape/domain/Slot.java +++ b/src/main/java/roomescape/domain/Slot.java @@ -1,20 +1,34 @@ package roomescape.domain; +import jakarta.persistence.Embeddable; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Objects; import roomescape.exception.custom.InvalidDomainValueException; +@Embeddable public class Slot { - private final LocalDate date; - private final ReservationTime time; - private final Theme theme; + private LocalDate reservationDate; - public Slot(LocalDate date, ReservationTime time, Theme theme) { - validate(date, time, theme); - this.date = date; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_id") + private ReservationTime time; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "theme_id") + private Theme theme; + + public Slot() { + } + + public Slot(LocalDate reservationDate, ReservationTime time, Theme theme) { + validate(reservationDate, time, theme); + this.reservationDate = reservationDate; this.time = time; this.theme = theme; } @@ -23,27 +37,35 @@ public boolean isPast(LocalDateTime now) { LocalDate nowDate = now.toLocalDate(); LocalTime nowTime = now.toLocalTime(); - if (date.isBefore(nowDate)) { + if (reservationDate.isBefore(nowDate)) { return true; } - if (date.isAfter(nowDate)) { + if (reservationDate.isAfter(nowDate)) { return false; } return time.isPast(nowTime); } - public LocalDate getDate() { - return date; + public LocalDate getReservationDate() { + return reservationDate; } public ReservationTime getTime() { return time; } + public Long getTimeId() { + return time.getId(); + } + public Theme getTheme() { return theme; } + public Long getThemeId() { + return theme.getId(); + } + private void validate(LocalDate date, ReservationTime time, Theme theme) { if (date == null) { throw new InvalidDomainValueException("예약 날짜는 비어 있을 수 없습니다."); @@ -62,12 +84,12 @@ public boolean equals(Object object) { return false; } Slot slot = (Slot) object; - return Objects.equals(date, slot.date) && Objects.equals(time, slot.time) + return Objects.equals(reservationDate, slot.reservationDate) && Objects.equals(time, slot.time) && Objects.equals(theme, slot.theme); } @Override public int hashCode() { - return Objects.hash(date, time, theme); + return Objects.hash(reservationDate, time, theme); } } diff --git a/src/main/java/roomescape/domain/Theme.java b/src/main/java/roomescape/domain/Theme.java index c07ba9a6c8..cc366706c9 100644 --- a/src/main/java/roomescape/domain/Theme.java +++ b/src/main/java/roomescape/domain/Theme.java @@ -1,14 +1,27 @@ package roomescape.domain; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import java.util.Objects; import roomescape.exception.custom.InvalidDomainValueException; +@Entity public class Theme { - private final Long id; - private final String name; - private final String description; - private final String thumbnailUrl; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + private String description; + + private String thumbnailUrl; + + public Theme() { + } public Theme(Long id, String name, String description, String thumbnailUrl) { validate(name, description, thumbnailUrl); diff --git a/src/main/java/roomescape/domain/Wait.java b/src/main/java/roomescape/domain/Wait.java index 93dfe900b3..7c859d38be 100644 --- a/src/main/java/roomescape/domain/Wait.java +++ b/src/main/java/roomescape/domain/Wait.java @@ -1,35 +1,59 @@ package roomescape.domain; +import jakarta.persistence.Embedded; +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.LocalDateTime; import java.util.Objects; import roomescape.exception.custom.InvalidDomainValueException; +@Entity public class Wait { - private final Long id; - private final LocalDateTime createdAt; - private final String name; - private final Slot slot; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public Wait(Long id, LocalDateTime createdAt, String name, Slot slot) { - validate(createdAt, name, slot); + private LocalDateTime createdAt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Embedded + private Slot slot; + + public Wait() { + } + + public Wait(Long id, LocalDateTime createdAt, Member member, Slot slot) { + validate(createdAt, member, slot); this.id = id; this.createdAt = createdAt; - this.name = name; + this.member = member; this.slot = slot; } - public Wait(LocalDateTime createdAt, String name, Slot slot) { - this(null, createdAt, name, slot); + public Wait(LocalDateTime createdAt, Member member, Slot slot) { + this(null, createdAt, member, slot); } public Wait withId(Long id) { - return new Wait(id, createdAt, name, slot); + return new Wait(id, createdAt, member, slot); + } + + public boolean isSameUser(Member other) { + return this.member.getId().equals(other.getId()); } public boolean isSameUser(String name) { - return this.name.equals(name); + return this.member.getName().equals(name); } public boolean isSameSlot(Slot otherSlot) { @@ -52,8 +76,12 @@ public LocalDateTime getCreatedAt() { return createdAt; } + public Member getMember() { + return member; + } + public String getName() { - return name; + return member.getName(); } public Slot getSlot() { @@ -61,38 +89,46 @@ public Slot getSlot() { } public LocalDate getReservationDate() { - return slot.getDate(); + return slot.getReservationDate(); } public ReservationTime getTime() { return slot.getTime(); } + public Long getTimeId() { + return slot.getTimeId(); + } + public Theme getTheme() { return slot.getTheme(); } + public Long getThemeId() { + return slot.getThemeId(); + } + @Override public boolean equals(Object object) { if (object == null || getClass() != object.getClass()) { return false; } Wait wait = (Wait) object; - return Objects.equals(createdAt, wait.createdAt) && Objects.equals(name, wait.name) + return Objects.equals(createdAt, wait.createdAt) && Objects.equals(member, wait.member) && Objects.equals(slot, wait.slot); } @Override public int hashCode() { - return Objects.hash(createdAt, name, slot); + return Objects.hash(createdAt, member, slot); } - private void validate(LocalDateTime createdAt, String name, Slot slot) { + private void validate(LocalDateTime createdAt, Member member, Slot slot) { if (createdAt == null) { throw new InvalidDomainValueException("대기 신청 시간은 비어 있을 수 없습니다."); } - if (name == null || name.isBlank()) { - throw new InvalidDomainValueException("대기자 이름은 비어 있을 수 없습니다."); + if (member == null) { + throw new InvalidDomainValueException("대기자는 비어 있을 수 없습니다."); } if (slot == null) { throw new InvalidDomainValueException("예약 슬롯은 비어 있을 수 없습니다."); diff --git a/src/main/java/roomescape/domain/Waits.java b/src/main/java/roomescape/domain/Waits.java index 9a034bb74a..ef923d4a7f 100644 --- a/src/main/java/roomescape/domain/Waits.java +++ b/src/main/java/roomescape/domain/Waits.java @@ -4,6 +4,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import roomescape.exception.custom.AlreadyWaitingException; import roomescape.exception.custom.WaitIsFullException; @@ -18,8 +19,8 @@ public Waits(List waits) { this.waits = waits; } - public void validateCreate(String name, Slot slot) { - validateNotDuplicated(name); + public void validateCreate(Member member, Slot slot) { + validateNotDuplicated(member, slot); validateWaitsIsNotFull(slot); } @@ -70,17 +71,9 @@ public Long calculateOrder(Wait myWait) { .count() + 1; } - public List getWaits() { - return List.copyOf(waits); - } - - public Long size() { - return (long) waits.size(); - } - - private void validateNotDuplicated(String name) { + private void validateNotDuplicated(Member member, Slot slot) { boolean isDuplicated = waits.stream() - .anyMatch(wait -> wait.isSameUser(name)); + .anyMatch(wait -> wait.isSameUser(member) && wait.isSameSlot(slot)); if (isDuplicated) { throw new AlreadyWaitingException(); } @@ -91,4 +84,18 @@ private void validateWaitsIsNotFull(Slot slot) { throw new WaitIsFullException(); } } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) { + return false; + } + Waits waits1 = (Waits) object; + return Objects.equals(waits, waits1.waits); + } + + @Override + public int hashCode() { + return Objects.hashCode(waits); + } } diff --git a/src/main/java/roomescape/exception/custom/MemberNotExistsException.java b/src/main/java/roomescape/exception/custom/MemberNotExistsException.java new file mode 100644 index 0000000000..534b748810 --- /dev/null +++ b/src/main/java/roomescape/exception/custom/MemberNotExistsException.java @@ -0,0 +1,8 @@ +package roomescape.exception.custom; + +public class MemberNotExistsException extends CustomException { + + public MemberNotExistsException() { + super("해당 회원이 존재하지 않습니다."); + } +} diff --git a/src/main/java/roomescape/facade/ReservationFacade.java b/src/main/java/roomescape/facade/ReservationFacade.java index 90c5e08eec..462ab49f0b 100644 --- a/src/main/java/roomescape/facade/ReservationFacade.java +++ b/src/main/java/roomescape/facade/ReservationFacade.java @@ -11,6 +11,7 @@ import roomescape.controller.dto.response.ReservationWaitListResponse; import roomescape.controller.dto.response.ReservationWaitResponse; import roomescape.controller.dto.response.WaitListResponse; +import roomescape.domain.Member; import roomescape.domain.Reservation; import roomescape.domain.ReservationTime; import roomescape.domain.Slot; @@ -18,6 +19,7 @@ import roomescape.domain.Wait; import roomescape.domain.Waits; import roomescape.exception.custom.AlreadyReservedException; +import roomescape.service.MemberService; import roomescape.service.ReservationService; import roomescape.service.ReservationTimeService; import roomescape.service.ThemeService; @@ -31,23 +33,38 @@ public class ReservationFacade { private final WaitService waitService; private final ReservationTimeService reservationTimeService; private final ThemeService themeService; + private final MemberService memberService; private final Clock clock; public ReservationFacade(ReservationService reservationService, WaitService waitService, - ReservationTimeService reservationTimeService, ThemeService themeService, Clock clock) { + ReservationTimeService reservationTimeService, ThemeService themeService, + MemberService memberService, Clock clock) { this.reservationService = reservationService; this.waitService = waitService; this.reservationTimeService = reservationTimeService; this.themeService = themeService; + this.memberService = memberService; this.clock = clock; } @Transactional - public ReservationWaitResponse save(ReservationCreateRequest request) { + public ReservationWaitResponse save(ReservationCreateRequest request, Long memberId) { + Member member = memberService.findMember(memberId); ReservationTime reservationTime = reservationTimeService.findReservationTime(request.timeId()); Theme theme = themeService.findTheme(request.themeId()); - return saveReservationOrWait(request, reservationTime, theme); + return saveReservationOrWait(request, member, reservationTime, theme); + } + + public ReservationWaitListResponse findByMemberId(Long memberId) { + ReservationListResponse reservationListResponse = ReservationListResponse.from( + reservationService.findByMemberId(memberId)); + + Waits waits = waitService.findByMemberId(memberId); + Map waitWithOrder = waits.waitsWithOrder(); + WaitListResponse waitListResponse = WaitListResponse.from(waitWithOrder); + + return new ReservationWaitListResponse(reservationListResponse, waitListResponse); } public ReservationWaitListResponse findByName(String name) { @@ -75,13 +92,15 @@ public ReservationWaitListResponse findAll() { @Transactional public void deleteReservation(Long id) { Reservation reservation = reservationService.findReservation(id); - reservationService.delete(reservation, false); Slot slot = new Slot(reservation.getDate(), reservation.getTime(), reservation.getTheme()); Waits waits = waitService.findBySlot(slot); + if (waits.isEmptyWaitsBySlot(slot)) { + reservationService.delete(reservation, false); return; } + reservationService.deleteAndFlush(reservation, false); confirmFirstWait(waits.firstWaitBySlot(slot)); } @@ -90,37 +109,36 @@ public void deleteWait(Long id) { waitService.delete(id, false); } - private ReservationWaitResponse saveReservationOrWait(ReservationCreateRequest request, + private ReservationWaitResponse saveReservationOrWait(ReservationCreateRequest request, Member member, ReservationTime reservationTime, Theme theme) { Optional reservation = reservationService.findBySlot(request.date(), request.timeId(), request.themeId()); if (reservation.isEmpty()) { - return saveReservation(request, reservationTime, theme); + return saveReservation(request, member, reservationTime, theme); } - if (reservation.get().isSameUser(request.name())) { + if (reservation.get().isSameUser(member)) { throw new AlreadyReservedException(); } - return saveWait(request, reservationTime, theme); + return saveWait(request, member, reservationTime, theme); } - private ReservationWaitResponse saveReservation(ReservationCreateRequest request, - ReservationTime reservationTime, - Theme theme) { - Reservation newReservationWithoutId = request.toReservation(reservationTime, theme); + private ReservationWaitResponse saveReservation(ReservationCreateRequest request, Member member, + ReservationTime reservationTime, Theme theme) { + Reservation newReservationWithoutId = request.toReservation(member, reservationTime, theme); Reservation newReservation = reservationService.save(newReservationWithoutId, false); return ReservationWaitResponse.from(newReservation); } - private ReservationWaitResponse saveWait(ReservationCreateRequest request, ReservationTime reservationTime, - Theme theme) { - Wait newWaitWithoutId = request.toWait(LocalDateTime.now(clock), reservationTime, theme); + private ReservationWaitResponse saveWait(ReservationCreateRequest request, Member member, + ReservationTime reservationTime, Theme theme) { + Wait newWaitWithoutId = request.toWait(LocalDateTime.now(clock), member, reservationTime, theme); Wait wait = waitService.save(newWaitWithoutId); return ReservationWaitResponse.from(wait, waitService.calculateOrder(wait)); } private void confirmFirstWait(Wait firstWait) { - Reservation reservationWithoutId = new Reservation(firstWait.getName(), + Reservation reservationWithoutId = new Reservation(firstWait.getMember(), new Slot(firstWait.getReservationDate(), firstWait.getTime(), firstWait.getTheme())); reservationService.save(reservationWithoutId, true); waitService.delete(firstWait.getId(), true); diff --git a/src/main/java/roomescape/facade/ReservationTimeFacade.java b/src/main/java/roomescape/facade/ReservationTimeFacade.java index 0660981627..ef582289d7 100644 --- a/src/main/java/roomescape/facade/ReservationTimeFacade.java +++ b/src/main/java/roomescape/facade/ReservationTimeFacade.java @@ -14,6 +14,7 @@ import roomescape.domain.ReservationTime; import roomescape.domain.Slot; import roomescape.domain.Theme; +import roomescape.domain.Waits; import roomescape.service.ReservationService; import roomescape.service.ReservationTimeService; import roomescape.service.ThemeService; @@ -75,7 +76,8 @@ private ReservationAvailability getAvailability(List reservedTi } Theme theme = themeService.findTheme(themeId); Slot slot = new Slot(date, reservationTime, theme); - if (waitService.findBySlot(slot).isFullWaitsBySlot(slot)) { + Waits waits = waitService.findBySlot(slot); + if (waits.isFullWaitsBySlot(slot)) { return ReservationAvailability.NOTHING_AVAILABLE; } return ReservationAvailability.WAITING_AVAILABLE; diff --git a/src/main/java/roomescape/repository/JdbcReservationRepository.java b/src/main/java/roomescape/repository/JdbcReservationRepository.java deleted file mode 100644 index 88038969e2..0000000000 --- a/src/main/java/roomescape/repository/JdbcReservationRepository.java +++ /dev/null @@ -1,143 +0,0 @@ -package roomescape.repository; - -import java.sql.Date; -import java.sql.PreparedStatement; -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; -import java.util.Optional; -import org.springframework.context.annotation.Primary; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.domain.Reservation; -import roomescape.domain.ReservationTime; -import roomescape.domain.Slot; -import roomescape.domain.Theme; - -@Primary -@Repository -public class JdbcReservationRepository implements ReservationRepository { - - private final JdbcTemplate jdbcTemplate; - - public JdbcReservationRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - @Override - public Reservation save(Reservation reservationWithoutId) { - String sql = "INSERT INTO `reservation`(`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement preparedStatement = connection.prepareStatement(sql, new String[]{"id"}); - preparedStatement.setString(1, reservationWithoutId.getName()); - preparedStatement.setDate(2, Date.valueOf(reservationWithoutId.getDate())); - preparedStatement.setLong(3, reservationWithoutId.getTime().getId()); - preparedStatement.setLong(4, reservationWithoutId.getTheme().getId()); - - return preparedStatement; - }, keyHolder); - - Long id = keyHolder.getKey().longValue(); - return reservationWithoutId.withId(id); - } - - @Override - public Optional findById(Long id) { - String sql = """ - SELECT r.id, r.name, r.date, t.id as time_id, t.start_at as time_value, - th.id as theme_id, th.name as theme_name, th.description as theme_description, th.thumbnail_url as theme_thumbnail_url - FROM `reservation` r - INNER JOIN `reservation_time` t ON r.time_id = t.id - INNER JOIN `theme` th ON r.theme_id = th.id - WHERE r.id = (?) - """; - - return jdbcTemplate.query(sql, reservationRowMapper(), id).stream().findFirst(); - } - - @Override - public Optional findBySlot(LocalDate date, Long timeId, Long themeId) { - String sql = """ - SELECT r.id, r.name, r.date, t.id as time_id, t.start_at as time_value, - th.id as theme_id, th.name as theme_name, th.description as theme_description, th.thumbnail_url as theme_thumbnail_url - FROM `reservation` r - INNER JOIN `reservation_time` t ON r.time_id = t.id - INNER JOIN `theme` th ON r.theme_id = th.id - WHERE r.date = (?) AND r.time_id = (?) AND r.theme_id = (?) - """; - - return jdbcTemplate.query(sql, reservationRowMapper(), date, timeId, themeId).stream().findFirst(); - } - - @Override - public List findByName(String name) { - String sql = """ - SELECT r.id, r.name, r.date, t.id as time_id, t.start_at as time_value, - th.id as theme_id, th.name as theme_name, th.description as theme_description, th.thumbnail_url as theme_thumbnail_url - FROM `reservation` r - INNER JOIN `reservation_time` t ON r.time_id = t.id - INNER JOIN `theme` th ON r.theme_id = th.id - WHERE r.name = (?) - """; - - return jdbcTemplate.query(sql, reservationRowMapper(), name); - } - - @Override - public List findAll() { - String sql = """ - SELECT r.id, r.name, r.date, t.id as time_id, t.start_at as time_value, - th.id as theme_id, th.name as theme_name, th.description as theme_description, th.thumbnail_url as theme_thumbnail_url - FROM `reservation` r - INNER JOIN `reservation_time` t ON r.time_id = t.id - INNER JOIN `theme` th ON r.theme_id = th.id - """; - - return jdbcTemplate.query(sql, reservationRowMapper()); - } - - private static RowMapper reservationRowMapper() { - return (resultSet, rowNum) -> { - Long id = resultSet.getLong("id"); - String name = resultSet.getString("name"); - LocalDate date = resultSet.getDate("date").toLocalDate(); - Long timeId = resultSet.getLong("time_id"); - LocalTime timeValue = resultSet.getTime("time_value").toLocalTime(); - Long themeId = resultSet.getLong("theme_id"); - String themeName = resultSet.getString("theme_name"); - String themeDescription = resultSet.getString("theme_description"); - String themeThumbnailUrl = resultSet.getString("theme_thumbnail_url"); - - ReservationTime reservationTime = new ReservationTime(timeId, timeValue); - Theme theme = new Theme(themeId, themeName, themeDescription, themeThumbnailUrl); - Slot slot = new Slot(date, reservationTime, theme); - return new Reservation(id, name, slot); - }; - } - - @Override - public void delete(Long id) { - String sql = "DELETE FROM `reservation` WHERE `id` = (?)"; - - jdbcTemplate.update(sql, id); - } - - @Override - public boolean existsByTimeId(Long timeId) { - String sql = "SELECT EXISTS (SELECT 1 FROM `reservation` WHERE `time_id` = (?)) AS exist"; - - return Boolean.TRUE.equals(jdbcTemplate.queryForObject(sql, Boolean.class, timeId)); - } - - @Override - public boolean existsByThemeId(Long themeId) { - String sql = "SELECT EXISTS (SELECT 1 FROM `reservation` WHERE `theme_id` = (?)) AS exist"; - - return Boolean.TRUE.equals(jdbcTemplate.queryForObject(sql, Boolean.class, themeId)); - } -} diff --git a/src/main/java/roomescape/repository/JdbcReservationTimeRepository.java b/src/main/java/roomescape/repository/JdbcReservationTimeRepository.java deleted file mode 100644 index 3b4f3361c1..0000000000 --- a/src/main/java/roomescape/repository/JdbcReservationTimeRepository.java +++ /dev/null @@ -1,98 +0,0 @@ -package roomescape.repository; - -import java.sql.PreparedStatement; -import java.sql.Time; -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; -import java.util.Optional; -import org.springframework.context.annotation.Primary; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.domain.ReservationTime; - -@Primary -@Repository -public class JdbcReservationTimeRepository implements ReservationTimeRepository { - - private final JdbcTemplate jdbcTemplate; - - public JdbcReservationTimeRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - @Override - public ReservationTime save(ReservationTime reservationTimeWithoutId) { - String sql = "INSERT INTO `reservation_time`(`start_at`) VALUES (?)"; - - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement preparedStatement = connection.prepareStatement(sql, new String[]{"id"}); - preparedStatement.setTime(1, Time.valueOf(reservationTimeWithoutId.getStartAt())); - - return preparedStatement; - }, keyHolder); - - Long id = keyHolder.getKey().longValue(); - return ReservationTime.withId(id, reservationTimeWithoutId); - } - - @Override - public Optional findById(Long id) { - String sql = "SELECT * FROM `reservation_time` WHERE `id` = (?)"; - - return jdbcTemplate.query(sql, (resultSet, rowNum) -> { - LocalTime startAt = resultSet.getTime("start_at").toLocalTime(); - return new ReservationTime(id, startAt); - }, id).stream().findFirst(); - } - - @Override - public List findAll() { - String sql = """ - SELECT * FROM `reservation_time` - ORDER BY start_at ASC - """; - - return jdbcTemplate.query(sql, (resultSet, rowNum) -> { - Long id = resultSet.getLong("id"); - LocalTime startAt = resultSet.getTime("start_at").toLocalTime(); - return new ReservationTime(id, startAt); - }); - } - - @Override - public void delete(Long id) { - String sql = "DELETE FROM `reservation_time` WHERE `id` = (?)"; - jdbcTemplate.update(sql, id); - } - - @Override - public List findReservedTimesByDateAndTheme(LocalDate date, Long themeId) { - String sql = """ - SELECT t.id as time_id, t.start_at as start_at - FROM `reservation_time` t - INNER JOIN `reservation` r ON r.time_id = t.id - WHERE r.date = (?) AND r.theme_id = (?) - """; - - return jdbcTemplate.query(sql, (resultSet, rowNum) -> { - Long id = resultSet.getLong("time_id"); - LocalTime startAt = resultSet.getTime("start_at").toLocalTime(); - return new ReservationTime(id, startAt); - }, date, themeId); - } - - @Override - public boolean existsByStartAt(LocalTime startAt) { - String sql = """ - SELECT EXISTS ( - SELECT 1 FROM `reservation_time` WHERE `start_at` = (?) - ) AS exist - """; - - return Boolean.TRUE.equals(jdbcTemplate.queryForObject(sql, Boolean.class, startAt)); - } -} diff --git a/src/main/java/roomescape/repository/JdbcThemeRepository.java b/src/main/java/roomescape/repository/JdbcThemeRepository.java deleted file mode 100644 index 91f519dd7e..0000000000 --- a/src/main/java/roomescape/repository/JdbcThemeRepository.java +++ /dev/null @@ -1,115 +0,0 @@ -package roomescape.repository; - -import java.sql.PreparedStatement; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; -import org.springframework.context.annotation.Primary; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.domain.Theme; - -@Primary -@Repository -public class JdbcThemeRepository implements ThemeRepository { - - private final JdbcTemplate jdbcTemplate; - - public JdbcThemeRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - @Override - public Theme save(Theme themeWithoutId) { - String sql = "INSERT INTO `theme`(`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement preparedStatement = connection.prepareStatement(sql, new String[]{"id"}); - preparedStatement.setString(1, themeWithoutId.getName()); - preparedStatement.setString(2, themeWithoutId.getDescription()); - preparedStatement.setString(3, themeWithoutId.getThumbnailUrl()); - - return preparedStatement; - }, keyHolder); - - Long id = keyHolder.getKey().longValue(); - return Theme.withId(id, themeWithoutId); - } - - @Override - public Optional findById(Long id) { - String sql = "SELECT * FROM `theme` WHERE `id` = (?)"; - - return jdbcTemplate.query(sql, (resultSet, rowNumber) -> { - String name = resultSet.getString("name"); - String description = resultSet.getString("description"); - String thumbnailUrl = resultSet.getString("thumbnail_url"); - return new Theme(id, name, description, thumbnailUrl); - }, id).stream().findAny(); - } - - @Override - public List findAll() { - String sql = "SELECT * FROM `theme`"; - - return jdbcTemplate.query( - sql, - (resultSet, rowNumber) -> { - Long id = resultSet.getLong("id"); - String name = resultSet.getString("name"); - String description = resultSet.getString("description"); - String thumbnailUrl = resultSet.getString("thumbnail_url"); - return new Theme(id, name, description, thumbnailUrl); - } - ); - } - - @Override - public void deleteById(Long id) { - String sql = "DELETE FROM theme WHERE id = (?)"; - - jdbcTemplate.update(sql, id); - } - - @Override - public List findRanking(LocalDate startDate, LocalDate endDate, int limit) { - String sql = """ - SELECT th.id AS theme_id, th.name, th.description, th.thumbnail_url, COUNT(r.id) AS reservation_count - FROM theme th - LEFT JOIN reservation r - ON r.theme_id = th.id - AND r.date BETWEEN (?) AND (?) - GROUP BY th.id, th.name, th.description, th.thumbnail_url - ORDER BY reservation_count DESC, th.id ASC - LIMIT (?) - """; - - return jdbcTemplate.query( - sql, - (resultSet, rowNumber) -> { - Long id = resultSet.getLong("theme_id"); - String name = resultSet.getString("name"); - String description = resultSet.getString("description"); - String thumbnailUrl = resultSet.getString("thumbnail_url"); - return new Theme(id, name, description, thumbnailUrl); - }, - startDate, - endDate, - limit - ); - } - - @Override - public boolean existsById(Long id) { - String sql = """ - SELECT EXISTS ( - SELECT 1 FROM `theme` WHERE `id` = (?) - ) AS exist - """; - - return Boolean.TRUE.equals(jdbcTemplate.queryForObject(sql, Boolean.class, id)); - } -} diff --git a/src/main/java/roomescape/repository/JdbcWaitRepository.java b/src/main/java/roomescape/repository/JdbcWaitRepository.java deleted file mode 100644 index 971af52d29..0000000000 --- a/src/main/java/roomescape/repository/JdbcWaitRepository.java +++ /dev/null @@ -1,179 +0,0 @@ -package roomescape.repository; - -import java.sql.Date; -import java.sql.PreparedStatement; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.Optional; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.domain.ReservationTime; -import roomescape.domain.Slot; -import roomescape.domain.Theme; -import roomescape.domain.Wait; -import roomescape.domain.Waits; - -@Repository -public class JdbcWaitRepository implements WaitRepository { - - private final JdbcTemplate jdbcTemplate; - - public JdbcWaitRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - @Override - public Wait save(Wait waitWithoutId) { - String sql = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement preparedStatement = connection.prepareStatement(sql, new String[]{"id"}); - preparedStatement.setObject(1, waitWithoutId.getCreatedAt()); - preparedStatement.setString(2, waitWithoutId.getName()); - preparedStatement.setDate(3, Date.valueOf(waitWithoutId.getReservationDate())); - preparedStatement.setLong(4, waitWithoutId.getTime().getId()); - preparedStatement.setLong(5, waitWithoutId.getTheme().getId()); - - return preparedStatement; - }, keyHolder); - - Long id = keyHolder.getKey().longValue(); - return waitWithoutId.withId(id); - } - - @Override - public Optional findById(Long id) { - String sql = """ - SELECT w.id, w.created_at, w.name, w.reservation_date, - t.id AS time_id, t.start_at AS time_value, - th.id AS theme_id, th.name AS theme_name, th.description AS theme_description, th.thumbnail_url AS theme_thumbnail_url - FROM wait w - INNER JOIN reservation_time t ON w.time_id = t.id - INNER JOIN theme th ON w.theme_id = th.id - WHERE w.id = (?) - """; - - return jdbcTemplate.query(sql, waitDetailDtoRowMapper(), id) - .stream() - .findFirst(); - } - - @Override - public Waits findBySlot(LocalDate date, Long timeId, Long themeId) { - String sql = """ - SELECT w.id, w.created_at, w.name, w.reservation_date, - rt.id AS time_id, rt.start_at AS time_value, - t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, t.thumbnail_url AS theme_thumbnail_url - FROM wait w - JOIN reservation_time rt ON w.time_id = rt.id - JOIN theme t ON w.theme_id = t.id - WHERE w.reservation_date = ? AND w.time_id = ? AND w.theme_id = ? - ORDER BY w.created_at, w.id - """; - - return new Waits( - jdbcTemplate.query(sql, waitDetailDtoRowMapper(), - date, - timeId, - themeId) - ); - } - - @Override - public Waits findByName(String name) { - String sql = """ - SELECT w.id, w.created_at, w.name, w.reservation_date, - rt.id AS time_id, rt.start_at AS time_value, - t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, t.thumbnail_url AS theme_thumbnail_url - FROM wait w - JOIN reservation_time rt ON w.time_id = rt.id - JOIN theme t ON w.theme_id = t.id - WHERE w.name = ? - ORDER BY w.created_at, w.id - """; - - return new Waits( - jdbcTemplate.query(sql, waitDetailDtoRowMapper(), name) - ); - } - - @Override - public Waits findAll() { - String sql = """ - SELECT w.id, w.created_at, w.name, w.reservation_date, - rt.id AS time_id, rt.start_at AS time_value, - t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, t.thumbnail_url AS theme_thumbnail_url - FROM wait w - JOIN reservation_time rt ON w.time_id = rt.id - JOIN theme t ON w.theme_id = t.id - ORDER BY w.created_at, w.id - """; - - return new Waits( - jdbcTemplate.query(sql, waitDetailDtoRowMapper()) - ); - } - - @Override - public Long findOrderByWait(Wait wait) { - String sql = """ - WITH slot_waiting_list AS ( - SELECT `id`, ROW_NUMBER() OVER (ORDER BY created_at, id) AS `order` - FROM wait - WHERE `reservation_date` = ? AND `time_id` = ? AND `theme_id` = ? - ) - SELECT `order` FROM slot_waiting_list WHERE `id` = ? - """; - - return jdbcTemplate.queryForObject(sql, Long.class, - wait.getReservationDate(), - wait.getTime().getId(), - wait.getTheme().getId(), - wait.getId()); - } - - @Override - public void deleteById(Long id) { - String sql = "DELETE FROM `wait` WHERE `id` = (?)"; - - jdbcTemplate.update(sql, id); - } - - @Override - public boolean existsByTimeId(Long timeId) { - String sql = "SELECT EXISTS (SELECT 1 FROM `wait` WHERE `time_id` = (?)) AS exist"; - - return Boolean.TRUE.equals(jdbcTemplate.queryForObject(sql, Boolean.class, timeId)); - } - - @Override - public boolean existsByThemeId(Long themeId) { - String sql = "SELECT EXISTS (SELECT 1 FROM `wait` WHERE `theme_id` = (?)) AS exist"; - - return Boolean.TRUE.equals(jdbcTemplate.queryForObject(sql, Boolean.class, themeId)); - } - - private static RowMapper waitDetailDtoRowMapper() { - return (resultSet, rowNum) -> { - Long id = resultSet.getLong("id"); - LocalDateTime createdAt = resultSet.getObject("created_at", LocalDateTime.class); - String name = resultSet.getString("name"); - LocalDate date = resultSet.getDate("reservation_date").toLocalDate(); - Long timeId = resultSet.getLong("time_id"); - LocalTime timeValue = resultSet.getTime("time_value").toLocalTime(); - Long themeId = resultSet.getLong("theme_id"); - String themeName = resultSet.getString("theme_name"); - String themeDescription = resultSet.getString("theme_description"); - String themeThumbnailUrl = resultSet.getString("theme_thumbnail_url"); - - ReservationTime reservationTime = new ReservationTime(timeId, timeValue); - Theme theme = new Theme(themeId, themeName, themeDescription, themeThumbnailUrl); - return new Wait(id, createdAt, name, new Slot(date, reservationTime, theme)); - }; - } -} diff --git a/src/main/java/roomescape/repository/MemberRepository.java b/src/main/java/roomescape/repository/MemberRepository.java new file mode 100644 index 0000000000..139f73469b --- /dev/null +++ b/src/main/java/roomescape/repository/MemberRepository.java @@ -0,0 +1,7 @@ +package roomescape.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import roomescape.domain.Member; + +public interface MemberRepository extends JpaRepository { +} diff --git a/src/main/java/roomescape/repository/ReservationRepository.java b/src/main/java/roomescape/repository/ReservationRepository.java index 7021e5efb2..537d9e453f 100644 --- a/src/main/java/roomescape/repository/ReservationRepository.java +++ b/src/main/java/roomescape/repository/ReservationRepository.java @@ -3,23 +3,34 @@ import java.time.LocalDate; import java.util.List; import java.util.Optional; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import roomescape.domain.Reservation; -public interface ReservationRepository { +public interface ReservationRepository extends JpaRepository { - Reservation save(Reservation reservationWithoutId); + @Query( + value = """ + SELECT r FROM Reservation r + WHERE r.slot.reservationDate = :reservationDate + AND r.slot.time.id = :timeId + AND r.slot.theme.id = :themeId + """) + @EntityGraph(attributePaths = {"member", "slot.time", "slot.theme"}) + Optional findBySlot( + @Param("reservationDate") LocalDate reservationDate, + @Param("timeId") Long timeId, + @Param("themeId") Long themeId); - Optional findById(Long id); + @EntityGraph(attributePaths = {"member", "slot.time", "slot.theme"}) + List findByMemberId(Long memberId); - Optional findBySlot(LocalDate date, Long timeId, Long themeId); + @EntityGraph(attributePaths = {"member", "slot.time", "slot.theme"}) + List findByMember_Name(String name); - List findByName(String name); + boolean existsBySlot_Time_Id(Long timeId); - List findAll(); - - void delete(Long id); - - boolean existsByTimeId(Long timeId); - - boolean existsByThemeId(Long themeId); + boolean existsBySlot_Theme_Id(Long themeId); } diff --git a/src/main/java/roomescape/repository/ReservationTimeRepository.java b/src/main/java/roomescape/repository/ReservationTimeRepository.java index 89253f1238..9f6f2d1c3c 100644 --- a/src/main/java/roomescape/repository/ReservationTimeRepository.java +++ b/src/main/java/roomescape/repository/ReservationTimeRepository.java @@ -3,20 +3,24 @@ 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.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import roomescape.domain.ReservationTime; -public interface ReservationTimeRepository { - - ReservationTime save(ReservationTime reservationTimeWithoutId); - - Optional findById(Long id); - - List findReservedTimesByDateAndTheme(LocalDate date, Long themeId); - - List findAll(); - - void delete(Long id); +public interface ReservationTimeRepository extends JpaRepository { + + @Query( + value = """ + SELECT t FROM ReservationTime t + INNER JOIN Reservation r ON r.slot.time.id = t.id + WHERE r.slot.reservationDate = :reservationDate + AND r.slot.theme.id = :themeId + """) + List findReservedTimesByDateAndTheme_Id( + @Param("reservationDate") LocalDate reservationDate, + @Param("themeId") Long themeId + ); boolean existsByStartAt(LocalTime startAt); } diff --git a/src/main/java/roomescape/repository/ThemeRepository.java b/src/main/java/roomescape/repository/ThemeRepository.java index ea0fe48631..1681f4134a 100644 --- a/src/main/java/roomescape/repository/ThemeRepository.java +++ b/src/main/java/roomescape/repository/ThemeRepository.java @@ -2,20 +2,29 @@ import java.time.LocalDate; import java.util.List; -import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import roomescape.domain.Theme; -public interface ThemeRepository { - - Theme save(Theme themeWithoutId); - - Optional findById(Long id); - - List findRanking(LocalDate startDate, LocalDate endDate, int limit); - - List findAll(); - - void deleteById(Long id); - - boolean existsById(Long id); +public interface ThemeRepository extends JpaRepository { + + @Query( + value = """ + SELECT th.id AS theme_id, th.name, th.description, th.thumbnail_url, COUNT(r.id) AS reservation_count + FROM theme th + LEFT JOIN reservation r + ON r.theme_id = th.id + AND r.reservation_date BETWEEN :startDate AND :endDate + GROUP BY th.id, th.name, th.description, th.thumbnail_url + ORDER BY reservation_count DESC, th.id ASC + LIMIT :limit + """, + nativeQuery = true + ) + List findRanking( + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate, + @Param("limit") int limit + ); } diff --git a/src/main/java/roomescape/repository/WaitRepository.java b/src/main/java/roomescape/repository/WaitRepository.java index a76437d7e5..110bbeb901 100644 --- a/src/main/java/roomescape/repository/WaitRepository.java +++ b/src/main/java/roomescape/repository/WaitRepository.java @@ -1,27 +1,56 @@ package roomescape.repository; import java.time.LocalDate; -import java.util.Optional; +import java.util.List; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import roomescape.domain.Wait; -import roomescape.domain.Waits; -public interface WaitRepository { - - Wait save(Wait waitWithoutId); - - Optional findById(Long id); - - Waits findBySlot(LocalDate reservationDate, Long timeId, Long themeId); - - Waits findByName(String name); - - Waits findAll(); - - Long findOrderByWait(Wait wait); - - void deleteById(Long id); - - boolean existsByTimeId(Long timeId); - - boolean existsByThemeId(Long themeId); +public interface WaitRepository extends JpaRepository { + + @Query("SELECT w FROM Wait w") + List findAllWaits(); + + @Query( + value = """ + SELECT w FROM Wait w + WHERE w.slot.reservationDate = :reservationDate + AND w.slot.time.id = :timeId + AND w.slot.theme.id = :themeId + """) + @EntityGraph(attributePaths = {"member", "slot.time", "slot.theme"}) + List findBySlot( + @Param("reservationDate") LocalDate reservationDate, + @Param("timeId") Long timeId, + @Param("themeId") Long themeId + ); + + @EntityGraph(attributePaths = {"member", "slot.time", "slot.theme"}) + List findByMemberId(Long memberId); + + @EntityGraph(attributePaths = {"member", "slot.time", "slot.theme"}) + List findByMember_Name(String name); + + @Query( + value = """ + WITH slot_waiting_list AS ( + SELECT `id`, ROW_NUMBER() OVER (ORDER BY created_at, id) AS `order` + FROM wait + WHERE `reservation_date` = :reservationDate AND `time_id` = :timeId AND `theme_id` = :themeId + ) + SELECT `order` FROM slot_waiting_list WHERE `id` = :waitId + """, + nativeQuery = true + ) + Long calculateWaitingOrder( + @Param("reservationDate") LocalDate reservationDate, + @Param("timeId") Long timeId, + @Param("themeId") Long themeId, + @Param("waitId") Long waitId); + + boolean existsBySlot_Time_Id(Long timeId); + + boolean existsBySlot_Theme_Id(Long themeId); } diff --git a/src/main/java/roomescape/service/MemberService.java b/src/main/java/roomescape/service/MemberService.java new file mode 100644 index 0000000000..575125d304 --- /dev/null +++ b/src/main/java/roomescape/service/MemberService.java @@ -0,0 +1,23 @@ +package roomescape.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import roomescape.domain.Member; +import roomescape.exception.custom.MemberNotExistsException; +import roomescape.repository.MemberRepository; + +@Service +@Transactional(readOnly = true) +public class MemberService { + + private final MemberRepository memberRepository; + + public MemberService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + + public Member findMember(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(MemberNotExistsException::new); + } +} diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index 4304572750..5f310bd8b3 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -34,8 +34,12 @@ public Reservation save(Reservation reservationWithoutId, boolean isAdmin) { return reservationRepository.save(reservationWithoutId); } + public List findByMemberId(Long memberId) { + return reservationRepository.findByMemberId(memberId); + } + public List findByName(String name) { - return reservationRepository.findByName(name); + return reservationRepository.findByMember_Name(name); } public List findAll() { @@ -51,7 +55,12 @@ public Reservation findReservation(Long reservationId) { public void delete(Reservation reservation, boolean isAdmin) { ReservationValidator reservationValidator = ReservationValidatorFactory.getValidator(isAdmin); reservationValidator.validateDelete(reservation, LocalDateTime.now(clock)); - reservationRepository.delete(reservation.getId()); + reservationRepository.deleteById(reservation.getId()); + } + + public void deleteAndFlush(Reservation reservation, boolean isAdmin) { + delete(reservation, isAdmin); + reservationRepository.flush(); } public Optional findBySlot(LocalDate date, Long timeId, Long themeId) { @@ -59,13 +68,13 @@ public Optional findBySlot(LocalDate date, Long timeId, Long themeI } public void validateReferencedTheme(Long themeId) { - if (reservationRepository.existsByThemeId(themeId)) { + if (reservationRepository.existsBySlot_Theme_Id(themeId)) { throw new CannotDeleteThemeInUseException(); } } - public void validateReferencedTime(Long id) { - if (reservationRepository.existsByTimeId(id)) { + public void validateReferencedTime(Long timeId) { + if (reservationRepository.existsBySlot_Time_Id(timeId)) { throw new CannotDeleteReservationTimeInUseException(); } } diff --git a/src/main/java/roomescape/service/ReservationTimeService.java b/src/main/java/roomescape/service/ReservationTimeService.java index f2c3e9079d..14c03ef4ae 100644 --- a/src/main/java/roomescape/service/ReservationTimeService.java +++ b/src/main/java/roomescape/service/ReservationTimeService.java @@ -44,7 +44,7 @@ private void validateDuplicatedReservationTime(LocalTime startAt) { public List findReservedTimesByDateAndTheme(LocalDate date, Long themeId) { validateNotPastDate(date); - return reservationTimeRepository.findReservedTimesByDateAndTheme(date, themeId); + return reservationTimeRepository.findReservedTimesByDateAndTheme_Id(date, themeId); } private void validateNotPastDate(LocalDate date) { @@ -55,7 +55,7 @@ private void validateNotPastDate(LocalDate date) { @Transactional public void delete(Long id) { - reservationTimeRepository.delete(id); + reservationTimeRepository.deleteById(id); } public ReservationTime findReservationTime(Long timeId) { diff --git a/src/main/java/roomescape/service/WaitService.java b/src/main/java/roomescape/service/WaitService.java index 5c42ecf5f3..6f10edb961 100644 --- a/src/main/java/roomescape/service/WaitService.java +++ b/src/main/java/roomescape/service/WaitService.java @@ -28,19 +28,25 @@ public WaitService(WaitRepository waitRepository, Clock clock) { @Transactional public Wait save(Wait waitWithoutId) { - Waits waits = waitRepository.findBySlot(waitWithoutId.getReservationDate(), waitWithoutId.getTime().getId(), - waitWithoutId.getTheme().getId()); - waits.validateCreate(waitWithoutId.getName(), waitWithoutId.getSlot()); + Waits waits = new Waits( + waitRepository.findBySlot(waitWithoutId.getReservationDate(), waitWithoutId.getTimeId(), + waitWithoutId.getThemeId()) + ); + waits.validateCreate(waitWithoutId.getMember(), waitWithoutId.getSlot()); return waitRepository.save(waitWithoutId); } + public Waits findByMemberId(Long memberId) { + return new Waits(waitRepository.findByMemberId(memberId)); + } + public Waits findByName(String name) { - return waitRepository.findByName(name); + return new Waits(waitRepository.findByMember_Name(name)); } public Waits findAll() { - return waitRepository.findAll(); + return new Waits(waitRepository.findAllWaits()); } @Transactional @@ -53,7 +59,8 @@ public void delete(Long id, boolean isAdmin) { } public Waits findBySlot(Slot slot) { - return waitRepository.findBySlot(slot.getDate(), slot.getTime().getId(), slot.getTheme().getId()); + return new Waits(waitRepository.findBySlot(slot.getReservationDate(), slot.getTimeId(), + slot.getThemeId())); } public Wait findWait(Long waitId) { @@ -62,17 +69,18 @@ public Wait findWait(Long waitId) { } public Long calculateOrder(Wait wait) { - return waitRepository.findOrderByWait(wait); + return waitRepository.calculateWaitingOrder(wait.getReservationDate(), wait.getTimeId(), wait.getThemeId(), + wait.getId()); } public void validateReferencedTime(Long timeId) { - if (waitRepository.existsByTimeId(timeId)) { + if (waitRepository.existsBySlot_Time_Id(timeId)) { throw new CannotDeleteReservationTimeInUseException(); } } public void validateReferencedTheme(Long themeId) { - if (waitRepository.existsByThemeId(themeId)) { + if (waitRepository.existsBySlot_Theme_Id(themeId)) { throw new CannotDeleteThemeInUseException(); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b63619d739..0447097470 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,7 @@ spring.h2.console.enabled=true spring.h2.console.path=/h2-console spring.datasource.url=jdbc:h2:mem:database +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.ddl-auto=create-drop +spring.jpa.defer-datasource-initialization=true diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 566830ac8e..26bcdf2609 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,104 +1,128 @@ -INSERT INTO reservation_time (start_at) VALUES -('10:00'), -('11:00'), -('12:00'), -('13:00'), -('14:00'), -('15:00'), -('16:00'), -('17:00'), -('18:00'), -('19:00'), -('20:00'); +INSERT INTO reservation_time (start_at) +VALUES ('10:00'), + ('11:00'), + ('12:00'), + ('13:00'), + ('14:00'), + ('15:00'), + ('16:00'), + ('17:00'), + ('18:00'), + ('19:00'), + ('20:00'); -INSERT INTO theme (name, description, thumbnail_url) VALUES -('잃어버린 왕국', '사라진 고대 왕국의 비밀을 추적하는 모험 테마', 'https://example.com/images/lost-kingdom.jpg'), -('심야의 연구소', '한밤중 폐쇄된 연구소에서 탈출 단서를 찾는 스릴러 테마', 'https://example.com/images/midnight-lab.jpg'), -('해적선의 저주', '저주받은 해적선에서 보물을 찾아 탈출하는 테마', 'https://example.com/images/pirate-curse.jpg'), -('시간 여행자', '흩어진 시간의 조각을 맞춰 현재로 돌아오는 SF 테마', 'https://example.com/images/time-traveler.jpg'), -('마법사의 서재', '마법 주문과 숨겨진 장치를 풀어내는 판타지 테마', 'https://example.com/images/wizard-library.jpg'), -('사라진 탐정', '실종된 탐정이 남긴 단서를 따라 사건을 해결하는 추리 테마', 'https://example.com/images/missing-detective.jpg'), -('지하 벙커', '폐쇄된 지하 벙커의 보안 시스템을 해제하는 생존 테마', 'https://example.com/images/underground-bunker.jpg'), -('고대 신전', '신전 깊은 곳의 퍼즐을 풀고 봉인을 해제하는 모험 테마', 'https://example.com/images/ancient-temple.jpg'), -('유령 호텔', '기묘한 호텔 방마다 숨은 이야기를 밝혀내는 공포 테마', 'https://example.com/images/ghost-hotel.jpg'), -('우주 정거장', '고장 난 우주 정거장을 복구하고 귀환하는 SF 테마', 'https://example.com/images/space-station.jpg'), -('비밀 카지노', '비밀 카지노의 금고 암호를 찾아내는 잠입 테마', 'https://example.com/images/secret-casino.jpg'), -('눈보라 산장', '눈보라에 갇힌 산장에서 범인을 찾아내는 추리 테마', 'https://example.com/images/snow-cabin.jpg'), -('인형의 집', '낡은 인형의 집에 숨겨진 진실을 발견하는 미스터리 테마', 'https://example.com/images/doll-house.jpg'), -('기억의 방', '잃어버린 기억을 되찾기 위해 단서를 연결하는 감성 테마', 'https://example.com/images/memory-room.jpg'), -('네온 시티', '미래 도시의 보안망을 돌파하는 사이버펑크 테마', 'https://example.com/images/neon-city.jpg'); +INSERT INTO theme (name, description, thumbnail_url) +VALUES ('잃어버린 왕국', '사라진 고대 왕국의 비밀을 추적하는 모험 테마', 'https://example.com/images/lost-kingdom.jpg'), + ('심야의 연구소', '한밤중 폐쇄된 연구소에서 탈출 단서를 찾는 스릴러 테마', 'https://example.com/images/midnight-lab.jpg'), + ('해적선의 저주', '저주받은 해적선에서 보물을 찾아 탈출하는 테마', 'https://example.com/images/pirate-curse.jpg'), + ('시간 여행자', '흩어진 시간의 조각을 맞춰 현재로 돌아오는 SF 테마', 'https://example.com/images/time-traveler.jpg'), + ('마법사의 서재', '마법 주문과 숨겨진 장치를 풀어내는 판타지 테마', 'https://example.com/images/wizard-library.jpg'), + ('사라진 탐정', '실종된 탐정이 남긴 단서를 따라 사건을 해결하는 추리 테마', 'https://example.com/images/missing-detective.jpg'), + ('지하 벙커', '폐쇄된 지하 벙커의 보안 시스템을 해제하는 생존 테마', 'https://example.com/images/underground-bunker.jpg'), + ('고대 신전', '신전 깊은 곳의 퍼즐을 풀고 봉인을 해제하는 모험 테마', 'https://example.com/images/ancient-temple.jpg'), + ('유령 호텔', '기묘한 호텔 방마다 숨은 이야기를 밝혀내는 공포 테마', 'https://example.com/images/ghost-hotel.jpg'), + ('우주 정거장', '고장 난 우주 정거장을 복구하고 귀환하는 SF 테마', 'https://example.com/images/space-station.jpg'), + ('비밀 카지노', '비밀 카지노의 금고 암호를 찾아내는 잠입 테마', 'https://example.com/images/secret-casino.jpg'), + ('눈보라 산장', '눈보라에 갇힌 산장에서 범인을 찾아내는 추리 테마', 'https://example.com/images/snow-cabin.jpg'), + ('인형의 집', '낡은 인형의 집에 숨겨진 진실을 발견하는 미스터리 테마', 'https://example.com/images/doll-house.jpg'), + ('기억의 방', '잃어버린 기억을 되찾기 위해 단서를 연결하는 감성 테마', 'https://example.com/images/memory-room.jpg'), + ('네온 시티', '미래 도시의 보안망을 돌파하는 사이버펑크 테마', 'https://example.com/images/neon-city.jpg'); -INSERT INTO reservation (name, date, time_id, theme_id) VALUES -('예약자01', '2026-05-01', 1, 1), -('예약자02', '2026-05-02', 2, 1), -('예약자03', '2026-05-03', 3, 1), -('예약자04', '2026-05-04', 4, 1), -('예약자05', '2026-05-05', 5, 1), -('예약자06', '2026-05-06', 6, 1), -('예약자07', '2026-05-07', 7, 1), -('예약자08', '2026-05-08', 8, 1), -('예약자09', '2026-05-09', 9, 1), -('예약자10', '2026-05-10', 10, 1), -('예약자11', '2026-05-01', 1, 2), -('예약자12', '2026-05-02', 2, 2), -('예약자13', '2026-05-03', 3, 2), -('예약자14', '2026-05-04', 4, 2), -('예약자15', '2026-05-05', 5, 2), -('예약자16', '2026-05-06', 6, 2), -('예약자17', '2026-05-07', 7, 2), -('예약자18', '2026-05-08', 8, 2), -('예약자19', '2026-05-09', 9, 2), -('예약자20', '2026-05-01', 1, 3), -('예약자21', '2026-05-02', 2, 3), -('예약자22', '2026-05-03', 3, 3), -('예약자23', '2026-05-04', 4, 3), -('예약자24', '2026-05-05', 5, 3), -('예약자25', '2026-05-06', 6, 3), -('예약자26', '2026-05-07', 7, 3), -('예약자27', '2026-05-08', 8, 3), -('예약자28', '2026-05-01', 1, 4), -('예약자29', '2026-05-02', 2, 4), -('예약자30', '2026-05-03', 3, 4), -('예약자31', '2026-05-04', 4, 4), -('예약자32', '2026-05-05', 5, 4), -('예약자33', '2026-05-06', 6, 4), -('예약자34', '2026-05-07', 7, 4), -('예약자35', '2026-05-01', 1, 5), -('예약자36', '2026-05-02', 2, 5), -('예약자37', '2026-05-03', 3, 5), -('예약자38', '2026-05-04', 4, 5), -('예약자39', '2026-05-05', 5, 5), -('예약자40', '2026-05-06', 6, 5), -('예약자41', '2026-05-01', 1, 6), -('예약자42', '2026-05-02', 2, 6), -('예약자43', '2026-05-03', 3, 6), -('예약자44', '2026-05-04', 4, 6), -('예약자45', '2026-05-05', 5, 6), -('예약자46', '2026-05-01', 1, 7), -('예약자47', '2026-05-02', 2, 7), -('예약자48', '2026-05-03', 3, 7), -('예약자49', '2026-05-04', 4, 7), -('예약자50', '2026-05-01', 1, 8), -('예약자51', '2026-05-02', 2, 8), -('예약자52', '2026-05-03', 3, 8), -('예약자53', '2026-05-01', 1, 9), -('예약자54', '2026-05-02', 2, 9), -('예약자55', '2026-05-01', 1, 10); +-- member id 1~10: 예약자01~예약자10 (한 예약자가 여러 예약 보유 가능) +INSERT INTO member (name) +VALUES ('예약자01'), + ('예약자02'), + ('예약자03'), + ('예약자04'), + ('예약자05'), + ('예약자06'), + ('예약자07'), + ('예약자08'), + ('예약자09'), + ('예약자10'); -INSERT INTO wait (created_at, name, reservation_date, time_id, theme_id) VALUES --- 슬롯 (2026-05-01, time 1, theme 1) - 예약자01이 예약 중 / 대기 순서: 02→11→20 -('2026-04-01 09:00:00', '예약자02', '2026-05-01', 1, 1), -('2026-04-01 10:00:00', '예약자11', '2026-05-01', 1, 1), -('2026-04-01 11:00:00', '예약자20', '2026-05-01', 1, 1), --- 슬롯 (2026-05-01, time 1, theme 2) - 예약자11이 예약 중 / 대기 순서: 01→20→28 -('2026-04-02 09:00:00', '예약자01', '2026-05-01', 1, 2), -('2026-04-02 10:00:00', '예약자20', '2026-05-01', 1, 2), -('2026-04-02 11:00:00', '예약자28', '2026-05-01', 1, 2), --- 슬롯 (2026-05-02, time 2, theme 1) - 예약자02가 예약 중 / 대기 순서: 12→21→01 -('2026-04-03 09:00:00', '예약자12', '2026-05-02', 2, 1), -('2026-04-03 10:00:00', '예약자21', '2026-05-02', 2, 1), -('2026-04-03 11:00:00', '예약자01', '2026-05-02', 2, 1), --- 슬롯 (2026-05-03, time 3, theme 1) - 예약자03이 예약 중 / 대기 순서: 04→13→22 -('2026-04-04 09:00:00', '예약자04', '2026-05-03', 3, 1), -('2026-04-04 10:00:00', '예약자13', '2026-05-03', 3, 1), -('2026-04-04 11:00:00', '예약자22', '2026-05-03', 3, 1); +-- theme1 (잃어버린 왕국) +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (1, '2026-05-01', 1, 1), + (2, '2026-05-02', 2, 1), + (3, '2026-05-03', 3, 1), + (4, '2026-05-04', 4, 1), + (5, '2026-05-05', 5, 1), + (6, '2026-05-06', 6, 1), + (7, '2026-05-07', 7, 1), + (8, '2026-05-08', 8, 1), + (9, '2026-05-09', 9, 1), + (10, '2026-05-10', 10, 1), +-- theme2 (심야의 연구소) + (1, '2026-05-01', 1, 2), + (2, '2026-05-02', 2, 2), + (3, '2026-05-03', 3, 2), + (4, '2026-05-04', 4, 2), + (5, '2026-05-05', 5, 2), + (6, '2026-05-06', 6, 2), + (7, '2026-05-07', 7, 2), + (8, '2026-05-08', 8, 2), + (9, '2026-05-09', 9, 2), +-- theme3 (해적선의 저주) + (10, '2026-05-01', 1, 3), + (1, '2026-05-02', 2, 3), + (2, '2026-05-03', 3, 3), + (3, '2026-05-04', 4, 3), + (4, '2026-05-05', 5, 3), + (5, '2026-05-06', 6, 3), + (6, '2026-05-07', 7, 3), + (7, '2026-05-08', 8, 3), +-- theme4 (시간 여행자) + (8, '2026-05-01', 1, 4), + (9, '2026-05-02', 2, 4), + (10, '2026-05-03', 3, 4), + (1, '2026-05-04', 4, 4), + (2, '2026-05-05', 5, 4), + (3, '2026-05-06', 6, 4), + (4, '2026-05-07', 7, 4), +-- theme5 (마법사의 서재) + (5, '2026-05-01', 1, 5), + (6, '2026-05-02', 2, 5), + (7, '2026-05-03', 3, 5), + (8, '2026-05-04', 4, 5), + (9, '2026-05-05', 5, 5), + (10, '2026-05-06', 6, 5), +-- theme6 (사라진 탐정) + (1, '2026-05-01', 1, 6), + (2, '2026-05-02', 2, 6), + (3, '2026-05-03', 3, 6), + (4, '2026-05-04', 4, 6), + (5, '2026-05-05', 5, 6), +-- theme7 (지하 벙커) + (6, '2026-05-01', 1, 7), + (7, '2026-05-02', 2, 7), + (8, '2026-05-03', 3, 7), + (9, '2026-05-04', 4, 7), +-- theme8 (고대 신전) + (10, '2026-05-01', 1, 8), + (1, '2026-05-02', 2, 8), + (2, '2026-05-03', 3, 8), +-- theme9 (유령 호텔) + (3, '2026-05-01', 1, 9), + (4, '2026-05-02', 2, 9), +-- theme10 (우주 정거장) + (5, '2026-05-01', 1, 10); + +INSERT INTO wait (created_at, member_id, reservation_date, time_id, theme_id) +VALUES +-- 슬롯 (2026-05-01, time1, theme1) - 예약자01이 예약 중 / 대기 순서: 02→03→04 +('2026-04-01 09:00:00', 2, '2026-05-01', 1, 1), +('2026-04-01 10:00:00', 3, '2026-05-01', 1, 1), +('2026-04-01 11:00:00', 4, '2026-05-01', 1, 1), +-- 슬롯 (2026-05-01, time1, theme2) - 예약자01이 예약 중 / 대기 순서: 05→06→07 +('2026-04-02 09:00:00', 5, '2026-05-01', 1, 2), +('2026-04-02 10:00:00', 6, '2026-05-01', 1, 2), +('2026-04-02 11:00:00', 7, '2026-05-01', 1, 2), +-- 슬롯 (2026-05-03, time3, theme1) - 예약자03이 예약 중 / 대기 순서: 01→02→04 +('2026-04-04 09:00:00', 1, '2026-05-03', 3, 1), +('2026-04-04 10:00:00', 2, '2026-05-03', 3, 1), +('2026-04-04 11:00:00', 4, '2026-05-03', 3, 1), +-- 슬롯 (2026-05-01, time1, theme10) - 예약자05가 예약 중 / 대기 순서: 01→03→04 +('2026-04-03 09:00:00', 1, '2026-05-01', 1, 10), +('2026-04-03 10:00:00', 3, '2026-05-01', 1, 10), +('2026-04-03 11:00:00', 4, '2026-05-01', 1, 10); diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8833743a76..964874bbc5 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -2,6 +2,7 @@ DROP TABLE IF EXISTS wait; DROP TABLE IF EXISTS reservation; DROP TABLE IF EXISTS reservation_time; DROP TABLE IF EXISTS theme; +DROP TABLE IF EXISTS member; CREATE TABLE reservation_time ( @@ -20,29 +21,38 @@ CREATE TABLE theme PRIMARY KEY (id) ); +CREATE TABLE member +( + id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +); + CREATE TABLE reservation ( - id BIGINT NOT NULL AUTO_INCREMENT, - name VARCHAR(255) NOT NULL, - date DATE NOT NULL, - time_id BIGINT NOT NULL, - theme_id BIGINT NOT NULL, + id BIGINT NOT NULL AUTO_INCREMENT, + member_id BIGINT NOT NULL, + reservation_date DATE NOT NULL, + time_id BIGINT NOT NULL, + theme_id BIGINT NOT NULL, PRIMARY KEY (id), + FOREIGN KEY (member_id) REFERENCES member (id), FOREIGN KEY (time_id) REFERENCES reservation_time (id), FOREIGN KEY (theme_id) REFERENCES theme (id), - CONSTRAINT uk_reservation_date_time_theme UNIQUE (date, time_id, theme_id) + CONSTRAINT uk_reservation_date_time_theme UNIQUE (reservation_date, time_id, theme_id) ); CREATE TABLE wait ( - id BIGINT NOT NULL AUTO_INCREMENT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - name VARCHAR(255) NOT NULL, - reservation_date DATE NOT NULL, - time_id BIGINT NOT NULL, - theme_id BIGINT NOT NULL, + id BIGINT NOT NULL AUTO_INCREMENT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + member_id BIGINT NOT NULL, + reservation_date DATE NOT NULL, + time_id BIGINT NOT NULL, + theme_id BIGINT NOT NULL, PRIMARY KEY (id), + FOREIGN KEY (member_id) REFERENCES member (id), FOREIGN KEY (time_id) REFERENCES reservation_time (id), FOREIGN KEY (theme_id) REFERENCES theme (id), - CONSTRAINT uk_wait_name_reservation_date_time_theme UNIQUE (name, reservation_date, time_id, theme_id) + CONSTRAINT uk_wait_member_reservation_date_time_theme UNIQUE (member_id, reservation_date, time_id, theme_id) ); diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index 255e9420aa..af2046d105 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -21,6 +21,7 @@ const elements = { searchForm: document.querySelector("#reservation-search-form"), searchNameInput: document.querySelector("#reservation-search-name"), searchResult: document.querySelector("#reservation-search-result"), + myReservationList: document.querySelector("#mine-reservation-list"), toast: document.querySelector("#toast"), }; @@ -33,6 +34,7 @@ async function initialize() { await loadThemes(); await loadRankingThemes(); await loadAvailableTimes(); + await loadMyReservations(); } function bindEvents() { @@ -211,6 +213,27 @@ async function handleReservationSubmit(event) { await loadAvailableTimes(); } +async function loadMyReservations() { + renderLoading(elements.myReservationList, "내 예약을 불러오는 중입니다."); + const response = await requestJson("/reservations/mine", {headers: {"Member-Id": "1"}}); + renderMyReservations(response); +} + +function renderMyReservations(response) { + elements.myReservationList.innerHTML = ""; + const reservations = response.reservations.items; + const waits = response.waits.items; + const all = [...reservations, ...waits]; + + if (all.length === 0) { + renderEmpty(elements.myReservationList, "예약 내역이 없습니다."); + return; + } + all.forEach((item) => { + elements.myReservationList.appendChild(createReservationItem(item)); + }); +} + async function handleReservationSearch(event) { event.preventDefault(); renderLoading(elements.searchResult, "예약을 조회하는 중입니다."); @@ -245,7 +268,7 @@ function createReservationSummary(item) { const summary = document.createElement("div"); summary.className = "reservation-summary"; summary.append(createTextElement("strong", item.theme.name)); - summary.append(createMetaText("예약자", item.name)); + summary.append(createMetaText("예약자", item.member.name)); summary.append(createMetaText("날짜", item.date)); summary.append(createMetaText("시간", formatStartAt(item.time.startAt))); if (item.status === "WAITING") { @@ -308,6 +331,7 @@ async function cancelReservation(item) { await request(url, {method: "DELETE"}); const message = item.status === "WAITING" ? "대기가 취소되었습니다." : "예약이 취소되었습니다."; showToast(message); + await loadMyReservations(); await refreshReservationSearch(); await loadAvailableTimes(); } @@ -324,7 +348,6 @@ async function refreshReservationSearch() { async function createReservation() { const payload = { - name: elements.nameInput.value.trim(), date: state.selectedDate, timeId: state.selectedTimeId, themeId: state.selectedThemeId, @@ -335,7 +358,7 @@ async function createReservation() { function createPostOption(payload) { return { method: "POST", - headers: {"Content-Type": "application/json"}, + headers: {"Content-Type": "application/json", "Member-Id": "1"}, body: JSON.stringify(payload), }; } @@ -345,7 +368,7 @@ function renderReservationResult(result) { const statusText = result.status === "WAITING" ? `대기 (순번: ${result.order}번)` : "예약 확정"; elements.result.innerHTML = `

${result.status === "WAITING" ? "대기 신청 완료" : "예약 완료"}

-

예약자: ${escapeHtml(result.name)}

+

예약자: ${escapeHtml(result.member.name)}

날짜: ${result.date}

시간: ${formatStartAt(result.time.startAt)}

테마: ${escapeHtml(result.theme.name)}

diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 9f050946a0..4bcc06faca 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -59,11 +59,21 @@

테마 선택

+
+
+
+

My Reservations

+

내 예약 목록

+
+
+
+
+
-

My Reservation

-

내 예약 조회

+

Search

+

이름으로 예약 조회

diff --git a/src/test/java/roomescape/acceptance/AcceptanceTest.java b/src/test/java/roomescape/acceptance/AcceptanceTest.java index 1a6f5d0664..bea05300f4 100644 --- a/src/test/java/roomescape/acceptance/AcceptanceTest.java +++ b/src/test/java/roomescape/acceptance/AcceptanceTest.java @@ -2,6 +2,7 @@ import java.util.List; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -19,8 +20,21 @@ public class AcceptanceTest { @Autowired private JdbcTemplate jdbcTemplate; + @BeforeEach + void setUp() { + cleanDatabase(); + jdbcTemplate.update("INSERT INTO member (name) VALUES ('예약자')"); + jdbcTemplate.update("INSERT INTO member (name) VALUES ('예약자2')"); + jdbcTemplate.update("INSERT INTO member (name) VALUES ('예약자3')"); + jdbcTemplate.update("INSERT INTO member (name) VALUES ('예약자4')"); + } + @AfterEach void afterEach() { + cleanDatabase(); + } + + private void cleanDatabase() { String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'"; List tableNames = jdbcTemplate.queryForList(sql, String.class); diff --git a/src/test/java/roomescape/acceptance/ReservationAcceptanceTest.java b/src/test/java/roomescape/acceptance/ReservationAcceptanceTest.java index 8cb792b0df..781816b428 100644 --- a/src/test/java/roomescape/acceptance/ReservationAcceptanceTest.java +++ b/src/test/java/roomescape/acceptance/ReservationAcceptanceTest.java @@ -17,11 +17,11 @@ void reservationApiSuccessTest() { ThemeSteps.createTheme("방탈출1", "방탈출1 설명", "theme/url.png"); // 3. 예약 추가 - ReservationSteps.saveReservation("예약자", NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(1L, NOW_DATE, 1L, 1L); // 4. 대기 추가 - ReservationSteps.saveReservation("예약자2", NOW_DATE, 1L, 1L); - ReservationSteps.saveReservation("예약자3", NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(2L, NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(3L, NOW_DATE, 1L, 1L); // 5. 이름 조회로 대기 추가 확인 ReservationSteps.findByName("예약자2", 1, ReservationStatus.WAITING); diff --git a/src/test/java/roomescape/acceptance/ReservationTimeAcceptanceTest.java b/src/test/java/roomescape/acceptance/ReservationTimeAcceptanceTest.java index 8994828f47..3c35979482 100644 --- a/src/test/java/roomescape/acceptance/ReservationTimeAcceptanceTest.java +++ b/src/test/java/roomescape/acceptance/ReservationTimeAcceptanceTest.java @@ -23,15 +23,15 @@ void reservationTimeApiSuccessTest() { ReservationTimeSteps.checkAvailableReservation(NOW_DATE, 1L, ReservationAvailability.RESERVATION_AVAILABLE); // 5. 예약 추가 - ReservationSteps.saveReservation("예약자", NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(1L, NOW_DATE, 1L, 1L); // 6. 예약 추가 후 해당 날짜, 테마의 시간 예약 가능 상태 변경 조회 ReservationTimeSteps.checkAvailableReservation(NOW_DATE, 1L, ReservationAvailability.WAITING_AVAILABLE); // 7. 대기 추가 - ReservationSteps.saveReservation("예약자2", NOW_DATE, 1L, 1L); - ReservationSteps.saveReservation("예약자3", NOW_DATE, 1L, 1L); - ReservationSteps.saveReservation("예약자4", NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(2L, NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(3L, NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(4L, NOW_DATE, 1L, 1L); // 8. 대기 추가 후, 해당 날짜, 테마의 시간 예약 가능 상태 변경 조회 ReservationTimeSteps.checkAvailableReservation(NOW_DATE, 1L, ReservationAvailability.NOTHING_AVAILABLE); diff --git a/src/test/java/roomescape/acceptance/ThemeAcceptanceTest.java b/src/test/java/roomescape/acceptance/ThemeAcceptanceTest.java index d2b5babac0..7865485842 100644 --- a/src/test/java/roomescape/acceptance/ThemeAcceptanceTest.java +++ b/src/test/java/roomescape/acceptance/ThemeAcceptanceTest.java @@ -19,7 +19,7 @@ void reservationTimeApiSuccessTest() { ReservationTimeSteps.createReservationTime(FUTURE_TIME); // 4. 예약 추가 - ReservationSteps.saveReservation("예약자", NOW_DATE, 1L, 1L); + ReservationSteps.saveReservation(1L, NOW_DATE, 1L, 1L); // 5. 특정 기간 내의 테마 랭킹 조회 ThemeSteps.checkThemeRanking("2026-04-20", "2026-05-01", 1); diff --git a/src/test/java/roomescape/acceptance/step/ReservationSteps.java b/src/test/java/roomescape/acceptance/step/ReservationSteps.java index 10f2caa619..830194ded6 100644 --- a/src/test/java/roomescape/acceptance/step/ReservationSteps.java +++ b/src/test/java/roomescape/acceptance/step/ReservationSteps.java @@ -10,14 +10,14 @@ public class ReservationSteps { - public static void saveReservation(String name, String date, Long timeId, Long themeId) { + public static void saveReservation(Long memberId, String date, Long timeId, Long themeId) { Map params = new HashMap<>(); - params.put("name", name); params.put("date", date); params.put("timeId", timeId); params.put("themeId", themeId); RestAssured.given().log().all() + .header("Member-Id", memberId) .contentType(ContentType.JSON) .body(params) .when().post("/reservations") diff --git a/src/test/java/roomescape/domain/ReservationTest.java b/src/test/java/roomescape/domain/ReservationTest.java index c0efecc928..b891bbe778 100644 --- a/src/test/java/roomescape/domain/ReservationTest.java +++ b/src/test/java/roomescape/domain/ReservationTest.java @@ -5,24 +5,21 @@ import java.time.LocalDate; import java.time.LocalTime; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import roomescape.exception.custom.InvalidDomainValueException; public class ReservationTest { - @ParameterizedTest - @ValueSource(strings = {"", " "}) - void nameBlankExceptionTest(String name) { + @Test + void memberNullExceptionTest() { ReservationTime reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); Theme theme = new Theme("피즈의 모험", "모험 이야기", "url.jpg"); - assertThatThrownBy(() -> new Reservation(1L, name, new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme))) + assertThatThrownBy(() -> new Reservation(1L, null, new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme))) .isInstanceOf(InvalidDomainValueException.class); } @Test void slotNullExceptionTest() { - assertThatThrownBy(() -> new Reservation(1L, "fizz", null)) + assertThatThrownBy(() -> new Reservation(1L, new Member(1L, "fizz"), null)) .isInstanceOf(InvalidDomainValueException.class); } } diff --git a/src/test/java/roomescape/domain/WaitTest.java b/src/test/java/roomescape/domain/WaitTest.java index 5bd3f5f809..868a8d1f5a 100644 --- a/src/test/java/roomescape/domain/WaitTest.java +++ b/src/test/java/roomescape/domain/WaitTest.java @@ -6,18 +6,15 @@ import java.time.LocalDateTime; import java.time.LocalTime; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import roomescape.exception.custom.InvalidDomainValueException; public class WaitTest { - @ParameterizedTest - @ValueSource(strings = {"", " "}) - void nameBlankExceptionTest(String name) { + @Test + void memberNullExceptionTest() { ReservationTime reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); Theme theme = new Theme("피즈의 모험", "모험 이야기", "url.jpg"); - assertThatThrownBy(() -> new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), name, + assertThatThrownBy(() -> new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), null, new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme))) .isInstanceOf(InvalidDomainValueException.class); } @@ -26,13 +23,13 @@ void nameBlankExceptionTest(String name) { void createdAtNullExceptionTest() { ReservationTime reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); Theme theme = new Theme("피즈의 모험", "모험 이야기", "url.jpg"); - assertThatThrownBy(() -> new Wait(1L, null, "fizz", new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme))) + assertThatThrownBy(() -> new Wait(1L, null, new Member(1L, "fizz"), new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme))) .isInstanceOf(InvalidDomainValueException.class); } @Test void slotNullExceptionTest() { - assertThatThrownBy(() -> new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", null)) + assertThatThrownBy(() -> new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), new Member(1L, "fizz"), null)) .isInstanceOf(InvalidDomainValueException.class); } } diff --git a/src/test/java/roomescape/domain/WaitsTest.java b/src/test/java/roomescape/domain/WaitsTest.java index b1de9ffda1..fd946ab0d1 100644 --- a/src/test/java/roomescape/domain/WaitsTest.java +++ b/src/test/java/roomescape/domain/WaitsTest.java @@ -18,22 +18,30 @@ public class WaitsTest { private ReservationTime reservationTime; private Theme theme; private Slot slot; + private Member luke; + private Member fizz; + private Member neo; + private Member brown; @BeforeEach void beforeEach() { reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); theme = new Theme(1L, "테마", "설명", "url.jpg"); slot = new Slot(LocalDate.of(2026, 5, 1), reservationTime, theme); + luke = new Member(1L, "luke"); + fizz = new Member(2L, "fizz"); + neo = new Member(3L, "neo"); + brown = new Member(4L, "brown"); } @Test void isFullWaitsBySlotTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), "luke", slot); - Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), "fizz", slot); - Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), "neo", slot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), luke, slot); + Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), fizz, slot); + Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), neo, slot); Slot otherSlot = new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme); - Wait otherWait = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), "neo", otherSlot); + Wait otherWait = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), neo, otherSlot); Waits waits1 = new Waits(List.of(wait1, wait2, otherWait)); assertThat(waits1.isFullWaitsBySlot(slot)).isFalse(); @@ -44,9 +52,9 @@ void isFullWaitsBySlotTest() { @Test void isEmptyWaitsBySlotTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), "luke", slot); - Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), "fizz", slot); - Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), "neo", slot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), luke, slot); + Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), fizz, slot); + Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), neo, slot); Slot otherSlot = new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme); @@ -59,20 +67,20 @@ void isEmptyWaitsBySlotTest() { @Test void validateCreateDuplicatedExceptionTest() { - Waits waits = new Waits(List.of(new Wait(1L, LocalDateTime.now(), "fizz", slot))); + Waits waits = new Waits(List.of(new Wait(1L, LocalDateTime.now(), fizz, slot))); - assertThatThrownBy(() -> waits.validateCreate("fizz", slot)) + assertThatThrownBy(() -> waits.validateCreate(fizz, slot)) .isInstanceOf(AlreadyWaitingException.class); } @Test void validateCreateDuplicatedWaitFullExceptionTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), "luke", slot); - Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), "fizz", slot); - Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), "neo", slot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), luke, slot); + Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), fizz, slot); + Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), neo, slot); Waits waits = new Waits(List.of(wait1, wait2, wait3)); - assertThatThrownBy(() -> waits.validateCreate("brown", slot)) + assertThatThrownBy(() -> waits.validateCreate(brown, slot)) .isInstanceOf(WaitIsFullException.class); } @@ -80,9 +88,9 @@ void validateCreateDuplicatedWaitFullExceptionTest() { void firstWaitBySlotTest() { Slot otherSlot = new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme); - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), "luke", slot); - Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), "fizz", otherSlot); - Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), "neo", otherSlot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), luke, slot); + Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 9, 1), fizz, otherSlot); + Wait wait3 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 2), neo, otherSlot); Waits waits = new Waits(List.of(wait1, wait2, wait3)); @@ -91,9 +99,9 @@ void firstWaitBySlotTest() { @Test void waitsWithOrderTest() { - Wait firstWait = new Wait(2L, LocalDateTime.of(2026, 5, 1, 7, 0), "fizz", slot); - Wait secondWait = new Wait(1L, LocalDateTime.of(2026, 5, 1, 8, 0), "luke", slot); - Wait thirdWait = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 0), "neo", slot); + Wait firstWait = new Wait(2L, LocalDateTime.of(2026, 5, 1, 7, 0), fizz, slot); + Wait secondWait = new Wait(1L, LocalDateTime.of(2026, 5, 1, 8, 0), luke, slot); + Wait thirdWait = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 0), neo, slot); Waits waits = new Waits(List.of(secondWait, thirdWait, firstWait)); @@ -108,9 +116,9 @@ void waitsWithOrderTest() { void waitsWithOrderByNameTest() { Slot otherSlot = new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme); - Wait firstWaitFizz = new Wait(2L, LocalDateTime.of(2026, 5, 1, 7, 0), "fizz", slot); - Wait firstWaitLuke = new Wait(1L, LocalDateTime.of(2026, 5, 1, 8, 0), "luke", otherSlot); - Wait secondWaitFizz = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 0), "fizz", otherSlot); + Wait firstWaitFizz = new Wait(2L, LocalDateTime.of(2026, 5, 1, 7, 0), fizz, slot); + Wait firstWaitLuke = new Wait(1L, LocalDateTime.of(2026, 5, 1, 8, 0), luke, otherSlot); + Wait secondWaitFizz = new Wait(3L, LocalDateTime.of(2026, 5, 1, 9, 0), fizz, otherSlot); Waits waits = new Waits(List.of(firstWaitFizz, firstWaitLuke, secondWaitFizz)); @@ -128,9 +136,9 @@ void calculateOrderTest() { Slot slotA = new Slot(LocalDate.of(2026, 5, 1), time, theme); Slot slotB = new Slot(LocalDate.of(2026, 5, 2), time, theme); - Wait waitB1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), "luke", slotB); - Wait waitA1 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 10, 0), "fizz", slotA); - Wait waitB2 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 11, 0), "neo", slotB); + Wait waitB1 = new Wait(1L, LocalDateTime.of(2026, 5, 1, 9, 0), luke, slotB); + Wait waitA1 = new Wait(2L, LocalDateTime.of(2026, 5, 1, 10, 0), fizz, slotA); + Wait waitB2 = new Wait(3L, LocalDateTime.of(2026, 5, 1, 11, 0), neo, slotB); Waits waits = new Waits(List.of(waitB1, waitA1, waitB2)); diff --git a/src/test/java/roomescape/facade/ReservationFacadeTest.java b/src/test/java/roomescape/facade/ReservationFacadeTest.java index 8f5a3ff73b..196a79dcbc 100644 --- a/src/test/java/roomescape/facade/ReservationFacadeTest.java +++ b/src/test/java/roomescape/facade/ReservationFacadeTest.java @@ -24,6 +24,7 @@ import roomescape.controller.dto.response.ReservationWaitListResponse; import roomescape.controller.dto.response.WaitListResponse; import roomescape.controller.dto.response.WaitResponse; +import roomescape.domain.Member; import roomescape.domain.Reservation; import roomescape.domain.ReservationTime; import roomescape.domain.Slot; @@ -33,6 +34,7 @@ import roomescape.exception.custom.AlreadyReservedException; import roomescape.exception.custom.CannotCreatePastReservationException; import roomescape.exception.custom.CannotDeletePastReservationException; +import roomescape.service.MemberService; import roomescape.service.ReservationService; import roomescape.service.ReservationTimeService; import roomescape.service.ThemeService; @@ -44,6 +46,7 @@ public class ReservationFacadeTest { private WaitService waitService; private ReservationTimeService reservationTimeService; private ThemeService themeService; + private MemberService memberService; private Clock clock; private ReservationFacade reservationFacade; @@ -51,6 +54,8 @@ public class ReservationFacadeTest { private ReservationTime reservationTime; private Theme theme; private LocalDateTime now; + private Member fizz; + private Member luke; @BeforeEach void beforeEach() { @@ -58,42 +63,47 @@ void beforeEach() { waitService = Mockito.mock(WaitService.class); reservationTimeService = Mockito.mock(ReservationTimeService.class); themeService = Mockito.mock(ThemeService.class); + memberService = Mockito.mock(MemberService.class); clock = Clock.fixed(Instant.parse("2026-05-02T00:00:00Z"), ZoneId.of("Asia/Seoul")); reservationDate = LocalDate.of(2026, 5, 3); reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); theme = new Theme(1L, "피즈의 모험", "모험 이야기", "url.jpg"); now = LocalDateTime.now(clock); + fizz = new Member(1L, "fizz"); + luke = new Member(2L, "luke"); reservationFacade = new ReservationFacade(reservationService, waitService, reservationTimeService, themeService, - clock); + memberService, clock); } @Test void saveReservationTest() { - ReservationCreateRequest request = new ReservationCreateRequest("fizz", reservationDate, + ReservationCreateRequest request = new ReservationCreateRequest(reservationDate, reservationTime.getId(), theme.getId()); - Reservation reservationWithoutId = request.toReservation(reservationTime, theme); + Reservation reservationWithoutId = request.toReservation(fizz, reservationTime, theme); Reservation reservation = reservationWithoutId.withId(1L); + when(memberService.findMember(1L)).thenReturn(fizz); when(reservationTimeService.findReservationTime(reservationTime.getId())).thenReturn(reservationTime); when(themeService.findTheme(theme.getId())).thenReturn(theme); when(reservationService.findBySlot(request.date(), request.timeId(), request.themeId())).thenReturn( Optional.empty()); when(reservationService.save(reservationWithoutId, false)).thenReturn(reservation); - assertThat(reservationFacade.save(request)).isEqualTo(ReservationResponse.from(reservation)); + assertThat(reservationFacade.save(request, 1L)).isEqualTo(ReservationResponse.from(reservation)); } @Test void saveWaitTest() { - ReservationCreateRequest request = new ReservationCreateRequest("fizz", reservationDate, + ReservationCreateRequest request = new ReservationCreateRequest(reservationDate, reservationTime.getId(), theme.getId()); - Reservation beforeReservation = new Reservation(1L, "luke", new Slot(reservationDate, reservationTime, theme)); - Wait waitWithoutId = request.toWait(now, reservationTime, theme); + Reservation beforeReservation = new Reservation(1L, luke, new Slot(reservationDate, reservationTime, theme)); + Wait waitWithoutId = request.toWait(now, fizz, reservationTime, theme); Wait wait = waitWithoutId.withId(1L); WaitResponse response = WaitResponse.of(wait, 1L); + when(memberService.findMember(1L)).thenReturn(fizz); when(reservationTimeService.findReservationTime(reservationTime.getId())).thenReturn(reservationTime); when(themeService.findTheme(theme.getId())).thenReturn(theme); when(reservationService.findBySlot(request.date(), request.timeId(), request.themeId())).thenReturn( @@ -101,15 +111,16 @@ void saveWaitTest() { when(waitService.save(waitWithoutId)).thenReturn(wait); when(waitService.calculateOrder(wait)).thenReturn(1L); - assertThat(reservationFacade.save(request)).isEqualTo(response); + assertThat(reservationFacade.save(request, 1L)).isEqualTo(response); } @Test void savePastTimeReservationCreateExceptionTest() { - ReservationCreateRequest request = new ReservationCreateRequest("fizz", LocalDate.of(2026, 3, 20), + ReservationCreateRequest request = new ReservationCreateRequest(LocalDate.of(2026, 3, 20), reservationTime.getId(), theme.getId()); - Reservation reservationWithoutId = request.toReservation(reservationTime, theme); + Reservation reservationWithoutId = request.toReservation(fizz, reservationTime, theme); + when(memberService.findMember(1L)).thenReturn(fizz); when(reservationTimeService.findReservationTime(reservationTime.getId())).thenReturn(reservationTime); when(themeService.findTheme(theme.getId())).thenReturn(theme); when(reservationService.findBySlot(request.date(), request.timeId(), request.themeId())).thenReturn( @@ -117,22 +128,23 @@ void savePastTimeReservationCreateExceptionTest() { when(reservationService.save(reservationWithoutId, false)).thenThrow( new CannotCreatePastReservationException()); - assertThatThrownBy(() -> reservationFacade.save(request)) + assertThatThrownBy(() -> reservationFacade.save(request, 1L)) .isInstanceOf(CannotCreatePastReservationException.class); } @Test void saveDuplicateReservationExceptionTest() { - ReservationCreateRequest request = new ReservationCreateRequest("fizz", reservationDate, + ReservationCreateRequest request = new ReservationCreateRequest(reservationDate, reservationTime.getId(), theme.getId()); - Reservation beforeReservation = new Reservation(1L, "fizz", new Slot(reservationDate, reservationTime, theme)); + Reservation beforeReservation = new Reservation(1L, fizz, new Slot(reservationDate, reservationTime, theme)); + when(memberService.findMember(1L)).thenReturn(fizz); when(reservationTimeService.findReservationTime(reservationTime.getId())).thenReturn(reservationTime); when(themeService.findTheme(theme.getId())).thenReturn(theme); when(reservationService.findBySlot(request.date(), request.timeId(), request.themeId())).thenReturn( Optional.of(beforeReservation)); - assertThatThrownBy(() -> reservationFacade.save(request)) + assertThatThrownBy(() -> reservationFacade.save(request, 1L)) .isInstanceOf(AlreadyReservedException.class); } @@ -141,10 +153,10 @@ void findByNameTest() { LocalDate otherDate = LocalDate.of(2026, 5, 19); List reservations = List.of( - new Reservation(1L, "fizz", new Slot(reservationDate, reservationTime, theme))); + new Reservation(1L, fizz, new Slot(reservationDate, reservationTime, theme))); - Wait fizzWait = new Wait(1L, now, "fizz", new Slot(otherDate, reservationTime, theme)); - Wait lukeWait = new Wait(2L, now, "luke", new Slot(reservationDate, reservationTime, theme)); + Wait fizzWait = new Wait(1L, now, fizz, new Slot(otherDate, reservationTime, theme)); + Wait lukeWait = new Wait(2L, now, luke, new Slot(reservationDate, reservationTime, theme)); Waits allWaits = new Waits(List.of(fizzWait, lukeWait)); WaitResponse waitResponse = WaitResponse.of(fizzWait, 1L); @@ -163,12 +175,12 @@ void findAllTest() { LocalDate otherDate = LocalDate.of(2026, 5, 19); List reservations = List.of( - new Reservation(1L, "fizz", new Slot(reservationDate, reservationTime, theme)), - new Reservation(2L, "luke", new Slot(otherDate, reservationTime, theme)) + new Reservation(1L, fizz, new Slot(reservationDate, reservationTime, theme)), + new Reservation(2L, luke, new Slot(otherDate, reservationTime, theme)) ); - Wait fizzWait = new Wait(1L, now, "fizz", new Slot(otherDate, reservationTime, theme)); - Wait lukeWait = new Wait(2L, now, "luke", new Slot(reservationDate, reservationTime, theme)); + Wait fizzWait = new Wait(1L, now, fizz, new Slot(otherDate, reservationTime, theme)); + Wait lukeWait = new Wait(2L, now, luke, new Slot(reservationDate, reservationTime, theme)); Waits allWaits = new Waits(List.of(fizzWait, lukeWait)); WaitResponse fizzResponse = WaitResponse.of(fizzWait, 1L); @@ -185,7 +197,7 @@ void findAllTest() { @Test void deleteReservationWithoutWaitTest() { - Reservation reservation = new Reservation(1L, "fizz", new Slot(reservationDate, reservationTime, theme)); + Reservation reservation = new Reservation(1L, fizz, new Slot(reservationDate, reservationTime, theme)); when(reservationService.findReservation(reservation.getId())).thenReturn(reservation); when(waitService.findBySlot(reservation.getSlot())).thenReturn(new Waits(List.of())); @@ -197,9 +209,9 @@ void deleteReservationWithoutWaitTest() { @Test void deleteReservationWithWaitTest() { - Reservation reservation = new Reservation(1L, "fizz", new Slot(reservationDate, reservationTime, theme)); - Wait firstWait = new Wait(1L, now, "luke", new Slot(reservationDate, reservationTime, theme)); - Reservation newReservationWithoutId = new Reservation(firstWait.getName(), + Reservation reservation = new Reservation(1L, fizz, new Slot(reservationDate, reservationTime, theme)); + Wait firstWait = new Wait(1L, now, luke, new Slot(reservationDate, reservationTime, theme)); + Reservation newReservationWithoutId = new Reservation(firstWait.getMember(), new Slot(firstWait.getReservationDate(), firstWait.getTime(), firstWait.getTheme())); when(reservationService.findReservation(reservation.getId())).thenReturn(reservation); @@ -208,16 +220,17 @@ void deleteReservationWithWaitTest() { reservationFacade.deleteReservation(reservation.getId()); verify(reservationService, times(1)).save(newReservationWithoutId, true); - verify(reservationService, times(1)).delete(reservation, false); + verify(reservationService, times(1)).deleteAndFlush(reservation, false); verify(waitService, times(1)).delete(firstWait.getId(), true); } @Test void deleteReservationExceptionTest() { LocalDate pastReservationDate = LocalDate.of(2026, 3, 20); - Reservation reservation = new Reservation(1L, "fizz", new Slot(pastReservationDate, reservationTime, theme)); + Reservation reservation = new Reservation(1L, fizz, new Slot(pastReservationDate, reservationTime, theme)); when(reservationService.findReservation(reservation.getId())).thenReturn(reservation); + when(waitService.findBySlot(reservation.getSlot())).thenReturn(new Waits(List.of())); doThrow(new CannotDeletePastReservationException()).when(reservationService).delete(reservation, false); assertThatThrownBy(() -> reservationFacade.deleteReservation(reservation.getId())) @@ -226,7 +239,7 @@ void deleteReservationExceptionTest() { @Test void deleteWaitTest() { - Wait wait = new Wait(1L, now, "fizz", new Slot(reservationDate, reservationTime, theme)); + Wait wait = new Wait(1L, now, fizz, new Slot(reservationDate, reservationTime, theme)); when(waitService.findWait(wait.getId())).thenReturn(wait); diff --git a/src/test/java/roomescape/facade/ReservationTimeFacadeTest.java b/src/test/java/roomescape/facade/ReservationTimeFacadeTest.java index d57e0602e8..0f470c93b5 100644 --- a/src/test/java/roomescape/facade/ReservationTimeFacadeTest.java +++ b/src/test/java/roomescape/facade/ReservationTimeFacadeTest.java @@ -17,6 +17,7 @@ import roomescape.controller.dto.response.ReservationTimeAvailabilityResponse; import roomescape.controller.dto.response.ReservationTimeListResponse; import roomescape.controller.dto.response.ReservationTimeResponse; +import roomescape.domain.Member; import roomescape.domain.ReservationAvailability; import roomescape.domain.ReservationTime; import roomescape.domain.Slot; @@ -93,9 +94,9 @@ void findAvailabilityByDateAndThemeTest() { Slot thirdSlot = new Slot(reservationDate, thirdTime, theme); Waits thirdTimeWaits = new Waits(List.of( - new Wait(1L, LocalDateTime.of(2026, 5, 3, 10, 0), "fizz", thirdSlot), - new Wait(2L, LocalDateTime.of(2026, 5, 3, 10, 1), "luke", thirdSlot), - new Wait(3L, LocalDateTime.of(2026, 5, 3, 10, 2), "neo", thirdSlot) + new Wait(1L, LocalDateTime.of(2026, 5, 3, 10, 0), new Member(1L, "fizz"), thirdSlot), + new Wait(2L, LocalDateTime.of(2026, 5, 3, 10, 1), new Member(2L, "luke"), thirdSlot), + new Wait(3L, LocalDateTime.of(2026, 5, 3, 10, 2), new Member(3L, "neo"), thirdSlot) )); List availabilityResponses = List.of( diff --git a/src/test/java/roomescape/repository/JdbcReservationRepositoryTest.java b/src/test/java/roomescape/repository/JdbcReservationRepositoryTest.java deleted file mode 100644 index 0dc1a49f3f..0000000000 --- a/src/test/java/roomescape/repository/JdbcReservationRepositoryTest.java +++ /dev/null @@ -1,168 +0,0 @@ -package roomescape.repository; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; -import java.util.Optional; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; -import org.springframework.jdbc.core.JdbcTemplate; -import roomescape.domain.Reservation; -import roomescape.domain.ReservationTime; -import roomescape.domain.Slot; -import roomescape.domain.Theme; - -@JdbcTest -public class JdbcReservationRepositoryTest { - - private ReservationRepository reservationRepository; - - private ReservationTime reservationTime; - - private Theme theme; - - @Autowired - private JdbcTemplate jdbcTemplate; - - @BeforeEach - void beforeEach() { - reservationRepository = new JdbcReservationRepository(jdbcTemplate); - - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(insertReservationTimeSql, "10:00"); - jdbcTemplate.update(insertReservationTimeSql, "11:00"); - jdbcTemplate.update(insertReservationTimeSql, "12:00"); - - String insertThemeSql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(insertThemeSql, "방탈출1", "방탈출1 설명", "url.jpg"); - - reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); - theme = new Theme(1L, "방탈출1", "방탈출1 설명", "url.jpg"); - } - - @AfterEach - void afterEach() { - String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'"; - List tableNames = jdbcTemplate.queryForList(sql, String.class); - - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY FALSE"); - for (String tableName : tableNames) { - jdbcTemplate.execute("TRUNCATE TABLE " + tableName); - jdbcTemplate.execute("ALTER TABLE " + tableName + " ALTER COLUMN ID RESTART WITH 1"); - } - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY TRUE"); - } - - @Test - void saveTest() { - Reservation reservationWithoutId = new Reservation("fizz", - new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme)); - - Reservation reservation = reservationRepository.save(reservationWithoutId); - - assertThat(reservation.getId()).isEqualTo(1L); - } - - @Test - void findByIdTest() { - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 1L, 1L); - - Reservation reservation = reservationRepository.findById(1L).get(); - - assertThat(reservation.getName()).isEqualTo("fizz"); - assertThat(reservation.getDate()).isEqualTo(LocalDate.of(2026, 5, 2)); - assertThat(reservation.getTime().getId()).isEqualTo(1L); - assertThat(reservation.getTheme().getId()).isEqualTo(1L); - } - - @Test - void findByNameTest() { - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 1L, 1L); - jdbcTemplate.update(sql, "tree", "2026-05-02", 2L, 1L); - jdbcTemplate.update(sql, "fizz", "2026-05-02", 3L, 1L); - - List reservations = reservationRepository.findByName("fizz"); - - assertThat(reservations.size()).isEqualTo(2); - assertThat(reservations.get(0).getName()).isEqualTo("fizz"); - assertThat(reservations.get(1).getName()).isEqualTo("fizz"); - - assertThat(reservationRepository.findByName("user").size()).isEqualTo(0); - } - - @Test - void findAllTest() { - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 1L, 1L); - jdbcTemplate.update(sql, "fizz", "2026-05-02", 2L, 1L); - - List reservations = reservationRepository.findAll(); - - assertThat(reservations.size()).isEqualTo(2); - } - - @Test - void deleteTest() { - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 1L, 1L); - - reservationRepository.delete(1L); - - String findReservationCountSql = "SELECT COUNT(*) FROM `reservation`"; - int count = jdbcTemplate.queryForObject(findReservationCountSql, Integer.class); - - Assertions.assertThat(count).isEqualTo(0); - } - - @Test - void existsByTimeIdTest() { - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`id`, `start_at`) VALUES (?, ?)"; - jdbcTemplate.update(insertReservationTimeSql, 100L, "13:00"); - - boolean exist = reservationRepository.existsByTimeId(100L); - assertThat(exist).isFalse(); - - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 100L, 1L); - - exist = reservationRepository.existsByTimeId(100L); - assertThat(exist).isTrue(); - } - - @Test - void existsByThemeIdTest() { - String insertThemeSql = "INSERT INTO `theme` (`id`, `name`, `description`, `thumbnail_url`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(insertThemeSql, 100L, "방탈출1", "방탈출1 설명", "url.jpg"); - - boolean exist = reservationRepository.existsByThemeId(100L); - assertThat(exist).isFalse(); - - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 1L, 100L); - - exist = reservationRepository.existsByThemeId(100L); - assertThat(exist).isTrue(); - } - - @Test - void findBySlotTest() { - String sql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, "fizz", "2026-05-02", 1L, 1L); - - Optional slot = reservationRepository.findBySlot(LocalDate.of(2026, 5, 2), 1L, 1L); - - assertThat(slot).isNotEmpty(); - assertThat(slot.get().getDate()).isEqualTo(LocalDate.of(2026, 5, 2)); - assertThat(slot.get().getName()).isEqualTo("fizz"); - assertThat(slot.get().getTime().getId()).isEqualTo(1L); - assertThat(slot.get().getTheme().getId()).isEqualTo(1L); - } -} diff --git a/src/test/java/roomescape/repository/JdbcReservationTimeRepositoryTest.java b/src/test/java/roomescape/repository/JdbcReservationTimeRepositoryTest.java deleted file mode 100644 index af1cd5eeae..0000000000 --- a/src/test/java/roomescape/repository/JdbcReservationTimeRepositoryTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package roomescape.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; -import org.springframework.jdbc.core.JdbcTemplate; -import roomescape.domain.ReservationTime; - -@JdbcTest -public class JdbcReservationTimeRepositoryTest { - - - private ReservationTimeRepository reservationTimeRepository; - - @Autowired - private JdbcTemplate jdbcTemplate; - - @BeforeEach - void beforeEach() { - reservationTimeRepository = new JdbcReservationTimeRepository(jdbcTemplate); - } - - @AfterEach - void afterEach() { - String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'"; - List tableNames = jdbcTemplate.queryForList(sql, String.class); - - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY FALSE"); - for (String tableName : tableNames) { - jdbcTemplate.execute("TRUNCATE TABLE " + tableName); - jdbcTemplate.execute("ALTER TABLE " + tableName + " ALTER COLUMN ID RESTART WITH 1"); - } - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY TRUE"); - } - - @Test - void saveTest() { - ReservationTime reservationTimeWithoutId = new ReservationTime(LocalTime.of(10, 0)); - ReservationTime reservationTime = reservationTimeRepository.save(reservationTimeWithoutId); - - assertThat(reservationTime.getId()).isEqualTo(1L); - } - - @Test - void findByIdTest() { - String sql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(sql, "10:00"); - - Optional reservationTime = reservationTimeRepository.findById(1L); - - assertThat(reservationTime.orElseThrow().getId()).isEqualTo(1L); - } - - @Test - void findAllTest() { - String sql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(sql, "10:00"); - jdbcTemplate.update(sql, "11:00"); - - List reservationTimes = reservationTimeRepository.findAll(); - assertThat(reservationTimes.size()).isEqualTo(2); - } - - @Test - void findReservedTimesByDateAndThemeTest() { - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(insertReservationTimeSql, "10:00"); - jdbcTemplate.update(insertReservationTimeSql, "11:00"); - jdbcTemplate.update(insertReservationTimeSql, "12:00"); - - String insertThemeSql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(insertThemeSql, "방탈출1", "방탈출1 설명", "url.jpg"); - - String insertReservationSql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(insertReservationSql, "fizz", "2026-05-02", 1L, 1L); - jdbcTemplate.update(insertReservationSql, "fizz", "2026-05-02", 2L, 1L); - - List reservedTimes = reservationTimeRepository.findReservedTimesByDateAndTheme( - LocalDate.of(2026, 5, 2), - 1L); - - assertThat(reservedTimes.get(0)).isEqualTo(new ReservationTime(1L, LocalTime.of(10, 0))); - assertThat(reservedTimes.get(1)).isEqualTo(new ReservationTime(2L, LocalTime.of(11, 0))); - } - - @Test - void deleteTest() { - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(insertReservationTimeSql, "10:00"); - - reservationTimeRepository.delete(1L); - - String readAllReservationTimeCountSql = "SELECT COUNT(*) FROM `reservation_time`"; - int count = jdbcTemplate.queryForObject(readAllReservationTimeCountSql, Integer.class); - - assertThat(count).isEqualTo(0); - } - - @Test - void existsByStartAt() { - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(insertReservationTimeSql, "10:00"); - - assertThat(reservationTimeRepository.existsByStartAt(LocalTime.of(10, 0))).isTrue(); - assertThat(reservationTimeRepository.existsByStartAt(LocalTime.of(11, 0))).isFalse(); - } -} diff --git a/src/test/java/roomescape/repository/JdbcThemeRepositoryTest.java b/src/test/java/roomescape/repository/JdbcThemeRepositoryTest.java deleted file mode 100644 index 4fc28b669a..0000000000 --- a/src/test/java/roomescape/repository/JdbcThemeRepositoryTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package roomescape.repository; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; -import org.springframework.jdbc.core.JdbcTemplate; -import roomescape.domain.Theme; - -@JdbcTest -public class JdbcThemeRepositoryTest { - - private ThemeRepository themeRepository; - - @Autowired - private JdbcTemplate jdbcTemplate; - - @BeforeEach - void beforeEach() { - themeRepository = new JdbcThemeRepository(jdbcTemplate); - } - - @AfterEach - void afterEach() { - String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'"; - List tableNames = jdbcTemplate.queryForList(sql, String.class); - - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY FALSE"); - for (String tableName : tableNames) { - jdbcTemplate.execute("TRUNCATE TABLE " + tableName); - jdbcTemplate.execute("ALTER TABLE " + tableName + " ALTER COLUMN ID RESTART WITH 1"); - } - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY TRUE"); - } - - @Test - void saveTest() { - Theme themeWithoutId = new Theme("방탈출", "설명", "url.jpg"); - Theme theme = themeRepository.save(themeWithoutId); - - assertThat(theme.getId()).isEqualTo(1L); - } - - @Test - void findByIdTest() { - String sql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(sql, "방탈출1", "방탈출1 설명", "url.jpg"); - - Optional theme = themeRepository.findById(1L); - - assertThat(theme.orElseThrow().getId()).isEqualTo(1L); - } - - @Test - void findAllTest() { - String sql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(sql, "방탈출1", "방탈출1 설명", "url.jpg"); - jdbcTemplate.update(sql, "방탈출2", "방탈출2 설명", "url.jpg"); - - List themes = themeRepository.findAll(); - assertThat(themes.size()).isEqualTo(2); - } - - @Test - void findRankingTest() { - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(insertReservationTimeSql, "10:00"); - jdbcTemplate.update(insertReservationTimeSql, "11:00"); - - String insertThemeSql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(insertThemeSql, "방탈출1", "방탈출1 설명", "url.jpg"); - jdbcTemplate.update(insertThemeSql, "방탈출2", "방탈출2 설명", "url.jpg"); - - String insertReservationSql = "INSERT INTO `reservation` (`name`, `date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(insertReservationSql, "fizz", "2026-05-02", 1L, 1L); - jdbcTemplate.update(insertReservationSql, "fizz", "2026-05-02", 2L, 1L); - jdbcTemplate.update(insertReservationSql, "fizz", "2026-05-02", 1L, 2L); - - List themes = themeRepository.findRanking(LocalDate.of(2026, 5, 2), LocalDate.of(2026, 5, 3), 2); - - assertThat(themes.get(0).getId()).isEqualTo(1L); - assertThat(themes.get(1).getId()).isEqualTo(2L); - } - - @Test - void deleteByIdTest() { - String sql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(sql, "방탈출", "설명", "url.jpg"); - - themeRepository.deleteById(1L); - - String readAllThemeCountSql = "SELECT COUNT(*) FROM `theme`"; - int count = jdbcTemplate.queryForObject(readAllThemeCountSql, Integer.class); - - assertThat(count).isEqualTo(0); - } - - @Test - void existsByIdTest() { - assertThat(themeRepository.existsById(1L)).isFalse(); - - String sql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(sql, "방탈출", "설명", "url.jpg"); - - assertThat(themeRepository.existsById(1L)).isTrue(); - } -} diff --git a/src/test/java/roomescape/repository/JdbcWaitRepositoryTest.java b/src/test/java/roomescape/repository/JdbcWaitRepositoryTest.java deleted file mode 100644 index 8cb0c6fc71..0000000000 --- a/src/test/java/roomescape/repository/JdbcWaitRepositoryTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package roomescape.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.List; -import java.util.Optional; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; -import org.springframework.jdbc.core.JdbcTemplate; -import roomescape.domain.ReservationTime; -import roomescape.domain.Slot; -import roomescape.domain.Theme; -import roomescape.domain.Wait; -import roomescape.domain.Waits; - -@JdbcTest -class JdbcWaitRepositoryTest { - - @Autowired - private JdbcTemplate jdbcTemplate; - - private WaitRepository waitRepository; - private ReservationTime reservationTime; - private Theme theme; - - @BeforeEach - void beforeEach() { - waitRepository = new JdbcWaitRepository(jdbcTemplate); - - String insertReservationTimeSql = "INSERT INTO `reservation_time` (`start_at`) VALUES (?)"; - jdbcTemplate.update(insertReservationTimeSql, "10:00"); - - String insertThemeSql = "INSERT INTO `theme` (`name`, `description`, `thumbnail_url`) VALUES (?, ?, ?)"; - jdbcTemplate.update(insertThemeSql, "방탈출1", "방탈출1 설명", "url.jpg"); - - reservationTime = new ReservationTime(1L, LocalTime.of(10, 0)); - theme = new Theme(1L, "방탈출1", "방탈출1 설명", "url.jpg"); - } - - @AfterEach - void afterEach() { - String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'"; - List tableNames = jdbcTemplate.queryForList(sql, String.class); - - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY FALSE"); - for (String tableName : tableNames) { - jdbcTemplate.execute("TRUNCATE TABLE " + tableName); - jdbcTemplate.execute("ALTER TABLE " + tableName + " ALTER COLUMN ID RESTART WITH 1"); - } - jdbcTemplate.execute("SET REFERENTIAL_INTEGRITY TRUE"); - } - - @Test - void saveTest() { - Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), "luke", - new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme)); - - Wait wait = waitRepository.save(waitWithoutId); - - assertThat(wait.getId()).isEqualTo(1L); - assertThat(wait.getName()).isEqualTo("luke"); - } - - @Test - void findByIdTest() { - String createWait = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "luke", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 1), - "fizz", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 2), - "neo", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - - Optional waitLuke = waitRepository.findById(1L); - Optional waitFizz = waitRepository.findById(2L); - Optional waitNeo = waitRepository.findById(3L); - - assertThat(waitLuke).isNotEmpty(); - - assertThat(waitFizz).isNotEmpty(); - - assertThat(waitNeo).isNotEmpty(); - } - - @Test - void findBySlotTest() { - String createWait = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "luke", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 1), - "fizz", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 2), - "neo", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - - Waits slots = waitRepository.findBySlot(LocalDate.of(2026, 5, 27), reservationTime.getId(), - theme.getId()); - - assertThat(slots.size()).isEqualTo(3); - } - - @Test - void findByNameTest() { - String createWait = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "luke", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "fizz", LocalDate.of(2026, 5, 28), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 1), - "luke", LocalDate.of(2026, 5, 28), reservationTime.getId(), theme.getId()); - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "fizz", LocalDate.of(2026, 5, 29), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 1), - "neo", LocalDate.of(2026, 5, 29), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 2), - "luke", LocalDate.of(2026, 5, 29), reservationTime.getId(), theme.getId()); - - Waits waits = waitRepository.findByName("luke"); - - assertThat(waits.size()).isEqualTo(3); - - assertThat(waits.getWaits().get(0).getName()).isEqualTo("luke"); - - assertThat(waits.getWaits().get(1).getName()).isEqualTo("luke"); - - assertThat(waits.getWaits().get(2).getName()).isEqualTo("luke"); - } - - @Test - void findAllTest() { - String createWait = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "luke", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 1), - "fizz", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 2), - "neo", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - - Waits waits = waitRepository.findAll(); - - assertThat(waits.size()).isEqualTo(3); - } - - @Test - void findOrderByWaitTest() { - String createWait = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "luke", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 1), - "fizz", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 2), - "neo", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - - Slot slot = new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme); - Wait waitLuke = new Wait(1L, LocalDateTime.of(2026, 5, 21, 10, 0), "luke", slot); - Wait waitFizz = new Wait(2L, LocalDateTime.of(2026, 5, 21, 10, 1), "fizz", slot); - Wait waitNeo = new Wait(3L, LocalDateTime.of(2026, 5, 21, 10, 2), "neo", slot); - - assertThat(waitRepository.findOrderByWait(waitLuke)).isEqualTo(1L); - assertThat(waitRepository.findOrderByWait(waitFizz)).isEqualTo(2L); - assertThat(waitRepository.findOrderByWait(waitNeo)).isEqualTo(3L); - } - - @Test - void deleteByIdTest() { - String createWait = "INSERT INTO `wait`(`created_at`, `name`, `reservation_date`, `time_id`, `theme_id`) VALUES (?, ?, ?, ?, ?)"; - - jdbcTemplate.update(createWait, LocalDateTime.of(2026, 5, 21, 10, 0), - "luke", LocalDate.of(2026, 5, 27), reservationTime.getId(), theme.getId()); - - waitRepository.deleteById(1L); - - String findWaitCountSql = "SELECT COUNT(*) FROM `wait`"; - int count = jdbcTemplate.queryForObject(findWaitCountSql, Integer.class); - - Assertions.assertThat(count).isEqualTo(0); - } -} diff --git a/src/test/java/roomescape/repository/ReservationRepositoryTest.java b/src/test/java/roomescape/repository/ReservationRepositoryTest.java new file mode 100644 index 0000000000..5577c0cb3c --- /dev/null +++ b/src/test/java/roomescape/repository/ReservationRepositoryTest.java @@ -0,0 +1,157 @@ +package roomescape.repository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import roomescape.domain.Member; +import roomescape.domain.Reservation; +import roomescape.domain.ReservationTime; +import roomescape.domain.Slot; +import roomescape.domain.Theme; + +@DataJpaTest +public class ReservationRepositoryTest { + + @Autowired + private ReservationRepository reservationRepository; + + @Autowired + private ReservationTimeRepository reservationTimeRepository; + + @Autowired + private ThemeRepository themeRepository; + + @Autowired + private MemberRepository memberRepository; + + private ReservationTime reservationTime1; + private ReservationTime reservationTime2; + private ReservationTime reservationTime3; + private Theme theme; + private Member member; + + @BeforeEach + void beforeEach() { + reservationTime1 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + reservationTime2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(11, 0))); + reservationTime3 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 0))); + theme = themeRepository.save(new Theme("방탈출1", "방탈출1 설명", "url.jpg")); + member = memberRepository.save(new Member("fizz")); + } + + @Test + void saveTest() { + Reservation reservationWithoutId = new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, theme)); + + Reservation reservation = reservationRepository.save(reservationWithoutId); + + assertThat(reservation.getId()).isNotNull(); + } + + @Test + void findByIdTest() { + Reservation saved = reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, theme))); + + Reservation reservation = reservationRepository.findById(saved.getId()).get(); + + assertThat(reservation.getName()).isEqualTo("fizz"); + assertThat(reservation.getDate()).isEqualTo(LocalDate.of(2026, 5, 2)); + assertThat(reservation.getTime().getId()).isEqualTo(reservationTime1.getId()); + assertThat(reservation.getTheme().getId()).isEqualTo(theme.getId()); + } + + @Test + void findByNameTest() { + Member tree = memberRepository.save(new Member("tree")); + + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, theme))); + reservationRepository.save(new Reservation(tree, + new Slot(LocalDate.of(2026, 5, 2), reservationTime2, theme))); + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime3, theme))); + + List reservations = reservationRepository.findByMember_Name("fizz"); + + assertThat(reservations.size()).isEqualTo(2); + assertThat(reservations.get(0).getName()).isEqualTo("fizz"); + assertThat(reservations.get(1).getName()).isEqualTo("fizz"); + + assertThat(reservationRepository.findByMember_Name("user").size()).isEqualTo(0); + } + + @Test + void findAllTest() { + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, theme))); + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime2, theme))); + + List reservations = reservationRepository.findAll(); + + assertThat(reservations.size()).isEqualTo(2); + } + + @Test + void deleteByIdTest() { + Reservation saved = reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, theme))); + + reservationRepository.deleteById(saved.getId()); + + Assertions.assertThat(reservationRepository.count()).isEqualTo(0); + } + + @Test + void existsByTimeIdTest() { + ReservationTime extraTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(13, 0))); + + boolean exist = reservationRepository.existsBySlot_Time_Id(extraTime.getId()); + assertThat(exist).isFalse(); + + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), extraTime, theme))); + + exist = reservationRepository.existsBySlot_Time_Id(extraTime.getId()); + assertThat(exist).isTrue(); + } + + @Test + void existsByThemeIdTest() { + Theme extraTheme = themeRepository.save(new Theme("방탈출2", "방탈출2 설명", "url.jpg")); + + boolean exist = reservationRepository.existsBySlot_Theme_Id(extraTheme.getId()); + assertThat(exist).isFalse(); + + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, extraTheme))); + + exist = reservationRepository.existsBySlot_Theme_Id(extraTheme.getId()); + assertThat(exist).isTrue(); + } + + @Test + void findByDateAndTimeIdAndThemeIdTest() { + reservationRepository.save(new Reservation(member, + new Slot(LocalDate.of(2026, 5, 2), reservationTime1, theme))); + + Optional slot = reservationRepository.findBySlot( + LocalDate.of(2026, 5, 2), reservationTime1.getId(), theme.getId()); + + assertThat(slot).isNotEmpty(); + assertThat(slot.get().getDate()).isEqualTo(LocalDate.of(2026, 5, 2)); + assertThat(slot.get().getName()).isEqualTo("fizz"); + assertThat(slot.get().getTime().getId()).isEqualTo(reservationTime1.getId()); + assertThat(slot.get().getTheme().getId()).isEqualTo(theme.getId()); + } +} diff --git a/src/test/java/roomescape/repository/ReservationTimeRepositoryTest.java b/src/test/java/roomescape/repository/ReservationTimeRepositoryTest.java new file mode 100644 index 0000000000..7d4d3bc023 --- /dev/null +++ b/src/test/java/roomescape/repository/ReservationTimeRepositoryTest.java @@ -0,0 +1,94 @@ +package roomescape.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import roomescape.domain.Member; +import roomescape.domain.Reservation; +import roomescape.domain.ReservationTime; +import roomescape.domain.Slot; +import roomescape.domain.Theme; + +@DataJpaTest +public class ReservationTimeRepositoryTest { + + @Autowired + private ReservationTimeRepository reservationTimeRepository; + + @Autowired + private ThemeRepository themeRepository; + + @Autowired + private ReservationRepository reservationRepository; + + @Autowired + private MemberRepository memberRepository; + + @Test + void saveTest() { + ReservationTime reservationTimeWithoutId = new ReservationTime(LocalTime.of(10, 0)); + ReservationTime reservationTime = reservationTimeRepository.save(reservationTimeWithoutId); + + assertThat(reservationTime.getId()).isNotNull(); + } + + @Test + void findByIdTest() { + ReservationTime saved = reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + + Optional reservationTime = reservationTimeRepository.findById(saved.getId()); + + assertThat(reservationTime.orElseThrow().getId()).isEqualTo(saved.getId()); + } + + @Test + void findAllTest() { + reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + reservationTimeRepository.save(new ReservationTime(LocalTime.of(11, 0))); + + List reservationTimes = reservationTimeRepository.findAll(); + assertThat(reservationTimes.size()).isEqualTo(2); + } + + @Test + void findReservedTimesByDateAndThemeIdTest() { + ReservationTime time1 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + ReservationTime time2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(11, 0))); + reservationTimeRepository.save(new ReservationTime(LocalTime.of(12, 0))); + + Theme theme = themeRepository.save(new Theme("방탈출1", "방탈출1 설명", "url.jpg")); + Member fizz = memberRepository.save(new Member("fizz")); + + reservationRepository.save(new Reservation(fizz, new Slot(LocalDate.of(2026, 5, 2), time1, theme))); + reservationRepository.save(new Reservation(fizz, new Slot(LocalDate.of(2026, 5, 2), time2, theme))); + + List reservedTimes = reservationTimeRepository.findReservedTimesByDateAndTheme_Id( + LocalDate.of(2026, 5, 2), theme.getId()); + + assertThat(reservedTimes.get(0)).isEqualTo(time1); + assertThat(reservedTimes.get(1)).isEqualTo(time2); + } + + @Test + void deleteTest() { + ReservationTime saved = reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + + reservationTimeRepository.deleteById(saved.getId()); + + assertThat(reservationTimeRepository.count()).isEqualTo(0); + } + + @Test + void existsByStartAt() { + reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + + assertThat(reservationTimeRepository.existsByStartAt(LocalTime.of(10, 0))).isTrue(); + assertThat(reservationTimeRepository.existsByStartAt(LocalTime.of(11, 0))).isFalse(); + } +} diff --git a/src/test/java/roomescape/repository/ThemeRepositoryTest.java b/src/test/java/roomescape/repository/ThemeRepositoryTest.java new file mode 100644 index 0000000000..d3ab49929c --- /dev/null +++ b/src/test/java/roomescape/repository/ThemeRepositoryTest.java @@ -0,0 +1,95 @@ +package roomescape.repository; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import roomescape.domain.Member; +import roomescape.domain.Reservation; +import roomescape.domain.ReservationTime; +import roomescape.domain.Slot; +import roomescape.domain.Theme; + +@DataJpaTest +public class ThemeRepositoryTest { + + @Autowired + private ThemeRepository themeRepository; + + @Autowired + private ReservationTimeRepository reservationTimeRepository; + + @Autowired + private ReservationRepository reservationRepository; + + @Autowired + private MemberRepository memberRepository; + + @Test + void saveTest() { + Theme themeWithoutId = new Theme("방탈출", "설명", "url.jpg"); + Theme theme = themeRepository.save(themeWithoutId); + + assertThat(theme.getId()).isNotNull(); + } + + @Test + void findByIdTest() { + Theme saved = themeRepository.save(new Theme("방탈출1", "방탈출1 설명", "url.jpg")); + + Optional theme = themeRepository.findById(saved.getId()); + + assertThat(theme.orElseThrow().getId()).isEqualTo(saved.getId()); + } + + @Test + void findAllTest() { + themeRepository.save(new Theme("방탈출1", "방탈출1 설명", "url.jpg")); + themeRepository.save(new Theme("방탈출2", "방탈출2 설명", "url.jpg")); + + List themes = themeRepository.findAll(); + assertThat(themes.size()).isEqualTo(2); + } + + @Test + void findRankingTest() { + ReservationTime time1 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + ReservationTime time2 = reservationTimeRepository.save(new ReservationTime(LocalTime.of(11, 0))); + Theme theme1 = themeRepository.save(new Theme("방탈출1", "방탈출1 설명", "url.jpg")); + Theme theme2 = themeRepository.save(new Theme("방탈출2", "방탈출2 설명", "url.jpg")); + Member member1 = memberRepository.save(new Member("fizz")); + Member member2 = memberRepository.save(new Member("tree")); + Member member3 = memberRepository.save(new Member("neo")); + + reservationRepository.save(new Reservation(member1, new Slot(LocalDate.of(2026, 5, 2), time1, theme1))); + reservationRepository.save(new Reservation(member2, new Slot(LocalDate.of(2026, 5, 2), time2, theme1))); + reservationRepository.save(new Reservation(member3, new Slot(LocalDate.of(2026, 5, 2), time1, theme2))); + + List themes = themeRepository.findRanking(LocalDate.of(2026, 5, 2), LocalDate.of(2026, 5, 3), 2); + + assertThat(themes.get(0).getId()).isEqualTo(theme1.getId()); + assertThat(themes.get(1).getId()).isEqualTo(theme2.getId()); + } + + @Test + void deleteByIdTest() { + Theme saved = themeRepository.save(new Theme("방탈출", "설명", "url.jpg")); + + themeRepository.deleteById(saved.getId()); + + assertThat(themeRepository.count()).isEqualTo(0); + } + + @Test + void existsByIdTest() { + Theme saved = themeRepository.save(new Theme("방탈출", "설명", "url.jpg")); + + assertThat(themeRepository.existsById(saved.getId())).isTrue(); + assertThat(themeRepository.existsById(saved.getId() + 1)).isFalse(); + } +} diff --git a/src/test/java/roomescape/repository/WaitRepositoryTest.java b/src/test/java/roomescape/repository/WaitRepositoryTest.java new file mode 100644 index 0000000000..2546bd4cda --- /dev/null +++ b/src/test/java/roomescape/repository/WaitRepositoryTest.java @@ -0,0 +1,156 @@ +package roomescape.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import roomescape.domain.Member; +import roomescape.domain.ReservationTime; +import roomescape.domain.Slot; +import roomescape.domain.Theme; +import roomescape.domain.Wait; + +@DataJpaTest +class WaitRepositoryTest { + + @Autowired + private WaitRepository waitRepository; + + @Autowired + private ReservationTimeRepository reservationTimeRepository; + + @Autowired + private ThemeRepository themeRepository; + + @Autowired + private MemberRepository memberRepository; + + private ReservationTime reservationTime; + private Theme theme; + private Member luke; + private Member fizz; + private Member neo; + + @BeforeEach + void beforeEach() { + reservationTime = reservationTimeRepository.save(new ReservationTime(LocalTime.of(10, 0))); + theme = themeRepository.save(new Theme("방탈출1", "방탈출1 설명", "url.jpg")); + luke = memberRepository.save(new Member("luke")); + fizz = memberRepository.save(new Member("fizz")); + neo = memberRepository.save(new Member("neo")); + } + + @Test + void saveTest() { + Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme)); + + Wait wait = waitRepository.save(waitWithoutId); + + assertThat(wait.getId()).isNotNull(); + assertThat(wait.getName()).isEqualTo("luke"); + } + + @Test + void findByIdTest() { + Wait wait1 = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + Wait wait2 = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 1), fizz, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + Wait wait3 = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 2), neo, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + + Optional waitLuke = waitRepository.findById(wait1.getId()); + Optional waitFizz = waitRepository.findById(wait2.getId()); + Optional waitNeo = waitRepository.findById(wait3.getId()); + + assertThat(waitLuke).isNotEmpty(); + assertThat(waitFizz).isNotEmpty(); + assertThat(waitNeo).isNotEmpty(); + } + + @Test + void findBySlotTest() { + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 1), fizz, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 2), neo, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + + List waits = waitRepository.findBySlot(LocalDate.of(2026, 5, 27), reservationTime.getId(), + theme.getId()); + + assertThat(waits.size()).isEqualTo(3); + } + + @Test + void findByNameTest() { + Member luke2 = memberRepository.save(new Member("luke2")); + + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), fizz, + new Slot(LocalDate.of(2026, 5, 28), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 1), luke2, + new Slot(LocalDate.of(2026, 5, 28), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), fizz, + new Slot(LocalDate.of(2026, 5, 29), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 1), neo, + new Slot(LocalDate.of(2026, 5, 29), reservationTime, theme))); + + List lukeWaits = waitRepository.findByMember_Name("luke"); + + assertThat(lukeWaits.size()).isEqualTo(1); + assertThat(lukeWaits.get(0).getName()).isEqualTo("luke"); + } + + @Test + void findAllTest() { + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 1), fizz, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 2), neo, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + + List waits = waitRepository.findAll(); + + assertThat(waits.size()).isEqualTo(3); + } + + @Test + void calculateWaitingOrderTest() { + Wait wait1 = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + Wait wait2 = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 1), fizz, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + Wait wait3 = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 2), neo, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + + assertThat(waitRepository.calculateWaitingOrder(wait1.getReservationDate(), wait1.getTimeId(), + wait1.getThemeId(), wait1.getId())).isEqualTo(1L); + assertThat(waitRepository.calculateWaitingOrder(wait2.getReservationDate(), wait2.getTimeId(), + wait2.getThemeId(), wait2.getId())).isEqualTo(2L); + assertThat(waitRepository.calculateWaitingOrder(wait3.getReservationDate(), wait3.getTimeId(), + wait3.getThemeId(), wait3.getId())).isEqualTo(3L); + } + + @Test + void deleteByIdTest() { + Wait saved = waitRepository.save(new Wait(LocalDateTime.of(2026, 5, 21, 10, 0), luke, + new Slot(LocalDate.of(2026, 5, 27), reservationTime, theme))); + + waitRepository.deleteById(saved.getId()); + + Assertions.assertThat(waitRepository.count()).isEqualTo(0); + } +} diff --git a/src/test/java/roomescape/service/ReservationServiceTest.java b/src/test/java/roomescape/service/ReservationServiceTest.java index f0a859a2f6..008f3cb9d1 100644 --- a/src/test/java/roomescape/service/ReservationServiceTest.java +++ b/src/test/java/roomescape/service/ReservationServiceTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import roomescape.controller.dto.request.ReservationCreateRequest; +import roomescape.domain.Member; import roomescape.domain.Reservation; import roomescape.domain.ReservationTime; import roomescape.domain.Slot; @@ -36,6 +36,8 @@ public class ReservationServiceTest { private Theme theme; private Slot slot; private Slot otherSlot; + private Member fizz; + private Member luke; @BeforeEach void beforeEach() { @@ -49,14 +51,13 @@ void beforeEach() { slot = new Slot(LocalDate.of(2026, 5, 2), reservationTime, theme); otherSlot = new Slot(LocalDate.of(2026, 5, 3), reservationTime, theme); + fizz = new Member(1L, "fizz"); + luke = new Member(2L, "luke"); } @Test void saveTest() { - ReservationCreateRequest request = new ReservationCreateRequest("fizz", LocalDate.of(2026, 5, 2), - reservationTime.getId(), theme.getId()); - - Reservation reservationWithoutId = request.toReservation(reservationTime, theme); + Reservation reservationWithoutId = new Reservation(fizz, slot); Reservation reservation = reservationWithoutId.withId(1L); when(reservationRepository.save(reservationWithoutId)).thenReturn(reservation); @@ -67,11 +68,11 @@ void saveTest() { @Test void findByNameTest() { List reservations = List.of( - new Reservation(1L, "fizz", slot), - new Reservation(2L, "fizz", otherSlot) + new Reservation(1L, fizz, slot), + new Reservation(2L, fizz, otherSlot) ); - when(reservationRepository.findByName("fizz")).thenReturn(reservations); + when(reservationRepository.findByMember_Name("fizz")).thenReturn(reservations); List results = reservationService.findByName("fizz"); assertThat(results.get(0)).isEqualTo(reservations.get(0)); @@ -81,8 +82,8 @@ void findByNameTest() { @Test void findAllTest() { List reservations = List.of( - new Reservation(1L, "fizz", slot), - new Reservation(2L, "luke", otherSlot) + new Reservation(1L, fizz, slot), + new Reservation(2L, luke, otherSlot) ); when(reservationRepository.findAll()).thenReturn(reservations); @@ -94,7 +95,7 @@ void findAllTest() { @Test void findReservationTest() { - Reservation reservation = new Reservation(1L, "fizz", slot); + Reservation reservation = new Reservation(1L, fizz, slot); when(reservationRepository.findById(1L)).thenReturn(Optional.of(reservation)); @@ -111,15 +112,15 @@ void findReservationExceptionTest() { @Test void deleteTest() { - Reservation reservation = new Reservation(1L, "fizz", slot); + Reservation reservation = new Reservation(1L, fizz, slot); reservationService.delete(reservation, false); - verify(reservationRepository, times(1)).delete(1L); + verify(reservationRepository, times(1)).deleteById(1L); } @Test void findBySlotTest() { - Reservation reservation = new Reservation(1L, "fizz", slot); + Reservation reservation = new Reservation(1L, fizz, slot); when(reservationRepository.findBySlot( reservation.getDate(), @@ -137,7 +138,7 @@ void findBySlotTest() { @Test void validateReferencedThemeExceptionTest() { - when(reservationRepository.existsByThemeId(1L)).thenReturn(true); + when(reservationRepository.existsBySlot_Theme_Id(1L)).thenReturn(true); assertThatThrownBy(() -> reservationService.validateReferencedTheme(1L)) .isInstanceOf(CannotDeleteThemeInUseException.class); @@ -145,7 +146,7 @@ void validateReferencedThemeExceptionTest() { @Test void validateReferencedTimeExceptionTest() { - when(reservationRepository.existsByTimeId(1L)).thenReturn(true); + when(reservationRepository.existsBySlot_Time_Id(1L)).thenReturn(true); assertThatThrownBy(() -> reservationService.validateReferencedTime(1L)) .isInstanceOf(CannotDeleteReservationTimeInUseException.class); diff --git a/src/test/java/roomescape/service/ReservationTimeServiceTest.java b/src/test/java/roomescape/service/ReservationTimeServiceTest.java index a0ecc9cf3f..60c436535e 100644 --- a/src/test/java/roomescape/service/ReservationTimeServiceTest.java +++ b/src/test/java/roomescape/service/ReservationTimeServiceTest.java @@ -79,7 +79,8 @@ void findReservedTimesByDateAndThemeTest() { new ReservationTime(3L, LocalTime.of(12, 0)) ); - when(reservationTimeRepository.findReservedTimesByDateAndTheme(date, theme.getId())).thenReturn(reservedTimes); + when(reservationTimeRepository.findReservedTimesByDateAndTheme_Id(date, theme.getId())).thenReturn( + reservedTimes); assertThat(reservationTimeService.findReservedTimesByDateAndTheme(date, theme.getId())).isEqualTo( reservedTimes); } @@ -96,7 +97,7 @@ void findAvailabilityByDateAndThemeExceptionTest() { void deleteTest() { reservationTimeService.delete(1L); - verify(reservationTimeRepository, times(1)).delete(1L); + verify(reservationTimeRepository, times(1)).deleteById(1L); } @Test diff --git a/src/test/java/roomescape/service/WaitServiceTest.java b/src/test/java/roomescape/service/WaitServiceTest.java index e85ecab052..aac1c61664 100644 --- a/src/test/java/roomescape/service/WaitServiceTest.java +++ b/src/test/java/roomescape/service/WaitServiceTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import roomescape.domain.Member; import roomescape.domain.ReservationTime; import roomescape.domain.Slot; import roomescape.domain.Theme; @@ -36,6 +37,10 @@ public class WaitServiceTest { private LocalDate reservationDate; private Slot slot; private Slot otherSlot; + private Member fizz; + private Member luke; + private Member neo; + private Member lucky; private Clock fixedClock; @@ -51,29 +56,35 @@ void beforeEach() { reservationDate = LocalDate.of(2026, 5, 2); slot = new Slot(reservationDate, reservationTime, theme); otherSlot = new Slot(LocalDate.of(2026, 5, 3), reservationTime, theme); + fizz = new Member(1L, "fizz"); + luke = new Member(2L, "luke"); + neo = new Member(3L, "neo"); + lucky = new Member(4L, "lucky"); } @Test void saveTest() { - Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", slot); + Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 2, 10, 0), fizz, slot); Wait wait = waitWithoutId.withId(1L); - Waits waits = new Waits(List.of()); + + List waits = List.of(); when(waitRepository.findBySlot(reservationDate, reservationTime.getId(), theme.getId())).thenReturn(waits); when(waitRepository.save(waitWithoutId)).thenReturn(wait); - when(waitRepository.findOrderByWait(wait)).thenReturn(1L); + when(waitRepository.calculateWaitingOrder(wait.getReservationDate(), wait.getTimeId(), wait.getThemeId(), + wait.getId())).thenReturn(1L); assertThat(waitService.save(waitWithoutId)).isEqualTo(wait); } @Test void saveDuplicatedExceptionTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 11, 0), "fizz", slot); - Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 2, 11, 0), "luke", slot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 11, 0), fizz, slot); + Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 2, 11, 0), luke, slot); - Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", slot); + Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 2, 10, 0), fizz, slot); - Waits waits = new Waits(List.of(wait1, wait2)); + List waits = List.of(wait1, wait2); when(waitRepository.findBySlot(reservationDate, reservationTime.getId(), theme.getId())).thenReturn(waits); @@ -83,13 +94,13 @@ void saveDuplicatedExceptionTest() { @Test void saveSizeFullExceptionTest() { - Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 2, 14, 0), "fizz", slot); + Wait waitWithoutId = new Wait(LocalDateTime.of(2026, 5, 2, 14, 0), fizz, slot); - Wait otherWait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 11, 0), "luke", slot); - Wait otherWait2 = new Wait(2L, LocalDateTime.of(2026, 5, 2, 12, 0), "neo", slot); - Wait otherWait3 = new Wait(3L, LocalDateTime.of(2026, 5, 2, 13, 0), "lucky", slot); + Wait otherWait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 11, 0), luke, slot); + Wait otherWait2 = new Wait(2L, LocalDateTime.of(2026, 5, 2, 12, 0), neo, slot); + Wait otherWait3 = new Wait(3L, LocalDateTime.of(2026, 5, 2, 13, 0), lucky, slot); - Waits waits = new Waits(List.of(otherWait1, otherWait2, otherWait3)); + List waits = List.of(otherWait1, otherWait2, otherWait3); when(waitRepository.findBySlot(reservationDate, reservationTime.getId(), theme.getId())).thenReturn(waits); @@ -99,30 +110,36 @@ void saveSizeFullExceptionTest() { @Test void findByNameTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", slot); - Wait wait2 = new Wait(3L, LocalDateTime.of(2026, 5, 2, 12, 0), "fizz", otherSlot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), fizz, slot); + Wait wait2 = new Wait(3L, LocalDateTime.of(2026, 5, 2, 12, 0), fizz, otherSlot); - Waits fizzWaits = new Waits(List.of(wait1, wait2)); + List fizzWaits = List.of(wait1, wait2); + Waits result = new Waits(fizzWaits); - when(waitRepository.findByName("fizz")).thenReturn(fizzWaits); - when(waitRepository.findOrderByWait(wait1)).thenReturn(1L); - when(waitRepository.findOrderByWait(wait2)).thenReturn(1L); + when(waitRepository.findByMember_Name("fizz")).thenReturn(fizzWaits); + when(waitRepository.calculateWaitingOrder(wait1.getReservationDate(), wait1.getTimeId(), wait1.getThemeId(), + wait1.getId())).thenReturn(1L); + when(waitRepository.calculateWaitingOrder(wait2.getReservationDate(), wait2.getTimeId(), wait2.getThemeId(), + wait2.getId())).thenReturn(2L); - assertThat(waitService.findByName("fizz")).isEqualTo(fizzWaits); + assertThat(waitService.findByName("fizz")).isEqualTo(result); } @Test void findAllTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", slot); - Wait wait2 = new Wait(3L, LocalDateTime.of(2026, 5, 2, 12, 0), "luke", otherSlot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), fizz, slot); + Wait wait2 = new Wait(3L, LocalDateTime.of(2026, 5, 2, 12, 0), luke, otherSlot); - Waits waits = new Waits(List.of(wait1, wait2)); + List waits = List.of(wait1, wait2); + Waits result = new Waits(waits); - when(waitRepository.findAll()).thenReturn(waits); - when(waitRepository.findOrderByWait(wait1)).thenReturn(1L); - when(waitRepository.findOrderByWait(wait2)).thenReturn(1L); + when(waitRepository.findAllWaits()).thenReturn(waits); + when(waitRepository.calculateWaitingOrder(wait1.getReservationDate(), wait1.getTimeId(), wait1.getThemeId(), + wait1.getId())).thenReturn(1L); + when(waitRepository.calculateWaitingOrder(wait2.getReservationDate(), wait2.getTimeId(), wait2.getThemeId(), + wait2.getId())).thenReturn(2L); - assertThat(waitService.findAll()).isEqualTo(waits); + assertThat(waitService.findAll()).isEqualTo(result); } @Test @@ -134,22 +151,26 @@ void deleteTest() { @Test void findBySlotTest() { - Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", slot); - Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 2, 12, 0), "luke", slot); + Wait wait1 = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), fizz, slot); + Wait wait2 = new Wait(2L, LocalDateTime.of(2026, 5, 2, 12, 0), luke, slot); - Waits waits = new Waits(List.of(wait1, wait2)); + List waits = List.of(wait1, wait2); + Waits result = new Waits(waits); - when(waitRepository.findBySlot(slot.getDate(), slot.getTime().getId(), slot.getTheme().getId())).thenReturn( + when(waitRepository.findBySlot(slot.getReservationDate(), slot.getTime().getId(), + slot.getTheme().getId())).thenReturn( waits); - when(waitRepository.findOrderByWait(wait1)).thenReturn(1L); - when(waitRepository.findOrderByWait(wait2)).thenReturn(2L); + when(waitRepository.calculateWaitingOrder(wait1.getReservationDate(), wait1.getTimeId(), wait1.getThemeId(), + wait1.getId())).thenReturn(1L); + when(waitRepository.calculateWaitingOrder(wait2.getReservationDate(), wait2.getTimeId(), wait2.getThemeId(), + wait2.getId())).thenReturn(2L); - assertThat(waitService.findBySlot(slot)).isEqualTo(waits); + assertThat(waitService.findBySlot(slot)).isEqualTo(result); } @Test void findWaitTest() { - Wait wait = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), "fizz", slot); + Wait wait = new Wait(1L, LocalDateTime.of(2026, 5, 2, 10, 0), fizz, slot); when(waitRepository.findById(1L)).thenReturn(Optional.of(wait)); assertThat(waitService.findWait(1L)).isEqualTo(wait); diff --git a/src/test/resources/available-time-test-data.sql b/src/test/resources/available-time-test-data.sql index f7fd7257e9..ae4f7e6e9b 100644 --- a/src/test/resources/available-time-test-data.sql +++ b/src/test/resources/available-time-test-data.sql @@ -5,5 +5,8 @@ VALUES (1, '10:00'), INSERT INTO theme (id, name, description, thumbnail_url) VALUES (1, '잃어버린 왕국', '사라진 고대 왕국의 비밀을 추적하는 모험 테마', 'https://example.com/images/lost-kingdom.jpg'); -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('fizz', '2026-05-01', 1, 1); +INSERT INTO member (id, name) +VALUES (1, 'fizz'); + +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (1, '2026-05-01', 1, 1); diff --git a/src/test/resources/ranking-test-data.sql b/src/test/resources/ranking-test-data.sql index 607b1df719..32da35f90f 100644 --- a/src/test/resources/ranking-test-data.sql +++ b/src/test/resources/ranking-test-data.sql @@ -27,89 +27,107 @@ VALUES (1, '잃어버린 왕국', '사라진 고대 왕국의 비밀을 추적 (14, '기억의 방', '잃어버린 기억을 되찾기 위해 단서를 연결하는 감성 테마', 'https://example.com/images/memory-room.jpg'), (15, '네온 시티', '미래 도시의 보안망을 돌파하는 사이버펑크 테마', 'https://example.com/images/neon-city.jpg'); +-- member id 1~55 (테마별 예약자) +INSERT INTO member (id, name) +VALUES (1, '왕국_01'), (2, '왕국_02'), (3, '왕국_03'), (4, '왕국_04'), (5, '왕국_05'), + (6, '왕국_06'), (7, '왕국_07'), (8, '왕국_08'), (9, '왕국_09'), (10, '왕국_10'), + (11, '연구소_01'), (12, '연구소_02'), (13, '연구소_03'), (14, '연구소_04'), (15, '연구소_05'), + (16, '연구소_06'), (17, '연구소_07'), (18, '연구소_08'), (19, '연구소_09'), + (20, '해적_01'), (21, '해적_02'), (22, '해적_03'), (23, '해적_04'), (24, '해적_05'), + (25, '해적_06'), (26, '해적_07'), (27, '해적_08'), + (28, '여행자_01'), (29, '여행자_02'), (30, '여행자_03'), (31, '여행자_04'), (32, '여행자_05'), + (33, '여행자_06'), (34, '여행자_07'), + (35, '마법사_01'), (36, '마법사_02'), (37, '마법사_03'), (38, '마법사_04'), (39, '마법사_05'), + (40, '마법사_06'), + (41, '탐정_01'), (42, '탐정_02'), (43, '탐정_03'), (44, '탐정_04'), (45, '탐정_05'), + (46, '벙커_01'), (47, '벙커_02'), (48, '벙커_03'), (49, '벙커_04'), + (50, '신전_01'), (51, '신전_02'), (52, '신전_03'), + (53, '호텔_01'), (54, '호텔_02'), + (55, '우주_01'); + -- [Theme 1: 잃어버린 왕국] - 총 10건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('왕국_01', '2026-05-01', 1, 1), - ('왕국_02', '2026-05-02', 2, 1), - ('왕국_03', '2026-05-03', 3, 1), - ('왕국_04', '2026-05-04', 4, 1), - ('왕국_05', '2026-05-05', 5, 1), - ('왕국_06', '2026-05-06', 6, 1), - ('왕국_07', '2026-05-07', 7, 1), - ('왕국_08', '2026-05-07', 8, 1), - ('왕국_09', '2026-05-07', 9, 1), - ('왕국_10', '2026-05-07', 10, 1); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (1, '2026-05-01', 1, 1), + (2, '2026-05-02', 2, 1), + (3, '2026-05-03', 3, 1), + (4, '2026-05-04', 4, 1), + (5, '2026-05-05', 5, 1), + (6, '2026-05-06', 6, 1), + (7, '2026-05-07', 7, 1), + (8, '2026-05-07', 8, 1), + (9, '2026-05-07', 9, 1), + (10, '2026-05-07', 10, 1); -- [Theme 2: 심야의 연구소] - 총 9건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('연구소_01', '2026-05-01', 1, 2), - ('연구소_02', '2026-05-02', 2, 2), - ('연구소_03', '2026-05-03', 3, 2), - ('연구소_04', '2026-05-04', 4, 2), - ('연구소_05', '2026-05-05', 5, 2), - ('연구소_06', '2026-05-06', 6, 2), - ('연구소_07', '2026-05-07', 7, 2), - ('연구소_08', '2026-05-07', 8, 2), - ('연구소_09', '2026-05-07', 9, 2); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (11, '2026-05-01', 1, 2), + (12, '2026-05-02', 2, 2), + (13, '2026-05-03', 3, 2), + (14, '2026-05-04', 4, 2), + (15, '2026-05-05', 5, 2), + (16, '2026-05-06', 6, 2), + (17, '2026-05-07', 7, 2), + (18, '2026-05-07', 8, 2), + (19, '2026-05-07', 9, 2); -- [Theme 3: 해적선의 저주] - 총 8건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('해적_01', '2026-05-01', 1, 3), - ('해적_02', '2026-05-02', 2, 3), - ('해적_03', '2026-05-03', 3, 3), - ('해적_04', '2026-05-04', 4, 3), - ('해적_05', '2026-05-05', 5, 3), - ('해적_06', '2026-05-06', 6, 3), - ('해적_07', '2026-05-07', 7, 3), - ('해적_08', '2026-05-07', 8, 3); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (20, '2026-05-01', 1, 3), + (21, '2026-05-02', 2, 3), + (22, '2026-05-03', 3, 3), + (23, '2026-05-04', 4, 3), + (24, '2026-05-05', 5, 3), + (25, '2026-05-06', 6, 3), + (26, '2026-05-07', 7, 3), + (27, '2026-05-07', 8, 3); -- [Theme 4: 시간 여행자] - 총 7건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('여행자_01', '2026-05-01', 1, 4), - ('여행자_02', '2026-05-02', 2, 4), - ('여행자_03', '2026-05-03', 3, 4), - ('여행자_04', '2026-05-04', 4, 4), - ('여행자_05', '2026-05-05', 5, 4), - ('여행자_06', '2026-05-06', 6, 4), - ('여행자_07', '2026-05-07', 7, 4); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (28, '2026-05-01', 1, 4), + (29, '2026-05-02', 2, 4), + (30, '2026-05-03', 3, 4), + (31, '2026-05-04', 4, 4), + (32, '2026-05-05', 5, 4), + (33, '2026-05-06', 6, 4), + (34, '2026-05-07', 7, 4); -- [Theme 5: 마법사의 서재] - 총 6건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('마법사_01', '2026-05-01', 1, 5), - ('마법사_02', '2026-05-02', 2, 5), - ('마법사_03', '2026-05-03', 3, 5), - ('마법사_04', '2026-05-04', 4, 5), - ('마법사_05', '2026-05-05', 5, 5), - ('마법사_06', '2026-05-06', 6, 5); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (35, '2026-05-01', 1, 5), + (36, '2026-05-02', 2, 5), + (37, '2026-05-03', 3, 5), + (38, '2026-05-04', 4, 5), + (39, '2026-05-05', 5, 5), + (40, '2026-05-06', 6, 5); -- [Theme 6: 사라진 탐정] - 총 5건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('탐정_01', '2026-05-01', 1, 6), - ('탐정_02', '2026-05-02', 2, 6), - ('탐정_03', '2026-05-03', 3, 6), - ('탐정_04', '2026-05-04', 4, 6), - ('탐정_05', '2026-05-05', 5, 6); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (41, '2026-05-01', 1, 6), + (42, '2026-05-02', 2, 6), + (43, '2026-05-03', 3, 6), + (44, '2026-05-04', 4, 6), + (45, '2026-05-05', 5, 6); -- [Theme 7: 지하 벙커] - 총 4건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('벙커_01', '2026-05-01', 1, 7), - ('벙커_02', '2026-05-02', 2, 7), - ('벙커_03', '2026-05-03', 3, 7), - ('벙커_04', '2026-05-04', 4, 7); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (46, '2026-05-01', 1, 7), + (47, '2026-05-02', 2, 7), + (48, '2026-05-03', 3, 7), + (49, '2026-05-04', 4, 7); -- [Theme 8: 고대 신전] - 총 3건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('신전_01', '2026-05-01', 1, 8), - ('신전_02', '2026-05-02', 2, 8), - ('신전_03', '2026-05-03', 3, 8); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (50, '2026-05-01', 1, 8), + (51, '2026-05-02', 2, 8), + (52, '2026-05-03', 3, 8); -- [Theme 9: 유령 호텔] - 총 2건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('호텔_01', '2026-05-01', 1, 9), - ('호텔_02', '2026-05-02', 2, 9); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (53, '2026-05-01', 1, 9), + (54, '2026-05-02', 2, 9); -- [Theme 10: 우주 정거장] - 총 1건 -INSERT INTO reservation (name, date, time_id, theme_id) -VALUES ('우주_01', '2026-05-01', 1, 10); +INSERT INTO reservation (member_id, reservation_date, time_id, theme_id) +VALUES (55, '2026-05-01', 1, 10); --- [Theme 11 ~ 15]: 예약 없음 (LEFT JOIN 테스트용) \ No newline at end of file +-- [Theme 11 ~ 15]: 예약 없음 (LEFT JOIN 테스트용)