Skip to content

Commit

Permalink
Merge pull request #58 from PawWithU/feat/51-get-home-reviews-api
Browse files Browse the repository at this point in the history
[Feature] 이동봉사자 봉사 후기 전체 조회 API 구현
  • Loading branch information
hojeong2747 authored Nov 13, 2023
2 parents 90cec58 + f3098e1 commit 99564b7
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.pawwithu.connectdog.domain.review.controller;

import com.pawwithu.connectdog.domain.review.dto.request.ReviewCreateRequest;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetAllResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetOneResponse;
import com.pawwithu.connectdog.domain.review.service.ReviewService;
import com.pawwithu.connectdog.error.dto.ErrorResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -12,6 +13,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Expand Down Expand Up @@ -45,16 +47,27 @@ public ResponseEntity<Void> createReview(@AuthenticationPrincipal UserDetails lo

@Operation(summary = "후기 단건 조회", description = "후기 단건 조회합니다.",
security = { @SecurityRequirement(name = "bearer-key") },
responses = { @ApiResponse(responseCode = "200", description = "후기 단건 조회 성공")
responses = {@ApiResponse(responseCode = "200", description = "후기 단건 조회 성공")
, @ApiResponse(responseCode = "400"
, description = "M1, 해당 이동봉사자를 찾을 수 없습니다."
, content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping("/volunteers/reviews/{reviewId}")
public ResponseEntity<ReviewGetResponse> getOneReview(@AuthenticationPrincipal UserDetails loginUser, @PathVariable Long reviewId) {
ReviewGetResponse response = reviewService.getOneReview(loginUser.getUsername(), reviewId);
public ResponseEntity<ReviewGetOneResponse> getOneReview(@AuthenticationPrincipal UserDetails loginUser, @PathVariable Long reviewId) {
ReviewGetOneResponse response = reviewService.getOneReview(loginUser.getUsername(), reviewId);
return ResponseEntity.ok(response);
}

@Operation(summary = "후기 전체 조회", description = "후기 전체를 최신 순으로 조회합니다.",
responses = {@ApiResponse(responseCode = "200", description = "후기 전체 조회 성공")
, @ApiResponse(responseCode = "400"
, description = ""
, content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping(value = "/volunteers/reviews")
public ResponseEntity<List<ReviewGetAllResponse>> getAllReviews(Pageable pageable) {
List<ReviewGetAllResponse> reviews = reviewService.getAllReviews(pageable);
return ResponseEntity.ok(reviews);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.pawwithu.connectdog.domain.review.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.LocalDate;
import java.util.List;

public record ReviewGetAllResponse(String dogName, String volunteerNickname, String mainImage, List<String> images,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate startDate,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate endDate,
String departureLoc, String arrivalLoc,
String intermediaryName, String content
) {

// 리뷰 이미지 리스트 필드를 제외한 생성자
public ReviewGetAllResponse(String dogName, String volunteerNickname, String mainImage,
LocalDate startDate, LocalDate endDate, String departureLoc, String arrivalLoc,
String intermediaryName, String content) {
this(dogName, volunteerNickname, mainImage, null, startDate, endDate, departureLoc, arrivalLoc, intermediaryName, content);
}

// 리뷰 이미지 리스트 필드를 포함한 생성자
public static ReviewGetAllResponse of(ReviewGetAllResponse response, List<String> images) {
return new ReviewGetAllResponse(response.dogName, response.volunteerNickname, response.mainImage, images,
response.startDate, response.endDate, response.departureLoc, response.arrivalLoc, response.intermediaryName, response.content);
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.pawwithu.connectdog.domain.review.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.LocalDate;
import java.util.List;

public record ReviewGetOneResponse(String dogName, String volunteerNickname, String mainImage, List<String> images,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate startDate,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate endDate,
String departureLoc, String arrivalLoc,
String intermediaryName, String content
) {

// 리뷰 이미지 리스트 필드를 제외한 생성자
public ReviewGetOneResponse(String dogName, String volunteerNickname, String mainImage,
LocalDate startDate, LocalDate endDate, String departureLoc, String arrivalLoc,
String intermediaryName, String content) {
this(dogName, volunteerNickname, mainImage, null, startDate, endDate, departureLoc, arrivalLoc, intermediaryName, content);
}

// 리뷰 이미지 리스트 필드를 포함한 생성자
public static ReviewGetOneResponse of(ReviewGetOneResponse response, List<String> images) {
return new ReviewGetOneResponse(response.dogName, response.volunteerNickname, response.mainImage, images,
response.startDate, response.endDate, response.departureLoc, response.arrivalLoc, response.intermediaryName, response.content);
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.pawwithu.connectdog.domain.review.repository;

import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetAllResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetOneResponse;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface CustomReviewRepository {
// 리뷰 상세 조회 (대표 이미지를 제외한 다른 이미지 포함 X)
ReviewGetResponse getOneReview(Long id, Long reviewId);

// 대표 이미지를 제외한 리뷰 이미지 조회
List<String> getOneReviewImages(Long reviewId);
// 리뷰 단건 조회 (대표 이미지를 제외한 다른 이미지 포함 X)
ReviewGetOneResponse getOneReview(Long id, Long reviewId);

// 리뷰 전체 조회 (최신 순)
List<ReviewGetAllResponse> getAllReviews(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.pawwithu.connectdog.domain.review.repository.impl;

import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetAllResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetOneResponse;
import com.pawwithu.connectdog.domain.review.repository.CustomReviewRepository;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.List;
Expand Down Expand Up @@ -38,9 +40,9 @@ public List<String> getOneReviewImages(Long reviewId) {

// 후기 단건 조회 (대표 이미지를 제외한 다른 이미지 포함 X)
@Override
public ReviewGetResponse getOneReview(Long id, Long reviewId) {
public ReviewGetOneResponse getOneReview(Long id, Long reviewId) {
return queryFactory
.select(Projections.constructor(ReviewGetResponse.class,
.select(Projections.constructor(ReviewGetOneResponse.class,
dog.name, volunteer.nickname, reviewImage.image,
post.startDate, post.endDate, post.departureLoc, post.arrivalLoc,
intermediary.name, review.content))
Expand All @@ -53,4 +55,27 @@ public ReviewGetResponse getOneReview(Long id, Long reviewId) {
.where(review.id.eq(reviewId))
.fetchOne();
}

// 후기 전체 조회
@Override
public List<ReviewGetAllResponse> getAllReviews(Pageable pageable) {
// 리뷰 정보와 함께 리뷰 ID를 조회
List<ReviewGetAllResponse> reviews = queryFactory
.select(Projections.constructor(ReviewGetAllResponse.class,
dog.name, volunteer.nickname, reviewImage.image,
post.startDate, post.endDate, post.departureLoc, post.arrivalLoc,
intermediary.name, review.content))
.from(review)
.join(review.volunteer, volunteer)
.join(review.mainImage, reviewImage)
.join(review.post, post)
.join(review.post.dog, dog)
.join(review.post.intermediary, intermediary)
.orderBy(review.createdDate.desc())
.offset(pageable.getOffset()) // 페이지 번호
.limit(pageable.getPageSize()) // 페이지 사이즈
.fetch();

return reviews;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import com.pawwithu.connectdog.domain.post.entity.Post;
import com.pawwithu.connectdog.domain.post.repository.PostRepository;
import com.pawwithu.connectdog.domain.review.dto.request.ReviewCreateRequest;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetAllResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetOneResponse;
import com.pawwithu.connectdog.domain.review.entity.Review;
import com.pawwithu.connectdog.domain.review.entity.ReviewImage;
import com.pawwithu.connectdog.domain.review.repository.CustomReviewRepository;
Expand All @@ -15,6 +16,7 @@
import com.pawwithu.connectdog.error.exception.custom.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand Down Expand Up @@ -63,13 +65,20 @@ public void createReview(String email, Long postId, ReviewCreateRequest request,
review.updateMainImage(reviewImages.get(0));
}

public ReviewGetResponse getOneReview(String email, Long reviewId) {
@Transactional(readOnly = true)
public ReviewGetOneResponse getOneReview(String email, Long reviewId) {
Volunteer volunteer = volunteerRepository.findByEmail(email).orElseThrow(() -> new BadRequestException(VOLUNTEER_NOT_FOUND));
// 후기 조회 (대표 이미지 포함)
ReviewGetResponse oneReview = customReviewRepository.getOneReview(volunteer.getId(), reviewId);
ReviewGetOneResponse oneReview = customReviewRepository.getOneReview(volunteer.getId(), reviewId);
// 후기 이미지 조회 (대표 이미지 제외)
List<String> oneReviewImages = customReviewRepository.getOneReviewImages(reviewId);
ReviewGetResponse response = ReviewGetResponse.of(oneReview, oneReviewImages);
ReviewGetOneResponse response = ReviewGetOneResponse.of(oneReview, oneReviewImages);
return response;
}

@Transactional(readOnly = true)
public List<ReviewGetAllResponse> getAllReviews(Pageable pageable) {
List<ReviewGetAllResponse> reviews = customReviewRepository.getAllReviews(pageable);
return reviews;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.pawwithu.connectdog.domain.dog.entity.DogGender;
import com.pawwithu.connectdog.domain.dog.entity.DogSize;
import com.pawwithu.connectdog.domain.review.dto.request.ReviewCreateRequest;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetAllResponse;
import com.pawwithu.connectdog.domain.review.dto.response.ReviewGetOneResponse;
import com.pawwithu.connectdog.domain.review.service.ReviewService;
import com.pawwithu.connectdog.utils.TestUserArgumentResolver;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -14,6 +13,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
Expand Down Expand Up @@ -48,7 +48,7 @@ class ReviewControllerTest {
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(reviewController)
.setCustomArgumentResolvers(new TestUserArgumentResolver())
.setCustomArgumentResolvers(new TestUserArgumentResolver(), new PageableHandlerMethodArgumentResolver())
.addFilter(new CharacterEncodingFilter("UTF-8", true))
.build();
}
Expand Down Expand Up @@ -85,7 +85,7 @@ void setUp() {
images.add("image1");
images.add("image2");

ReviewGetResponse response = new ReviewGetResponse("겨울이", "호짱", "mainImage", images, startDate, endDate,
ReviewGetOneResponse response = new ReviewGetOneResponse("겨울이", "호짱", "mainImage", images, startDate, endDate,
"서울시 노원구", "서울시 성북구", "이동봉사 중개", "후기 작성 테스트입니다.");

// when
Expand All @@ -99,4 +99,32 @@ void setUp() {
verify(reviewService, times(1)).getOneReview(anyString(), anyLong());
}

@Test
void 후기_전체_조회() throws Exception {
// given
List<ReviewGetAllResponse> response = new ArrayList<>();
LocalDate startDate = LocalDate.of(2023, 10, 2);
LocalDate endDate = LocalDate.of(2023, 11, 7);

List<String> images = new ArrayList<>();
images.add("image1");
images.add("image2");

response.add(new ReviewGetAllResponse("봄이", "호짱", "mainImage", images, startDate, endDate,
"서울시 노원구", "서울시 성북구", "이동봉사 중개", "후기 작성 테스트입니다."));
response.add(new ReviewGetAllResponse("겨울이", "호짱", "mainImage", images, startDate, endDate,
"서울시 노원구", "서울시 성북구", "이동봉사 중개", "후기 작성 테스트입니다."));

// when
given(reviewService.getAllReviews(any())).willReturn(response);
ResultActions result = mockMvc.perform(
get("/volunteers/reviews")
.param("page", "0")
.param("size", "2")
);

// then
result.andExpect(status().isOk());
verify(reviewService, times(1)).getAllReviews(any());
}
}

0 comments on commit 99564b7

Please sign in to comment.