From 7673e26fd057e56b52e5b69ab632f35e40b18068 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 09:15:36 +0900 Subject: [PATCH 01/15] =?UTF-8?q?rename=20:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/controller/BookController.java | 8 ++++---- .../review/adaptor/ReviewAdaptor.java | 16 ---------------- .../{ => model}/response/ReviewResponse.java | 0 3 files changed, 4 insertions(+), 20 deletions(-) delete mode 100644 src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java rename src/main/java/com/t3t/frontserver/review/{ => model}/response/ReviewResponse.java (100%) diff --git a/src/main/java/com/t3t/frontserver/book/controller/BookController.java b/src/main/java/com/t3t/frontserver/book/controller/BookController.java index bb5c02f6..39a02dd7 100644 --- a/src/main/java/com/t3t/frontserver/book/controller/BookController.java +++ b/src/main/java/com/t3t/frontserver/book/controller/BookController.java @@ -7,8 +7,8 @@ import com.t3t.frontserver.index.OrderFormRequest; import com.t3t.frontserver.model.response.BaseResponse; import com.t3t.frontserver.model.response.PageResponse; -import com.t3t.frontserver.review.adaptor.ReviewAdaptor; -import com.t3t.frontserver.review.response.ReviewResponse; +import com.t3t.frontserver.review.client.ReviewApiClient; +import com.t3t.frontserver.review.model.response.ReviewResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -26,7 +26,7 @@ public class BookController { private final CategoryApiClient categoryAdaptor; private final BookApiClient bookApiClient; - private final ReviewAdaptor reviewAdaptor; + private final ReviewApiClient reviewApiClient; @GetMapping("books/{bookId}") public String getBook(Model model, @PathVariable Long bookId, @@ -69,7 +69,7 @@ private BookDetailResponse getDataFromBookAdaptor(Long bookId) { } private PageResponse getDataFromReviewAdaptor(Long bookId, int pageNo) { - ResponseEntity>> reviewsResponse = reviewAdaptor.getBook(bookId, pageNo); + ResponseEntity>> reviewsResponse = reviewApiClient.findReviewsByBookId(bookId, pageNo); return handleResponse(reviewsResponse); } } diff --git a/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java b/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java deleted file mode 100644 index 2774f20f..00000000 --- a/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.t3t.frontserver.review.adaptor; - -import com.t3t.frontserver.model.response.BaseResponse; -import com.t3t.frontserver.model.response.PageResponse; -import com.t3t.frontserver.review.response.ReviewResponse; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; - -@FeignClient(name = "reviewAdaptor", url = "${t3t.feignClient.url}") -public interface ReviewAdaptor { - @GetMapping(value = "/t3t/bookstore/reviews/book/{bookId}") - ResponseEntity>> getBook(@PathVariable Long bookId, @RequestParam int pageNo); -} \ No newline at end of file diff --git a/src/main/java/com/t3t/frontserver/review/response/ReviewResponse.java b/src/main/java/com/t3t/frontserver/review/model/response/ReviewResponse.java similarity index 100% rename from src/main/java/com/t3t/frontserver/review/response/ReviewResponse.java rename to src/main/java/com/t3t/frontserver/review/model/response/ReviewResponse.java From 75942266d1f29a11516d18e9feb0a2d19b6e61d2 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:15:41 +0900 Subject: [PATCH 02/15] =?UTF-8?q?#99=20=ED=9A=8C=EC=9B=90=EB=B3=84=20?= =?UTF-8?q?=EC=A3=BC=EB=AC=B8=20=EB=82=B4=EC=97=AD=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20-=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20url=20=EC=88=98=EC=A0=95=20(=EC=B6=94?= =?UTF-8?q?=ED=9B=84=20=EB=B3=80=EA=B2=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/templates/main/page/mypageOrder.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/resources/templates/main/page/mypageOrder.html b/src/main/resources/templates/main/page/mypageOrder.html index 86195679..8e637779 100644 --- a/src/main/resources/templates/main/page/mypageOrder.html +++ b/src/main/resources/templates/main/page/mypageOrder.html @@ -4,6 +4,13 @@ layout:decorate="main/layout/mypageLayout"> +

주문 목록

@@ -28,7 +35,9 @@
배송 중 (배송 예정일: 2024-05-01) - + + +
From cad3ad783b686c077e1f6aa3670397ed857783a9 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:17:11 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat=20:=20#99=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/fragment/mypageSidebar.html | 8 +++++++ .../templates/main/layout/reviewLayout.html | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/resources/templates/main/layout/reviewLayout.html diff --git a/src/main/resources/templates/main/fragment/mypageSidebar.html b/src/main/resources/templates/main/fragment/mypageSidebar.html index ea33368e..38ac15da 100644 --- a/src/main/resources/templates/main/fragment/mypageSidebar.html +++ b/src/main/resources/templates/main/fragment/mypageSidebar.html @@ -49,6 +49,14 @@

쿠폰 및 포인트 정보

쿠폰 이력 + + +

작성한 리뷰 확인

+ diff --git a/src/main/resources/templates/main/layout/reviewLayout.html b/src/main/resources/templates/main/layout/reviewLayout.html new file mode 100644 index 00000000..18d29bda --- /dev/null +++ b/src/main/resources/templates/main/layout/reviewLayout.html @@ -0,0 +1,24 @@ + + + + + + + +
+ +
+
+
+ +
+
+ +
+
+ + + + + + From cbb3c55c22ab49039f771bfdd5a086ee3a08b749 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:17:56 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat=20:=20#99=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EB=B3=84=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20view=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/main/page/mypageReview.html | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/main/resources/templates/main/page/mypageReview.html diff --git a/src/main/resources/templates/main/page/mypageReview.html b/src/main/resources/templates/main/page/mypageReview.html new file mode 100644 index 00000000..1e2a5fa2 --- /dev/null +++ b/src/main/resources/templates/main/page/mypageReview.html @@ -0,0 +1,73 @@ + + + + +
+ +
+
+
+
+ + + + + +
+

작성자 : 작성자 이름

+

This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.

+

등록일 : 리뷰 등록일

+

수정일 : 리뷰 등록일

+
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+ \ No newline at end of file From 7df195f066ecca6d63171fc6ce100c75ef084db8 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:19:06 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat=20:=20#99=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20view=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/main/page/registerReview.html | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/main/resources/templates/main/page/registerReview.html diff --git a/src/main/resources/templates/main/page/registerReview.html b/src/main/resources/templates/main/page/registerReview.html new file mode 100644 index 00000000..f18aef41 --- /dev/null +++ b/src/main/resources/templates/main/page/registerReview.html @@ -0,0 +1,54 @@ + + + + +
+
+ +
+

리뷰 내용을 작성해주세요.

+ +
+ + +
+

도서의 평점은 몇점인가요?

+
+
+ + +
+
+
+ + +
+

등록할 리뷰 이미지를 최대 5개까지 선택할 수 있습니다!

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ From 9757fa093a4b50154caab0ca2693c4efc715e691 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:19:59 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat=20:=20#99=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20view=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/main/page/editReview.html | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/resources/templates/main/page/editReview.html diff --git a/src/main/resources/templates/main/page/editReview.html b/src/main/resources/templates/main/page/editReview.html new file mode 100644 index 00000000..4d5b5fc7 --- /dev/null +++ b/src/main/resources/templates/main/page/editReview.html @@ -0,0 +1,53 @@ + + + + +
+ +
+

기존 리뷰 :

+
+

수정할 리뷰 내용을 작성해주세요.

+ + +
+ + +
+

기존 평점 :

+
+

수정할 도서의 평점은 몇점인가요?

+
+
+ + +
+
+ +
+ +
+

기존에 등록된 이미지 :

+
+ +
+ 이미지 + +
+
+
+ +

이미지를 추가할 수 있습니다.

+ + +
+
+ +
+ From a28ceab976d441be908be729eb8085d5d983f794 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:21:54 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat=20:=20#99=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B4=80=EB=A0=A8=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?js=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/static/assets/main/js/api.js | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/main/resources/static/assets/main/js/api.js diff --git a/src/main/resources/static/assets/main/js/api.js b/src/main/resources/static/assets/main/js/api.js new file mode 100644 index 00000000..2e07bdcb --- /dev/null +++ b/src/main/resources/static/assets/main/js/api.js @@ -0,0 +1,116 @@ +/* + * Bookstore API 호출 함수 + */ + +/** + * 리뷰 comment 업데이트 요청 + * @author Yujin-nKim(김유진) + */ +function updateReviewComment(reviewId) { + var comment = document.getElementById('updateReviewComment').value; + + $.ajax({ + url: '/reviews/' + reviewId + '/comment', + type: 'PUT', + contentType: 'application/json', + data: JSON.stringify({ "comment": comment }), + success: function(response, textStatus, xhr) { + }, + error: function(xhr, status, error) { + console.error('Error:', error); + } + }); +} + +/** + * 리뷰 score 업데이트 요청 + * @author Yujin-nKim(김유진) + */ +function updateReviewScore(reviewId) { + var selectedRadioButton = document.querySelector('input[class="form-check-input"]:checked'); + + if (selectedRadioButton == null) { + alert("평점을 선택해주세요."); + return; + } + + $.ajax({ + url: '/reviews/' + reviewId + '/score', + type: 'PUT', + data : { + score : selectedRadioButton.value + }, + success: function(response, textStatus, xhr) { + }, + error: function(xhr, status, error) { + console.error('Error:', error); + } + }); +} + +/** + * 리뷰 이미지 삭제 요청 + * @author Yujin-nKim(김유진) + */ +function deleteImage(reviewId, imageUrl) { + + alert("선택한 이미지를 삭제합니다."); + var imageName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); + + $.ajax({ + url: '/reviews/' + reviewId + '/image', + type: 'DELETE', + data : { + reviewImageName : imageName + }, + success: function(response, textStatus, xhr) { + }, + error: function(xhr, status, error) { + console.error('Error:', error); + } + }); +} + +/** + * 리뷰 이미지 추가 요청 + * @author Yujin-nKim(김유진) + */ +function addImage(reviewId) { + + // 선택한 이미지 파일 + var imageList = document.querySelector('input[name="reviewImageList"]').files; + // 기존 이미지 개수 + var existingImageCount = document.querySelectorAll('.image-container').length; + // 선택한 이미지 개수 + var addedImageCount = imageList.length; + + var totalImageCount = existingImageCount + addedImageCount; + + if (addedImageCount == 0) { + alert("이미지를 선택해주세요."); + return; + } + + if (totalImageCount > 5) { + alert("이미지는 최대 5개까지만 등록할 수 있습니다."); + return; + } + + var formData = new FormData(); + + for (var i = 0; i < imageList.length; i++) { + formData.append('imageList', imageList[i]); + } + + $.ajax({ + url: '/reviews/' + reviewId + '/image', + type: 'POST', + processData: false, + contentType: false, + data: formData, + success: function(response) { + }, + error: function(xhr, status, error) { + } + }); +} \ No newline at end of file From 9ec3d5d50f5069c8a1b067293820e436888b51f5 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:24:56 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat=20:=20#99=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D,=20=EC=88=98=EC=A0=95=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20Feign=20Client=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/adaptor/ReviewAdaptor.java | 161 ++++++++++++++++++ .../review/client/ReviewApiClient.java | 92 ++++++++++ .../review/client/ReviewFormApiClient.java | 39 +++++ .../request/ReviewCommentUpdateRequest.java | 15 ++ .../model/request/ReviewRegisterRequest.java | 29 ++++ .../review/model/response/ReviewResponse.java | 4 +- 6 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java create mode 100644 src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java create mode 100644 src/main/java/com/t3t/frontserver/review/client/ReviewFormApiClient.java create mode 100644 src/main/java/com/t3t/frontserver/review/model/request/ReviewCommentUpdateRequest.java create mode 100644 src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java diff --git a/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java b/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java new file mode 100644 index 00000000..cd5f854f --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java @@ -0,0 +1,161 @@ +package com.t3t.frontserver.review.adaptor; + +import com.t3t.frontserver.common.exception.ApiDataFetchException; +import com.t3t.frontserver.model.response.BaseResponse; +import com.t3t.frontserver.model.response.PageResponse; +import com.t3t.frontserver.review.client.ReviewApiClient; +import com.t3t.frontserver.review.client.ReviewFormApiClient; +import com.t3t.frontserver.review.model.request.ReviewCommentUpdateRequest; +import com.t3t.frontserver.review.model.request.ReviewRegisterRequest; +import com.t3t.frontserver.review.model.response.ReviewResponse; +import com.t3t.frontserver.util.FeignClientUtils; +import feign.FeignException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + + +@Slf4j +@Component +@RequiredArgsConstructor +public class ReviewAdaptor { + private final ReviewApiClient reviewApiClient; + private final ReviewFormApiClient reviewFormApiClient; + + /** + * 특정 회원과 특정 도서에 대한 리뷰가 이미 등록되어 있는지 확인 + * @param memberId 회원 식별자 + * @param bookId 도서 식별자 + * @return 해당 회원이 해당 도서에 대해 리뷰를 작성했으면 true를 반환하고, 그렇지 않으면 false를 반환 + * @author Yujin-nKim(김유진) + */ + public boolean existsReview(Long memberId, Long bookId) { + try { + return Optional.ofNullable(reviewApiClient.existsReview(memberId, bookId).getBody()) + .map(BaseResponse::getData) + .orElseThrow(ApiDataFetchException::new); + } catch (FeignException e) { + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 리뷰 생성 요청 + * @param request 리뷰 생성 요청 객체 + * @return 200 OK, 성공 메세지 + * @author Yujin-nKim(김유진) + */ + public void createReview(ReviewRegisterRequest request) { + try { + reviewFormApiClient.createReview(request); + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 사용자 식별자에 해당하는 리뷰 페이지 조회 + * @param memberId 회원 식별자 + * @param pageNo 페이지 번호 + * @param pageSize 페이지 크기 + * @param sortBy 정렬 기준 + * @return 주어진 회원 식별자에 대한 리뷰 페이지 응답 + * @author Yujin-nKim(김유진) + */ + public PageResponse findReviewsByMemberId(Long memberId, int pageNo, int pageSize, String sortBy) { + try { + ResponseEntity>> response = reviewApiClient.findReviewsByMemberId(memberId, pageNo, pageSize, sortBy); + if (response.getStatusCode() == HttpStatus.OK) { + return Objects.requireNonNull(response.getBody()).getData(); + } + return null; + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 리뷰 상세 조회 + * @param reviewId 리뷰 ID + * @return 리뷰 상세 + * @author Yujin-nKim(김유진) + */ + public ReviewResponse findReviewByReviewId(Long reviewId) { + try { + return Optional.ofNullable(reviewApiClient.findReviewByReviewId(reviewId).getBody()) + .map(BaseResponse::getData) + .orElseThrow(ApiDataFetchException::new); + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 리뷰 comment 수정 요청 + * @param reviewId 수정할 review ID + * @param request 리뷰 수정 요청 객체 + * @author Yujin-nKim(김유진) + */ + public void updateReviewDetail(Long reviewId, ReviewCommentUpdateRequest request) { + try { + reviewApiClient.updateReviewDetail(reviewId, request); + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 리뷰 score 수정 요청 + * @param reviewId 수정할 review ID + * @param score 수정할 점수 + * @author Yujin-nKim(김유진) + */ + public void updateReviewScore(Long reviewId, Integer score) { + try { + reviewApiClient.updateReviewScore(reviewId, score); + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 리뷰 이미지 삭제 요청 + * @param reviewImageName 삭제할 이미지 name + * @author Yujin-nKim(김유진) + */ + public void deleteReviewImage(String reviewImageName) { + try { + reviewApiClient.deleteReviewImage(reviewImageName); + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } + + /** + * 리뷰 이미지 추가 요청 + * @param reviewId 수정할 review ID + * @param imageList 추가할 이미지 + * @author Yujin-nKim(김유진) + */ + public void addReviewImage(Long reviewId, List imageList) { + try { + reviewFormApiClient.addReviewImage(reviewId, imageList); + } catch (FeignException e) { + log.error(e.getMessage()); + throw new ApiDataFetchException(FeignClientUtils.getMessageFromFeignException(e)); + } + } +} diff --git a/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java b/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java new file mode 100644 index 00000000..d2a091e3 --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java @@ -0,0 +1,92 @@ +package com.t3t.frontserver.review.client; + +import com.t3t.frontserver.model.response.BaseResponse; +import com.t3t.frontserver.model.response.PageResponse; +import com.t3t.frontserver.review.model.request.ReviewCommentUpdateRequest; +import com.t3t.frontserver.review.model.response.ReviewResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@FeignClient(name = "ReviewApiClient", url = "${t3t.feignClient.url}") +public interface ReviewApiClient { + + /** + * 책 ID에 해당하는 리뷰 페이지 조회 + * @param bookId 리뷰를 검색할 책의 식별자 + * @param pageNo 페이지 번호 + * @return 주어진 책 식별자에 대한 리뷰 페이지 응답 + * @author Yujin-nKim(김유진) + */ + @GetMapping(value = "/t3t/bookstore/reviews/book/{bookId}") + ResponseEntity>> findReviewsByBookId( + @PathVariable Long bookId, + @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo); + + /** + * 특정 회원과 특정 도서에 대한 리뷰가 이미 등록되어 있는지 확인 + * @param memberId 회원 식별자 + * @param bookId 도서 식별자 + * @return 특정 회원이 특정 도서에 대한 리뷰가 이미 등록되어 있는지 여부 + * @author Yujin-nKim(김유진) + */ + @GetMapping("/t3t/bookstore/reviews/members/{memberId}/exists") + ResponseEntity> existsReview(@PathVariable Long memberId, @RequestParam Long bookId); + + /** + * 사용자 식별자에 해당하는 리뷰 페이지 조회 + * @param memberId 회원 식별자 + * @param pageNo 페이지 번호 + * @param pageSize 페이지 크기 + * @param sortBy 정렬 기준 + * @return 주어진 회원 식별자에 대한 리뷰 페이지 응답 + * @author Yujin-nKim(김유진) + */ + @GetMapping("/t3t/bookstore/reviews/members/{memberId}") + ResponseEntity>> findReviewsByMemberId( + @PathVariable Long memberId, + @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo, + @RequestParam(value = "pageSize", defaultValue = "10", required = false) int pageSize, + @RequestParam(value = "sortBy", defaultValue = "reviewCreatedAt", required = false) String sortBy); + + /** + * 리뷰 상세 조회 + * @param reviewId 리뷰 ID + * @return 리뷰 상세 + * @author Yujin-nKim(김유진) + */ + @GetMapping("/t3t/bookstore/reviews/{reviewId}") + ResponseEntity> findReviewByReviewId(@PathVariable Long reviewId); + + /** + * 리뷰 comment 수정 요청 + * @param reviewId 수정할 review ID + * @param request 리뷰 수정 요청 객체 + * @return 200 OK, 성공 메세지 + * @author Yujin-nKim(김유진) + */ + @PutMapping("/t3t/bookstore/reviews/{reviewId}/comment") + ResponseEntity> updateReviewDetail(@PathVariable Long reviewId, + @RequestBody ReviewCommentUpdateRequest request); + + /** + * 리뷰 score 수정 요청 + * @param reviewId 수정할 review ID + * @param score 수정할 점수 + * @return 200 OK, 성공 메세지 + * @author Yujin-nKim(김유진) + */ + @PutMapping("/t3t/bookstore/reviews/{reviewId}/score") + ResponseEntity> updateReviewScore(@PathVariable Long reviewId, + @RequestParam Integer score); + + /** + * 리뷰 이미지 삭제 요청 + * @param reviewImageName 삭제할 이미지 name + * @return 200 OK, 성공 메세지 + * @author Yujin-nKim(김유진) + */ + @DeleteMapping("/t3t/bookstore/reviews/image") + ResponseEntity> deleteReviewImage(@RequestParam String reviewImageName); +} diff --git a/src/main/java/com/t3t/frontserver/review/client/ReviewFormApiClient.java b/src/main/java/com/t3t/frontserver/review/client/ReviewFormApiClient.java new file mode 100644 index 00000000..7de51ed5 --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/client/ReviewFormApiClient.java @@ -0,0 +1,39 @@ +package com.t3t.frontserver.review.client; + +import com.t3t.frontserver.config.FormConfiguration; +import com.t3t.frontserver.model.response.BaseResponse; +import com.t3t.frontserver.review.model.request.ReviewRegisterRequest; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + + +@FeignClient(name = "reviewFormApiClient", url = "${t3t.feignClient.url}", configuration = FormConfiguration.class) +public interface ReviewFormApiClient { + + /** + * 리뷰 생성 요청 + * @param request 리뷰 생성 요청 객체 + * @return 200 OK, 성공 메세지 + * @author Yujin-nKim(김유진) + */ + @PostMapping(value = "/t3t/bookstore/reviews", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity> createReview(@ModelAttribute ReviewRegisterRequest request); + + /** + * 리뷰 이미지 추가 요청 + * @param reviewId 수정할 review ID + * @param imageList 추가할 이미지 + * @return 200 OK, 성공 메세지 + * @author Yujin-nKim(김유진) + */ + @PostMapping(value = "/t3t/bookstore/reviews/{reviewId}/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity> addReviewImage(@PathVariable Long reviewId, + @ModelAttribute List imageList); +} diff --git a/src/main/java/com/t3t/frontserver/review/model/request/ReviewCommentUpdateRequest.java b/src/main/java/com/t3t/frontserver/review/model/request/ReviewCommentUpdateRequest.java new file mode 100644 index 00000000..39c1fea8 --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/model/request/ReviewCommentUpdateRequest.java @@ -0,0 +1,15 @@ +package com.t3t.frontserver.review.model.request; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * 리뷰 comment 업데이트 요청을 나타내는 객체 + * @author Yujin-nKim(김유진) + */ +@Data +public class ReviewCommentUpdateRequest { + @NotBlank(message = "리뷰 내용을 입력해주세요.") + String comment; +} \ No newline at end of file diff --git a/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java b/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java new file mode 100644 index 00000000..1f7cc37a --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java @@ -0,0 +1,29 @@ +package com.t3t.frontserver.review.model.request; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.*; +import java.util.List; + +/** + * 리뷰 등록 요청을 나타내는 객체 + * @author Yujin-nKim(김유진) + */ +@Data +public class ReviewRegisterRequest { + private Long memberId; // 리뷰 작성자 id + + private Long bookId; // 리뷰를 작성할 도서 id + + @NotBlank(message = "리뷰 내용을 입력해주세요.") + private String comment; // 리뷰 내용 + + @NotNull(message = "평점을 입력해주세요.") + @Min(value = 1, message = "리뷰는 1~5점 사이에서 선택할 수 있습니다.") + @Max(value = 5, message = "리뷰는 1~5점 사이에서 선택할 수 있습니다.") + private Integer score; // 리뷰 평점 + + @Size(max = 5, message = "최대 5개의 리뷰 이미지를 등록할 수 있습니다.") + private List reviewImageList; // 도서 리뷰 이미지 +} \ No newline at end of file diff --git a/src/main/java/com/t3t/frontserver/review/model/response/ReviewResponse.java b/src/main/java/com/t3t/frontserver/review/model/response/ReviewResponse.java index d5d44bd8..56d5ff1c 100644 --- a/src/main/java/com/t3t/frontserver/review/model/response/ReviewResponse.java +++ b/src/main/java/com/t3t/frontserver/review/model/response/ReviewResponse.java @@ -1,4 +1,4 @@ -package com.t3t.frontserver.review.response; +package com.t3t.frontserver.review.model.response; import lombok.Getter; @@ -7,6 +7,8 @@ @Getter public class ReviewResponse { + private Long reviewId; // 리뷰 id + private Long bookId; // 도서 id private String name; // 리뷰 작성자 이름 private String comment; // 리뷰 내용 private Integer reviewScore; // 평가 점수 From 2cb7e0d507c02151f7ce804f7e45b8510d38cb3e Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:31:31 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat=20:=20#99=20=EB=A6=AC=EB=B7=B0=20api?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20controller,=20service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ApiDataFetchException.java | 4 + .../review/controller/ReviewController.java | 198 ++++++++++++++++++ .../review/service/ReviewService.java | 101 +++++++++ 3 files changed, 303 insertions(+) create mode 100644 src/main/java/com/t3t/frontserver/review/controller/ReviewController.java create mode 100644 src/main/java/com/t3t/frontserver/review/service/ReviewService.java diff --git a/src/main/java/com/t3t/frontserver/common/exception/ApiDataFetchException.java b/src/main/java/com/t3t/frontserver/common/exception/ApiDataFetchException.java index f72cb431..f487098a 100644 --- a/src/main/java/com/t3t/frontserver/common/exception/ApiDataFetchException.java +++ b/src/main/java/com/t3t/frontserver/common/exception/ApiDataFetchException.java @@ -10,4 +10,8 @@ public ApiDataFetchException() { public ApiDataFetchException(int statusCode) { super(String.format("%s (%s)", DEFAULT_MESSAGE, statusCode)); } + + public ApiDataFetchException(String message) { + super(DEFAULT_MESSAGE + message); + } } \ No newline at end of file diff --git a/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java b/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java new file mode 100644 index 00000000..8d35de8a --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java @@ -0,0 +1,198 @@ +package com.t3t.frontserver.review.controller; + +import com.t3t.frontserver.auth.util.SecurityContextUtils; +import com.t3t.frontserver.model.response.PageResponse; +import com.t3t.frontserver.review.model.request.ReviewCommentUpdateRequest; +import com.t3t.frontserver.review.model.request.ReviewRegisterRequest; +import com.t3t.frontserver.review.model.response.ReviewResponse; +import com.t3t.frontserver.review.service.ReviewService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.validation.Valid; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Controller +public class ReviewController { + private final ReviewService reviewService; + + /** + * 리뷰 점수를 가져오는 메서드 + * @return 리뷰 점수를 담고 있는 맵 객체
+ * key - 점수를 나타내는 문자열, value - 값은 해당 점수를 나타내는 정수 + * @author Yujin-nKim(김유진) + */ + @ModelAttribute("score") + public Map reviews() { + Map score = new LinkedHashMap<>(); + score.put(1, "1점"); + score.put(2, "2점"); + score.put(3, "3점"); + score.put(4, "4점"); + score.put(5, "5점"); + return score; + } + + /** + * 리뷰 등록 가능 여부를 확인 후 리뷰 등록 페이지로 이동 또는 리다이렉트 + * @param model 뷰에 전달할 데이터를 담는 모델 객체 + * @param redirectAttributes 리다이렉트 속성 객체 + * @param bookId 리뷰를 작성할 도서의 식별자 + * @return 리뷰 등록 페이지 뷰 + * @author Yujin-nKim(김유진) + */ + @GetMapping("/reviews/register") + public String reviewPage(Model model, RedirectAttributes redirectAttributes, @RequestParam Long bookId) { + + Long memberId = SecurityContextUtils.getMemberId(); + + // 현재 회원이 해당 도서에 대해 이미 리뷰를 작성했는지 확인 + if(!reviewService.existsReview(memberId, bookId)) { + // 리뷰를 작성하지 않은 경우 리뷰 작성 페이지로 이동 + model.addAttribute("reviewRegisterRequest", new ReviewRegisterRequest()); + model.addAttribute("bookId", bookId); + return "main/page/registerReview"; + } + + // 이미 리뷰를 작성한 경우, 메세지와 함께 리다이렉트 + redirectAttributes.addFlashAttribute("message", "이미 리뷰를 작성했습니다."); + return "redirect:/mypage/order"; + } + + /** + * 리뷰 수정 페이지 요청 + * @param model 뷰에 전달할 데이터를 담는 모델 객체 + * @param reviewId 리뷰 ID + * @return 요청 수정 페이지 + * @author Yujin-nKim(김유진) + */ + @GetMapping("/reviews/edit") + public String reviewEditPage(Model model, @ModelAttribute("score") Map score, @RequestParam Long reviewId) { + ReviewResponse review = reviewService.findReviewByReviewId(reviewId); + model.addAttribute("review", review); + model.addAttribute("score", score); + return "main/page/editReview"; + } + + /** + * 리뷰 생성 요청 + * @param request 리뷰 생성 요청 객체 + * @param bindingResult 데이터 바인딩 프로세스의 결과 + * @param redirectAttributes 리디렉션시에 사용되는 속성 + * @return 요청 처리 후 리디렉션할 뷰 + * @author Yujin-nKim(김유진) + */ + @PostMapping("/reviews") + public String createReview(@ModelAttribute @Valid ReviewRegisterRequest request, BindingResult bindingResult, RedirectAttributes redirectAttributes) { + if (bindingResult.hasErrors()) { + // 바인딩 오류가 발생한 경우 + log.error(bindingResult.toString()); + String errorMessage = bindingResult.getFieldErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()).toString(); + redirectAttributes.addFlashAttribute("error", errorMessage); + return "redirect:/reviews/register?bookId=" + request.getBookId(); + } + + log.info("리뷰 생성 요청 : {}", request.toString()); + reviewService.createReview(request); + return "redirect:/mypage/order"; + } + + /** + * 사용자 ID에 해당하는 리뷰 페이지 조회 + * @param pageNo 페이지 번호 + * @param pageSize 페이지 크기 + * @param sortBy 정렬 기준 + * @return 주어진 회원 ID에 대한 리뷰 페이지 응답 + * @author Yujin-nKim(김유진) + */ + @GetMapping("/mypage/review") + public String findReviewsByMemberId(Model model, + @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo, + @RequestParam(value = "pageSize", defaultValue = "10", required = false) int pageSize, + @RequestParam(value = "sortBy", defaultValue = "reviewCreatedAt", required = false) String sortBy) { + + PageResponse reviewList = reviewService.findReviewsByMemberId(pageNo, pageSize, sortBy); + + if (reviewList != null) { + int blockLimit = 5; // 현재 페이지 앞뒤로 보여줄 개수 설정 + int nowPage = reviewList.getPageNo() + 1; + int startPage = Math.max(nowPage - blockLimit, 1); + int endPage = Math.min(nowPage + blockLimit, reviewList.getTotalPages()); + + model.addAttribute("nowPage", nowPage); + model.addAttribute("startPage", startPage); + model.addAttribute("endPage", endPage); + model.addAttribute("reviewList", reviewList.getContent()); + } + + return "main/page/mypageReview"; + } + + /** + * 리뷰 comment 수정 요청 + * @param reviewId 리뷰 ID + * @param request 리뷰 comment 수정 요청 객체 + * @return 요청 처리 후 리디렉션할 뷰 + * @author Yujin-nKim(김유진) + */ + @PutMapping("/reviews/{reviewId}/comment") + public String updateReviewDetail(@PathVariable Long reviewId, @RequestBody ReviewCommentUpdateRequest request) { + reviewService.updateReviewDetail(reviewId, request); + return "redirect:/reviews/edit?reviewId="+reviewId; + } + + /** + * 리뷰 score 수정 요청 + * @param reviewId 리뷰 ID + * @param score 수정할 리뷰 평점 + * @return 요청 처리 후 리디렉션할 뷰 + * @author Yujin-nKim(김유진) + */ + @PutMapping("/reviews/{reviewId}/score") + public String updateReviewScore(@PathVariable Long reviewId, @RequestParam Integer score) { + reviewService.updateReviewScore(reviewId, score); + return "redirect:/reviews/edit?reviewId="+reviewId; + } + + /** + * 리뷰 이미지 delete 요청 + * @param reviewId 리뷰 ID + * @param reviewImageName 삭제할 리뷰 이미지 + * @return 요청 처리 후 리디렉션할 뷰 + * @author Yujin-nKim(김유진) + */ + @DeleteMapping("/reviews/{reviewId}/image") + public String deleteReviewImage(@PathVariable Long reviewId, @RequestParam String reviewImageName) { + reviewService.deleteReviewImage(reviewImageName); + return "redirect:/reviews/edit?reviewId="+reviewId; + } + + /** + * 리뷰 이미지 추가 요청 + * @param reviewId 리뷰 ID + * @param imageList 추가할 리뷰 이미지 리스트 + * @return 요청 처리 후 리디렉션할 뷰 + * @author Yujin-nKim(김유진) + */ + @PostMapping("/reviews/{reviewId}/image") + public String addReviewImage(@PathVariable Long reviewId, + @ModelAttribute List imageList) { + + reviewService.addReviewImage(reviewId, imageList); + return "redirect:/reviews/edit?reviewId="+reviewId; + } +} diff --git a/src/main/java/com/t3t/frontserver/review/service/ReviewService.java b/src/main/java/com/t3t/frontserver/review/service/ReviewService.java new file mode 100644 index 00000000..88027d00 --- /dev/null +++ b/src/main/java/com/t3t/frontserver/review/service/ReviewService.java @@ -0,0 +1,101 @@ +package com.t3t.frontserver.review.service; + +import com.t3t.frontserver.auth.util.SecurityContextUtils; +import com.t3t.frontserver.model.response.PageResponse; +import com.t3t.frontserver.review.adaptor.ReviewAdaptor; +import com.t3t.frontserver.review.model.request.ReviewCommentUpdateRequest; +import com.t3t.frontserver.review.model.request.ReviewRegisterRequest; +import com.t3t.frontserver.review.model.response.ReviewResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ReviewService { + private final ReviewAdaptor reviewAdaptor; + + /** + * 특정 회원과 특정 도서에 대한 리뷰가 이미 등록되어 있는지 확인 + * @param memberId 특정 회원의 식별자 + * @param bookId 특정 도서의 식별자 + * @return 해당 회원이 해당 도서에 대해 리뷰를 작성했으면 true를 반환하고, 그렇지 않으면 false를 반환 + * @author Yujin-nKim(김유진) + */ + public boolean existsReview(Long memberId, Long bookId) { + return reviewAdaptor.existsReview(memberId, bookId); + } + + /** + * 리뷰 생성 요청 + * @param request 리뷰 생성 요청 객체 + * @author Yujin-nKim(김유진) + */ + public void createReview(ReviewRegisterRequest request) { + request.setMemberId(SecurityContextUtils.getMemberId()); + reviewAdaptor.createReview(request); + } + + /** + * 사용자 ID에 해당하는 리뷰 페이지 조회 + * @param pageNo 페이지 번호 + * @param pageSize 페이지 크기 + * @param sortBy 정렬 기준 + * @return 주어진 회원 ID에 대한 리뷰 페이지 응답 + * @author Yujin-nKim(김유진) + */ + public PageResponse findReviewsByMemberId(int pageNo, int pageSize, String sortBy) { + return reviewAdaptor.findReviewsByMemberId(SecurityContextUtils.getMemberId(), pageNo, pageSize, sortBy); + } + + /** + * 리뷰 ID에 해당하는 리뷰 상세 조회 + * @param reviewId 리뷰 ID + * @return 리뷰 ID에 해당하는 리뷰 상세 + * @author Yujin-nKim(김유진) + */ + public ReviewResponse findReviewByReviewId(Long reviewId) { + return reviewAdaptor.findReviewByReviewId(reviewId); + } + + /** + * 리뷰 comment 수정 요청 + * @param reviewId 리뷰 ID + * @param request 리뷰 comment 수정 요청 객체 + * @author Yujin-nKim(김유진) + */ + public void updateReviewDetail(Long reviewId, ReviewCommentUpdateRequest request) { + reviewAdaptor.updateReviewDetail(reviewId, request); + } + + /** + * 리뷰 score 수정 요청 + * @param reviewId 리뷰 ID + * @param score 수정할 리뷰 평점 + * @author Yujin-nKim(김유진) + */ + public void updateReviewScore(Long reviewId, Integer score) { + reviewAdaptor.updateReviewScore(reviewId, score); + } + + /** + * 리뷰 이미지 delete 요청 + * @param reviewImageName 삭제할 리뷰 이미지 + * @author Yujin-nKim(김유진) + */ + public void deleteReviewImage(String reviewImageName) { + reviewAdaptor.deleteReviewImage(reviewImageName); + } + + /** + * 리뷰 이미지 추가 요청 + * @param reviewId 리뷰 ID + * @param imageList 추가할 리뷰 이미지 리스트 + * @author Yujin-nKim(김유진) + */ + public void addReviewImage(Long reviewId, List imageList) { + reviewAdaptor.addReviewImage(reviewId, imageList); + } +} From 1b357701a83e02cb6a02c1e62f419a3b45963205 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 15:33:36 +0900 Subject: [PATCH 10/15] =?UTF-8?q?fix=20:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=20disable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/templates/admin/page/main.html | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/resources/templates/admin/page/main.html b/src/main/resources/templates/admin/page/main.html index 5e4a23dc..22d0acac 100644 --- a/src/main/resources/templates/admin/page/main.html +++ b/src/main/resources/templates/admin/page/main.html @@ -9,36 +9,36 @@

준비중입니다...

-
- -
-
- - - - - - - - -
-
+ + + + + + + + + + + + + + - -
- - + + + + - - + + - - + + - - -
-
+ + + +
From 9b728e1963b6d90edd09c96644faa1a00157efde Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 20:54:27 +0900 Subject: [PATCH 11/15] =?UTF-8?q?fix=20:=20#99=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/adaptor/ReviewAdaptor.java | 9 +++++---- .../review/client/ReviewApiClient.java | 9 +++++---- .../review/controller/ReviewController.java | 15 ++++++++------- .../review/service/ReviewService.java | 9 +++++---- .../templates/main/page/mypageOrder.html | 16 +++++++++------- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java b/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java index cd5f854f..a97124d4 100644 --- a/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java +++ b/src/main/java/com/t3t/frontserver/review/adaptor/ReviewAdaptor.java @@ -30,15 +30,16 @@ public class ReviewAdaptor { private final ReviewFormApiClient reviewFormApiClient; /** - * 특정 회원과 특정 도서에 대한 리뷰가 이미 등록되어 있는지 확인 + * 리뷰 작성 가능 여부를 확인 * @param memberId 회원 식별자 * @param bookId 도서 식별자 - * @return 해당 회원이 해당 도서에 대해 리뷰를 작성했으면 true를 반환하고, 그렇지 않으면 false를 반환 + * @param orderDetailId 주문상세 ID + * @return 리뷰 작성 가능 여부 * @author Yujin-nKim(김유진) */ - public boolean existsReview(Long memberId, Long bookId) { + public boolean checkReviewCapability(Long memberId, Long bookId, Long orderDetailId) { try { - return Optional.ofNullable(reviewApiClient.existsReview(memberId, bookId).getBody()) + return Optional.ofNullable(reviewApiClient.checkReviewCapability(memberId, bookId, orderDetailId).getBody()) .map(BaseResponse::getData) .orElseThrow(ApiDataFetchException::new); } catch (FeignException e) { diff --git a/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java b/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java index d2a091e3..49fab2a8 100644 --- a/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java +++ b/src/main/java/com/t3t/frontserver/review/client/ReviewApiClient.java @@ -25,14 +25,15 @@ ResponseEntity>> findReviewsByBookId( @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo); /** - * 특정 회원과 특정 도서에 대한 리뷰가 이미 등록되어 있는지 확인 + * 리뷰 작성 가능 여부를 확인 * @param memberId 회원 식별자 * @param bookId 도서 식별자 - * @return 특정 회원이 특정 도서에 대한 리뷰가 이미 등록되어 있는지 여부 + * @param orderDetailId 주문상세 ID + * @return 리뷰 작성 가능 여부 * @author Yujin-nKim(김유진) */ - @GetMapping("/t3t/bookstore/reviews/members/{memberId}/exists") - ResponseEntity> existsReview(@PathVariable Long memberId, @RequestParam Long bookId); + @GetMapping("/t3t/bookstore/reviews/members/{memberId}/capability") + ResponseEntity> checkReviewCapability(@PathVariable Long memberId, @RequestParam Long bookId, @RequestParam Long orderDetailId); /** * 사용자 식별자에 해당하는 리뷰 페이지 조회 diff --git a/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java b/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java index 8d35de8a..be8dfddb 100644 --- a/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java +++ b/src/main/java/com/t3t/frontserver/review/controller/ReviewController.java @@ -50,24 +50,25 @@ public Map reviews() { * @param model 뷰에 전달할 데이터를 담는 모델 객체 * @param redirectAttributes 리다이렉트 속성 객체 * @param bookId 리뷰를 작성할 도서의 식별자 + * @param orderDetailId 주문상세 ID * @return 리뷰 등록 페이지 뷰 * @author Yujin-nKim(김유진) */ @GetMapping("/reviews/register") - public String reviewPage(Model model, RedirectAttributes redirectAttributes, @RequestParam Long bookId) { - + public String reviewPage(Model model, RedirectAttributes redirectAttributes, @RequestParam Long bookId, @RequestParam Long orderDetailId) { Long memberId = SecurityContextUtils.getMemberId(); - // 현재 회원이 해당 도서에 대해 이미 리뷰를 작성했는지 확인 - if(!reviewService.existsReview(memberId, bookId)) { - // 리뷰를 작성하지 않은 경우 리뷰 작성 페이지로 이동 + // 리뷰 작성 가능한지 확인 + if(!reviewService.checkReviewCapability(memberId, bookId, orderDetailId)) { + // 리뷰를 작성 가능한 경우 리뷰 작성 페이지로 이동 model.addAttribute("reviewRegisterRequest", new ReviewRegisterRequest()); model.addAttribute("bookId", bookId); + model.addAttribute("orderDetailId", orderDetailId); return "main/page/registerReview"; } - // 이미 리뷰를 작성한 경우, 메세지와 함께 리다이렉트 - redirectAttributes.addFlashAttribute("message", "이미 리뷰를 작성했습니다."); + // 리뷰를 작성이 불가능한 경우, 메세지와 함께 리다이렉트 + redirectAttributes.addFlashAttribute("message", "이미 리뷰를 작성했거나, 리뷰를 작성할 수 없습니다."); return "redirect:/mypage/order"; } diff --git a/src/main/java/com/t3t/frontserver/review/service/ReviewService.java b/src/main/java/com/t3t/frontserver/review/service/ReviewService.java index 88027d00..97f6e239 100644 --- a/src/main/java/com/t3t/frontserver/review/service/ReviewService.java +++ b/src/main/java/com/t3t/frontserver/review/service/ReviewService.java @@ -18,14 +18,15 @@ public class ReviewService { private final ReviewAdaptor reviewAdaptor; /** - * 특정 회원과 특정 도서에 대한 리뷰가 이미 등록되어 있는지 확인 + * 리뷰 작성 가능 여부를 확인 * @param memberId 특정 회원의 식별자 * @param bookId 특정 도서의 식별자 - * @return 해당 회원이 해당 도서에 대해 리뷰를 작성했으면 true를 반환하고, 그렇지 않으면 false를 반환 + * @param orderDetailId 주문상세 ID + * @return 리뷰 작성 가능하면 true를 반환하고, 그렇지 않으면 false를 반환 * @author Yujin-nKim(김유진) */ - public boolean existsReview(Long memberId, Long bookId) { - return reviewAdaptor.existsReview(memberId, bookId); + public boolean checkReviewCapability(Long memberId, Long bookId, Long orderDetailId) { + return reviewAdaptor.checkReviewCapability(memberId, bookId, orderDetailId); } /** diff --git a/src/main/resources/templates/main/page/mypageOrder.html b/src/main/resources/templates/main/page/mypageOrder.html index 8e637779..b9c8911c 100644 --- a/src/main/resources/templates/main/page/mypageOrder.html +++ b/src/main/resources/templates/main/page/mypageOrder.html @@ -29,15 +29,15 @@
주문번호: 111-1250912120
-
배송 중 (배송 예정일: 2024-05-01)
-

상품명: 상품 A

-

상품 가격: 10,000원

+
배송 완료 (배송 예정일: 2024-05-01)
+

상품명: 내 몸에 꼭 맞는 사상체질 건강요리 - 태양인 이제마의 사상체질의 실용서

+

상품 가격: 20000

- - + +
@@ -62,13 +62,15 @@
주문번호: 111-1250912120
-
배송 중 (배송 예정일: 2024-05-01)
-

상품명: 상품 A

+
배송 중 (배송 예정일: 2024-05-20)
+

상품명: 요리하는 다이어터의 맛있게 살 빼는 다이어트 레시피

상품 가격: 10,000원

+ +
From 03f19f659b7bd478f2ffba30423de7f4c41dadf1 Mon Sep 17 00:00:00 2001 From: yujinKim Date: Wed, 15 May 2024 20:55:58 +0900 Subject: [PATCH 12/15] =?UTF-8?q?fix=20:=20ReviewRegisterRequest=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontserver/review/model/request/ReviewRegisterRequest.java | 2 ++ src/main/resources/templates/main/page/registerReview.html | 1 + 2 files changed, 3 insertions(+) diff --git a/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java b/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java index 1f7cc37a..1a47f706 100644 --- a/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java +++ b/src/main/java/com/t3t/frontserver/review/model/request/ReviewRegisterRequest.java @@ -16,6 +16,8 @@ public class ReviewRegisterRequest { private Long bookId; // 리뷰를 작성할 도서 id + private Long orderDetailId; // 주문 상세 id + @NotBlank(message = "리뷰 내용을 입력해주세요.") private String comment; // 리뷰 내용 diff --git a/src/main/resources/templates/main/page/registerReview.html b/src/main/resources/templates/main/page/registerReview.html index f18aef41..68c0e1c4 100644 --- a/src/main/resources/templates/main/page/registerReview.html +++ b/src/main/resources/templates/main/page/registerReview.html @@ -42,6 +42,7 @@

등록할 리뷰 이미지를 최대 5개까지 선택할 수 있습니다!< +