diff --git a/src/main/java/com/server/running_handai/domain/bookmark/controller/BookmarkController.java b/src/main/java/com/server/running_handai/domain/bookmark/controller/BookmarkController.java index a1ac4df..ab027f3 100644 --- a/src/main/java/com/server/running_handai/domain/bookmark/controller/BookmarkController.java +++ b/src/main/java/com/server/running_handai/domain/bookmark/controller/BookmarkController.java @@ -1,6 +1,8 @@ package com.server.running_handai.domain.bookmark.controller; +import com.server.running_handai.domain.bookmark.dto.BookmarkedCourseInfoDto; import com.server.running_handai.domain.bookmark.service.BookmarkService; +import com.server.running_handai.domain.course.entity.Area; import com.server.running_handai.global.oauth.CustomOAuth2User; import com.server.running_handai.global.response.CommonResponse; import com.server.running_handai.global.response.ResponseCode; @@ -9,20 +11,21 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequiredArgsConstructor -@RequestMapping("/api/courses/{courseId}/bookmarks") @Tag(name = "Bookmark", description = "북마크 관련 API") public class BookmarkController { @@ -35,7 +38,7 @@ public class BookmarkController { @ApiResponse(responseCode = "400", description = "실패 (이미 북마크한 코스)"), @ApiResponse(responseCode = "401", description = "실패 (인증 실패)") }) - @PostMapping + @PostMapping("/api/courses/{courseId}/bookmarks") public ResponseEntity> registerBookmark( @Parameter(description = "북마크 대상 코스", required = true) @PathVariable Long courseId, @AuthenticationPrincipal CustomOAuth2User customOAuth2User @@ -54,7 +57,7 @@ public ResponseEntity> registerBookmark( @ApiResponse(responseCode = "404", description = "실패 (존재하지 않는 북마크)"), @ApiResponse(responseCode = "401", description = "실패 (인증 실패)") }) - @DeleteMapping + @DeleteMapping("/api/courses/{courseId}/bookmarks") public ResponseEntity> deleteBookmark( @Parameter(description = "북마크 대상 코스", required = true) @PathVariable Long courseId, @AuthenticationPrincipal CustomOAuth2User customOAuth2User @@ -66,4 +69,25 @@ public ResponseEntity> deleteBookmark( return ResponseEntity.ok(CommonResponse.success(ResponseCode.SUCCESS_BOOKMARK_DELETE, null)); } + @Operation(summary = "북마크한 코스 조회", description = "회원이 북마크한 코스를 조회합니다. 코스의 지역으로 조건 검색이 가능합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "성공"), + @ApiResponse(responseCode = "200", description = "성공 (북마크한 코스 없음)"), + @ApiResponse(responseCode = "400", description = "실패 (요청 파라미터 오류)"), + @ApiResponse(responseCode = "401", description = "실패 (인증 실패)") + }) + @GetMapping("/api/members/me/courses/bookmarks") + public ResponseEntity> getBookmarkedCourses( + @Parameter(description = "지역 조건 (전체 보기인 경우 null)") + @RequestParam(required = false) Area area, + @AuthenticationPrincipal CustomOAuth2User customOAuth2User + ) { + Long memberId = customOAuth2User.getMember().getId(); + log.info("[북마크한 코스 조회] memberId={}, area={}", memberId, (area != null) ? area.name() : null); + List responseData = bookmarkService.getBookmarkedCoursesByMemberAndArea(memberId, area); + if (responseData.isEmpty()) { + return ResponseEntity.ok(CommonResponse.success(ResponseCode.SUCCESS_EMPTY_BOOKMARKS, responseData)); + } + return ResponseEntity.ok(CommonResponse.success(ResponseCode.SUCCESS, responseData)); + } } diff --git a/src/main/java/com/server/running_handai/domain/bookmark/dto/BookmarkedCourseInfoDto.java b/src/main/java/com/server/running_handai/domain/bookmark/dto/BookmarkedCourseInfoDto.java new file mode 100644 index 0000000..fd80ca6 --- /dev/null +++ b/src/main/java/com/server/running_handai/domain/bookmark/dto/BookmarkedCourseInfoDto.java @@ -0,0 +1,32 @@ +package com.server.running_handai.domain.bookmark.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonPropertyOrder({ + "bookmarkId", + "courseId", + "thumbnailUrl", + "distance", + "duration", + "maxElevation", + "isBookmarked", + "bookmarkCount" +}) +public interface BookmarkedCourseInfoDto { + long getBookmarkId(); + long getCourseId(); + String getThumbnailUrl(); + double getDistance(); + int getDuration(); + + @JsonIgnore + double getRawMaxElevation(); // JPA 전용 + + default int getMaxElevation() { // 클라이언트 전용 + return (int) getRawMaxElevation(); + } + + boolean getIsBookmarked(); + int getBookmarkCount(); +} diff --git a/src/main/java/com/server/running_handai/domain/bookmark/repository/BookmarkRepository.java b/src/main/java/com/server/running_handai/domain/bookmark/repository/BookmarkRepository.java index 25a999d..724a8cd 100644 --- a/src/main/java/com/server/running_handai/domain/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/com/server/running_handai/domain/bookmark/repository/BookmarkRepository.java @@ -1,7 +1,9 @@ package com.server.running_handai.domain.bookmark.repository; +import com.server.running_handai.domain.bookmark.dto.BookmarkedCourseInfoDto; import com.server.running_handai.domain.bookmark.entity.Bookmark; import com.server.running_handai.domain.course.dto.BookmarkCountDto; +import com.server.running_handai.domain.course.entity.Area; import com.server.running_handai.domain.course.entity.Course; import com.server.running_handai.domain.member.entity.Member; import java.util.List; @@ -30,8 +32,44 @@ public interface BookmarkRepository extends JpaRepository { + "GROUP BY b.course.id") List countByCourseIdIn(@Param("courseIds") List courseIds); - // 사용자가 북마크한 코스 ID 목록 조회 + // 코스 목록 중에서 사용자가 북마크한 코스 조회 @Query("SELECT b.course.id FROM Bookmark b WHERE b.course.id IN :courseIds AND b.member.id = :memberId") Set findBookmarkedCourseIdsByMember(@Param("courseIds") List courseIds, @Param("memberId") Long memberId); + // 사용자가 북마크한 모든 코스 조회 + @Query("SELECT " + + "b.id AS bookmarkId, " + + "c.id AS courseId, " + + "ci.imgUrl AS thumbnailUrl, " + + "c.distance AS distance, " + + "c.duration AS duration, " + + "c.maxElevation AS rawMaxElevation, " + + "true AS isBookmarked, " + + "(SELECT count(b2.id) FROM Bookmark b2 WHERE b2.course.id = c.id) AS bookmarkCount " // 총 북마크 수 계산 + + "FROM Bookmark b " + + "LEFT JOIN b.course c " + + "LEFT JOIN c.courseImage ci " + + "WHERE b.member.id = :memberId " + + "ORDER BY b.createdAt DESC " + ) + List findBookmarkedCoursesByMemberId(Long memberId); + + // 사용자가 북마크한 코스 중에서 특정 지역의 코스만 조회 + @Query("SELECT " + + "b.id AS bookmarkId, " + + "c.id AS courseId, " + + "ci.imgUrl AS thumbnailUrl, " + + "c.distance AS distance, " + + "c.duration AS duration, " + + "c.maxElevation AS rawMaxElevation, " + + "true AS isBookmarked, " + + "(SELECT count(b2.id) FROM Bookmark b2 WHERE b2.course.id = c.id) AS bookmarkCount " // 총 북마크 수 계산 + + "FROM Bookmark b " + + "LEFT JOIN b.course c " + + "LEFT JOIN c.courseImage ci " + + "WHERE b.member.id = :memberId " + + "AND c.area = :area " + + "ORDER BY b.createdAt DESC " + ) + List findBookmarkedCoursesByMemberIdAndArea(Long memberId, Area area); } diff --git a/src/main/java/com/server/running_handai/domain/bookmark/service/BookmarkService.java b/src/main/java/com/server/running_handai/domain/bookmark/service/BookmarkService.java index 12b8891..e1fe5c7 100644 --- a/src/main/java/com/server/running_handai/domain/bookmark/service/BookmarkService.java +++ b/src/main/java/com/server/running_handai/domain/bookmark/service/BookmarkService.java @@ -1,19 +1,24 @@ package com.server.running_handai.domain.bookmark.service; +import com.server.running_handai.domain.bookmark.dto.BookmarkedCourseInfoDto; import com.server.running_handai.domain.bookmark.entity.Bookmark; import com.server.running_handai.domain.bookmark.repository.BookmarkRepository; +import com.server.running_handai.domain.course.entity.Area; import com.server.running_handai.domain.course.entity.Course; import com.server.running_handai.domain.course.repository.CourseRepository; import com.server.running_handai.global.response.ResponseCode; import com.server.running_handai.global.response.exception.BusinessException; import com.server.running_handai.domain.member.entity.Member; import com.server.running_handai.domain.member.repository.MemberRepository; +import java.util.Collections; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class BookmarkService { private final BookmarkRepository bookmarkRepository; @@ -53,4 +58,25 @@ public void deleteBookmark(Long memberId, Long courseId) { // 북마크 삭제 bookmarkRepository.delete(bookmark); } + + /** + * 회원이 북마크한 코스를 조회합니다. + * + * @param memberId 요청한 회원의 ID + * @return 북마크한 코스 정보 DTO + */ + public List getBookmarkedCoursesByMemberAndArea(Long memberId, Area area) { + List bookmarkedCourseInfoDtos; + if (area == null) { // 지역 전체인 경우 + bookmarkedCourseInfoDtos = bookmarkRepository.findBookmarkedCoursesByMemberId(memberId); + } else { // 특정 지역 필터링한 경우 + bookmarkedCourseInfoDtos = bookmarkRepository.findBookmarkedCoursesByMemberIdAndArea(memberId, area); + } + + if (bookmarkedCourseInfoDtos.isEmpty()) { + return Collections.emptyList(); + } + + return bookmarkedCourseInfoDtos; + } } diff --git a/src/main/java/com/server/running_handai/domain/review/controller/ReviewController.java b/src/main/java/com/server/running_handai/domain/review/controller/ReviewController.java index a95ac8d..77bf32f 100644 --- a/src/main/java/com/server/running_handai/domain/review/controller/ReviewController.java +++ b/src/main/java/com/server/running_handai/domain/review/controller/ReviewController.java @@ -1,5 +1,6 @@ package com.server.running_handai.domain.review.controller; +import com.server.running_handai.domain.review.dto.MyReviewInfoDto; import com.server.running_handai.domain.review.dto.ReviewCreateResponseDto; import com.server.running_handai.domain.review.dto.ReviewInfoListDto; import com.server.running_handai.domain.review.dto.ReviewCreateRequestDto; @@ -14,9 +15,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -104,4 +105,22 @@ public ResponseEntity> deleteReview( return ResponseEntity.ok(CommonResponse.success(ResponseCode.SUCCESS, null)); } + @Operation(summary = "내 리뷰 조회", description = "회원이 작성한 리뷰를 조회합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "성공"), + @ApiResponse(responseCode = "200", description = "성공 (리뷰 없음)") + }) + @GetMapping("/api/me/reviews") + public ResponseEntity>> getMyReviews( + @AuthenticationPrincipal CustomOAuth2User customOAuth2User + ) { + List responseData = reviewService.getMyReviews(customOAuth2User.getMember().getId()); + + if (responseData.isEmpty()) { + return ResponseEntity.ok(CommonResponse.success(ResponseCode.SUCCESS_EMPTY_REVIEWS, responseData)); + } + + return ResponseEntity.ok(CommonResponse.success(ResponseCode.SUCCESS, responseData)); + } + } diff --git a/src/main/java/com/server/running_handai/domain/review/dto/MyReviewInfoDto.java b/src/main/java/com/server/running_handai/domain/review/dto/MyReviewInfoDto.java new file mode 100644 index 0000000..129edec --- /dev/null +++ b/src/main/java/com/server/running_handai/domain/review/dto/MyReviewInfoDto.java @@ -0,0 +1,39 @@ +package com.server.running_handai.domain.review.dto; + +import com.server.running_handai.domain.course.entity.Course; +import com.server.running_handai.domain.review.entity.Review; +import java.time.format.DateTimeFormatter; + +public record MyReviewInfoDto( + long reviewId, + long courseId, + String courseName, + String thumbnailUrl, + String area, + double distance, + int duration, + int maxElevation, + double stars, + String contents, + String createdAt +) { + public static MyReviewInfoDto from(Review review) { + Course course = review.getCourse(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String formattedCreatedAt = review.getCreatedAt().format(formatter); + + return new MyReviewInfoDto( + review.getId(), + course.getId(), + course.getName(), + (course.getCourseImage() != null) ? course.getCourseImage().getImgUrl() : null, + course.getArea().name(), + course.getDistance(), + course.getDuration(), + (int) course.getMaxElevation().doubleValue(), + review.getStars(), + review.getContents(), + formattedCreatedAt + ); + } +} diff --git a/src/main/java/com/server/running_handai/domain/review/repository/ReviewRepository.java b/src/main/java/com/server/running_handai/domain/review/repository/ReviewRepository.java index 1a2e6c2..10a1262 100644 --- a/src/main/java/com/server/running_handai/domain/review/repository/ReviewRepository.java +++ b/src/main/java/com/server/running_handai/domain/review/repository/ReviewRepository.java @@ -29,4 +29,13 @@ public interface ReviewRepository extends JpaRepository { */ @Query("SELECT AVG(r.stars) FROM Review r WHERE r.course.id = :courseId") Double findAverageStarsByCourseId(@Param("courseId") Long courseId); + + /** + * 특정 회원이 작성한 리뷰 조회 (연관 엔티티 동시 조회) + */ + @Query("SELECT r FROM Review r " + + "LEFT JOIN FETCH r.course c " + + "LEFT JOIN FETCH c.courseImage ci " + + "WHERE r.writer.id = :memberId") + List findReviewsWithDetailsByMemberId(@Param("memberId") Long memberId); } diff --git a/src/main/java/com/server/running_handai/domain/review/service/ReviewService.java b/src/main/java/com/server/running_handai/domain/review/service/ReviewService.java index 4e056d9..729961e 100644 --- a/src/main/java/com/server/running_handai/domain/review/service/ReviewService.java +++ b/src/main/java/com/server/running_handai/domain/review/service/ReviewService.java @@ -4,6 +4,7 @@ import com.server.running_handai.domain.course.repository.CourseRepository; import com.server.running_handai.domain.member.entity.Member; import com.server.running_handai.domain.member.repository.MemberRepository; +import com.server.running_handai.domain.review.dto.MyReviewInfoDto; import com.server.running_handai.domain.review.dto.ReviewCreateResponseDto; import com.server.running_handai.domain.review.dto.ReviewInfoDto; import com.server.running_handai.domain.review.dto.ReviewInfoListDto; @@ -14,8 +15,10 @@ import com.server.running_handai.domain.review.repository.ReviewRepository; import com.server.running_handai.global.response.ResponseCode; import com.server.running_handai.global.response.exception.BusinessException; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -163,4 +166,22 @@ public void deleteReview(Long reviewId, Long memberId) { reviewRepository.delete(review); } + /** + * 회원이 작성한 리뷰를 조회합니다. + * + * @param memberId 요청한 회원의 ID + * @return 내 리뷰 조회용 DTO + */ + public List getMyReviews(Long memberId) { + List reviews = reviewRepository.findReviewsWithDetailsByMemberId(memberId); + + if (reviews.isEmpty()) { + return Collections.emptyList(); + } + + return reviews.stream() + .map(MyReviewInfoDto::from) + .collect(Collectors.toList()); + } + } diff --git a/src/main/java/com/server/running_handai/global/response/ResponseCode.java b/src/main/java/com/server/running_handai/global/response/ResponseCode.java index 14e7aad..5642b7f 100644 --- a/src/main/java/com/server/running_handai/global/response/ResponseCode.java +++ b/src/main/java/com/server/running_handai/global/response/ResponseCode.java @@ -16,6 +16,7 @@ public enum ResponseCode { SUCCESS_BOOKMARK_CREATE(OK, "북마크 등록 완료했습니다."), SUCCESS_BOOKMARK_DELETE(OK, "북마크 해제 완료했습니다."), SUCCESS_EMPTY_REVIEWS(OK, "리뷰 조회 결과가 없습니다."), + SUCCESS_EMPTY_BOOKMARKS(OK, "북마크한 코스가 없습니다."), /** 비즈니스 에러 코드 */ // BAD_REQUEST (400) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d34d0f1..0984ea2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -91,6 +91,10 @@ external: durunubi: base-url: http://apis.data.go.kr/B551011/Durunubi service-key: ${DURUNUBI_SERVICE_KEY} + spot: + base-url: http://apis.data.go.kr/B551011/KorService2 + service-key: ${SPOT_SERVICE_KEY} + radius: 50000 # [국문 관광정보] 위치기반 관광정보 조회 API 거리 반경 (50000m = 5km) springdoc: default-produces-media-type: application/json diff --git a/src/test/java/com/server/running_handai/domain/bookmark/service/BookmarkServiceTest.java b/src/test/java/com/server/running_handai/domain/bookmark/service/BookmarkServiceTest.java index 55d3a58..76d9af8 100644 --- a/src/test/java/com/server/running_handai/domain/bookmark/service/BookmarkServiceTest.java +++ b/src/test/java/com/server/running_handai/domain/bookmark/service/BookmarkServiceTest.java @@ -4,25 +4,46 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import com.server.running_handai.domain.bookmark.dto.BookmarkedCourseInfoDto; import com.server.running_handai.domain.bookmark.entity.Bookmark; import com.server.running_handai.domain.bookmark.repository.BookmarkRepository; +import com.server.running_handai.domain.course.entity.Area; import com.server.running_handai.domain.course.entity.Course; +import com.server.running_handai.domain.course.entity.CourseLevel; import com.server.running_handai.domain.course.repository.CourseRepository; import com.server.running_handai.global.response.ResponseCode; import com.server.running_handai.global.response.exception.BusinessException; import com.server.running_handai.domain.member.entity.Member; import com.server.running_handai.domain.member.repository.MemberRepository; +import io.swagger.v3.oas.annotations.Parameter; +import java.util.Collections; +import java.util.List; import java.util.Optional; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.util.ReflectionTestUtils; @ActiveProfiles("test") @ExtendWith(MockitoExtension.class) @@ -192,4 +213,78 @@ void deleteBookmark_fail_courseNotFound() { // then assertThat(exception.getResponseCode()).isEqualTo(ResponseCode.COURSE_NOT_FOUND); } + + @Nested + @DisplayName("북마크한 코스 조회 테스트") + class GetBookmarkedCourseTest { + + private static Stream provideAreaArguments() { + return Stream.of( + Arguments.of((Object) null), + Arguments.of(Area.HAEUN_GWANGAN) + ); + } + + @ParameterizedTest + @MethodSource("provideAreaArguments") + @DisplayName("북마크한 코스 조회 성공 - 결과 있음") + void getBookmarkedCourse_success_hasContent(Area area) { + // given + Long memberId = 1L; + BookmarkedCourseInfoDto dto1 = mock(BookmarkedCourseInfoDto.class); + BookmarkedCourseInfoDto dto2 = mock(BookmarkedCourseInfoDto.class); + List expectedDtos = List.of(dto1, dto2); + + if (area == null) { + given(bookmarkRepository.findBookmarkedCoursesByMemberId(memberId)).willReturn(expectedDtos); + } else { + given(bookmarkRepository.findBookmarkedCoursesByMemberIdAndArea(memberId, area)).willReturn(expectedDtos); + } + + // when + List actualDtos = + bookmarkService.getBookmarkedCoursesByMemberAndArea(memberId, area); + + // then + assertThat(actualDtos.size()).isEqualTo(expectedDtos.size()); + assertThat(actualDtos).isEqualTo(expectedDtos); + + if (area == null) { + verify(bookmarkRepository).findBookmarkedCoursesByMemberId(memberId); + verify(bookmarkRepository, never()).findBookmarkedCoursesByMemberIdAndArea(any(), any()); + } else { + verify(bookmarkRepository, never()).findBookmarkedCoursesByMemberId(any()); + verify(bookmarkRepository).findBookmarkedCoursesByMemberIdAndArea(memberId, area); + } + } + + @ParameterizedTest + @MethodSource("provideAreaArguments") + @DisplayName("북마크한 코스 조회 성공 - 결과 없음") + void getBookmarkedCourse_success_noContent(Area area) { + // given + Long memberId = 1L; + + if (area == null) { + given(bookmarkRepository.findBookmarkedCoursesByMemberId(memberId)).willReturn(Collections.emptyList()); + } else { + given(bookmarkRepository.findBookmarkedCoursesByMemberIdAndArea(memberId, area)).willReturn(Collections.emptyList()); + } + + // when + List result = + bookmarkService.getBookmarkedCoursesByMemberAndArea(memberId, area); + + // then + assertThat(result).isEmpty(); + + if (area == null) { + verify(bookmarkRepository).findBookmarkedCoursesByMemberId(memberId); + verify(bookmarkRepository, never()).findBookmarkedCoursesByMemberIdAndArea(any(), any()); + } else { + verify(bookmarkRepository, never()).findBookmarkedCoursesByMemberId(any()); + verify(bookmarkRepository).findBookmarkedCoursesByMemberIdAndArea(memberId, area); + } + } + } } \ No newline at end of file diff --git a/src/test/java/com/server/running_handai/domain/review/service/ReviewServiceTest.java b/src/test/java/com/server/running_handai/domain/review/service/ReviewServiceTest.java index 3dbb017..6fca5d6 100644 --- a/src/test/java/com/server/running_handai/domain/review/service/ReviewServiceTest.java +++ b/src/test/java/com/server/running_handai/domain/review/service/ReviewServiceTest.java @@ -8,12 +8,14 @@ import com.server.running_handai.domain.course.entity.Area; import com.server.running_handai.domain.course.entity.Course; +import com.server.running_handai.domain.course.entity.CourseImage; import com.server.running_handai.domain.course.entity.CourseLevel; import com.server.running_handai.domain.course.repository.CourseRepository; import com.server.running_handai.domain.member.entity.Member; import com.server.running_handai.domain.member.entity.Provider; import com.server.running_handai.domain.member.entity.Role; import com.server.running_handai.domain.member.repository.MemberRepository; +import com.server.running_handai.domain.review.dto.MyReviewInfoDto; import com.server.running_handai.domain.review.dto.ReviewCreateResponseDto; import com.server.running_handai.domain.review.dto.ReviewInfoListDto; import com.server.running_handai.domain.review.dto.ReviewCreateRequestDto; @@ -28,6 +30,7 @@ import jakarta.validation.Validator; import jakarta.validation.ValidatorFactory; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; @@ -91,7 +94,7 @@ void setUp() throws ParseException { course = Course.builder() .name("courseName1") - .distance(10) + .distance(10.5) .duration(120) .level(CourseLevel.MEDIUM) .area(Area.HAEUN_GWANGAN) @@ -106,10 +109,14 @@ void setUp() throws ParseException { .contents(VALID_REVIEW_CONTENTS) .build(); + CourseImage courseImage = CourseImage.builder().imgUrl("img_url").build(); + // 테스트 리뷰에 연관관계 설정 review.setWriter(member); review.setCourse(course); + course.updateCourseImage(courseImage); + ReflectionTestUtils.setField(course, "id", 10L); ReflectionTestUtils.setField(review, "id", 100L); ReflectionTestUtils.setField(member, "id", 50L); ReflectionTestUtils.setField(review, "createdAt", LocalDateTime.now()); @@ -547,4 +554,53 @@ void deleteReview_fail_accessDenied() { } } + + @Nested + @DisplayName("내 리뷰 조회 테스트") + class GetMyReviewTest { + + @Test + @DisplayName("내 리뷰 조회 성공 - 리뷰가 있을 때") + void getMyReview_success_hasReview() { + // given + Long memberId = member.getId(); + List myReviews = List.of(review); + + given(reviewRepository.findReviewsWithDetailsByMemberId(memberId)).willReturn(myReviews); + + // when + List myReviewInfoDtos = reviewService.getMyReviews(memberId); + + // then + MyReviewInfoDto firstDto = myReviewInfoDtos.getFirst(); + assertThat(myReviewInfoDtos.size()).isEqualTo(1); + assertThat(firstDto.reviewId()).isEqualTo(review.getId()); + assertThat(firstDto.courseId()).isEqualTo(course.getId()); + assertThat(firstDto.courseName()).isEqualTo(course.getName()); + assertThat(firstDto.thumbnailUrl()).isEqualTo(course.getCourseImage().getImgUrl()); + assertThat(firstDto.area()).isEqualTo(course.getArea().name()); + assertThat(firstDto.distance()).isEqualTo(course.getDistance()); + assertThat(firstDto.duration()).isEqualTo(course.getDuration()); + assertThat(firstDto.maxElevation()).isEqualTo((int) course.getMaxElevation().doubleValue()); + + verify(reviewRepository).findReviewsWithDetailsByMemberId(memberId); + } + + @Test + @DisplayName("내 리뷰 조회 성공 - 리뷰가 없을 때") + void getMyReview_success_HasNoReview() { + // given + Long memberId = member.getId(); + + given(reviewRepository.findReviewsWithDetailsByMemberId(memberId)).willReturn(Collections.emptyList()); + + // when + List myReviewInfoDtos = reviewService.getMyReviews(memberId); + + // then + assertThat(myReviewInfoDtos).isEmpty(); + + verify(reviewRepository).findReviewsWithDetailsByMemberId(memberId); + } + } } \ No newline at end of file