From a3848741c5ae1ca67b21d2c1164b5c8fbcc5fc55 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 00:36:12 +0900 Subject: [PATCH 01/14] =?UTF-8?q?chore=20:=20scheduler=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=84=A4=EC=A0=95=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scheduler/build.gradle | 4 ++++ .../main/java/dev/hooon/SchedulerApplication.java | 12 ++++++++++++ scheduler/src/main/resources/application.yml | 13 +++++++++++++ settings.gradle | 2 ++ 4 files changed, 31 insertions(+) create mode 100644 scheduler/build.gradle create mode 100644 scheduler/src/main/java/dev/hooon/SchedulerApplication.java create mode 100644 scheduler/src/main/resources/application.yml diff --git a/scheduler/build.gradle b/scheduler/build.gradle new file mode 100644 index 00000000..b6318c31 --- /dev/null +++ b/scheduler/build.gradle @@ -0,0 +1,4 @@ +dependencies { + implementation project(':core') + testImplementation(testFixtures(project(':core'))) +} diff --git a/scheduler/src/main/java/dev/hooon/SchedulerApplication.java b/scheduler/src/main/java/dev/hooon/SchedulerApplication.java new file mode 100644 index 00000000..29a30708 --- /dev/null +++ b/scheduler/src/main/java/dev/hooon/SchedulerApplication.java @@ -0,0 +1,12 @@ +package dev.hooon; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SchedulerApplication { + + public static void main(String[] args) { + SpringApplication.run(SchedulerApplication.class, args); + } +} diff --git a/scheduler/src/main/resources/application.yml b/scheduler/src/main/resources/application.yml new file mode 100644 index 00000000..990772f0 --- /dev/null +++ b/scheduler/src/main/resources/application.yml @@ -0,0 +1,13 @@ +spring: + jpa: + show-sql: true + hibernate: + ddl-auto: create + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + format_sql: true + +logging: + level: + org.hibernate.sql: info \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index c9a55f93..09da2b54 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,3 +2,5 @@ rootProject.name = 'interpark' include 'api' include 'core' +include 'scheduler' + From b350dfa998eba5f7ef513d2cf2f29c0de00c20dd Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 00:39:59 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat=20:=206=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=EC=97=90=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EB=8A=94=20expireAt=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20&=20getSelectedSeatIds=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/entity/WaitingBooking.java | 9 +++++++++ .../domain/entity/WaitingBookingTest.java | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java b/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java index 1a924d05..94241102 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java +++ b/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java @@ -7,6 +7,7 @@ import static jakarta.persistence.FetchType.*; import static jakarta.persistence.GenerationType.*; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -52,6 +53,8 @@ public class WaitingBooking extends TimeBaseEntity { private int seatCount; + private LocalDateTime expireAt; + @OneToMany(mappedBy = "waitingBooking", cascade = {REMOVE, PERSIST}) List waitingBookingSeats = new ArrayList<>(); @@ -108,4 +111,10 @@ public static WaitingBooking of( ) { return new WaitingBooking(user, seatCount, seatIds); } + + public List getSelectedSeatIds() { + return waitingBookingSeats.stream() + .map(WaitingBookingSeat::getSeatId) + .toList(); + } } diff --git a/core/src/test/java/dev/hooon/waitingbooking/domain/entity/WaitingBookingTest.java b/core/src/test/java/dev/hooon/waitingbooking/domain/entity/WaitingBookingTest.java index 56016f14..5476222e 100644 --- a/core/src/test/java/dev/hooon/waitingbooking/domain/entity/WaitingBookingTest.java +++ b/core/src/test/java/dev/hooon/waitingbooking/domain/entity/WaitingBookingTest.java @@ -95,4 +95,24 @@ void ofTest_4() { .isInstanceOf(ValidationException.class) .hasMessageContaining(WaitingBookingErrorCode.INVALID_SELECTED_SEAT_COUNT.getMessage()); } + + @Test + @DisplayName("[선택한 좌석의 ID List 를 응답한다]") + void getSelectedSeatIdsTest() { + //given + List selectedSeatIds = List.of(1L, 2L, 3L, 4L, 5L); + WaitingBooking waitingBooking = WaitingBooking.of( + new User(), + 3, + selectedSeatIds + ); + + //when + List result = waitingBooking.getSelectedSeatIds(); + + //then + assertThat(result) + .hasSameSizeAs(selectedSeatIds) + .containsAll(selectedSeatIds); + } } \ No newline at end of file From 239049b1d6289b315b9f10091a9faba9ecce6d2f Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 00:40:46 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat=20:=20User=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/hooon/user/domain/entity/User.java | 29 ++++++++++++------- .../domain/repository/UserRepository.java | 2 ++ .../adaptor/UserRepositoryAdaptor.java | 5 ++++ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/dev/hooon/user/domain/entity/User.java b/core/src/main/java/dev/hooon/user/domain/entity/User.java index e710f4fd..e813a482 100644 --- a/core/src/main/java/dev/hooon/user/domain/entity/User.java +++ b/core/src/main/java/dev/hooon/user/domain/entity/User.java @@ -19,18 +19,25 @@ @NoArgsConstructor public class User extends TimeBaseEntity { - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "user_id") - private Long id; + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "user_id") + private Long id; - @Column(name = "user_email", nullable = false, unique = true) - private String email; + @Column(name = "user_email", nullable = false, unique = true) + private String email; - @Column(name = "user_name", nullable = false) - private String name; + @Column(name = "user_name", nullable = false) + private String name; - @Enumerated(STRING) - @Column(name = "user_role", nullable = false) - private UserRole userRole; + @Enumerated(STRING) + @Column(name = "user_role", nullable = false) + private UserRole userRole; + + // 테스트용 AllArgsConstructor + public User(String email, String name, UserRole userRole) { + this.email = email; + this.name = name; + this.userRole = userRole; + } } diff --git a/core/src/main/java/dev/hooon/user/domain/repository/UserRepository.java b/core/src/main/java/dev/hooon/user/domain/repository/UserRepository.java index c8d95ecf..a6e19844 100644 --- a/core/src/main/java/dev/hooon/user/domain/repository/UserRepository.java +++ b/core/src/main/java/dev/hooon/user/domain/repository/UserRepository.java @@ -6,5 +6,7 @@ public interface UserRepository { + User save(User user); + Optional findById(Long id); } diff --git a/core/src/main/java/dev/hooon/user/infrastructure/adaptor/UserRepositoryAdaptor.java b/core/src/main/java/dev/hooon/user/infrastructure/adaptor/UserRepositoryAdaptor.java index 30104443..df946a24 100644 --- a/core/src/main/java/dev/hooon/user/infrastructure/adaptor/UserRepositoryAdaptor.java +++ b/core/src/main/java/dev/hooon/user/infrastructure/adaptor/UserRepositoryAdaptor.java @@ -15,6 +15,11 @@ public class UserRepositoryAdaptor implements UserRepository { private final UserJpaRepository userJpaRepository; + @Override + public User save(User user) { + return userJpaRepository.save(user); + } + @Override public Optional findById(Long id) { return userJpaRepository.findById(id); From 6cff37849be83314949ff2bb91ee10ff9e4dc735 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 00:43:49 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat=20:=20SeatStatus=20=EC=97=90=20?= =?UTF-8?q?=EC=98=88=EB=A7=A4=EB=8C=80=EA=B8=B0=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/dev/hooon/show/domain/entity/seat/SeatStatus.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/dev/hooon/show/domain/entity/seat/SeatStatus.java b/core/src/main/java/dev/hooon/show/domain/entity/seat/SeatStatus.java index 22ab934a..c6304a82 100644 --- a/core/src/main/java/dev/hooon/show/domain/entity/seat/SeatStatus.java +++ b/core/src/main/java/dev/hooon/show/domain/entity/seat/SeatStatus.java @@ -9,7 +9,9 @@ public enum SeatStatus { AVAILABLE("예매 가능"), BOOKED("예매 완료"), - CANCELED("예매 취소"); + CANCELED("예매 취소"), + // 취소된 좌석이 예매대기로 인해 6시간동안 예매대기를 한 사용자에 한해 예약이 가능한 상태 + WAITING("예매 대기"); private final String description; } From 0b866969ecde3075a146cde8f80f61a1d1e5e150 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 00:45:25 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat=20:=20SeatRepository=20-=20findById,?= =?UTF-8?q?=20findByStatusIsCanceled,=20updateStatusByIdIn=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/SeatRepository.java | 9 +++ .../adaptor/SeatRepositoryAdaptor.java | 18 ++++++ .../repository/SeatJpaRepository.java | 10 ++++ .../domain/repository/SeatRepositoryTest.java | 56 +++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/core/src/main/java/dev/hooon/show/domain/repository/SeatRepository.java b/core/src/main/java/dev/hooon/show/domain/repository/SeatRepository.java index 8411dd2e..5da4b845 100644 --- a/core/src/main/java/dev/hooon/show/domain/repository/SeatRepository.java +++ b/core/src/main/java/dev/hooon/show/domain/repository/SeatRepository.java @@ -1,13 +1,22 @@ package dev.hooon.show.domain.repository; +import java.util.Collection; import java.util.List; +import java.util.Optional; import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.entity.seat.SeatStatus; import dev.hooon.show.dto.query.SeatDateRoundDto; public interface SeatRepository { void saveAll(Iterable seats); + Optional findById(Long id); + List findSeatDateRoundInfoByShowId(Long showId); + + List findByStatusIsCanceled(); + + void updateStatusByIdIn(Collection ids, SeatStatus status); } diff --git a/core/src/main/java/dev/hooon/show/infrastructure/adaptor/SeatRepositoryAdaptor.java b/core/src/main/java/dev/hooon/show/infrastructure/adaptor/SeatRepositoryAdaptor.java index c32c8bb0..2b24f3b8 100644 --- a/core/src/main/java/dev/hooon/show/infrastructure/adaptor/SeatRepositoryAdaptor.java +++ b/core/src/main/java/dev/hooon/show/infrastructure/adaptor/SeatRepositoryAdaptor.java @@ -1,10 +1,13 @@ package dev.hooon.show.infrastructure.adaptor; +import java.util.Collection; import java.util.List; +import java.util.Optional; import org.springframework.stereotype.Repository; import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.entity.seat.SeatStatus; import dev.hooon.show.domain.repository.SeatRepository; import dev.hooon.show.dto.query.SeatDateRoundDto; import dev.hooon.show.infrastructure.repository.SeatJpaRepository; @@ -21,8 +24,23 @@ public void saveAll(Iterable seats) { seatJpaRepository.saveAll(seats); } + @Override + public Optional findById(Long id) { + return seatJpaRepository.findById(id); + } + @Override public List findSeatDateRoundInfoByShowId(Long showId) { return seatJpaRepository.findSeatDateRoundInfoByShowId(showId); } + + @Override + public List findByStatusIsCanceled() { + return seatJpaRepository.findBySeatStatus(SeatStatus.CANCELED); + } + + @Override + public void updateStatusByIdIn(Collection ids, SeatStatus status) { + seatJpaRepository.updateStatusByIdIn(ids, status); + } } \ No newline at end of file diff --git a/core/src/main/java/dev/hooon/show/infrastructure/repository/SeatJpaRepository.java b/core/src/main/java/dev/hooon/show/infrastructure/repository/SeatJpaRepository.java index 2d0b4932..2c67fc7d 100644 --- a/core/src/main/java/dev/hooon/show/infrastructure/repository/SeatJpaRepository.java +++ b/core/src/main/java/dev/hooon/show/infrastructure/repository/SeatJpaRepository.java @@ -1,11 +1,15 @@ package dev.hooon.show.infrastructure.repository; +import java.util.Collection; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.entity.seat.SeatStatus; import dev.hooon.show.dto.query.SeatDateRoundDto; public interface SeatJpaRepository extends JpaRepository { @@ -18,4 +22,10 @@ public interface SeatJpaRepository extends JpaRepository { order by s.showDate, s.showRound.round """) List findSeatDateRoundInfoByShowId(Long showId); + + List findBySeatStatus(SeatStatus status); + + @Modifying + @Query("update Seat s SET s.seatStatus = :status where s.id in :ids") + void updateStatusByIdIn(@Param("ids") Collection ids, @Param("status") SeatStatus status); } \ No newline at end of file diff --git a/core/src/test/java/dev/hooon/show/domain/repository/SeatRepositoryTest.java b/core/src/test/java/dev/hooon/show/domain/repository/SeatRepositoryTest.java index 0cdf25c7..a9d3f295 100644 --- a/core/src/test/java/dev/hooon/show/domain/repository/SeatRepositoryTest.java +++ b/core/src/test/java/dev/hooon/show/domain/repository/SeatRepositoryTest.java @@ -10,17 +10,23 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.util.ReflectionTestUtils; import dev.hooon.common.fixture.SeatFixture; import dev.hooon.common.support.DataJpaTestSupport; import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.entity.seat.SeatStatus; import dev.hooon.show.dto.query.SeatDateRoundDto; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; @DisplayName("[SeatJpaRepository 테스트]") class SeatRepositoryTest extends DataJpaTestSupport { @Autowired private SeatRepository seatRepository; + @PersistenceContext + private EntityManager entityManager; private void assertSeatDateRoundDto(SeatDateRoundDto dto, Seat expected) { assertAll( @@ -52,4 +58,54 @@ void findSeatDateRoundInfoByShowIdTest() { assertSeatDateRoundDto(result.get(1), seats.get(2)); assertSeatDateRoundDto(result.get(2), seats.get(3)); } + + @Test + @DisplayName("[상태가 CANCELED 상태인 좌석을 조회한다]") + void findByStatusIsCanceledTest() { + //given + List seats = List.of( + SeatFixture.getSeat(), + SeatFixture.getSeat(), + SeatFixture.getSeat() + ); + // 0번 Seat 만 Canceled 상태로 변경(취소 기능이 아직 구현되지 않아서 리플렉션 사용) + ReflectionTestUtils.setField(seats.get(0), "seatStatus", SeatStatus.CANCELED); + + seatRepository.saveAll(seats); + + //when + List result = seatRepository.findByStatusIsCanceled(); + + //then + assertThat(result) + .hasSize(1) + .contains(seats.get(0)); + } + + @Test + @DisplayName("[id 와 status 를 입력하면 id 에 해당하는 좌석의 상태를 입력한 status 로 변경한다]") + void updateStatusByIdInTest() { + //given + List seats = List.of( + SeatFixture.getSeat(), + SeatFixture.getSeat(), + SeatFixture.getSeat() + ); + seatRepository.saveAll(seats); + + List seatIds = seats.stream().map(Seat::getId).toList(); + + //when + seatRepository.updateStatusByIdIn(seatIds, SeatStatus.CANCELED); + entityManager.flush(); + entityManager.clear(); + + //then + List actual = seatRepository.findByStatusIsCanceled(); + List actualIds = actual.stream().map(Seat::getId).toList(); + + assertThat(actualIds) + .hasSameSizeAs(seatIds) + .containsAll(seatIds); + } } \ No newline at end of file From 79017128d2b3a7f8f99afc8488772225d53de8e2 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 00:59:29 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat=20:=20WaitingBookingRepository=20-?= =?UTF-8?q?=20findById,=20findByStatusIsWaiting,=20updateToActiveById=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/WaitingBookingRepository.java | 8 ++ .../WaitingBookingRepositoryAdaptor.java | 21 +++++ .../WaitingBookingJpaRepository.java | 18 +++++ .../WaitingBookingRepositoryTest.java | 79 +++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java diff --git a/core/src/main/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepository.java b/core/src/main/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepository.java index 38655781..8705dbf1 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepository.java +++ b/core/src/main/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepository.java @@ -1,6 +1,7 @@ package dev.hooon.waitingbooking.domain.repository; import java.util.List; +import java.util.Optional; import dev.hooon.waitingbooking.domain.entity.WaitingBooking; @@ -8,5 +9,12 @@ public interface WaitingBookingRepository { void save(WaitingBooking waitingBooking); + Optional findById(Long id); + List findAll(); + + // WaitingStatus 가 WAITING 인 데이터를 최신순으로 조회하는 쿼리 + List findByStatusIsWaiting(); + + void updateToActiveById(Long id); } diff --git a/core/src/main/java/dev/hooon/waitingbooking/infrastructure/adaptor/WaitingBookingRepositoryAdaptor.java b/core/src/main/java/dev/hooon/waitingbooking/infrastructure/adaptor/WaitingBookingRepositoryAdaptor.java index f8644523..1d46e5e7 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/infrastructure/adaptor/WaitingBookingRepositoryAdaptor.java +++ b/core/src/main/java/dev/hooon/waitingbooking/infrastructure/adaptor/WaitingBookingRepositoryAdaptor.java @@ -1,10 +1,13 @@ package dev.hooon.waitingbooking.infrastructure.adaptor; +import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; import org.springframework.stereotype.Repository; import dev.hooon.waitingbooking.domain.entity.WaitingBooking; +import dev.hooon.waitingbooking.domain.entity.WaitingStatus; import dev.hooon.waitingbooking.domain.repository.WaitingBookingRepository; import dev.hooon.waitingbooking.infrastructure.repository.WaitingBookingJpaRepository; import lombok.RequiredArgsConstructor; @@ -20,9 +23,27 @@ public void save(WaitingBooking waitingBooking) { waitingBookingJpaRepository.save(waitingBooking); } + @Override + public Optional findById(Long id) { + return waitingBookingJpaRepository.findById(id); + } + @Override public List findAll() { return waitingBookingJpaRepository.findAll(); } + @Override + public List findByStatusIsWaiting() { + return waitingBookingJpaRepository.findByStatusOrderByIdDesc(WaitingStatus.WAITING); + } + + @Override + public void updateToActiveById(Long id) { + waitingBookingJpaRepository.updateStatusAndExpireAt( + id, + WaitingStatus.ACTIVATION, + LocalDateTime.now().plusHours(6) + ); + } } diff --git a/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java b/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java index 62453814..9dfc30a6 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java +++ b/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java @@ -1,8 +1,26 @@ package dev.hooon.waitingbooking.infrastructure.repository; +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import dev.hooon.waitingbooking.domain.entity.WaitingBooking; +import dev.hooon.waitingbooking.domain.entity.WaitingStatus; public interface WaitingBookingJpaRepository extends JpaRepository { + + @EntityGraph(attributePaths = {"user", "waitingBookingSeats"}) + List findByStatusOrderByIdDesc(WaitingStatus status); + + @Modifying + @Query("update WaitingBooking w SET w.status = :status, w.expireAt = :expireAt where w.id = :id") + void updateStatusAndExpireAt( + @Param("id") Long id, + @Param("status") WaitingStatus status, + @Param("expireAt") LocalDateTime expireAt); } diff --git a/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java b/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java new file mode 100644 index 00000000..a8059153 --- /dev/null +++ b/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java @@ -0,0 +1,79 @@ +package dev.hooon.waitingbooking.domain.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.util.ReflectionTestUtils; + +import dev.hooon.common.fixture.WaitingBookingFixture; +import dev.hooon.common.support.DataJpaTestSupport; +import dev.hooon.user.domain.entity.User; +import dev.hooon.user.domain.entity.UserRole; +import dev.hooon.user.domain.repository.UserRepository; +import dev.hooon.waitingbooking.domain.entity.WaitingBooking; +import dev.hooon.waitingbooking.domain.entity.WaitingStatus; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +@DisplayName("[WaitingBookingRepository 테스트]") +class WaitingBookingRepositoryTest extends DataJpaTestSupport { + + @Autowired + private WaitingBookingRepository waitingBookingRepository; + @Autowired + private UserRepository userRepository; + @PersistenceContext + private EntityManager entityManager; + + private final User user = new User("hello123@naver.com", "name", UserRole.BUYER); + + @Test + @DisplayName("[WAITING 상태인 데이터를 최신순으로 조회한다]") + void findByStatusIsWaitingTest() { + //given + userRepository.save(user); + + List waitingBookings = List.of( + WaitingBookingFixture.getWaitingBooking(user), + WaitingBookingFixture.getWaitingBooking(user), + WaitingBookingFixture.getWaitingBooking(user) + ); + // 0번 데이터만 상태를 ACTIVATION 으로 변경 + ReflectionTestUtils.setField(waitingBookings.get(0), "status", WaitingStatus.ACTIVATION); + + waitingBookings.forEach(waitingBooking -> waitingBookingRepository.save(waitingBooking)); + + //when + List result = waitingBookingRepository.findByStatusIsWaiting(); + + //then + assertThat(result) + .hasSize(2) + .containsExactly(waitingBookings.get(2), waitingBookings.get(1)); + } + + @Test + @DisplayName("[id 에 해당하는 데이터의 status 를 ACTIVATION 으로 변경하고 expireAt 을 6시간뒤로 설정한다]") + void updateToActiveByIdTest() { + //given + userRepository.save(user); + + WaitingBooking waitingBooking = WaitingBookingFixture.getWaitingBooking(user); + waitingBookingRepository.save(waitingBooking); + + //when + waitingBookingRepository.updateToActiveById(waitingBooking.getId()); + entityManager.flush(); + entityManager.clear(); + + //then + WaitingBooking actual = waitingBookingRepository.findById(waitingBooking.getId()).orElseThrow(); + assertThat(actual.getStatus()).isEqualTo(WaitingStatus.ACTIVATION); + assertThat(actual.getExpireAt()).isEqualToIgnoringSeconds(LocalDateTime.now().plusHours(6)); + } +} \ No newline at end of file From 8921b0373a767e415aba75a213c4a99e6180d5d8 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:00:23 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feat=20:=20SeatFixture=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/hooon/common/fixture/SeatFixture.java | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/core/src/testFixtures/java/dev/hooon/common/fixture/SeatFixture.java b/core/src/testFixtures/java/dev/hooon/common/fixture/SeatFixture.java index 76b16397..3dd2e3a4 100644 --- a/core/src/testFixtures/java/dev/hooon/common/fixture/SeatFixture.java +++ b/core/src/testFixtures/java/dev/hooon/common/fixture/SeatFixture.java @@ -16,16 +16,20 @@ */ public class SeatFixture { + private static final Show SHOW; // Show 정보가 필요없을때 사용하는 ID 를 가진 Show + + static { + SHOW = new Show(); + ReflectionTestUtils.setField(SHOW, "id", 1L); + } + private SeatFixture() { } // show 에 대한 정보가 필요없는 경우에 사용 public static Seat getSeat(LocalDate showDate, int round, LocalTime startTime) { - Show show = new Show(); - ReflectionTestUtils.setField(show, "id", 1L); - return Seat.of( - show, + SHOW, SeatGrade.VIP, true, "1층", @@ -38,4 +42,31 @@ public static Seat getSeat(LocalDate showDate, int round, LocalTime startTime) { SeatStatus.AVAILABLE ); } + + // show 에 대한 정보가 필요없는 경우에 사용 + public static Seat getSeat() { + return getSeat(LocalDate.now(), 1, LocalTime.now()); + } + + public static Seat getSeat(SeatStatus status) { + return Seat.of( + SHOW, + SeatGrade.VIP, + true, + "1층", + "A", + 10, + 100000, + LocalDate.now(), + 1, + LocalTime.now(), + status + ); + } + + public static Seat getSeat(Long seatId) { + Seat seat = getSeat(LocalDate.now(), 1, LocalTime.now()); + ReflectionTestUtils.setField(seat, "id", seatId); + return seat; + } } From 566ab5df31e6389f7e9dfd18b99b886032e03589 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:01:05 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat=20:=20WaitingBookingFixture=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/fixture/WaitingBookingFixture.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 core/src/testFixtures/java/dev/hooon/common/fixture/WaitingBookingFixture.java diff --git a/core/src/testFixtures/java/dev/hooon/common/fixture/WaitingBookingFixture.java b/core/src/testFixtures/java/dev/hooon/common/fixture/WaitingBookingFixture.java new file mode 100644 index 00000000..74df7b40 --- /dev/null +++ b/core/src/testFixtures/java/dev/hooon/common/fixture/WaitingBookingFixture.java @@ -0,0 +1,36 @@ +package dev.hooon.common.fixture; + +import java.util.List; + +import org.springframework.test.util.ReflectionTestUtils; + +import dev.hooon.user.domain.entity.User; +import dev.hooon.waitingbooking.domain.entity.WaitingBooking; + +public final class WaitingBookingFixture { + + private WaitingBookingFixture() { + } + + public static WaitingBooking getWaitingBooking(User user) { + return WaitingBooking.of( + user, + 3, + List.of(1L, 2L, 3L, 4L, 5L) + ); + } + + public static WaitingBooking getWaitingBooking( + Long waitingBookingId, + int seatCount, + List seatIds + ) { + WaitingBooking waitingBooking = WaitingBooking.of( + new User(), + seatCount, + seatIds + ); + ReflectionTestUtils.setField(waitingBooking, "id", waitingBookingId); + return waitingBooking; + } +} From 5aa39f25f3225c39f43dc06f42c54214fd39145b Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:05:11 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat=20:=20SeatService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20&=20getCanceledSeatIds,=20updateSeatToWaiting,=20up?= =?UTF-8?q?dateSeatToAvailable=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooon/show/application/SeatService.java | 40 ++++++++++++++++ .../show/application/SeatServiceTest.java | 47 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 core/src/main/java/dev/hooon/show/application/SeatService.java create mode 100644 core/src/test/java/dev/hooon/show/application/SeatServiceTest.java diff --git a/core/src/main/java/dev/hooon/show/application/SeatService.java b/core/src/main/java/dev/hooon/show/application/SeatService.java new file mode 100644 index 00000000..489aa692 --- /dev/null +++ b/core/src/main/java/dev/hooon/show/application/SeatService.java @@ -0,0 +1,40 @@ +package dev.hooon.show.application; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.entity.seat.SeatStatus; +import dev.hooon.show.domain.repository.SeatRepository; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class SeatService { + + private final SeatRepository seatRepository; + + // 취소 상태인 좌석의 ID 를 모두 조회 + public Set getCanceledSeatIds() { + return seatRepository.findByStatusIsCanceled() + .stream() + .map(Seat::getId) + .collect(Collectors.toSet()); + } + + // 좌석을 대기중 상태로 변경 + @Transactional + public void updateSeatToWaiting(Collection targetIds) { + seatRepository.updateStatusByIdIn(targetIds, SeatStatus.WAITING); + } + + // 좌석을 대기중 예약가능 상태로 변경 + @Transactional + public void updateSeatToAvailable(Collection targetIds) { + seatRepository.updateStatusByIdIn(targetIds, SeatStatus.AVAILABLE); + } +} diff --git a/core/src/test/java/dev/hooon/show/application/SeatServiceTest.java b/core/src/test/java/dev/hooon/show/application/SeatServiceTest.java new file mode 100644 index 00000000..20e4ed6a --- /dev/null +++ b/core/src/test/java/dev/hooon/show/application/SeatServiceTest.java @@ -0,0 +1,47 @@ +package dev.hooon.show.application; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import dev.hooon.common.fixture.SeatFixture; +import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.repository.SeatRepository; + +@DisplayName("[SeatService 테스트]") +@ExtendWith(MockitoExtension.class) +class SeatServiceTest { + + @InjectMocks + private SeatService seatService; + @Mock + private SeatRepository seatRepository; + + @Test + void getCanceledSeatIds() { + //given + List seats = List.of( + SeatFixture.getSeat(1L), + SeatFixture.getSeat(2L), + SeatFixture.getSeat(3L) + ); + given(seatRepository.findByStatusIsCanceled()) + .willReturn(seats); + + //when + Set result = seatService.getCanceledSeatIds(); + + //then + assertThat(result).hasSameSizeAs(seats); + seats.forEach(seat -> assertThat(result).contains(seat.getId())); + } +} \ No newline at end of file From 0924434e62146feaaa379c66c016ebde17834051 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:07:02 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat=20:=20WaitingBookingService=20-=20ge?= =?UTF-8?q?tWaitingBookingsByStatusIsWaiting,=20activateWaitingBooking=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/WaitingBookingService.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/java/dev/hooon/waitingbooking/application/WaitingBookingService.java b/core/src/main/java/dev/hooon/waitingbooking/application/WaitingBookingService.java index ab40fba7..5237b9bf 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/application/WaitingBookingService.java +++ b/core/src/main/java/dev/hooon/waitingbooking/application/WaitingBookingService.java @@ -1,6 +1,9 @@ package dev.hooon.waitingbooking.application; +import java.util.List; + import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import dev.hooon.user.domain.entity.User; import dev.hooon.waitingbooking.domain.entity.WaitingBooking; @@ -20,4 +23,15 @@ public WaitingBooking createWaitingBooking(User user, WaitingRegisterRequest req return waitingBooking; } + + // 대기 상태인 WaitingBooking 조회 + public List getWaitingBookingsByStatusIsWaiting() { + return waitingBookingRepository.findByStatusIsWaiting(); + } + + // ID 에 해당하는 WaitingBooking ACTIVATION 상태로 변경하고 expireAt 6시간뒤로 설정 + @Transactional + public void activateWaitingBooking(Long waitingBookingId) { + waitingBookingRepository.updateToActiveById(waitingBookingId); + } } From 47d2ab0b5db2d2cd6fef2f46ca94712f6c9180e5 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:15:52 +0900 Subject: [PATCH 11/14] =?UTF-8?q?feat=20:=20WaitingBookingFacade=20-=20?= =?UTF-8?q?=EC=98=88=EC=95=BD=EB=8C=80=EA=B8=B0=EB=A5=BC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8A=94=20processWaitingBooking=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facade/WaitingBookingFacade.java | 57 +++++++++++++++++++ .../facade/WaitingBookingFacadeTest.java | 37 ++++++++++++ 2 files changed, 94 insertions(+) diff --git a/core/src/main/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacade.java b/core/src/main/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacade.java index 56668947..d662beb3 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacade.java +++ b/core/src/main/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacade.java @@ -1,7 +1,12 @@ package dev.hooon.waitingbooking.application.facade; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + import org.springframework.stereotype.Component; +import dev.hooon.show.application.SeatService; import dev.hooon.user.application.UserService; import dev.hooon.user.domain.entity.User; import dev.hooon.waitingbooking.application.WaitingBookingService; @@ -16,6 +21,25 @@ public class WaitingBookingFacade { private final WaitingBookingService waitingBookingService; private final UserService userService; + private final SeatService seatService; + + // 선택한 좌석중에서 취소좌석에 포함되는 좌석 ID 를 LIST 로 응답 + private List fetchMatchingSeatIds( + Set canceledSeatIds, + List selectedSeatIds, + int seatCount + ) { + List matchSeatIds = new ArrayList<>(); + for (Long selectedSeatId : selectedSeatIds) { + if (canceledSeatIds.contains(selectedSeatId)) { + matchSeatIds.add(selectedSeatId); + } + if (matchSeatIds.size() == seatCount) { + break; + } + } + return matchSeatIds; + } public WaitingRegisterResponse registerWaitingBooking(Long userId, WaitingRegisterRequest request) { User user = userService.getUserById(userId); @@ -23,4 +47,37 @@ public WaitingRegisterResponse registerWaitingBooking(Long userId, WaitingRegist return new WaitingRegisterResponse(waitingBooking.getId()); } + + public void processWaitingBooking() { + // 1. 취소된 좌석을 모두 조회한다 (PK Set 으로) + Set canceledSeatIds = seatService.getCanceledSeatIds(); + // 2. 대기중 상태인 예약대기 목록을 날짜순으로 조회한다 + List waitingList = waitingBookingService.getWaitingBookingsByStatusIsWaiting(); + // 3. waitingList 반복하면서 아래 작업을 수행 + /* + * (1) 대기목록에 포함된 좌석의 PK 가 취소된 좌석 Set 에 존재하는지 확인 + * (2) 대기목록의 좌석중에서 취소목록에 포함되는 좌석 아이디 가져옴 + * (3) 가져온 좌석 아이디 사이즈가 선택좌석개수와 같으면 취소좌석 SET 에서 해당 PK 지우고 좌석 상태를 취소에서 대기로 바꾸고 예약대기를 활성화 + * + 사용자에게 메일 발송 (구현 예정) + */ + waitingList.forEach(waitingBooking -> { + // (1) + List selectedSeatIds = waitingBooking.getSelectedSeatIds(); + // (2) + List matchSeatIds = fetchMatchingSeatIds( + canceledSeatIds, + selectedSeatIds, + waitingBooking.getSeatCount() + ); + // (3) + if (matchSeatIds.size() == waitingBooking.getSeatCount()) { + matchSeatIds.forEach(canceledSeatIds::remove); + seatService.updateSeatToWaiting(matchSeatIds); + waitingBookingService.activateWaitingBooking(waitingBooking.getId()); + // 메일 알림 이벤트 발행 + } + }); + // 4. 반복이 끝났는데 남아있는 취소 좌석들은 예약가능 상태로 변경 + seatService.updateSeatToAvailable(canceledSeatIds); + } } diff --git a/core/src/test/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacadeTest.java b/core/src/test/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacadeTest.java index 3a363337..5b4ca96c 100644 --- a/core/src/test/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacadeTest.java +++ b/core/src/test/java/dev/hooon/waitingbooking/application/facade/WaitingBookingFacadeTest.java @@ -3,7 +3,9 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.*; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,6 +15,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; +import dev.hooon.common.fixture.WaitingBookingFixture; +import dev.hooon.show.application.SeatService; import dev.hooon.user.application.UserService; import dev.hooon.user.domain.entity.User; import dev.hooon.waitingbooking.application.WaitingBookingService; @@ -30,8 +34,11 @@ class WaitingBookingFacadeTest { private WaitingBookingService waitingBookingService; @Mock private UserService userService; + @Mock + private SeatService seatService; @Test + @DisplayName("[사용자와 예약대기 정보를 통해 예약대기를 등록한다]") void registerWaitingBookingTest() { //given User user = new User(); @@ -50,4 +57,34 @@ void registerWaitingBookingTest() { //then assertThat(result.waitingBookingId()).isEqualTo(1L); } + + @Test + @DisplayName("[현재 대기중인 예약대기를 처리한다]") + void processWaitingBookingTest() { + //given + Set canceledSeatIds = new HashSet<>(); + // 1L~10L 값세팅 + for (long i = 1L; i <= 10L; i++) { + canceledSeatIds.add(i); + } + given(seatService.getCanceledSeatIds()) + .willReturn(canceledSeatIds); + + List waitingBookings = List.of( + WaitingBookingFixture.getWaitingBooking(1L, 3, List.of(1L, 2L, 3L, 4L)), + WaitingBookingFixture.getWaitingBooking(2L, 1, List.of(1L, 2L, 4L)), + WaitingBookingFixture.getWaitingBooking(3L, 2, List.of(1L, 2L, 3L, 4L, 5L)) + ); + given(waitingBookingService.getWaitingBookingsByStatusIsWaiting()) + .willReturn(waitingBookings); + + //when + waitingBookingFacade.processWaitingBooking(); + + //then + verify(seatService, times(2)).updateSeatToWaiting(anyCollection()); + verify(waitingBookingService, times(1)).activateWaitingBooking(waitingBookings.get(0).getId()); + verify(waitingBookingService, times(1)).activateWaitingBooking(waitingBookings.get(1).getId()); + verify(seatService, times(1)).updateSeatToAvailable(anyCollection()); + } } \ No newline at end of file From 6c771cefb8914733b8a0b5bc2c3711c47c889a31 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:16:44 +0900 Subject: [PATCH 12/14] =?UTF-8?q?feat=20:=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EC=8A=A4=EB=A0=88=EB=93=9C=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooon/common/config/SchedulerConfig.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 scheduler/src/main/java/dev/hooon/common/config/SchedulerConfig.java diff --git a/scheduler/src/main/java/dev/hooon/common/config/SchedulerConfig.java b/scheduler/src/main/java/dev/hooon/common/config/SchedulerConfig.java new file mode 100644 index 00000000..47acbae0 --- /dev/null +++ b/scheduler/src/main/java/dev/hooon/common/config/SchedulerConfig.java @@ -0,0 +1,23 @@ +package dev.hooon.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +@Configuration +@EnableScheduling +public class SchedulerConfig implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + + scheduler.setPoolSize(10); + scheduler.setThreadNamePrefix("scheduler-thread-"); + scheduler.initialize(); + + taskRegistrar.setScheduler(scheduler); + } +} From 5d75bc6980bc6ad5dc2de9711b269ff57bc99893 Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 01:17:10 +0900 Subject: [PATCH 13/14] =?UTF-8?q?feat=20:=20=EC=98=88=EC=95=BD=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/WaitingBookingScheduler.java | 19 ++++ .../WaitingBookingSchedulerTest.java | 96 +++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 scheduler/src/main/java/dev/hooon/waitingbooking/scheduler/WaitingBookingScheduler.java create mode 100644 scheduler/src/test/java/dev/hooon/waitingbooking/scheduler/WaitingBookingSchedulerTest.java diff --git a/scheduler/src/main/java/dev/hooon/waitingbooking/scheduler/WaitingBookingScheduler.java b/scheduler/src/main/java/dev/hooon/waitingbooking/scheduler/WaitingBookingScheduler.java new file mode 100644 index 00000000..8ca0ede2 --- /dev/null +++ b/scheduler/src/main/java/dev/hooon/waitingbooking/scheduler/WaitingBookingScheduler.java @@ -0,0 +1,19 @@ +package dev.hooon.waitingbooking.scheduler; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import dev.hooon.waitingbooking.application.facade.WaitingBookingFacade; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class WaitingBookingScheduler { + + private final WaitingBookingFacade waitingBookingFacade; + + @Scheduled(cron = "0 */10 * * * *") + public void scheduleWaitingBookingProcess() { + waitingBookingFacade.processWaitingBooking(); + } +} diff --git a/scheduler/src/test/java/dev/hooon/waitingbooking/scheduler/WaitingBookingSchedulerTest.java b/scheduler/src/test/java/dev/hooon/waitingbooking/scheduler/WaitingBookingSchedulerTest.java new file mode 100644 index 00000000..29d45236 --- /dev/null +++ b/scheduler/src/test/java/dev/hooon/waitingbooking/scheduler/WaitingBookingSchedulerTest.java @@ -0,0 +1,96 @@ +package dev.hooon.waitingbooking.scheduler; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import dev.hooon.common.fixture.SeatFixture; +import dev.hooon.common.support.TestContainerSupport; +import dev.hooon.show.domain.entity.seat.Seat; +import dev.hooon.show.domain.entity.seat.SeatStatus; +import dev.hooon.show.domain.repository.SeatRepository; +import dev.hooon.user.domain.entity.User; +import dev.hooon.user.domain.entity.UserRole; +import dev.hooon.user.domain.repository.UserRepository; +import dev.hooon.waitingbooking.domain.entity.WaitingBooking; +import dev.hooon.waitingbooking.domain.entity.WaitingStatus; +import dev.hooon.waitingbooking.domain.repository.WaitingBookingRepository; + +@DisplayName("[WaitingBookingScheduler 테스트]") +@SpringBootTest +class WaitingBookingSchedulerTest extends TestContainerSupport { + + @Autowired + private WaitingBookingScheduler waitingBookingScheduler; + @Autowired + private WaitingBookingRepository waitingBookingRepository; + @Autowired + private UserRepository userRepository; + @Autowired + private SeatRepository seatRepository; + + private void assertSeatStatus(Long id, SeatStatus expected) { + assertThat(seatRepository.findById(id)).isPresent() + .get() + .extracting("seatStatus") + .isEqualTo(expected); + } + + private void assertWaitingBookingStatus(Long id, WaitingStatus expected) { + assertThat(waitingBookingRepository.findById(id)).isPresent() + .get() + .extracting("status") + .isEqualTo(expected); + } + + @Test + @DisplayName("[현재 대기중인 예약대기를 처리한다]") + void scheduleWaitingBookingProcess() { + //given + List seats = List.of( + SeatFixture.getSeat(SeatStatus.CANCELED), + SeatFixture.getSeat(SeatStatus.CANCELED), + SeatFixture.getSeat(SeatStatus.CANCELED) + ); + seatRepository.saveAll(seats); + + User user = new User("hello123@naver.com", "name", UserRole.BUYER); + userRepository.save(user); + + List waitingBookings = List.of( + WaitingBooking.of( + user, + 1, + List.of(1000L, seats.get(0).getId(), 2000L) + ), + WaitingBooking.of( + user, + 1, + List.of(seats.get(1).getId(), 2000L, 3000L) + ), + WaitingBooking.of( + user, + 1, + List.of(1000L, 2000L, 3000L) + ) + ); + waitingBookings.forEach(waitingBooking -> waitingBookingRepository.save(waitingBooking)); + + //when + waitingBookingScheduler.scheduleWaitingBookingProcess(); + + //then + assertSeatStatus(seats.get(0).getId(), SeatStatus.WAITING); + assertSeatStatus(seats.get(1).getId(), SeatStatus.WAITING); + assertSeatStatus(seats.get(2).getId(), SeatStatus.AVAILABLE); + + assertWaitingBookingStatus(waitingBookings.get(0).getId(), WaitingStatus.ACTIVATION); + assertWaitingBookingStatus(waitingBookings.get(1).getId(), WaitingStatus.ACTIVATION); + assertWaitingBookingStatus(waitingBookings.get(2).getId(), WaitingStatus.WAITING); + } +} \ No newline at end of file From b84ab45c978ec4a5c0e64e77f061dcaa9a3559af Mon Sep 17 00:00:00 2001 From: EunChanNam Date: Thu, 28 Dec 2023 18:52:25 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor=20:=20expireAt=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=20expiredAt=20=EC=9C=BC=EB=A1=9C=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/hooon/waitingbooking/domain/entity/WaitingBooking.java | 2 +- .../infrastructure/repository/WaitingBookingJpaRepository.java | 2 +- .../domain/repository/WaitingBookingRepositoryTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java b/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java index 94241102..183b82bc 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java +++ b/core/src/main/java/dev/hooon/waitingbooking/domain/entity/WaitingBooking.java @@ -53,7 +53,7 @@ public class WaitingBooking extends TimeBaseEntity { private int seatCount; - private LocalDateTime expireAt; + private LocalDateTime expiredAt; @OneToMany(mappedBy = "waitingBooking", cascade = {REMOVE, PERSIST}) List waitingBookingSeats = new ArrayList<>(); diff --git a/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java b/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java index 9dfc30a6..b3fab660 100644 --- a/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java +++ b/core/src/main/java/dev/hooon/waitingbooking/infrastructure/repository/WaitingBookingJpaRepository.java @@ -18,7 +18,7 @@ public interface WaitingBookingJpaRepository extends JpaRepository findByStatusOrderByIdDesc(WaitingStatus status); @Modifying - @Query("update WaitingBooking w SET w.status = :status, w.expireAt = :expireAt where w.id = :id") + @Query("update WaitingBooking w SET w.status = :status, w.expiredAt = :expireAt where w.id = :id") void updateStatusAndExpireAt( @Param("id") Long id, @Param("status") WaitingStatus status, diff --git a/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java b/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java index a8059153..ec3ebd33 100644 --- a/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java +++ b/core/src/test/java/dev/hooon/waitingbooking/domain/repository/WaitingBookingRepositoryTest.java @@ -74,6 +74,6 @@ void updateToActiveByIdTest() { //then WaitingBooking actual = waitingBookingRepository.findById(waitingBooking.getId()).orElseThrow(); assertThat(actual.getStatus()).isEqualTo(WaitingStatus.ACTIVATION); - assertThat(actual.getExpireAt()).isEqualToIgnoringSeconds(LocalDateTime.now().plusHours(6)); + assertThat(actual.getExpiredAt()).isEqualToIgnoringSeconds(LocalDateTime.now().plusHours(6)); } } \ No newline at end of file