diff --git a/src/main/java/site/campingon/campingon/common/oauth/TestController.java b/src/main/java/site/campingon/campingon/common/oauth/TestController.java deleted file mode 100644 index fd6482fd..00000000 --- a/src/main/java/site/campingon/campingon/common/oauth/TestController.java +++ /dev/null @@ -1,52 +0,0 @@ -package site.campingon.campingon.common.oauth; - -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.servlet.view.RedirectView; -import site.campingon.campingon.common.oauth.service.CustomOAuth2UserService; - -@Controller -@RequiredArgsConstructor -public class TestController { - - private final CustomOAuth2UserService customOAuth2UserService; - - @GetMapping("/") - @ResponseBody - public String home() { - return "home"; - } - - @GetMapping("/oauth/success") - @ResponseBody - public String success() { - return "oauth google login success"; - } - - @GetMapping("/oauth/fail") - @ResponseBody - public String fail() { - return "access denied"; - } - - @GetMapping("/oauth/logout/success") - @ResponseBody - public String logoutSuccess() { - return "oauth google logout success"; - } - - @GetMapping("/oauth/logout") - @ResponseBody - public RedirectView logout(@AuthenticationPrincipal CustomOAuth2User customOAuth2User) { - if (customOAuth2User != null) { - customOAuth2UserService.deleteGoogleAccount(customOAuth2User); - } - - return new RedirectView("/oauth/logout/success"); - } - -} diff --git a/src/main/java/site/campingon/campingon/reservation/entity/Reservation.java b/src/main/java/site/campingon/campingon/reservation/entity/Reservation.java index a3930127..42f00459 100644 --- a/src/main/java/site/campingon/campingon/reservation/entity/Reservation.java +++ b/src/main/java/site/campingon/campingon/reservation/entity/Reservation.java @@ -69,20 +69,9 @@ public void cancel(String reason) { this.cancelReason = reason; } - // 체크인 체크아웃 시간 고정 설정 - public void setDefaultCheckTime(LocalDateTime checkin, LocalDateTime checkout) { - this.checkin = checkin.withHour(15).truncatedTo(ChronoUnit.HOURS); - this.checkout = checkout.withHour(11).truncatedTo(ChronoUnit.HOURS); - } - - - // json 반환 시 파싱해서 반환 - public String[] parseDateTime(LocalDateTime checkin, LocalDateTime checkout) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); - return new String[] { - checkin.format(formatter), - checkout.format(formatter) - }; + // 상태 변경 + public void changeStatus(ReservationStatus status) { + this.status = status; } } diff --git a/src/main/java/site/campingon/campingon/reservation/entity/ReservationStatus.java b/src/main/java/site/campingon/campingon/reservation/entity/ReservationStatus.java index 6c1acc34..8c409599 100644 --- a/src/main/java/site/campingon/campingon/reservation/entity/ReservationStatus.java +++ b/src/main/java/site/campingon/campingon/reservation/entity/ReservationStatus.java @@ -9,7 +9,9 @@ public enum ReservationStatus { RESERVED("예약완료"), CANCELED("예약취소"), - COMPLETED("체크인완료"); // 체크인 이후로 (자동) 변경 + COMPLETED("체크인완료"), // 체크인 이후로 (자동) 변경 + + NOTCANCELABLE("취소불가"); private final String status; diff --git a/src/main/java/site/campingon/campingon/reservation/repository/ReservationRepository.java b/src/main/java/site/campingon/campingon/reservation/repository/ReservationRepository.java index 07294a72..8c552043 100644 --- a/src/main/java/site/campingon/campingon/reservation/repository/ReservationRepository.java +++ b/src/main/java/site/campingon/campingon/reservation/repository/ReservationRepository.java @@ -9,6 +9,8 @@ import org.springframework.stereotype.Repository; import site.campingon.campingon.reservation.entity.Reservation; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; @Repository @@ -31,5 +33,5 @@ public interface ReservationRepository extends JpaRepository @Query("SELECT r FROM Reservation r WHERE r.user.id = :userId AND r.status = 'RESERVED' ORDER BY r.checkin ASC LIMIT 1") Reservation findUpcomingReservationByUserId(@Param("userId")Long userId); - + List findByCheckin(LocalDateTime targetTime); } diff --git a/src/main/java/site/campingon/campingon/reservation/service/ReservationStatusUpdate.java b/src/main/java/site/campingon/campingon/reservation/service/ReservationStatusUpdate.java new file mode 100644 index 00000000..2d076400 --- /dev/null +++ b/src/main/java/site/campingon/campingon/reservation/service/ReservationStatusUpdate.java @@ -0,0 +1,58 @@ +package site.campingon.campingon.reservation.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import site.campingon.campingon.reservation.entity.Reservation; +import site.campingon.campingon.reservation.entity.ReservationStatus; +import site.campingon.campingon.reservation.repository.ReservationRepository; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ReservationStatusUpdate { + + private final ReservationRepository reservationRepository; + + // 매일 자정에 예약불가 실행 + @Scheduled(cron = "0 59 23 * * ?") + public void updateStatusToNotCancelable() { + LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul")); + LocalDate targetDate = LocalDate.from(now.plusDays(1)); + + LocalDateTime targetTime = targetDate.atTime(15, 0); + + List reservations = reservationRepository.findByCheckin(targetTime); + + reservations.stream() + .filter(reservation -> reservation.getStatus() != ReservationStatus.NOTCANCELABLE) + .forEach(reservation -> reservation.changeStatus(ReservationStatus.NOTCANCELABLE)); + + reservationRepository.saveAll(reservations); + + log.debug("예약불가 업데이트 스케줄러가 실행되었습니다."); + } + + // 매일 3시에 체크인완료 실행 + @Scheduled(cron = "0 0 15 * * ?") + public void updateStatusToCompleted() { + LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul")); + LocalDateTime targetDate = now.toLocalDate().atTime(15, 0); + + List reservations = reservationRepository.findByCheckin(targetDate); + for (Reservation reservation : reservations) { + if (reservation.getStatus() != ReservationStatus.COMPLETED) { + reservation.changeStatus(ReservationStatus.COMPLETED); + } + } + reservationRepository.saveAll(reservations); + + log.debug("체크인완료 업데이트 스케줄러가 실행되었습니다."); + } +} \ No newline at end of file diff --git a/src/test/java/site/campingon/campingon/camp/service/CampSiteServiceTest.java b/src/test/java/site/campingon/campingon/camp/service/CampSiteServiceTest.java index af660be6..09ec9bdc 100644 --- a/src/test/java/site/campingon/campingon/camp/service/CampSiteServiceTest.java +++ b/src/test/java/site/campingon/campingon/camp/service/CampSiteServiceTest.java @@ -19,6 +19,7 @@ import site.campingon.campingon.camp.repository.CampSiteRepository; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Arrays; import java.util.Collections; @@ -89,7 +90,10 @@ void getAvailableCampSitesSuccess() { LocalDate checkout = LocalDate.of(2023, 10, 5); List availableCampSites = Arrays.asList(mockCampSite); - when(campSiteRepository.findAvailableCampSites(campId, checkin, checkout)).thenReturn(availableCampSites); + LocalDateTime newCheckin = LocalDateTime.of(2023, 10, 1,0,0); + LocalDateTime newCheckout = LocalDateTime.of(2023, 10, 5,0,0); + + when(campSiteRepository.findAvailableCampSites(campId, newCheckin, newCheckout)).thenReturn(availableCampSites); when(campSiteMapper.toCampSiteListResponseDto(any(CampSite.class))).thenReturn(mockCampSiteListDto); // when @@ -100,7 +104,7 @@ void getAvailableCampSitesSuccess() { assertEquals(1, result.size()); assertEquals(mockCampSiteListDto, result.get(0)); - verify(campSiteRepository).findAvailableCampSites(campId, checkin, checkout); + verify(campSiteRepository).findAvailableCampSites(campId, newCheckin, newCheckout); verify(campSiteMapper).toCampSiteListResponseDto(any(CampSite.class)); } @@ -115,7 +119,10 @@ void getAvailableCampSitesReturnsEmptyList() { LocalDate checkout = LocalDate.of(2023, 10, 5); List emptyCampSites = Collections.emptyList(); - when(campSiteRepository.findAvailableCampSites(campId, checkin, checkout)).thenReturn(emptyCampSites); + LocalDateTime newCheckin = LocalDateTime.of(2023, 10, 1,0,0); + LocalDateTime newCheckout = LocalDateTime.of(2023, 10, 5,0,0); + + when(campSiteRepository.findAvailableCampSites(campId, newCheckin, newCheckout)).thenReturn(emptyCampSites); // when List result = campSiteReserveService.getAvailableCampSites(campId, checkin, checkout); @@ -124,7 +131,7 @@ void getAvailableCampSitesReturnsEmptyList() { assertNotNull(result); assertTrue(result.isEmpty()); - verify(campSiteRepository).findAvailableCampSites(campId, checkin, checkout); + verify(campSiteRepository).findAvailableCampSites(campId, newCheckin, newCheckout); verify(campSiteMapper, never()).toCampSiteListResponseDto(any(CampSite.class)); } @@ -174,7 +181,6 @@ void testCreateCampSiteWithSingleInduty() { .price(campSite.getPrice()) .maximumPeople(campSite.getMaximumPeople()) .indoorFacility(campSite.getIndoorFacility()) - .isAvailable(campSite.isAvailable()) .build()); // When @@ -232,7 +238,6 @@ void testUpdateCampSite() { .price(updatedCampSite.getPrice()) .maximumPeople(updatedCampSite.getMaximumPeople()) .indoorFacility(updatedCampSite.getIndoorFacility()) - .isAvailable(updatedCampSite.isAvailable()) .build()); // When diff --git a/src/test/java/site/campingon/campingon/reservation/service/ReservationServiceTest.java b/src/test/java/site/campingon/campingon/reservation/service/ReservationServiceTest.java index d389eb1c..fdc89af0 100644 --- a/src/test/java/site/campingon/campingon/reservation/service/ReservationServiceTest.java +++ b/src/test/java/site/campingon/campingon/reservation/service/ReservationServiceTest.java @@ -24,6 +24,7 @@ import site.campingon.campingon.reservation.utils.ReservationValidate; import site.campingon.campingon.user.entity.User; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -138,8 +139,8 @@ void createReservationSuccess() { ReservationCreateRequestDto requestDto = new ReservationCreateRequestDto( mockCamp.getId(), mockCampSite.getId(), - LocalDateTime.now(), - LocalDateTime.now().plusDays(1), + LocalDate.now(), + LocalDate.now().plusDays(1), 2, 50000 ); @@ -189,8 +190,8 @@ void createReservationUserNotFound() { ReservationCreateRequestDto requestDto = new ReservationCreateRequestDto( mockCamp.getId(), mockCampSite.getId(), - LocalDateTime.now(), - LocalDateTime.now().plusDays(1), + LocalDate.now(), + LocalDate.now().plusDays(1), 2, 50000 ); @@ -212,8 +213,8 @@ void createReservationCampSiteNotFound() { ReservationCreateRequestDto requestDto = new ReservationCreateRequestDto( mockCamp.getId(), mockCampSite.getId(), - LocalDateTime.now(), - LocalDateTime.now().plusDays(1), + LocalDate.now(), + LocalDate.now().plusDays(1), 2, 50000 ); diff --git a/src/test/java/site/campingon/campingon/reservation/service/ReservationStatusUpdateTest.java b/src/test/java/site/campingon/campingon/reservation/service/ReservationStatusUpdateTest.java new file mode 100644 index 00000000..cfaef686 --- /dev/null +++ b/src/test/java/site/campingon/campingon/reservation/service/ReservationStatusUpdateTest.java @@ -0,0 +1,100 @@ +package site.campingon.campingon.reservation.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import site.campingon.campingon.camp.entity.Camp; +import site.campingon.campingon.camp.entity.CampSite; +import site.campingon.campingon.camp.entity.Induty; +import site.campingon.campingon.reservation.entity.Reservation; +import site.campingon.campingon.reservation.entity.ReservationStatus; +import site.campingon.campingon.reservation.repository.ReservationRepository; +import site.campingon.campingon.user.entity.User; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ReservationStatusUpdateTest { + + @Mock + private ReservationRepository reservationRepository; + + @InjectMocks + private ReservationStatusUpdate reservationStatusUpdate; + + @Mock + private Reservation reservation1; + @Mock + private Reservation reservation2; + + @BeforeEach + void setUp() { + when(reservation1.getStatus()).thenReturn(ReservationStatus.RESERVED); + when(reservation2.getStatus()).thenReturn(ReservationStatus.NOTCANCELABLE); + } + + @Test + @DisplayName("갱신성공 - 예약불가상태로 갱신") + void testUpdateStatusToNotCancelable() { + + // Given - 밀리초 제거 필요 + LocalDateTime targetTime = LocalDateTime.now().plusDays(1).withHour(15).withMinute(0).withSecond(0).withNano(0); + List reservations = Arrays.asList(reservation1, reservation2); + + when(reservationRepository.findByCheckin(targetTime)).thenReturn(reservations); + + // 밀리초를 제거한 targetTime으로 설정 + when(reservationRepository.findByCheckin(targetTime)).thenReturn(reservations); + + // When + reservationStatusUpdate.updateStatusToNotCancelable(); + + // Then + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(reservationRepository, times(1)).saveAll(captor.capture()); + + List savedReservations = captor.getValue(); + assertEquals(2, savedReservations.size()); + verify(reservation1).changeStatus(ReservationStatus.NOTCANCELABLE); + verify(reservation2, never()).changeStatus(ReservationStatus.NOTCANCELABLE); + } + + @Test + @DisplayName("갱신성공 - 체크인완료상태로 갱신") + void testUpdateStatusToCompleted() { + + // Given - 밀리초 제거 필요 + LocalDateTime targetTime = LocalDateTime.now().withHour(15).withMinute(0).withSecond(0).withNano(0); + + // 상태를 변경할 수 있도록 설정 + when(reservation1.getStatus()).thenReturn(ReservationStatus.RESERVED); + when(reservation2.getStatus()).thenReturn(ReservationStatus.COMPLETED); + + List reservations = Arrays.asList(reservation1, reservation2); + + // 밀리초를 제거한 targetTime으로 설정 + when(reservationRepository.findByCheckin(targetTime)).thenReturn(reservations); + + // When + reservationStatusUpdate.updateStatusToCompleted(); + + // Then + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(reservationRepository, times(1)).saveAll(captor.capture()); + + List savedReservations = captor.getValue(); + assertEquals(2, savedReservations.size()); + verify(reservation1).changeStatus(ReservationStatus.COMPLETED); + verify(reservation2, never()).changeStatus(ReservationStatus.COMPLETED); + } +} \ No newline at end of file diff --git a/src/test/java/site/campingon/campingon/review/service/ReviewServiceTest.java b/src/test/java/site/campingon/campingon/review/service/ReviewServiceTest.java index b254a8dc..e5ab2835 100644 --- a/src/test/java/site/campingon/campingon/review/service/ReviewServiceTest.java +++ b/src/test/java/site/campingon/campingon/review/service/ReviewServiceTest.java @@ -83,15 +83,15 @@ void setUp() { .campSite(CampSite.builder() .siteType(CAR_SITE) .price(CAR_SITE.getPrice()) - .maximumPeople(CAR_SITE.getMaximum_people()) + .maximumPeople(CAR_SITE.getMaximumPeople()) .build()) .user(User.builder() .id(123L) .name("Mock User") .email("mockuser@example.com") .build()) - .checkIn(LocalDateTime.of(2024, 12, 1, 14, 0)) - .checkOut(LocalDateTime.of(2024, 12, 3, 11, 0)) + .checkin(LocalDateTime.of(2024, 12, 1, 14, 0)) + .checkout(LocalDateTime.of(2024, 12, 3, 11, 0)) .guestCnt(2) .status(ReservationStatus.COMPLETED) .totalPrice(160000)