diff --git a/back/src/main/java/com/back/domain/member/member/service/MemberStorage.java b/back/src/main/java/com/back/domain/member/member/service/MemberStorage.java index 76b73197..c7d44a1c 100644 --- a/back/src/main/java/com/back/domain/member/member/service/MemberStorage.java +++ b/back/src/main/java/com/back/domain/member/member/service/MemberStorage.java @@ -10,8 +10,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.Optional; - @Component @RequiredArgsConstructor public class MemberStorage { @@ -29,9 +27,6 @@ public Mentor findMentorByMemberId(Long memberId) { return mentorRepository.findByMemberIdWithMember(memberId) .orElseThrow(() -> new ServiceException(MemberErrorCode.NOT_FOUND_MENTOR)); } - public Optional findMentorByMemberOptional(Member member) { - return mentorRepository.findByMemberIdWithMember(member.getId()); - } public Mentee findMenteeByMember(Member member) { return menteeRepository.findByMemberIdWithMember(member.getId()) diff --git a/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java b/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java index 64b65bc5..c2a87c85 100644 --- a/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java +++ b/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java @@ -31,4 +31,8 @@ public Mentee(Member member, Long jobId) { public void delete() { this.isDeleted = true; } + + public boolean isMember(Member member) { + return this.member.equals(member); + } } diff --git a/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java b/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java index fe961834..144b3775 100644 --- a/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java +++ b/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java @@ -43,4 +43,8 @@ public void updateCareerYears(Integer careerYears) { public void delete() { this.isDeleted = true; } + + public boolean isMember(Member member) { + return this.member.equals(member); + } } diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java index 9a4abd88..bdc44f91 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java @@ -11,13 +11,19 @@ public record MentoringWithTagsDto( @Schema(description = "멘토링 제목") String title, @Schema(description = "멘토링 태그") - List tags + List tags, + @Schema(description = "멘토 ID") + Long mentorId, + @Schema(description = "멘토 닉네임") + String nickname ) { public static MentoringWithTagsDto from(Mentoring mentoring) { return new MentoringWithTagsDto( mentoring.getId(), mentoring.getTitle(), - mentoring.getTags() + mentoring.getTags(), + mentoring.getMentor().getId(), + mentoring.getMentor().getMember().getNickname() ); } } diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/controller/ReservationController.java b/back/src/main/java/com/back/domain/mentoring/reservation/controller/ReservationController.java index 6fd2b52f..4053018f 100644 --- a/back/src/main/java/com/back/domain/mentoring/reservation/controller/ReservationController.java +++ b/back/src/main/java/com/back/domain/mentoring/reservation/controller/ReservationController.java @@ -4,7 +4,9 @@ import com.back.domain.member.member.service.MemberStorage; import com.back.domain.member.mentee.entity.Mentee; import com.back.domain.member.mentor.entity.Mentor; +import com.back.domain.mentoring.reservation.dto.ReservationDto; import com.back.domain.mentoring.reservation.dto.request.ReservationRequest; +import com.back.domain.mentoring.reservation.dto.response.ReservationPagingResponse; import com.back.domain.mentoring.reservation.dto.response.ReservationResponse; import com.back.domain.mentoring.reservation.service.ReservationService; import com.back.global.rq.Rq; @@ -13,11 +15,10 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import java.util.Optional; - @RestController @RequestMapping("/reservations") @RequiredArgsConstructor @@ -28,6 +29,39 @@ public class ReservationController { private final ReservationService reservationService; private final MemberStorage memberStorage; + @GetMapping + @Operation(summary = "나의 예약 목록 조회", description = "본인의 예약 목록을 조회합니다. 로그인 후 조회할 수 있습니다.") + public RsData getReservations( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size + ) { + Member member = rq.getActor(); + Page reservationPage = reservationService.getReservations(member, page, size); + ReservationPagingResponse resDto = ReservationPagingResponse.from(reservationPage); + + return new RsData<>( + "200", + "예약 목록을 조회하였습니다.", + resDto + ); + } + + + @GetMapping("/{reservationId}") + @Operation(summary = "예약 조회", description = "특정 예약을 조회합니다. 로그인 후 예약 조회할 수 있습니다.") + public RsData getReservation( + @PathVariable Long reservationId + ) { + Member member = rq.getActor(); + ReservationResponse resDto = reservationService.getReservation(member, reservationId); + + return new RsData<>( + "200", + "예약을 조회하였습니다.", + resDto + ); + } + @PostMapping @PreAuthorize("hasRole('MENTEE')") @Operation(summary = "예약 신청", description = "멘티가 멘토의 슬롯을 선택해 예약 신청을 합니다. 로그인한 멘티만 예약 신청할 수 있습니다.") @@ -35,7 +69,6 @@ public RsData createReservation( @RequestBody @Valid ReservationRequest reqDto ) { Mentee mentee = memberStorage.findMenteeByMember(rq.getActor()); - ReservationResponse resDto = reservationService.createReservation(mentee, reqDto); return new RsData<>( @@ -52,7 +85,6 @@ public RsData approveReservation( @PathVariable Long reservationId ) { Mentor mentor = memberStorage.findMentorByMember(rq.getActor()); - ReservationResponse resDto = reservationService.approveReservation(mentor, reservationId); return new RsData<>( @@ -69,7 +101,6 @@ public RsData rejectReservation( @PathVariable Long reservationId ) { Mentor mentor = memberStorage.findMentorByMember(rq.getActor()); - ReservationResponse resDto = reservationService.rejectReservation(mentor, reservationId); return new RsData<>( @@ -85,15 +116,7 @@ public RsData cancelReservation( @PathVariable Long reservationId ) { Member member = rq.getActor(); - ReservationResponse resDto; - - Optional mentor = memberStorage.findMentorByMemberOptional(member); - if (mentor.isPresent()) { - resDto = reservationService.cancelReservation(mentor.get(), reservationId); - } else { - Mentee mentee = memberStorage.findMenteeByMember(member); - resDto = reservationService.cancelReservation(mentee, reservationId); - } + ReservationResponse resDto = reservationService.cancelReservation(member, reservationId); return new RsData<>( "200", diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/dto/ReservationDto.java b/back/src/main/java/com/back/domain/mentoring/reservation/dto/ReservationDto.java new file mode 100644 index 00000000..8da541c5 --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/reservation/dto/ReservationDto.java @@ -0,0 +1,38 @@ +package com.back.domain.mentoring.reservation.dto; + +import com.back.domain.mentoring.reservation.constant.ReservationStatus; +import com.back.domain.mentoring.reservation.entity.Reservation; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.time.LocalDateTime; + +public record ReservationDto( + @Schema(description = "예약 ID") + Long reservationId, + @Schema(description = "예약 상태") + ReservationStatus status, + + @Schema(description = "멘토링 ID") + Long mentoringId, + @Schema(description = "멘토링 제목") + String title, + + @Schema(description = "멘토 슬롯 ID") + Long mentorSlotId, + @Schema(description = "시작 일시") + LocalDateTime startDateTime, + @Schema(description = "종료 일시") + LocalDateTime endDateTime +) { + public static ReservationDto from(Reservation reservation) { + return new ReservationDto( + reservation.getId(), + reservation.getStatus(), + reservation.getMentoring().getId(), + reservation.getMentoring().getTitle(), + reservation.getMentorSlot().getId(), + reservation.getMentorSlot().getStartDateTime(), + reservation.getMentorSlot().getEndDateTime() + ); + } +} diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/dto/response/ReservationPagingResponse.java b/back/src/main/java/com/back/domain/mentoring/reservation/dto/response/ReservationPagingResponse.java new file mode 100644 index 00000000..6e04d525 --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/reservation/dto/response/ReservationPagingResponse.java @@ -0,0 +1,30 @@ +package com.back.domain.mentoring.reservation.dto.response; + +import com.back.domain.mentoring.reservation.dto.ReservationDto; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.data.domain.Page; + +import java.util.List; + +public record ReservationPagingResponse( + @Schema(description = "예약 목록") + List reservations, + @Schema(description = "현재 페이지 (0부터 시작)") + int currentPage, + @Schema(description = "총 페이지") + int totalPage, + @Schema(description = "총 개수") + long totalElements, + @Schema(description = "다음 페이지 존재 여부") + boolean hasNext +) { + public static ReservationPagingResponse from(Page page) { + return new ReservationPagingResponse( + page.getContent(), + page.getNumber(), + page.getTotalPages(), + page.getTotalElements(), + page.hasNext() + ); + } +} diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/entity/Reservation.java b/back/src/main/java/com/back/domain/mentoring/reservation/entity/Reservation.java index 858b335b..773ec2fd 100644 --- a/back/src/main/java/com/back/domain/mentoring/reservation/entity/Reservation.java +++ b/back/src/main/java/com/back/domain/mentoring/reservation/entity/Reservation.java @@ -1,5 +1,6 @@ package com.back.domain.mentoring.reservation.entity; +import com.back.domain.member.member.entity.Member; import com.back.domain.member.mentee.entity.Mentee; import com.back.domain.member.mentor.entity.Mentor; import com.back.domain.mentoring.mentoring.entity.Mentoring; @@ -14,6 +15,13 @@ import lombok.NoArgsConstructor; @Entity +@Table( + name = "reservation", + indexes = { + @Index(name = "idx_reservation_mentor", columnList = "mentor_id"), + @Index(name = "idx_reservation_mentee", columnList = "mentee_id") + } +) @Getter @NoArgsConstructor public class Reservation extends BaseEntity { @@ -86,15 +94,10 @@ public void reject(Mentor mentor) { updateStatus(ReservationStatus.REJECTED); } - public void cancel(Mentor mentor) { - ensureMentor(mentor); - ensureCanCancel(); - ensureNotPast(); - updateStatus(ReservationStatus.CANCELED); - } - - public void cancel(Mentee mentee) { - ensureMentee(mentee); + public void cancel(Member member) { + if (!mentor.isMember(member) && !mentee.isMember(member) ) { + throw new ServiceException(ReservationErrorCode.FORBIDDEN_NOT_PARTICIPANT); + } ensureCanCancel(); ensureNotPast(); updateStatus(ReservationStatus.CANCELED); @@ -114,12 +117,6 @@ private void ensureMentor(Mentor mentor) { } } - private void ensureMentee(Mentee mentee) { - if (!isMentee(mentee)) { - throw new ServiceException(ReservationErrorCode.FORBIDDEN_NOT_MENTEE); - } - } - private void ensureCanApprove() { if(!this.status.canApprove()) { throw new ServiceException(ReservationErrorCode.CANNOT_APPROVE); diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/error/ReservationErrorCode.java b/back/src/main/java/com/back/domain/mentoring/reservation/error/ReservationErrorCode.java index dd54c5b4..f97e753e 100644 --- a/back/src/main/java/com/back/domain/mentoring/reservation/error/ReservationErrorCode.java +++ b/back/src/main/java/com/back/domain/mentoring/reservation/error/ReservationErrorCode.java @@ -20,6 +20,8 @@ public enum ReservationErrorCode implements ErrorCode { // 403 FORBIDDEN_NOT_MENTOR("403-1", "해당 예약에 대한 멘토 권한이 없습니다."), FORBIDDEN_NOT_MENTEE("403-2", "해당 예약에 대한 멘티 권한이 없습니다."), + FORBIDDEN_NOT_PARTICIPANT("403-3", "해당 예약에 대한 권한이 없습니다"), + RESERVATION_NOT_ACCESSIBLE("403-4", "예약을 찾을 수 없거나 권한이 없습니다"), // 404 NOT_FOUND_RESERVATION("404-1", "예약이 존재하지 않습니다."), diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/repository/ReservationRepository.java b/back/src/main/java/com/back/domain/mentoring/reservation/repository/ReservationRepository.java index a0b9ad50..39fb5bfe 100644 --- a/back/src/main/java/com/back/domain/mentoring/reservation/repository/ReservationRepository.java +++ b/back/src/main/java/com/back/domain/mentoring/reservation/repository/ReservationRepository.java @@ -1,8 +1,13 @@ package com.back.domain.mentoring.reservation.repository; +import com.back.domain.member.member.entity.Member; import com.back.domain.mentoring.reservation.constant.ReservationStatus; import com.back.domain.mentoring.reservation.entity.Reservation; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; @@ -11,6 +16,40 @@ public interface ReservationRepository extends JpaRepository Optional findTopByOrderByIdDesc(); Optional findByMentorSlotIdAndStatusIn(Long mentorSlotId, List statuses); + @Query(""" + SELECT r + FROM Reservation r + WHERE r.id = :reservationId + AND (r.mentee.member = :member + OR r.mentor.member = :member) + """) + Optional findByIdAndMember( + @Param("reservationId") Long reservationId, + @Param("member") Member member + ); + + @Query(""" + SELECT r + FROM Reservation r + WHERE r.mentor.member = :member + ORDER BY r.mentorSlot.startDateTime DESC + """) + Page findAllByMentorMember( + @Param("member") Member member, + Pageable pageable + ); + + @Query(""" + SELECT r + FROM Reservation r + WHERE r.mentee.member = :member + ORDER BY r.mentorSlot.startDateTime DESC + """) + Page findAllByMenteeMember( + @Param("member") Member member, + Pageable pageable + ); + boolean existsByMentoringId(Long mentoringId); /** diff --git a/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java b/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java index 03e310f8..86d68c2b 100644 --- a/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java +++ b/back/src/main/java/com/back/domain/mentoring/reservation/service/ReservationService.java @@ -1,10 +1,12 @@ package com.back.domain.mentoring.reservation.service; +import com.back.domain.member.member.entity.Member; import com.back.domain.member.mentee.entity.Mentee; import com.back.domain.member.mentor.entity.Mentor; import com.back.domain.mentoring.mentoring.entity.Mentoring; import com.back.domain.mentoring.mentoring.service.MentoringStorage; import com.back.domain.mentoring.reservation.constant.ReservationStatus; +import com.back.domain.mentoring.reservation.dto.ReservationDto; import com.back.domain.mentoring.reservation.dto.request.ReservationRequest; import com.back.domain.mentoring.reservation.dto.response.ReservationResponse; import com.back.domain.mentoring.reservation.entity.Reservation; @@ -15,6 +17,9 @@ import com.back.global.exception.ServiceException; import jakarta.persistence.OptimisticLockException; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,6 +33,28 @@ public class ReservationService { private final ReservationRepository reservationRepository; private final MentoringStorage mentoringStorage; + @Transactional(readOnly = true) + public Page getReservations(Member member, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + + Page reservations; + + if (member.getRole() == Member.Role.MENTOR) { + reservations = reservationRepository.findAllByMentorMember(member, pageable); + } else { + reservations = reservationRepository.findAllByMenteeMember(member, pageable); + } + return reservations.map(ReservationDto::from); + } + + @Transactional(readOnly = true) + public ReservationResponse getReservation(Member member, Long reservationId) { + Reservation reservation = reservationRepository.findByIdAndMember(reservationId, member) + .orElseThrow(() -> new ServiceException(ReservationErrorCode.RESERVATION_NOT_ACCESSIBLE)); + + return ReservationResponse.from(reservation); + } + @Transactional public ReservationResponse createReservation(Mentee mentee, ReservationRequest reqDto) { try { @@ -78,16 +105,9 @@ public ReservationResponse rejectReservation(Mentor mentor, Long reservationId) } @Transactional - public ReservationResponse cancelReservation(Mentor mentor, Long reservationId) { - Reservation reservation = mentoringStorage.findReservation(reservationId); - reservation.cancel(mentor); - return ReservationResponse.from(reservation); - } - - @Transactional - public ReservationResponse cancelReservation(Mentee mentee, Long reservationId) { + public ReservationResponse cancelReservation(Member member, Long reservationId) { Reservation reservation = mentoringStorage.findReservation(reservationId); - reservation.cancel(mentee); + reservation.cancel(member); return ReservationResponse.from(reservation); } diff --git a/back/src/test/java/com/back/domain/mentoring/reservation/entity/ReservationTest.java b/back/src/test/java/com/back/domain/mentoring/reservation/entity/ReservationTest.java index e4643496..02e2dd79 100644 --- a/back/src/test/java/com/back/domain/mentoring/reservation/entity/ReservationTest.java +++ b/back/src/test/java/com/back/domain/mentoring/reservation/entity/ReservationTest.java @@ -34,16 +34,16 @@ class ReservationTest { @BeforeEach void setUp() { - Member mentorMember = MemberFixture.create("mentor@test.com", "Mentor", "pass123"); + Member mentorMember = MemberFixture.create(1L, "mentor@test.com", "Mentor", "pass123", Member.Role.MENTOR); mentor = MentorFixture.create(1L, mentorMember); - Member otherMentorMember = MemberFixture.create("other@test.com", "Other", "pass123"); + Member otherMentorMember = MemberFixture.create(2L, "other@test.com", "Other", "pass123", Member.Role.MENTOR); otherMentor = MentorFixture.create(2L, otherMentorMember); - Member menteeMember = MemberFixture.create("mentee@test.com", "Mentee", "pass123"); + Member menteeMember = MemberFixture.create(3L, "mentee@test.com", "Mentee", "pass123", Member.Role.MENTEE); mentee = MenteeFixture.create(1L, menteeMember); - Member otherMenteeMember = MemberFixture.create("other_mentee@test.com", "OtherMentee", "pass123"); + Member otherMenteeMember = MemberFixture.create(4L, "other_mentee@test.com", "OtherMentee", "pass123", Member.Role.MENTEE); otherMentee = MenteeFixture.create(2L, otherMenteeMember); mentoring = MentoringFixture.create(1L, mentor); @@ -145,7 +145,7 @@ class Describe_cancelByMentor { @DisplayName("취소 성공 - PENDING") void cancelPending() { // when - reservation.cancel(mentor); + reservation.cancel(mentor.getMember()); // then assertThat(reservation.getStatus()).isEqualTo(ReservationStatus.CANCELED); @@ -160,7 +160,7 @@ void cancelApproved() { reservation.approve(mentor); // when - reservation.cancel(mentor); + reservation.cancel(mentor.getMember()); // then assertThat(reservation.getStatus()).isEqualTo(ReservationStatus.CANCELED); @@ -176,7 +176,7 @@ void throwExceptionWhenCannotCancel() { ReflectionTestUtils.setField(reservation, "status", ReservationStatus.COMPLETED); // when & then - assertThatThrownBy(() -> reservation.cancel(mentor)) + assertThatThrownBy(() -> reservation.cancel(mentor.getMember())) .isInstanceOf(ServiceException.class) .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.CANNOT_CANCEL.getCode()); } @@ -190,7 +190,7 @@ class Describe_cancelByMentee { @DisplayName("취소 성공 - PENDING") void cancelPending() { // when - reservation.cancel(mentee); + reservation.cancel(mentee.getMember()); // then assertThat(reservation.getStatus()).isEqualTo(ReservationStatus.CANCELED); @@ -205,7 +205,7 @@ void cancelApproved() { reservation.approve(mentor); // when - reservation.cancel(mentee); + reservation.cancel(mentee.getMember()); // then assertThat(reservation.getStatus()).isEqualTo(ReservationStatus.CANCELED); @@ -216,9 +216,9 @@ void cancelApproved() { @Test @DisplayName("다른 멘티가 취소하려고 하면 예외") void throwExceptionWhenNotMentee() { - assertThatThrownBy(() -> reservation.cancel(otherMentee)) + assertThatThrownBy(() -> reservation.cancel(otherMentee.getMember())) .isInstanceOf(ServiceException.class) - .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.FORBIDDEN_NOT_MENTEE.getCode()); + .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.FORBIDDEN_NOT_PARTICIPANT.getCode()); } @Test @@ -229,7 +229,7 @@ void throwExceptionWhenCannotCancel() { ReflectionTestUtils.setField(reservation, "status", ReservationStatus.COMPLETED); // when & then - assertThatThrownBy(() -> reservation.cancel(mentee)) + assertThatThrownBy(() -> reservation.cancel(mentee.getMember())) .isInstanceOf(ServiceException.class) .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.CANNOT_CANCEL.getCode()); } diff --git a/back/src/test/java/com/back/domain/mentoring/reservation/service/ReservationServiceTest.java b/back/src/test/java/com/back/domain/mentoring/reservation/service/ReservationServiceTest.java index e429cad1..fff1a4f3 100644 --- a/back/src/test/java/com/back/domain/mentoring/reservation/service/ReservationServiceTest.java +++ b/back/src/test/java/com/back/domain/mentoring/reservation/service/ReservationServiceTest.java @@ -6,6 +6,7 @@ import com.back.domain.mentoring.mentoring.entity.Mentoring; import com.back.domain.mentoring.mentoring.service.MentoringStorage; import com.back.domain.mentoring.reservation.constant.ReservationStatus; +import com.back.domain.mentoring.reservation.dto.ReservationDto; import com.back.domain.mentoring.reservation.dto.request.ReservationRequest; import com.back.domain.mentoring.reservation.dto.response.ReservationResponse; import com.back.domain.mentoring.reservation.entity.Reservation; @@ -29,6 +30,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.test.util.ReflectionTestUtils; import java.time.LocalDateTime; @@ -61,13 +66,13 @@ class ReservationServiceTest { @BeforeEach void setUp() { - Member mentorMember = MemberFixture.create("mentor@test.com", "Mentor", "pass123"); + Member mentorMember = MemberFixture.create(1L, "mentor@test.com", "Mentor", "pass123", Member.Role.MENTOR); mentor = MentorFixture.create(1L, mentorMember); - Member menteeMember = MemberFixture.create("mentee@test.com", "Mentee", "pass123"); + Member menteeMember = MemberFixture.create(2L, "mentee@test.com", "Mentee", "pass123", Member.Role.MENTEE); mentee = MenteeFixture.create(1L, menteeMember); - Member menteeMember2 = MemberFixture.create("mentee2@test.com", "Mentee2", "pass123"); + Member menteeMember2 = MemberFixture.create(3L, "mentee2@test.com", "Mentee2", "pass123", Member.Role.MENTEE); mentee2 = MenteeFixture.create(2L, menteeMember2); mentoring = MentoringFixture.create(1L, mentor); @@ -76,6 +81,84 @@ void setUp() { reservation = ReservationFixture.create(1L, mentoring, mentee, mentorSlot2); } + @Nested + @DisplayName("멘토링 예약 목록 조회") + class Describe_getReservations { + + @Test + void getReservations() { + // given + int page = 1; + int size = 5; + Pageable pageable = PageRequest.of(page, size); + + Page reservationPage = new PageImpl<>( + List.of(reservation), + pageable, + 10 + ); + + when(reservationRepository.findAllByMentorMember(mentor.getMember(), pageable)) + .thenReturn(reservationPage); + + // when + Page result = reservationService.getReservations( + mentor.getMember(), + page, + size + ); + + // then + assertThat(result.getNumber()).isEqualTo(1); + assertThat(result.getSize()).isEqualTo(5); + assertThat(result.getTotalElements()).isEqualTo(10); + assertThat(result.getTotalPages()).isEqualTo(2); + verify(reservationRepository).findAllByMentorMember(mentor.getMember(), pageable); + } + } + + @Nested + @DisplayName("멘토링 예약 조회") + class Describe_getReservation { + + @Test + void getReservation() { + // given + Long reservationId = reservation.getId(); + + when(reservationRepository.findByIdAndMember(reservationId, mentor.getMember())) + .thenReturn(Optional.of(reservation)); + + // when + ReservationResponse response = reservationService.getReservation( + mentor.getMember(), + reservationId + ); + + // then + assertThat(response).isNotNull(); + assertThat(response.mentoring().mentoringId()).isEqualTo(mentoring.getId()); + assertThat(response.mentee().menteeId()).isEqualTo(mentee.getId()); + assertThat(response.mentor().mentorId()).isEqualTo(mentor.getId()); + assertThat(response.reservation().mentorSlotId()).isEqualTo(mentorSlot2.getId()); + verify(reservationRepository).findByIdAndMember(reservationId, mentor.getMember()); + } + + @Test + @DisplayName("권한이 없을 경우 예외") + void getReservation_notAccessible() { + // given + when(reservationRepository.findByIdAndMember(reservation.getId(), mentee2.getMember())) + .thenReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> reservationService.getReservation(mentee2.getMember(), reservation.getId())) + .isInstanceOf(ServiceException.class) + .hasFieldOrPropertyWithValue("resultCode", + ReservationErrorCode.RESERVATION_NOT_ACCESSIBLE.getCode()); + } + } + @Nested @DisplayName("멘토링 예약 생성") class Describe_createReservation { @@ -339,7 +422,7 @@ void cancelReservationByMentor() { .thenReturn(reservation); // when - ReservationResponse result = reservationService.cancelReservation(mentor, reservation.getId()); + ReservationResponse result = reservationService.cancelReservation(mentor.getMember(), reservation.getId()); // then assertThat(result.reservation().status()).isEqualTo(ReservationStatus.CANCELED); @@ -353,7 +436,7 @@ void cancelReservationByMentee() { .thenReturn(reservation); // when - ReservationResponse result = reservationService.cancelReservation(mentee, reservation.getId()); + ReservationResponse result = reservationService.cancelReservation(mentee.getMember(), reservation.getId()); // then assertThat(result.reservation().status()).isEqualTo(ReservationStatus.CANCELED); @@ -376,7 +459,7 @@ void throwExceptionWhenCompleted() { .thenReturn(completedReservation); // when & then - assertThatThrownBy(() -> reservationService.cancelReservation(mentor, completedReservation.getId())) + assertThatThrownBy(() -> reservationService.cancelReservation(mentor.getMember(), completedReservation.getId())) .isInstanceOf(ServiceException.class) .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.CANNOT_CANCEL.getCode()); } @@ -391,9 +474,9 @@ void throwExceptionWhenNotMentor() { .thenReturn(reservation); // when & then - assertThatThrownBy(() -> reservationService.cancelReservation(anotherMentor, reservation.getId())) + assertThatThrownBy(() -> reservationService.cancelReservation(anotherMentor.getMember(), reservation.getId())) .isInstanceOf(ServiceException.class) - .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.FORBIDDEN_NOT_MENTOR.getCode()); + .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.FORBIDDEN_NOT_PARTICIPANT.getCode()); } @Test @@ -404,9 +487,9 @@ void throwExceptionWhenNotMentee() { .thenReturn(reservation); // when & then - assertThatThrownBy(() -> reservationService.cancelReservation(mentee2, reservation.getId())) + assertThatThrownBy(() -> reservationService.cancelReservation(mentee2.getMember(), reservation.getId())) .isInstanceOf(ServiceException.class) - .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.FORBIDDEN_NOT_MENTEE.getCode()); + .hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.FORBIDDEN_NOT_PARTICIPANT.getCode()); } } }