From c95f681f9c462873824c5859f89458405a64b572 Mon Sep 17 00:00:00 2001 From: dungbik Date: Wed, 3 Sep 2025 17:03:18 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EC=B6=94=EA=B0=80=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BookmarkController.java | 36 +++++++++++ .../docs/BookmarkControllerDocs.java | 17 ++++++ .../flipnote/bookmark/entity/Bookmark.java | 59 +++++++++++++++++++ .../bookmark/entity/BookmarkTargetType.java | 5 ++ .../bookmark/exception/BookmarkErrorCode.java | 24 ++++++++ .../bookmark/model/BookmarkTargetType.java | 11 ++++ .../repository/BookmarkRepository.java | 10 ++++ .../service/BookmarkPolicyService.java | 29 +++++++++ .../bookmark/service/BookmarkService.java | 51 ++++++++++++++++ .../service/BookmarkTargetFetchService.java | 39 ++++++++++++ .../fetcher/BookmarkCardSetFetcher.java | 24 ++++++++ .../fetcher/BookmarkTargetFetcher.java | 9 +++ .../common/model/response/IdResponse.java | 10 ++++ .../project/flipnote/like/entity/Like.java | 9 ++- .../like/model/LikeTargetTypeRequest.java | 7 +-- ...etFetcher.java => LikeCardSetFetcher.java} | 2 +- 16 files changed, 334 insertions(+), 8 deletions(-) create mode 100644 src/main/java/project/flipnote/bookmark/controller/BookmarkController.java create mode 100644 src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java create mode 100644 src/main/java/project/flipnote/bookmark/entity/Bookmark.java create mode 100644 src/main/java/project/flipnote/bookmark/entity/BookmarkTargetType.java create mode 100644 src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java create mode 100644 src/main/java/project/flipnote/bookmark/model/BookmarkTargetType.java create mode 100644 src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java create mode 100644 src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java create mode 100644 src/main/java/project/flipnote/bookmark/service/BookmarkService.java create mode 100644 src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java create mode 100644 src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java create mode 100644 src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java create mode 100644 src/main/java/project/flipnote/common/model/response/IdResponse.java rename src/main/java/project/flipnote/like/service/fetcher/{CardSetFetcher.java => LikeCardSetFetcher.java} (91%) diff --git a/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java b/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java new file mode 100644 index 00000000..70e74bd9 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java @@ -0,0 +1,36 @@ +package project.flipnote.bookmark.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +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.RestController; + +import lombok.RequiredArgsConstructor; +import project.flipnote.bookmark.controller.docs.BookmarkControllerDocs; +import project.flipnote.bookmark.model.BookmarkTargetType; +import project.flipnote.bookmark.service.BookmarkService; +import project.flipnote.common.model.response.IdResponse; +import project.flipnote.common.security.dto.AuthPrinciple; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/v1/bookmarks/{targetType}") +public class BookmarkController implements BookmarkControllerDocs { + + private final BookmarkService bookmarkService; + + @PostMapping("/{targetId}") + public ResponseEntity addBookmark( + @PathVariable("targetType") BookmarkTargetType targetType, + @PathVariable("targetId") Long targetId, + @AuthenticationPrincipal AuthPrinciple authPrinciple + ) { + IdResponse res = bookmarkService.addBookmark(authPrinciple.userId(), targetType.toDomainType(), targetId); + + return ResponseEntity.status(HttpStatus.CREATED).body(res); + } + +} diff --git a/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java b/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java new file mode 100644 index 00000000..6f7678ce --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java @@ -0,0 +1,17 @@ +package project.flipnote.bookmark.controller.docs; + +import org.springframework.http.ResponseEntity; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import project.flipnote.bookmark.model.BookmarkTargetType; +import project.flipnote.common.model.response.IdResponse; +import project.flipnote.common.security.dto.AuthPrinciple; + +@Tag(name = "Bookmark", description = "Bookmark API") +public interface BookmarkControllerDocs { + + @Operation(summary = "즐겨찾기 추가", security = {@SecurityRequirement(name = "access-token")}) + ResponseEntity addBookmark(BookmarkTargetType targetType, Long targetId, AuthPrinciple authPrinciple); +} diff --git a/src/main/java/project/flipnote/bookmark/entity/Bookmark.java b/src/main/java/project/flipnote/bookmark/entity/Bookmark.java new file mode 100644 index 00000000..079611af --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/entity/Bookmark.java @@ -0,0 +1,59 @@ +package project.flipnote.bookmark.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import project.flipnote.common.entity.BaseEntity; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table( + name = "bookmarks", + indexes = { + @Index( + name = "idx_bookmarks_targettype_targetid_userid", + columnList = "target_type, target_id, user_id" + ) + }, + uniqueConstraints = { + @UniqueConstraint( + name = "uk_bookmarks_targettype_targetid_userid", + columnNames = {"target_type", "target_id", "user_id"} + ) + } +) +@Entity +public class Bookmark extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private BookmarkTargetType targetType; + + @Column(nullable = false) + private Long targetId; + + @Column(nullable = false) + private Long userId; + + @Builder + public Bookmark(BookmarkTargetType targetType, Long targetId, Long userId) { + this.targetType = targetType; + this.targetId = targetId; + this.userId = userId; + } +} diff --git a/src/main/java/project/flipnote/bookmark/entity/BookmarkTargetType.java b/src/main/java/project/flipnote/bookmark/entity/BookmarkTargetType.java new file mode 100644 index 00000000..153e0983 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/entity/BookmarkTargetType.java @@ -0,0 +1,5 @@ +package project.flipnote.bookmark.entity; + +public enum BookmarkTargetType { + CARD_SET +} diff --git a/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java b/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java new file mode 100644 index 00000000..ca731339 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java @@ -0,0 +1,24 @@ +package project.flipnote.bookmark.exception; + +import org.springframework.http.HttpStatus; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import project.flipnote.common.exception.ErrorCode; + +@Getter +@RequiredArgsConstructor +public enum BookmarkErrorCode implements ErrorCode { + BOOKMARK_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND, "BOOKMARK_001", "즐겨찾기 대상이 존재하지 않습니다."), + BOOKMARK_ALREADY_EXISTS(HttpStatus.CONFLICT, "BOOKMARK_002", "이미 즐겨찾기 되어 있습니다."), + BOOKMARK_FETCHER_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "BOOKMARK_003", "현재 즐겨찾기 할 수 없는 대상입니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public int getStatus() { + return httpStatus.value(); + } +} diff --git a/src/main/java/project/flipnote/bookmark/model/BookmarkTargetType.java b/src/main/java/project/flipnote/bookmark/model/BookmarkTargetType.java new file mode 100644 index 00000000..c51f6c47 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/model/BookmarkTargetType.java @@ -0,0 +1,11 @@ +package project.flipnote.bookmark.model; + +public enum BookmarkTargetType { + card_sets; + + public project.flipnote.bookmark.entity.BookmarkTargetType toDomainType() { + return switch (this) { + case card_sets -> project.flipnote.bookmark.entity.BookmarkTargetType.CARD_SET; + }; + } +} diff --git a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java new file mode 100644 index 00000000..c53c365b --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java @@ -0,0 +1,10 @@ +package project.flipnote.bookmark.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import project.flipnote.bookmark.entity.Bookmark; +import project.flipnote.bookmark.entity.BookmarkTargetType; + +public interface BookmarkRepository extends JpaRepository { + boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); +} diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java new file mode 100644 index 00000000..7ca15ad2 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java @@ -0,0 +1,29 @@ +package project.flipnote.bookmark.service; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import project.flipnote.bookmark.entity.BookmarkTargetType; +import project.flipnote.bookmark.exception.BookmarkErrorCode; +import project.flipnote.bookmark.repository.BookmarkRepository; +import project.flipnote.common.exception.BizException; + +@RequiredArgsConstructor +@Service +public class BookmarkPolicyService { + + private final BookmarkRepository bookmarkRepository; + private final BookmarkTargetFetchService bookmarkTargetFetchService; + + public void validateTargetExists(BookmarkTargetType targetType, Long targetId) { + if (!bookmarkTargetFetchService.existsByTypeAndId(targetType, targetId)) { + throw new BizException(BookmarkErrorCode.BOOKMARK_TARGET_NOT_FOUND); + } + } + + public void validateBookmarkNotExists(BookmarkTargetType targetType, Long targetId, Long userId) { + if (bookmarkRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId)) { + throw new BizException(BookmarkErrorCode.BOOKMARK_ALREADY_EXISTS); + } + } +} diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java new file mode 100644 index 00000000..db151d36 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java @@ -0,0 +1,51 @@ +package project.flipnote.bookmark.service; + +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import lombok.RequiredArgsConstructor; +import project.flipnote.bookmark.entity.Bookmark; +import project.flipnote.bookmark.entity.BookmarkTargetType; +import project.flipnote.bookmark.exception.BookmarkErrorCode; +import project.flipnote.bookmark.repository.BookmarkRepository; +import project.flipnote.common.exception.BizException; +import project.flipnote.common.model.response.IdResponse; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class BookmarkService { + + private final BookmarkPolicyService bookmarkPolicyService; + private final BookmarkRepository bookmarkRepository; + + /** + * 즐겨찾기 추가 + * + * @param userId 즐겨찾기 추가 요청한 사용자 ID + * @param targetType 즐겨찾기 대상 타입 + * @param targetId 즐겨찾기 대상 ID + * @return 생성된 즐겨찾기 엔티티의 ID를 담은 응답 + * @author 윤정환 + */ + @Transactional + public IdResponse addBookmark(Long userId, BookmarkTargetType targetType, Long targetId) { + bookmarkPolicyService.validateBookmarkNotExists(targetType, targetId, userId); + bookmarkPolicyService.validateTargetExists(targetType, targetId); + + Bookmark bookmark = Bookmark.builder() + .targetType(targetType) + .targetId(targetId) + .userId(userId) + .build(); + + try { + bookmarkRepository.save(bookmark); + } catch (DataIntegrityViolationException e) { + throw new BizException(BookmarkErrorCode.BOOKMARK_ALREADY_EXISTS); + } + + return IdResponse.from(bookmark.getId()); + } +} diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java new file mode 100644 index 00000000..3ff86838 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java @@ -0,0 +1,39 @@ +package project.flipnote.bookmark.service; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; + +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import project.flipnote.bookmark.entity.BookmarkTargetType; +import project.flipnote.bookmark.exception.BookmarkErrorCode; +import project.flipnote.bookmark.service.fetcher.BookmarkTargetFetcher; +import project.flipnote.common.exception.BizException; + +@RequiredArgsConstructor +@Service +public class BookmarkTargetFetchService { + + private final List fetchers; + + private Map fetcherMap; + + @PostConstruct + public void init() { + this.fetcherMap = this.fetchers.stream() + .collect(Collectors.toMap(BookmarkTargetFetcher::getTargetType, Function.identity())); + } + + public boolean existsByTypeAndId(BookmarkTargetType targetType, Long targetId) { + BookmarkTargetFetcher targetFetcher = fetcherMap.get(targetType); + if (targetFetcher == null) { + throw new BizException(BookmarkErrorCode.BOOKMARK_FETCHER_NOT_FOUND); + } + + return targetFetcher.existsById(targetId); + } +} diff --git a/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java new file mode 100644 index 00000000..516c9c13 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java @@ -0,0 +1,24 @@ +package project.flipnote.bookmark.service.fetcher; + +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; +import project.flipnote.bookmark.entity.BookmarkTargetType; +import project.flipnote.cardset.service.CardSetService; + +@RequiredArgsConstructor +@Component +public class BookmarkCardSetFetcher implements BookmarkTargetFetcher { + + private final CardSetService cardSetService; + + @Override + public BookmarkTargetType getTargetType() { + return BookmarkTargetType.CARD_SET; + } + + @Override + public boolean existsById(Long targetId) { + return cardSetService.existsById(targetId); + } +} diff --git a/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java new file mode 100644 index 00000000..2550a262 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java @@ -0,0 +1,9 @@ +package project.flipnote.bookmark.service.fetcher; + +import project.flipnote.bookmark.entity.BookmarkTargetType; + +public interface BookmarkTargetFetcher { + BookmarkTargetType getTargetType(); + + boolean existsById(Long targetId); +} diff --git a/src/main/java/project/flipnote/common/model/response/IdResponse.java b/src/main/java/project/flipnote/common/model/response/IdResponse.java new file mode 100644 index 00000000..629695cf --- /dev/null +++ b/src/main/java/project/flipnote/common/model/response/IdResponse.java @@ -0,0 +1,10 @@ +package project.flipnote.common.model.response; + +public record IdResponse( + Long id +) { + + public static IdResponse from(Long id) { + return new IdResponse(id); + } +} diff --git a/src/main/java/project/flipnote/like/entity/Like.java b/src/main/java/project/flipnote/like/entity/Like.java index 1769460d..4407734f 100644 --- a/src/main/java/project/flipnote/like/entity/Like.java +++ b/src/main/java/project/flipnote/like/entity/Like.java @@ -21,12 +21,15 @@ @Table( name = "likes", indexes = { - @Index(name = "idx_type_target_user", columnList = "type, target_id, user_id") + @Index( + name = "idx_likes_targettype_targetid_userid", + columnList = "target_type, target_id, user_id" + ) }, uniqueConstraints = { @UniqueConstraint( - name = "uk_type_target_user", - columnNames = {"type", "target_id", "user_id"} + name = "uk_likes_targettype_targetid_userid", + columnNames = {"target_type", "target_id", "user_id"} ) } ) diff --git a/src/main/java/project/flipnote/like/model/LikeTargetTypeRequest.java b/src/main/java/project/flipnote/like/model/LikeTargetTypeRequest.java index f595e25e..8bc862b2 100644 --- a/src/main/java/project/flipnote/like/model/LikeTargetTypeRequest.java +++ b/src/main/java/project/flipnote/like/model/LikeTargetTypeRequest.java @@ -6,9 +6,8 @@ public enum LikeTargetTypeRequest { card_set; public LikeTargetType toDomainType() { - switch (this) { - case card_set: return LikeTargetType.CARD_SET; - default: throw new IllegalArgumentException("Invalid LikeTargetTypeRequest"); - } + return switch (this) { + case card_set -> LikeTargetType.CARD_SET; + }; } } diff --git a/src/main/java/project/flipnote/like/service/fetcher/CardSetFetcher.java b/src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java similarity index 91% rename from src/main/java/project/flipnote/like/service/fetcher/CardSetFetcher.java rename to src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java index c0295f2b..a0581982 100644 --- a/src/main/java/project/flipnote/like/service/fetcher/CardSetFetcher.java +++ b/src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java @@ -15,7 +15,7 @@ @RequiredArgsConstructor @Component -public class CardSetFetcher implements LikeTargetFetcher { +public class LikeCardSetFetcher implements LikeTargetFetcher { private final CardSetService cardSetService; From 4f980659a9c0d674ef38184cded6a361348ba4bb Mon Sep 17 00:00:00 2001 From: dungbik Date: Wed, 3 Sep 2025 17:11:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=9C=EA=B1=B0=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BookmarkController.java | 11 ++++++++++ .../docs/BookmarkControllerDocs.java | 7 +++++++ .../bookmark/exception/BookmarkErrorCode.java | 3 ++- .../repository/BookmarkRepository.java | 4 ++++ .../bookmark/service/BookmarkService.java | 21 ++++++++++++++++++- 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java b/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java index 70e74bd9..97a97de2 100644 --- a/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java +++ b/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpStatus; 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.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -33,4 +34,14 @@ public ResponseEntity addBookmark( return ResponseEntity.status(HttpStatus.CREATED).body(res); } + @DeleteMapping("/{targetId}") + public ResponseEntity deleteBookmark( + @PathVariable("targetType") BookmarkTargetType targetType, + @PathVariable("targetId") Long targetId, + @AuthenticationPrincipal AuthPrinciple authPrinciple + ) { + IdResponse res = bookmarkService.deleteBookmark(authPrinciple.userId(), targetType.toDomainType(), targetId); + + return ResponseEntity.ok(res); + } } diff --git a/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java b/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java index 6f7678ce..e67c0405 100644 --- a/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java +++ b/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java @@ -14,4 +14,11 @@ public interface BookmarkControllerDocs { @Operation(summary = "즐겨찾기 추가", security = {@SecurityRequirement(name = "access-token")}) ResponseEntity addBookmark(BookmarkTargetType targetType, Long targetId, AuthPrinciple authPrinciple); + + @Operation(summary = "즐겨찾기 제거", security = {@SecurityRequirement(name = "access-token")}) + ResponseEntity deleteBookmark( + BookmarkTargetType targetType, + Long targetId, + AuthPrinciple authPrinciple + ); } diff --git a/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java b/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java index ca731339..f510e00b 100644 --- a/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java +++ b/src/main/java/project/flipnote/bookmark/exception/BookmarkErrorCode.java @@ -11,7 +11,8 @@ public enum BookmarkErrorCode implements ErrorCode { BOOKMARK_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND, "BOOKMARK_001", "즐겨찾기 대상이 존재하지 않습니다."), BOOKMARK_ALREADY_EXISTS(HttpStatus.CONFLICT, "BOOKMARK_002", "이미 즐겨찾기 되어 있습니다."), - BOOKMARK_FETCHER_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "BOOKMARK_003", "현재 즐겨찾기 할 수 없는 대상입니다."); + BOOKMARK_FETCHER_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "BOOKMARK_003", "현재 즐겨찾기 할 수 없는 대상입니다."), + BOOKMARK_NOT_EXISTS(HttpStatus.NOT_FOUND, "BOOKMARK_004", "즐겨찾기가 되어 있지 않습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java index c53c365b..eeb074a9 100644 --- a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java @@ -1,5 +1,7 @@ package project.flipnote.bookmark.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import project.flipnote.bookmark.entity.Bookmark; @@ -7,4 +9,6 @@ public interface BookmarkRepository extends JpaRepository { boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); + + Optional findByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); } diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java index db151d36..b7c8a127 100644 --- a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java @@ -26,7 +26,7 @@ public class BookmarkService { * @param userId 즐겨찾기 추가 요청한 사용자 ID * @param targetType 즐겨찾기 대상 타입 * @param targetId 즐겨찾기 대상 ID - * @return 생성된 즐겨찾기 엔티티의 ID를 담은 응답 + * @return 생성된 즐겨찾기 대상의 ID를 담은 응답 * @author 윤정환 */ @Transactional @@ -48,4 +48,23 @@ public IdResponse addBookmark(Long userId, BookmarkTargetType targetType, Long t return IdResponse.from(bookmark.getId()); } + + /** + * 즐겨찾기 제거 + * + * @param userId 즐겨찾기 제거 요청한 회원 ID + * @param targetType 즐겨찾기 대상 타입 + * @param targetId 즐겨찾기 대상 ID + * @return 삭제된 즐겨찾기 대상의 ID를 담은 응답 + * @author 윤정환 + */ + @Transactional + public IdResponse deleteBookmark(Long userId, BookmarkTargetType targetType, Long targetId) { + Bookmark bookmark = bookmarkRepository.findByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId) + .orElseThrow(() -> new BizException(BookmarkErrorCode.BOOKMARK_NOT_EXISTS)); + + bookmarkRepository.delete(bookmark); + + return IdResponse.from(bookmark.getId()); + } } From c8403dd44440d458df634d3c284897c422b7c024 Mon Sep 17 00:00:00 2001 From: dungbik Date: Wed, 3 Sep 2025 17:52:46 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BookmarkController.java | 19 ++++++++ .../docs/BookmarkControllerDocs.java | 11 +++++ .../bookmark/model/BookmarkResponse.java | 17 +++++++ .../bookmark/model/BookmarkSearchRequest.java | 18 ++++++++ .../model/BookmarkTargetResponse.java | 5 +++ .../model/CardSetBookmarkResponse.java | 18 ++++++++ .../repository/BookmarkRepository.java | 4 ++ .../service/BookmarkPolicyService.java | 3 +- .../bookmark/service/BookmarkService.java | 44 +++++++++++++++++++ .../service/BookmarkTargetFetchService.java | 29 +++++++++--- .../fetcher/BookmarkCardSetFetcher.java | 15 ++++++- .../fetcher/BookmarkTargetFetcher.java | 8 +++- .../cardset/model/CardSetSearchRequest.java | 2 +- .../cardset/service/CardSetService.java | 3 +- .../common/model/request/PagingRequest.java | 4 +- .../model/GroupInvitationListRequest.java | 2 +- .../like/model/LikeSearchRequest.java | 2 +- .../flipnote/like/service/LikeService.java | 5 +-- .../service/fetcher/LikeCardSetFetcher.java | 4 +- .../service/fetcher/LikeTargetFetcher.java | 4 +- 20 files changed, 195 insertions(+), 22 deletions(-) create mode 100644 src/main/java/project/flipnote/bookmark/model/BookmarkResponse.java create mode 100644 src/main/java/project/flipnote/bookmark/model/BookmarkSearchRequest.java create mode 100644 src/main/java/project/flipnote/bookmark/model/BookmarkTargetResponse.java create mode 100644 src/main/java/project/flipnote/bookmark/model/CardSetBookmarkResponse.java diff --git a/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java b/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java index 97a97de2..4235949d 100644 --- a/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java +++ b/src/main/java/project/flipnote/bookmark/controller/BookmarkController.java @@ -4,16 +4,23 @@ 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.ModelAttribute; 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.RestController; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import project.flipnote.bookmark.controller.docs.BookmarkControllerDocs; +import project.flipnote.bookmark.model.BookmarkResponse; +import project.flipnote.bookmark.model.BookmarkSearchRequest; +import project.flipnote.bookmark.model.BookmarkTargetResponse; import project.flipnote.bookmark.model.BookmarkTargetType; import project.flipnote.bookmark.service.BookmarkService; import project.flipnote.common.model.response.IdResponse; +import project.flipnote.common.model.response.PagingResponse; import project.flipnote.common.security.dto.AuthPrinciple; @RequiredArgsConstructor @@ -44,4 +51,16 @@ public ResponseEntity deleteBookmark( return ResponseEntity.ok(res); } + + @GetMapping + public ResponseEntity>> getBookmarks( + @PathVariable("targetType") BookmarkTargetType targetType, + @Valid @ModelAttribute BookmarkSearchRequest req, + @AuthenticationPrincipal AuthPrinciple authPrinciple + ) { + PagingResponse> res + = bookmarkService.getBookmarks(authPrinciple.userId(), targetType.toDomainType(), req); + + return ResponseEntity.ok(res); + } } diff --git a/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java b/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java index e67c0405..ff3c1f3d 100644 --- a/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java +++ b/src/main/java/project/flipnote/bookmark/controller/docs/BookmarkControllerDocs.java @@ -5,8 +5,12 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import project.flipnote.bookmark.model.BookmarkResponse; +import project.flipnote.bookmark.model.BookmarkSearchRequest; +import project.flipnote.bookmark.model.BookmarkTargetResponse; import project.flipnote.bookmark.model.BookmarkTargetType; import project.flipnote.common.model.response.IdResponse; +import project.flipnote.common.model.response.PagingResponse; import project.flipnote.common.security.dto.AuthPrinciple; @Tag(name = "Bookmark", description = "Bookmark API") @@ -21,4 +25,11 @@ ResponseEntity deleteBookmark( Long targetId, AuthPrinciple authPrinciple ); + + @Operation(summary = "즐겨찾기 목록 조회", security = {@SecurityRequirement(name = "access-token")}) + ResponseEntity>> getBookmarks( + BookmarkTargetType targetType, + BookmarkSearchRequest req, + AuthPrinciple authPrinciple + ); } diff --git a/src/main/java/project/flipnote/bookmark/model/BookmarkResponse.java b/src/main/java/project/flipnote/bookmark/model/BookmarkResponse.java new file mode 100644 index 00000000..64f21a7f --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/model/BookmarkResponse.java @@ -0,0 +1,17 @@ +package project.flipnote.bookmark.model; + +import java.time.LocalDateTime; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@AllArgsConstructor +@Data +public class BookmarkResponse { + private T target; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime bookmarkedAt; +} diff --git a/src/main/java/project/flipnote/bookmark/model/BookmarkSearchRequest.java b/src/main/java/project/flipnote/bookmark/model/BookmarkSearchRequest.java new file mode 100644 index 00000000..d2a3ce21 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/model/BookmarkSearchRequest.java @@ -0,0 +1,18 @@ +package project.flipnote.bookmark.model; + +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; + +import lombok.Getter; +import lombok.Setter; +import project.flipnote.common.model.request.PagingRequest; + +@Getter +@Setter +public class BookmarkSearchRequest extends PagingRequest { + + @Override + public PageRequest getPageRequest() { + return PageRequest.of(getPage() - 1, getSize(), Sort.by(Sort.Direction.DESC, "id")); + } +} diff --git a/src/main/java/project/flipnote/bookmark/model/BookmarkTargetResponse.java b/src/main/java/project/flipnote/bookmark/model/BookmarkTargetResponse.java new file mode 100644 index 00000000..5c32882b --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/model/BookmarkTargetResponse.java @@ -0,0 +1,5 @@ +package project.flipnote.bookmark.model; + +public abstract class BookmarkTargetResponse { + public abstract Long getId(); +} diff --git a/src/main/java/project/flipnote/bookmark/model/CardSetBookmarkResponse.java b/src/main/java/project/flipnote/bookmark/model/CardSetBookmarkResponse.java new file mode 100644 index 00000000..66233259 --- /dev/null +++ b/src/main/java/project/flipnote/bookmark/model/CardSetBookmarkResponse.java @@ -0,0 +1,18 @@ +package project.flipnote.bookmark.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import project.flipnote.cardset.model.CardSetSummaryResponse; + +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@Data +public class CardSetBookmarkResponse extends BookmarkTargetResponse { + private Long id; + private String name; + + public static CardSetBookmarkResponse from(CardSetSummaryResponse res) { + return new CardSetBookmarkResponse(res.cardSetId(), res.name()); + } +} diff --git a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java index eeb074a9..c2980e09 100644 --- a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java @@ -2,6 +2,8 @@ import java.util.Optional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import project.flipnote.bookmark.entity.Bookmark; @@ -11,4 +13,6 @@ public interface BookmarkRepository extends JpaRepository { boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); Optional findByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); + + Page findAllByTargetTypeAndUserId(BookmarkTargetType targetType, Long userId, Pageable pageable); } diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java index 7ca15ad2..80b51391 100644 --- a/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import project.flipnote.bookmark.entity.BookmarkTargetType; import project.flipnote.bookmark.exception.BookmarkErrorCode; +import project.flipnote.bookmark.model.BookmarkTargetResponse; import project.flipnote.bookmark.repository.BookmarkRepository; import project.flipnote.common.exception.BizException; @@ -13,7 +14,7 @@ public class BookmarkPolicyService { private final BookmarkRepository bookmarkRepository; - private final BookmarkTargetFetchService bookmarkTargetFetchService; + private final BookmarkTargetFetchService bookmarkTargetFetchService; public void validateTargetExists(BookmarkTargetType targetType, Long targetId) { if (!bookmarkTargetFetchService.existsByTypeAndId(targetType, targetId)) { diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java index b7c8a127..c835a83e 100644 --- a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java @@ -1,6 +1,12 @@ package project.flipnote.bookmark.service; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -8,9 +14,13 @@ import project.flipnote.bookmark.entity.Bookmark; import project.flipnote.bookmark.entity.BookmarkTargetType; import project.flipnote.bookmark.exception.BookmarkErrorCode; +import project.flipnote.bookmark.model.BookmarkResponse; +import project.flipnote.bookmark.model.BookmarkSearchRequest; +import project.flipnote.bookmark.model.BookmarkTargetResponse; import project.flipnote.bookmark.repository.BookmarkRepository; import project.flipnote.common.exception.BizException; import project.flipnote.common.model.response.IdResponse; +import project.flipnote.common.model.response.PagingResponse; @RequiredArgsConstructor @Transactional(readOnly = true) @@ -19,6 +29,7 @@ public class BookmarkService { private final BookmarkPolicyService bookmarkPolicyService; private final BookmarkRepository bookmarkRepository; + private final BookmarkTargetFetchService bookmarkTargetFetchService; /** * 즐겨찾기 추가 @@ -67,4 +78,37 @@ public IdResponse deleteBookmark(Long userId, BookmarkTargetType targetType, Lon return IdResponse.from(bookmark.getId()); } + + /** + * 즐겨찾기 목록 조회 + * + * @param userId 즐겨찾기 목록 조회하는 회원 ID + * @param targetType 즐겨찾기 목록 대상 타입 + * @param req 페이징 및 검색 조건이 포함된 요청 정보 + * @return 페이징된 즐겨찾기 목록 + * @author 윤정환 + */ + public PagingResponse> getBookmarks( + Long userId, + BookmarkTargetType targetType, + BookmarkSearchRequest req + ) { + Page bookmarkPage + = bookmarkRepository.findAllByTargetTypeAndUserId(targetType, userId, req.getPageRequest()); + Map likedAtMap = bookmarkPage.stream() + .collect(Collectors.toMap(Bookmark::getTargetId, Bookmark::getCreatedAt)); + Set targetIds = likedAtMap.keySet(); + + Map targetMap + = bookmarkTargetFetchService.fetchByTypeAndIds(targetType, targetIds); + Page> content + = bookmarkPage.map(bookmark -> + new BookmarkResponse<>( + targetMap.get(bookmark.getTargetId()), + likedAtMap.get(bookmark.getTargetId()) + ) + ); + + return PagingResponse.from(content); + } } diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java index 3ff86838..f67c84cb 100644 --- a/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkTargetFetchService.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -11,16 +12,17 @@ import lombok.RequiredArgsConstructor; import project.flipnote.bookmark.entity.BookmarkTargetType; import project.flipnote.bookmark.exception.BookmarkErrorCode; +import project.flipnote.bookmark.model.BookmarkTargetResponse; import project.flipnote.bookmark.service.fetcher.BookmarkTargetFetcher; import project.flipnote.common.exception.BizException; @RequiredArgsConstructor @Service -public class BookmarkTargetFetchService { +public class BookmarkTargetFetchService { - private final List fetchers; + private final List> fetchers; - private Map fetcherMap; + private Map> fetcherMap; @PostConstruct public void init() { @@ -29,11 +31,26 @@ public void init() { } public boolean existsByTypeAndId(BookmarkTargetType targetType, Long targetId) { - BookmarkTargetFetcher targetFetcher = fetcherMap.get(targetType); - if (targetFetcher == null) { + BookmarkTargetFetcher targetFetcher = getFetcher(targetType); + + return targetFetcher.existsById(targetId); + } + + public Map fetchByTypeAndIds( + BookmarkTargetType targetType, + Set targetIds + ) { + BookmarkTargetFetcher targetFetcher = getFetcher(targetType); + + return targetFetcher.fetchByIds(targetIds); + } + + private BookmarkTargetFetcher getFetcher(BookmarkTargetType targetType) { + BookmarkTargetFetcher fetcher = fetcherMap.get(targetType); + if (fetcher == null) { throw new BizException(BookmarkErrorCode.BOOKMARK_FETCHER_NOT_FOUND); } - return targetFetcher.existsById(targetId); + return fetcher; } } diff --git a/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java index 516c9c13..77f30121 100644 --- a/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java +++ b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkCardSetFetcher.java @@ -1,14 +1,20 @@ package project.flipnote.bookmark.service.fetcher; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; import project.flipnote.bookmark.entity.BookmarkTargetType; +import project.flipnote.bookmark.model.CardSetBookmarkResponse; import project.flipnote.cardset.service.CardSetService; @RequiredArgsConstructor @Component -public class BookmarkCardSetFetcher implements BookmarkTargetFetcher { +public class BookmarkCardSetFetcher implements BookmarkTargetFetcher { private final CardSetService cardSetService; @@ -21,4 +27,11 @@ public BookmarkTargetType getTargetType() { public boolean existsById(Long targetId) { return cardSetService.existsById(targetId); } + + @Override + public Map fetchByIds(Set ids) { + return cardSetService.getCardSetsByIds(ids).stream() + .map(CardSetBookmarkResponse::from) + .collect(Collectors.toMap(CardSetBookmarkResponse::getId, Function.identity())); + } } diff --git a/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java index 2550a262..0be48db7 100644 --- a/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java +++ b/src/main/java/project/flipnote/bookmark/service/fetcher/BookmarkTargetFetcher.java @@ -1,9 +1,15 @@ package project.flipnote.bookmark.service.fetcher; +import java.util.Map; +import java.util.Set; + import project.flipnote.bookmark.entity.BookmarkTargetType; +import project.flipnote.bookmark.model.BookmarkTargetResponse; -public interface BookmarkTargetFetcher { +public interface BookmarkTargetFetcher { BookmarkTargetType getTargetType(); boolean existsById(Long targetId); + + Map fetchByIds(Set ids); } diff --git a/src/main/java/project/flipnote/cardset/model/CardSetSearchRequest.java b/src/main/java/project/flipnote/cardset/model/CardSetSearchRequest.java index a2450404..5833b52a 100644 --- a/src/main/java/project/flipnote/cardset/model/CardSetSearchRequest.java +++ b/src/main/java/project/flipnote/cardset/model/CardSetSearchRequest.java @@ -30,6 +30,6 @@ public PageRequest getPageRequest() { direction = Sort.Direction.DESC; } - return PageRequest.of(getPage() - 1, getSize() + 1, Sort.by(direction, effectiveSortBy)); + return PageRequest.of(getPage() - 1, getSize(), Sort.by(direction, effectiveSortBy)); } } diff --git a/src/main/java/project/flipnote/cardset/service/CardSetService.java b/src/main/java/project/flipnote/cardset/service/CardSetService.java index 583dcf46..41b8e02a 100644 --- a/src/main/java/project/flipnote/cardset/service/CardSetService.java +++ b/src/main/java/project/flipnote/cardset/service/CardSetService.java @@ -1,6 +1,7 @@ package project.flipnote.cardset.service; import java.util.List; +import java.util.Set; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; @@ -212,7 +213,7 @@ public void decrementLikeCount(Long cardSetId) { * @author 윤정환 */ @Transactional - public List getCardSetsByIds(List targetIds) { + public List getCardSetsByIds(Set targetIds) { // TODO: MSA로 전환시 전용 DTO로 변경 필요 return cardSetRepository.findAllById(targetIds).stream() .map(CardSetSummaryResponse::from) diff --git a/src/main/java/project/flipnote/common/model/request/PagingRequest.java b/src/main/java/project/flipnote/common/model/request/PagingRequest.java index 1fd7d51b..bc53bbee 100644 --- a/src/main/java/project/flipnote/common/model/request/PagingRequest.java +++ b/src/main/java/project/flipnote/common/model/request/PagingRequest.java @@ -27,7 +27,7 @@ public class PagingRequest { @Schema(hidden = true) public PageRequest getPageRequest() { if (sortBy == null || sortBy.isEmpty()) { - return PageRequest.of(page - 1, size + 1); + return PageRequest.of(page - 1, size); } else { Sort.Direction direction; try { @@ -36,7 +36,7 @@ public PageRequest getPageRequest() { direction = Sort.Direction.DESC; } - return PageRequest.of(page - 1, size + 1, Sort.by(direction, sortBy)); + return PageRequest.of(page - 1, size, Sort.by(direction, sortBy)); } } } diff --git a/src/main/java/project/flipnote/group/model/GroupInvitationListRequest.java b/src/main/java/project/flipnote/group/model/GroupInvitationListRequest.java index 68a1c9b4..869376e5 100644 --- a/src/main/java/project/flipnote/group/model/GroupInvitationListRequest.java +++ b/src/main/java/project/flipnote/group/model/GroupInvitationListRequest.java @@ -9,6 +9,6 @@ public class GroupInvitationListRequest extends PagingRequest { @Override public PageRequest getPageRequest() { - return PageRequest.of(getPage() - 1, getSize() + 1, Sort.by(Sort.Direction.DESC, "id")); + return PageRequest.of(getPage() - 1, getSize(), Sort.by(Sort.Direction.DESC, "id")); } } diff --git a/src/main/java/project/flipnote/like/model/LikeSearchRequest.java b/src/main/java/project/flipnote/like/model/LikeSearchRequest.java index 1c87f0c6..c6ef3b9b 100644 --- a/src/main/java/project/flipnote/like/model/LikeSearchRequest.java +++ b/src/main/java/project/flipnote/like/model/LikeSearchRequest.java @@ -13,6 +13,6 @@ public class LikeSearchRequest extends PagingRequest { @Override public PageRequest getPageRequest() { - return PageRequest.of(getPage() - 1, getSize() + 1, Sort.by(Sort.Direction.DESC, "id")); + return PageRequest.of(getPage() - 1, getSize(), Sort.by(Sort.Direction.DESC, "id")); } } diff --git a/src/main/java/project/flipnote/like/service/LikeService.java b/src/main/java/project/flipnote/like/service/LikeService.java index 6f432a96..d7bf9df4 100644 --- a/src/main/java/project/flipnote/like/service/LikeService.java +++ b/src/main/java/project/flipnote/like/service/LikeService.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -111,9 +112,7 @@ public PagingResponse> getLikes( Page likePage = likeRepository.findByTargetTypeAndUserId(targetType, userId, req.getPageRequest()); Map likedAtMap = likePage.stream() .collect(Collectors.toMap(Like::getTargetId, Like::getCreatedAt)); - List targetIds = likePage.stream() - .map(Like::getTargetId) - .toList(); + Set targetIds = likedAtMap.keySet(); // TODO: 제네릭이 아닌 타입 별로 엔드포인트를 따로 만드는게 좋으려나 고민중, 현재 방법을 유지하면서 더 나은 구조 알고싶음... LikeTargetFetcher fetcher = (LikeTargetFetcher)fetcherMap.get(targetType); diff --git a/src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java b/src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java index a0581982..2c45c2d1 100644 --- a/src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java +++ b/src/main/java/project/flipnote/like/service/fetcher/LikeCardSetFetcher.java @@ -1,7 +1,7 @@ package project.flipnote.like.service.fetcher; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -25,7 +25,7 @@ public LikeTargetType getTargetType() { } @Override - public Map fetchByIds(List ids) { + public Map fetchByIds(Set ids) { return cardSetService.getCardSetsByIds(ids).stream() .map(CardSetLikeResponse::from) .collect(Collectors.toMap(LikeTargetResponse::getId, Function.identity())); diff --git a/src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java b/src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java index 03f5b5a2..73339084 100644 --- a/src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java +++ b/src/main/java/project/flipnote/like/service/fetcher/LikeTargetFetcher.java @@ -1,7 +1,7 @@ package project.flipnote.like.service.fetcher; -import java.util.List; import java.util.Map; +import java.util.Set; import project.flipnote.like.entity.LikeTargetType; import project.flipnote.like.model.LikeTargetResponse; @@ -9,5 +9,5 @@ public interface LikeTargetFetcher { LikeTargetType getTargetType(); - Map fetchByIds(List ids); + Map fetchByIds(Set ids); } From 5db1b94069007698dbc02a538bb99deaecce64ce Mon Sep 17 00:00:00 2001 From: dungbik Date: Wed, 3 Sep 2025 18:02:07 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Feat:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=9D=B8=EB=8D=B1?= =?UTF-8?q?=EC=8A=A4=20=ED=9A=A8=EC=9C=A8=EC=9D=84=20=EC=9C=84=ED=95=B4=20?= =?UTF-8?q?WHERE=20=EC=A0=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/project/flipnote/bookmark/entity/Bookmark.java | 8 ++++---- .../flipnote/bookmark/repository/BookmarkRepository.java | 4 ++-- .../flipnote/bookmark/service/BookmarkPolicyService.java | 4 ++-- .../flipnote/bookmark/service/BookmarkService.java | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/project/flipnote/bookmark/entity/Bookmark.java b/src/main/java/project/flipnote/bookmark/entity/Bookmark.java index 079611af..63c5a0fe 100644 --- a/src/main/java/project/flipnote/bookmark/entity/Bookmark.java +++ b/src/main/java/project/flipnote/bookmark/entity/Bookmark.java @@ -22,14 +22,14 @@ name = "bookmarks", indexes = { @Index( - name = "idx_bookmarks_targettype_targetid_userid", - columnList = "target_type, target_id, user_id" + name = "idx_bookmarks_targettype_userid_targetid", + columnList = "target_type, user_id, target_id" ) }, uniqueConstraints = { @UniqueConstraint( - name = "uk_bookmarks_targettype_targetid_userid", - columnNames = {"target_type", "target_id", "user_id"} + name = "uk_bookmarks_targettype_userid_targetid", + columnNames = {"target_type", "user_id", "target_id"} ) } ) diff --git a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java index c2980e09..2a9289f9 100644 --- a/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java +++ b/src/main/java/project/flipnote/bookmark/repository/BookmarkRepository.java @@ -10,9 +10,9 @@ import project.flipnote.bookmark.entity.BookmarkTargetType; public interface BookmarkRepository extends JpaRepository { - boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); + boolean existsByTargetTypeAndUserIdAndTargetId(BookmarkTargetType targetType, Long userId, Long targetId); - Optional findByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId); + Optional findByTargetTypeAndUserIdAndTargetId(BookmarkTargetType targetType, Long userId, Long targetId); Page findAllByTargetTypeAndUserId(BookmarkTargetType targetType, Long userId, Pageable pageable); } diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java index 80b51391..10750ae3 100644 --- a/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkPolicyService.java @@ -22,8 +22,8 @@ public void validateTargetExists(BookmarkTargetType targetType, Long targetId) { } } - public void validateBookmarkNotExists(BookmarkTargetType targetType, Long targetId, Long userId) { - if (bookmarkRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId)) { + public void validateBookmarkNotExists(BookmarkTargetType targetType, Long userId, Long targetId) { + if (bookmarkRepository.existsByTargetTypeAndUserIdAndTargetId(targetType, userId, targetId)) { throw new BizException(BookmarkErrorCode.BOOKMARK_ALREADY_EXISTS); } } diff --git a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java index c835a83e..7350ebaf 100644 --- a/src/main/java/project/flipnote/bookmark/service/BookmarkService.java +++ b/src/main/java/project/flipnote/bookmark/service/BookmarkService.java @@ -42,7 +42,7 @@ public class BookmarkService { */ @Transactional public IdResponse addBookmark(Long userId, BookmarkTargetType targetType, Long targetId) { - bookmarkPolicyService.validateBookmarkNotExists(targetType, targetId, userId); + bookmarkPolicyService.validateBookmarkNotExists(targetType, userId, targetId); bookmarkPolicyService.validateTargetExists(targetType, targetId); Bookmark bookmark = Bookmark.builder() @@ -71,7 +71,7 @@ public IdResponse addBookmark(Long userId, BookmarkTargetType targetType, Long t */ @Transactional public IdResponse deleteBookmark(Long userId, BookmarkTargetType targetType, Long targetId) { - Bookmark bookmark = bookmarkRepository.findByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId) + Bookmark bookmark = bookmarkRepository.findByTargetTypeAndUserIdAndTargetId(targetType, userId, targetId) .orElseThrow(() -> new BizException(BookmarkErrorCode.BOOKMARK_NOT_EXISTS)); bookmarkRepository.delete(bookmark);