From 7fd98284e34b5686a630d44b2c2a27c0d9ab58b8 Mon Sep 17 00:00:00 2001 From: "gt.park" Date: Thu, 10 Jul 2025 00:27:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?:sparkles:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=98=88=EC=95=BD=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservation/ReservationFacade.java | 7 +++++++ .../reservation/ReservationService.java | 12 ++++++++++++ .../reservation/ReservationController.java | 19 +++++++++++++++++++ .../dto/ReservationResponseDto.java | 2 +- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java b/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java index 2c793d54..7de97721 100644 --- a/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java +++ b/src/main/java/spring/fitlinkbe/application/reservation/ReservationFacade.java @@ -79,6 +79,13 @@ public List getReservations(LocalDate date, SecurityUser user) { return List.of(); } + public List getTrainerReservations(LocalDate date, SecurityUser user) { + ConnectingInfo connectingInfo = memberService.getConnectingInfo(user.getMemberId()); + Trainer trainer = connectingInfo.getTrainer(); + + return reservationService.getTrainerReservations(date, trainer.getTrainerId()); + } + public ReservationResult.ReservationDetail getReservationDetail(Long reservationId) { diff --git a/src/main/java/spring/fitlinkbe/domain/reservation/ReservationService.java b/src/main/java/spring/fitlinkbe/domain/reservation/ReservationService.java index 35ba2478..2dc6faeb 100644 --- a/src/main/java/spring/fitlinkbe/domain/reservation/ReservationService.java +++ b/src/main/java/spring/fitlinkbe/domain/reservation/ReservationService.java @@ -7,6 +7,7 @@ import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import spring.fitlinkbe.domain.common.enums.UserRole; import spring.fitlinkbe.domain.common.exception.CustomException; import spring.fitlinkbe.domain.common.exception.ErrorCode; import spring.fitlinkbe.domain.producer.EventTopic; @@ -15,6 +16,7 @@ import spring.fitlinkbe.domain.reservation.strategy.cancel.ReservationCancelStrategy; import spring.fitlinkbe.domain.trainer.Trainer; import spring.fitlinkbe.support.security.SecurityUser; +import spring.fitlinkbe.support.utils.DateUtils; import java.time.LocalDate; import java.time.LocalDateTime; @@ -51,6 +53,16 @@ public List getReservations(ReservationCommand.GetReservations comm .toList(); } + public List getTrainerReservations(LocalDate date, Long trainerId) { + LocalDateTime startDate = date.atStartOfDay(); + LocalDateTime endDate = DateUtils.getTwoWeekAfterDate(startDate); + List reservations = reservationRepository.getReservations(UserRole.TRAINER, trainerId); + + return reservations.stream() + .filter(reservation -> reservation.isReservationInRange(startDate, endDate)) + .toList(); + } + public Reservation getReservation(Long reservationId) { return reservationRepository.getReservation(reservationId) .orElseThrow(() -> new CustomException(ErrorCode.RESERVATION_NOT_FOUND, diff --git a/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/ReservationController.java b/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/ReservationController.java index 4cef5d5a..136c7a11 100644 --- a/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/ReservationController.java +++ b/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/ReservationController.java @@ -47,6 +47,25 @@ public ApiResultResponse> getReservations(@ .toList()); } + /** + * 트레이너 예약 목록 조회 + * + * @param date 예약 정보를 받고 싶은 date 정보 + * @param user 인증된 유저 정보 + * @return ApiResultResponse 예약 목록을 반환한다. + */ + @RoleCheck(allowedRoles = {UserRole.MEMBER}) + @GetMapping("/trainers") + public ApiResultResponse> getTrainerReservations(@RequestParam LocalDate date, + @Login SecurityUser user) { + + List result = reservationFacade.getTrainerReservations(date, user); + + return ApiResultResponse.ok(result.stream() + .map(ReservationResponseDto.Summary::of) + .toList()); + } + /** * 예약 상세 조회 * diff --git a/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/dto/ReservationResponseDto.java b/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/dto/ReservationResponseDto.java index 83d78258..f5b32a5f 100644 --- a/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/dto/ReservationResponseDto.java +++ b/src/main/java/spring/fitlinkbe/interfaces/controller/reservation/dto/ReservationResponseDto.java @@ -57,7 +57,7 @@ public static ReservationResponseDto.Summary of(Reservation reservation) { .build(); } - private record MemberInfo(Long memberId, String name) { + public record MemberInfo(Long memberId, String name) { } } From b6920bf3046e82b623a1c22f9a1fabf4639ad326 Mon Sep 17 00:00:00 2001 From: "gt.park" Date: Thu, 10 Jul 2025 00:28:16 +0900 Subject: [PATCH 2/2] =?UTF-8?q?:white=5Fcheck=5Fmark:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=84=88=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationIntegrationTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java b/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java index c402be5c..ee5f28a0 100644 --- a/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java +++ b/src/test/java/spring/fitlinkbe/integration/ReservationIntegrationTest.java @@ -353,6 +353,106 @@ void getReservationsWithMemberIn1MonthWithEmptyDate() { } + @Nested + @DisplayName("트레이너 예약 목록 조회 Integration TEST") + class GetTrainerReservationsIntegrationTest { + @Test + @DisplayName("멤버가 트레이너의 예약 목록 조회 성공") + void GetTrainerReservations() { + // given + Map params = new HashMap<>(); + params.put("date", LocalDate.now().toString()); + + Trainer trainer = trainerRepository.getTrainerInfo(1L).orElseThrow(); + Member member = memberRepository.getMember(1L).orElseThrow(); + + LocalDate dayOffDate = LocalDate.now().plusDays(1); + + DayOff dayOff1 = DayOff.builder() + .trainer(trainer) + .dayOffDate(LocalDate.now().plusDays(1)) + .build(); + + DayOff dayOff2 = DayOff.builder() + .trainer(trainer) + .dayOffDate(LocalDate.now().plusDays(2)) + .build(); + + trainerRepository.saveDayOff(dayOff1); + trainerRepository.saveDayOff(dayOff2); + + ConnectingInfo connectingInfo = ConnectingInfo.builder() + .trainer(trainer) + .member(member) + .status(ConnectingInfo.ConnectingStatus.CONNECTED) + .build(); + + connectingInfoRepository.save(connectingInfo); + // 예약 불가 설정1 + Reservation disabledReservation1 = Reservation.builder() + .reservationDates(List.of(dayOffDate.atStartOfDay())) + .trainer(trainer) + .isDayOff(true) + .status(DISABLED_TIME_RESERVATION) + .createdAt(LocalDateTime.now().plusSeconds(2)) + .build(); + + reservationRepository.saveReservation(disabledReservation1); + + // 예약 불가 설정2 + Reservation disabledReservation2 = Reservation.builder() + .reservationDates(List.of(dayOffDate.atStartOfDay())) + .trainer(trainer) + .isDayOff(true) + .status(DISABLED_TIME_RESERVATION) + .createdAt(LocalDateTime.now().plusDays(3)) + .build(); + + reservationRepository.saveReservation(disabledReservation2); + + // 새로운 회원 생성 + Member newMember = testDataHandler.createMember(); + SessionInfo sessionInfo = testDataHandler.createSessionInfo(newMember, trainer); + + // 새로운 회원 예약 생성 + Reservation confirmedReservation = Reservation.builder() + .trainer(trainer) + .member(newMember) + .sessionInfo(sessionInfo) + .reservationDates(List.of(LocalDateTime.now().plusDays(4))) + .status(RESERVATION_APPROVED) + .confirmDate(LocalDateTime.now().plusDays(4)) + .createdAt(LocalDateTime.now().plusSeconds(3)) + .build(); + + reservationRepository.saveReservation(confirmedReservation).orElseThrow(); + + PersonalDetail personalDetail = personalDetailRepository.getMemberDetail(1L) + .orElseThrow(); + + String accessToken = tokenProvider.createAccessToken(PersonalDetail.Status.NORMAL, + personalDetail.getPersonalDetailId(), personalDetail.getUserRole()); + + // when + ExtractableResponse result = get(LOCAL_HOST + port + PATH + "/trainers", params, accessToken); + + // then + assertSoftly(softly -> { + softly.assertThat(result.statusCode()).isEqualTo(200); + List content = result.body().jsonPath() + .getList("data", ReservationResponseDto.Summary.class); + softly.assertThat(content.size()).isEqualTo(3); + softly.assertThat(content.get(0).isDayOff()).isTrue(); + softly.assertThat(content.get(2).memberInfo().memberId()).isNotEqualTo(member.getMemberId()); + + // 휴무일 잘 등록됐는지 확인 + List dayOffs = trainerRepository.findScheduledDayOff(1L); + softly.assertThat(dayOffs.size()).isEqualTo(2); + }); + } + + } + @Nested @DisplayName("예약 상세 조회 Integration TEST") class GetReservationDetailIntegrationTest {