From 349960769f2ac97c858fc972731e9d4a33d2f98b Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Tue, 29 Jul 2025 19:37:48 +0900 Subject: [PATCH] =?UTF-8?q?refactor(Bookmark):=20=EC=A3=BC=EC=A0=90=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20softDelete=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BookmarkController.java | 13 ++++++--- .../bookmark/service/BookmarkService.java | 29 +++++++++++++------ .../store/service/StoreServiceImpl.java | 6 ++-- .../bookmark/entity/Bookmark.java | 13 +++++++++ .../repository/BookmarkRepository.java | 24 ++++++++++++--- 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/controller/BookmarkController.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/controller/BookmarkController.java index 2aab9ca0..d57bd64c 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/controller/BookmarkController.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/controller/BookmarkController.java @@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; + @Tag(name = "Bookmark API", description = "북마크 API") @RestController @RequestMapping("/bookmarks") @@ -29,8 +30,9 @@ public class BookmarkController { @PostMapping("/{storeId}") @Operation(summary = "북마크 생성", description = "특정 주점에 대한 북마크 생성") @ApiResponse(responseCode = "201", description = "북마크 생성") - public ResponseEntity createBookmark(@PathVariable Long storeId,@AuthenticationPrincipal CustomOAuth2User customOAuth2User) { - BookmarkCreateResponse response = bookmarkService.createBookmark(storeId,customOAuth2User); + public ResponseEntity createBookmark(@PathVariable Long storeId, + @AuthenticationPrincipal CustomOAuth2User customOAuth2User) { + BookmarkCreateResponse response = bookmarkService.createBookmark(storeId, customOAuth2User); return ResponseEntity .status(HttpStatus.CREATED) @@ -40,6 +42,7 @@ public ResponseEntity createBookmark(@PathVariable Long storeId,@Authenticati ) ); } + @GetMapping @Operation(summary = "북마크 조회", description = "내가 북마크한 주점 조회") @ApiResponse(responseCode = "200", description = "북마크 조회") @@ -52,15 +55,17 @@ public ResponseEntity getAllBookmarks(@AuthenticationPrincipal CustomOAuth2Us ) ); } + @DeleteMapping("/{bookmarkId}") @Operation(summary = "북마크 삭제", description = "특정 주점에 대한 북마크 삭제") @ApiResponse(responseCode = "200", description = "북마크 삭제") - public ResponseEntity deleteBookmark(@PathVariable Long bookmarkId, @AuthenticationPrincipal CustomOAuth2User customOAuth2User) { + public ResponseEntity deleteBookmark(@PathVariable Long bookmarkId, + @AuthenticationPrincipal CustomOAuth2User customOAuth2User) { return ResponseEntity .ok() .body( ApiUtils.success( - bookmarkService.deleteBookmark(bookmarkId,customOAuth2User) + bookmarkService.deleteBookmark(bookmarkId, customOAuth2User) ) ); } diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/service/BookmarkService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/service/BookmarkService.java index 744d5201..4b2da27d 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/service/BookmarkService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/bookmark/service/BookmarkService.java @@ -3,6 +3,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import org.springframework.stereotype.Service; @@ -33,19 +34,29 @@ public class BookmarkService { @Transactional public BookmarkCreateResponse createBookmark(Long storeId, CustomOAuth2User customOAuth2User) { + parameterValidation(storeId, customOAuth2User); Store store = storeRepository.findById(storeId) .orElseThrow(() -> new EntityNotFoundException(storeId + " store not found.")); User user = userRepository.findById(customOAuth2User.getUserId()) .orElseThrow(() -> new EntityNotFoundException("User not found")); - if (bookmarkRepository.existsByUserAndStore(user, store)) { - throw new IllegalArgumentException("already bookmarked"); + Optional isBookmark = bookmarkRepository.findRawByUserAndStoreAndDeletedFalse(user, store); + + if (isBookmark.isPresent()) { + Bookmark bookmark = isBookmark.get(); + if (bookmark.isDeleted()) { + bookmark.restore(); + return BookmarkCreateResponse.fromEntity(bookmarkRepository.save(bookmark)); + } else { + throw new IllegalArgumentException("already bookmarked"); + } } Bookmark bookmark = Bookmark.builder() .store(store) .user(user) + .deleted(false) .build(); return BookmarkCreateResponse.fromEntity(bookmarkRepository.save(bookmark)); @@ -56,7 +67,7 @@ public List getBookmarks(CustomOAuth2User customOAuth2Use User user = userRepository.findById(customOAuth2User.getUserId()) .orElseThrow(UserNotFoundException::new); - List storeIds = bookmarkRepository.findAllByUser(user) + List storeIds = bookmarkRepository.findAllByUserAndDeletedFalse(user) .stream() .map(Bookmark::getStore) .map(Store::getStoreId) @@ -68,15 +79,15 @@ public List getBookmarks(CustomOAuth2User customOAuth2Use } @Transactional - public String deleteBookmark(Long bookmarkId, CustomOAuth2User customOAuth2User) { - parameterValidation(bookmarkId, customOAuth2User); - Bookmark bookmark = bookmarkRepository.findById(bookmarkId) - .orElseThrow(() -> new EntityNotFoundException(bookmarkId + " bookmark not found.")); + public String deleteBookmark(Long storeId, CustomOAuth2User customOAuth2User) { + parameterValidation(storeId, customOAuth2User); + Bookmark bookmark = bookmarkRepository.findActiveByUserIdAndStoreId(storeId, customOAuth2User.getUserId()) + .orElseThrow(() -> new EntityNotFoundException(storeId + " bookmark not found.")); if (!Objects.equals(bookmark.getUser().getId(), customOAuth2User.getUserId())) { throw new IllegalArgumentException("you can only delete your own bookmark"); } - bookmarkRepository.delete(bookmark); - return "Bookmark ID " + bookmarkId + " deleted."; + bookmark.softDelete(); + return "Bookmark ID " + storeId + " deleted."; } private static void parameterValidation(Long storeId, CustomOAuth2User customOAuth2User) { diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java index c02b2259..6082ae37 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/service/StoreServiceImpl.java @@ -79,7 +79,7 @@ public StoreDepartmentReadResponse getAllStoresByPageAndDeparments(Pageable page .toList(); // 2) 사용자 북마크된 storeId 집합 조회 - List storeBookmarkIds = bookmarkRepository.findAllByUser(user) + List storeBookmarkIds = bookmarkRepository.findAllByUserAndDeletedFalse(user) .stream() .map(Bookmark::getStore) .map(Store::getStoreId) @@ -118,7 +118,7 @@ public StoreDepartmentReadResponse getAllStoresByPageAndDeparments(Pageable page Department::getName )); - List allBookmarks = bookmarkRepository.findStoreIdByUser(user); + List allBookmarks = bookmarkRepository.findStoreIdByUserAndDeletedFalse(user); Map bookmarkMap = allBookmarks.stream() .collect(Collectors.toMap( bookmark -> bookmark.getStore().getStoreId(), @@ -155,7 +155,7 @@ public StoreDetailReadResponse getStoreByStoreId(Long storeId, CustomOAuth2User .map(Department::getName) .orElse("Unknown Department"); - boolean isBookmark = bookmarkRepository.existsByUserAndStore(user, store); + boolean isBookmark = bookmarkRepository.existsByUserAndStoreAndDeletedFalse(user, store); // 2-1) Redis에서 각 Store의 웨이팅 사이즈 조회 String key = "waiting:" + storeId; diff --git a/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/entity/Bookmark.java b/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/entity/Bookmark.java index 1f5b3fa2..c36a0dd5 100644 --- a/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/entity/Bookmark.java +++ b/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/entity/Bookmark.java @@ -1,9 +1,12 @@ package com.nowait.domainuserrdb.bookmark.entity; +import java.time.LocalDateTime; + import com.nowait.domaincorerdb.base.entity.BaseTimeEntity; import com.nowait.domaincorerdb.store.entity.Store; import com.nowait.domaincorerdb.user.entity.User; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -43,4 +46,14 @@ public class Bookmark extends BaseTimeEntity { @JoinColumn(name = "store_id") private Store store; + @Column(nullable = false) + private boolean deleted = false; + + public void softDelete() { + this.deleted = true; + } + + public void restore() { + this.deleted = false; + } } diff --git a/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/repository/BookmarkRepository.java b/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/repository/BookmarkRepository.java index dd27e0c9..53914f5c 100644 --- a/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/repository/BookmarkRepository.java +++ b/nowait-domain/domain-user-rdb/src/main/java/com/nowait/domainuserrdb/bookmark/repository/BookmarkRepository.java @@ -1,10 +1,12 @@ package com.nowait.domainuserrdb.bookmark.repository; -import java.awt.print.Book; import java.util.Collection; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import com.nowait.domaincorerdb.store.entity.Store; @@ -13,9 +15,23 @@ @Repository public interface BookmarkRepository extends JpaRepository { - boolean existsByUserAndStore(User user, Store store); + boolean existsByUserAndStoreAndDeletedFalse(User user, Store store); - Collection findAllByUser(User user); + Optional findRawByUserAndStoreAndDeletedFalse(User user, Store store); - List findStoreIdByUser(User user); + Collection findAllByUserAndDeletedFalse(User user); + + List findStoreIdByUserAndDeletedFalse(User user); + + @Query(""" + select b + from Bookmark b + where b.user.id = :userId + and b.store.storeId = :storeId + and b.deleted = false + """) + Optional findActiveByUserIdAndStoreId( + @Param("storeId") Long storeId, + @Param("userId") Long userId + ); }