From 24b0ffa9aa74793beecf6fafdbecd8f9286fdabd Mon Sep 17 00:00:00 2001 From: Seong Jin Date: Wed, 26 Nov 2025 15:30:25 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor=20:=20isbn=20=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=EC=B1=85=20=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hansung/leafly/domain/book/service/BookServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hansung/leafly/domain/book/service/BookServiceImpl.java b/src/main/java/com/hansung/leafly/domain/book/service/BookServiceImpl.java index 629a0e0..adb39d8 100644 --- a/src/main/java/com/hansung/leafly/domain/book/service/BookServiceImpl.java +++ b/src/main/java/com/hansung/leafly/domain/book/service/BookServiceImpl.java @@ -58,10 +58,10 @@ public List search(String keyword, BookFilterReq req, Member member) List filters = req.getGenres(); return response.item().stream() - .filter(item -> matchesGenre(item, filters)) + .filter(item -> item.isbn13() != null && !item.isbn13().isBlank()) // ISBN 없는 책 제외 .map(item -> SearchRes.from( item, - bookmarkedSet.contains(Long.parseLong(item.isbn13())) // 북마크 여부 체크 + bookmarkedSet.contains(Long.parseLong(item.isbn13())) )) .toList(); } From dd4c6d7ac96ae6f956eddaeab7606be39dba0346 Mon Sep 17 00:00:00 2001 From: Seong Jin Date: Wed, 26 Nov 2025 16:13:57 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat=20:=20=EB=8F=85=ED=9B=84=EA=B0=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/bookreview/entity/BookReview.java | 15 ++++++++++ .../bookreview/service/BookReviewService.java | 3 ++ .../service/BookReviewServiceImpl.java | 26 +++++++++++++--- .../web/controller/BookReviewController.java | 12 ++++++++ .../bookreview/web/dto/ReviewUpdateReq.java | 30 +++++++++++++++++++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/hansung/leafly/domain/bookreview/web/dto/ReviewUpdateReq.java diff --git a/src/main/java/com/hansung/leafly/domain/bookreview/entity/BookReview.java b/src/main/java/com/hansung/leafly/domain/bookreview/entity/BookReview.java index b87be40..a05f47f 100644 --- a/src/main/java/com/hansung/leafly/domain/bookreview/entity/BookReview.java +++ b/src/main/java/com/hansung/leafly/domain/bookreview/entity/BookReview.java @@ -1,6 +1,7 @@ package com.hansung.leafly.domain.bookreview.entity; import com.hansung.leafly.domain.bookreview.web.dto.ReviewReq; +import com.hansung.leafly.domain.bookreview.web.dto.ReviewUpdateReq; import com.hansung.leafly.domain.member.entity.Member; import com.hansung.leafly.global.entity.BaseEntity; import jakarta.persistence.*; @@ -61,4 +62,18 @@ public static BookReview toEntity(Member member, ReviewReq req) { .build(); } + public void update(ReviewUpdateReq req) { + if (req.getTitle() != null) this.title = req.getTitle(); + if (req.getAuthor() != null) this.author = req.getAuthor(); + if (req.getThumbnail() != null) this.thumbnail = req.getThumbnail(); + if (req.getRating() != null) this.rating = req.getRating(); + if (req.getReviewTitle() != null) this.reviewTitle = req.getReviewTitle(); + if (req.getContent() != null) this.content = req.getContent(); + } + + public void replaceImages(List newImages) { + this.images.clear(); + this.images.addAll(newImages); + } + } diff --git a/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewService.java b/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewService.java index b20017e..7818a68 100644 --- a/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewService.java +++ b/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewService.java @@ -3,6 +3,7 @@ import com.hansung.leafly.domain.bookreview.web.dto.ReviewDetailsRes; import com.hansung.leafly.domain.bookreview.web.dto.ReviewListRes; import com.hansung.leafly.domain.bookreview.web.dto.ReviewReq; +import com.hansung.leafly.domain.bookreview.web.dto.ReviewUpdateReq; import com.hansung.leafly.domain.member.entity.Member; public interface BookReviewService { @@ -13,4 +14,6 @@ public interface BookReviewService { ReviewListRes getList(Member member); ReviewDetailsRes getDetails(Long reviewId,Member member); + + void update(Member member, Long reviewId, ReviewUpdateReq req); } diff --git a/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewServiceImpl.java b/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewServiceImpl.java index a69eac1..32d0ddc 100644 --- a/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewServiceImpl.java +++ b/src/main/java/com/hansung/leafly/domain/bookreview/service/BookReviewServiceImpl.java @@ -8,10 +8,7 @@ import com.hansung.leafly.domain.bookreview.repository.BookReviewRepository; import com.hansung.leafly.domain.bookreview.repository.BookTagRepository; import com.hansung.leafly.domain.bookreview.repository.ReviewImageRepository; -import com.hansung.leafly.domain.bookreview.web.dto.ReviewDetailsRes; -import com.hansung.leafly.domain.bookreview.web.dto.ReviewListRes; -import com.hansung.leafly.domain.bookreview.web.dto.ReviewReq; -import com.hansung.leafly.domain.bookreview.web.dto.ReviewRes; +import com.hansung.leafly.domain.bookreview.web.dto.*; import com.hansung.leafly.domain.member.entity.Member; import com.hansung.leafly.infra.s3.S3Service; import com.hansung.leafly.infra.s3.exception.S3RequestFailedException; @@ -82,6 +79,27 @@ public ReviewDetailsRes getDetails(Long reviewId, Member member) { return ReviewDetailsRes.from(review); } + @Override + @Transactional + public void update(Member member, Long reviewId, ReviewUpdateReq req) { + BookReview review = bookReviewRepository.findById(reviewId) + .orElseThrow(BookReviewNotFoundException::new); + + if (!review.getMember().getId().equals(member.getId())) { + throw new BookReviewAccessDeniedException(); + } + review.update(req); + + // 이미지 수정이 요청된 경우에만 처리 + if (req.getImages() != null && !req.getImages().isEmpty()) { + List newImages = processImages(req.getImages(), review); + //기존 이미지 제거 & 새로 교체 + review.replaceImages(newImages); + } + + bookReviewRepository.save(review); + } + // 카테고리 태그화 private List processTags(String rawTags, BookReview review) { if (rawTags == null || rawTags.isEmpty()) { diff --git a/src/main/java/com/hansung/leafly/domain/bookreview/web/controller/BookReviewController.java b/src/main/java/com/hansung/leafly/domain/bookreview/web/controller/BookReviewController.java index 983ed08..07c6dbf 100644 --- a/src/main/java/com/hansung/leafly/domain/bookreview/web/controller/BookReviewController.java +++ b/src/main/java/com/hansung/leafly/domain/bookreview/web/controller/BookReviewController.java @@ -5,6 +5,7 @@ import com.hansung.leafly.domain.bookreview.web.dto.ReviewDetailsRes; import com.hansung.leafly.domain.bookreview.web.dto.ReviewListRes; import com.hansung.leafly.domain.bookreview.web.dto.ReviewReq; +import com.hansung.leafly.domain.bookreview.web.dto.ReviewUpdateReq; import com.hansung.leafly.global.auth.security.CustomMemberDetails; import com.hansung.leafly.global.response.SuccessResponse; import jakarta.validation.Valid; @@ -58,4 +59,15 @@ public ResponseEntity> getDetails( ReviewDetailsRes res = bookReviewService.getDetails(reviewId,memberDetails.getMember()); return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(res)); } + + //독후감 수정 + @PatchMapping("/{reviewId}") + public ResponseEntity> update( + @AuthenticationPrincipal CustomMemberDetails memberDetails, + @PathVariable Long reviewId, + @ModelAttribute @Valid ReviewUpdateReq req + ){ + bookReviewService.update(memberDetails.getMember(), reviewId, req); + return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(null)); + } } diff --git a/src/main/java/com/hansung/leafly/domain/bookreview/web/dto/ReviewUpdateReq.java b/src/main/java/com/hansung/leafly/domain/bookreview/web/dto/ReviewUpdateReq.java new file mode 100644 index 0000000..173a9c2 --- /dev/null +++ b/src/main/java/com/hansung/leafly/domain/bookreview/web/dto/ReviewUpdateReq.java @@ -0,0 +1,30 @@ +package com.hansung.leafly.domain.bookreview.web.dto; + +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +public class ReviewUpdateReq { + + private String title; + private String author; + private String thumbnail; + private Integer rating; + private String category; + + @Size(max = 20) + private String reviewTitle; + + @Size(min = 10) + private String content; + + @Size(max = 3) + private List images; +}