From 7c4e1c5e5104af62bb99749babfd9e63aa1da3ad Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 9 Jun 2026 14:12:44 +0900 Subject: [PATCH 01/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=EC=9D=84=20Application?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeedManagementApplication.java | 79 +++++++++++++++++++ .../feed/controller/FeedController.java | 4 +- .../WSSServer/feed/service/FeedService.java | 5 ++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java new file mode 100644 index 00000000..a3ee2099 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -0,0 +1,79 @@ +package org.websoso.WSSServer.feed.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import org.websoso.WSSServer.dto.feed.FeedCreateRequest; +import org.websoso.WSSServer.dto.feed.FeedCreateResponse; +import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.service.FeedService; +import org.websoso.WSSServer.novel.service.NovelServiceImpl; +import org.websoso.WSSServer.service.ImageClient; +import org.websoso.WSSServer.user.domain.User; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class FeedManagementApplication { + + private final FeedService feedService; + private final NovelServiceImpl novelService; + private final ImageClient imageUploader; + + @Transactional + public FeedCreateResponse create(User user, FeedCreateRequest request, FeedImageCreateRequest imagesRequest) { + + // 입력한 소설이 존재하는지만 체크 (트랜잭션을 여기서는 잠글 필요가 없음?) + if (request.novelId() != null) { + novelService.getNovelOrException(request.novelId()); + } + + // 이미지 업로드 + List feedImages = processFeedImages(imagesRequest.images()); + + // 피드 객체 생성 + Feed feed = Feed.create(request.feedContent(), request.novelId(), request.isSpoiler(), request.isPublic(), user, feedImages); + + // 피드 저장 + feedService.createFeed(feed); + + // 반환 + return FeedCreateResponse.of(feedImages); + } + + // TODO: 이미지 업로드 로직이 여기에서 관리되지 않도록 수정 예정 + private List processFeedImages(List images) { + List uploadedImageUrls = new ArrayList<>(); + + if (images != null && !images.isEmpty()) { + try { + for (MultipartFile image : images) { + String imageUrl = imageUploader.uploadFeedImage(image); + uploadedImageUrls.add(imageUrl); + } + } catch (Exception e) { + if (!uploadedImageUrls.isEmpty()) { + imageUploader.deleteImages(uploadedImageUrls); + } + + throw e; + } + } + + List feedImages = new ArrayList<>(); + if (!uploadedImageUrls.isEmpty()) { + feedImages.add(FeedImage.createThumbnail(uploadedImageUrls.get(0))); + for (int i = 1; i < uploadedImageUrls.size(); i++) { + feedImages.add(FeedImage.createCommon(uploadedImageUrls.get(i), i)); + } + } + + return feedImages; + } + +} diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index e724125f..acedf520 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -30,6 +30,7 @@ import org.websoso.WSSServer.dto.feed.FeedsGetResponse; import org.websoso.WSSServer.dto.feed.InterestFeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; +import org.websoso.WSSServer.feed.application.FeedManagementApplication; import org.websoso.WSSServer.feed.service.FeedService; import org.websoso.WSSServer.user.domain.User; @@ -39,6 +40,7 @@ public class FeedController { private final FeedService feedService; + private final FeedManagementApplication feedManagementApplication; private final FeedFindApplication feedFindApplication; @PostMapping @@ -48,7 +50,7 @@ public ResponseEntity createFeed(@AuthenticationPrincipal Us @Valid @ModelAttribute FeedImageCreateRequest requestImage) { return ResponseEntity .status(CREATED) - .body(feedService.createFeed(user, request, requestImage)); + .body(feedManagementApplication.create(user, request, requestImage)); } @GetMapping("/{feedId}") diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java index 91aaffb3..073268f7 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java @@ -125,6 +125,11 @@ public FeedCreateResponse createFeed(User user, FeedCreateRequest request, FeedI return FeedCreateResponse.of(feedImages); } + @Transactional + public void createFeed(Feed feed) { + feedRepository.save(feed); + } + @Transactional public FeedCreateResponse updateFeed(Long feedId, FeedUpdateRequest request, FeedImageUpdateRequest imagesRequest) { Feed feed = getFeedOrException(feedId); From adcea7323870d68e45adb84176d67c698c84fee9 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 9 Jun 2026 14:27:40 +0900 Subject: [PATCH 02/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20Application=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeedManagementApplication.java | 49 +++++++++++++++++-- .../feed/controller/FeedController.java | 4 +- .../WSSServer/feed/service/FeedService.java | 19 ------- .../feed/service/FeedServiceImpl.java | 5 ++ 4 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java index a3ee2099..e961b145 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -1,15 +1,16 @@ package org.websoso.WSSServer.feed.application; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import org.websoso.WSSServer.dto.feed.FeedCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedCreateResponse; -import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; +import org.websoso.WSSServer.dto.feed.*; +import org.websoso.WSSServer.exception.exception.CustomNovelException; +import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; -import org.websoso.WSSServer.feed.service.FeedService; +import org.websoso.WSSServer.feed.service.FeedServiceImpl; import org.websoso.WSSServer.novel.service.NovelServiceImpl; import org.websoso.WSSServer.service.ImageClient; import org.websoso.WSSServer.user.domain.User; @@ -17,14 +18,19 @@ import java.util.ArrayList; import java.util.List; +import static org.websoso.WSSServer.exception.error.CustomNovelError.NOVEL_NOT_FOUND; +import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; + @Service @RequiredArgsConstructor public class FeedManagementApplication { - private final FeedService feedService; + private final FeedServiceImpl feedService; private final NovelServiceImpl novelService; private final ImageClient imageUploader; + private final ApplicationEventPublisher eventPublisher; + @Transactional public FeedCreateResponse create(User user, FeedCreateRequest request, FeedImageCreateRequest imagesRequest) { @@ -46,6 +52,39 @@ public FeedCreateResponse create(User user, FeedCreateRequest request, FeedImage return FeedCreateResponse.of(feedImages); } + @Transactional + public FeedCreateResponse update(User user, Long feedId, FeedUpdateRequest request, FeedImageUpdateRequest imagesRequest) { + + // 존재하는 피드인지 확인 + Feed feed = feedService.getFeedOrException(feedId); + + // 본인이 작성한 피드인지 확인 + if (!feed.isMine(user.getUserId())) { + throw new CustomUserException(INVALID_AUTHORIZED, + "User with ID " + user.getUserId() + " is not the owner of feed " + feed.getFeedId()); + } + + // 기존 이미지를 임시 저장 + List oldImages = new ArrayList<>(feed.getImages()); + + // 소설이 변경된 경우 존재하는 소설인지 체크 + if (request.novelId() != null && feed.isNovelChanged(request.novelId())) { + novelService.getNovelOrException(request.novelId()); + } + + // 이미지 업로드 + List feedImages = processFeedImages(imagesRequest.images()); + + // 피드 업데이트 + feed.updateFeed(request.feedContent(), request.isSpoiler(), request.isPublic(), request.novelId(), feedImages); + + // 과거 이미지를 String 리스트로 변환 및 이벤트 리스너를 통해 커밋시 삭제 + List oldImageUrls = oldImages.stream().map(FeedImage::getUrl).toList(); + eventPublisher.publishEvent(new FeedImageDeleteEvent(oldImageUrls)); + + return FeedCreateResponse.of(feedImages); + } + // TODO: 이미지 업로드 로직이 여기에서 관리되지 않도록 수정 예정 private List processFeedImages(List images) { List uploadedImageUrls = new ArrayList<>(); diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index acedf520..da430195 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -73,14 +73,14 @@ public ResponseEntity getFeeds(@AuthenticationPrincipal User u } @PutMapping("/{feedId}") - @PreAuthorize("isAuthenticated() and @authorizationService.validate(#feedId, #user, T(org.websoso.WSSServer.feed.domain.Feed))") + @PreAuthorize("isAuthenticated()") public ResponseEntity updateFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId, @Valid @RequestPart("feed") FeedUpdateRequest request, @Valid @ModelAttribute FeedImageUpdateRequest requestImage) { return ResponseEntity .status(OK) - .body(feedService.updateFeed(feedId, request, requestImage)); + .body(feedManagementApplication.update(user, feedId, request, requestImage)); } @DeleteMapping("/{feedId}") diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java index 073268f7..c2a04388 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java @@ -111,25 +111,6 @@ public class FeedService { private final ImageClient imageClient; private final FCMClient fcmClient; - @Transactional - public FeedCreateResponse createFeed(User user, FeedCreateRequest request, FeedImageCreateRequest imagesRequest) { - List feedImages = processFeedImages(imagesRequest.images()); - - Optional.ofNullable(request.novelId()).ifPresent(novelId -> novelRepository.findById(novelId) - .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, "novel with the given id is not found"))); - Feed feed = Feed.create(request.feedContent(), request.novelId(), request.isSpoiler(), request.isPublic(), user, - feedImages); - - feedRepository.save(feed); - - return FeedCreateResponse.of(feedImages); - } - - @Transactional - public void createFeed(Feed feed) { - feedRepository.save(feed); - } - @Transactional public FeedCreateResponse updateFeed(Long feedId, FeedUpdateRequest request, FeedImageUpdateRequest imagesRequest) { Feed feed = getFeedOrException(feedId); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 5082e6c4..1a0cedc0 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -39,6 +39,11 @@ public class FeedServiceImpl { private static final String DEFAULT_CATEGORY = "all"; + @Transactional + public void createFeed(Feed feed) { + feedRepository.save(feed); + } + @Transactional(readOnly = true) public Feed getFeedOrException(Long feedId) { return feedRepository.findById(feedId) From c7dcc0a8f103b5cf60fc45df2faf846d0d8f037d Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 9 Jun 2026 14:35:46 +0900 Subject: [PATCH 03/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=8B=9C=20=ED=94=BC=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=EC=9E=90=EA=B0=80=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A7=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20Service=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeedManagementApplication.java | 22 +++++++------------ .../feed/service/FeedServiceImpl.java | 13 +++++++++++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java index e961b145..f1e34117 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -5,9 +5,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import org.websoso.WSSServer.dto.feed.*; -import org.websoso.WSSServer.exception.exception.CustomNovelException; -import org.websoso.WSSServer.exception.exception.CustomUserException; +import org.websoso.WSSServer.dto.feed.FeedCreateRequest; +import org.websoso.WSSServer.dto.feed.FeedCreateResponse; +import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; +import org.websoso.WSSServer.dto.feed.FeedImageDeleteEvent; +import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; +import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.service.FeedServiceImpl; @@ -18,9 +21,6 @@ import java.util.ArrayList; import java.util.List; -import static org.websoso.WSSServer.exception.error.CustomNovelError.NOVEL_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; - @Service @RequiredArgsConstructor public class FeedManagementApplication { @@ -55,14 +55,8 @@ public FeedCreateResponse create(User user, FeedCreateRequest request, FeedImage @Transactional public FeedCreateResponse update(User user, Long feedId, FeedUpdateRequest request, FeedImageUpdateRequest imagesRequest) { - // 존재하는 피드인지 확인 - Feed feed = feedService.getFeedOrException(feedId); - - // 본인이 작성한 피드인지 확인 - if (!feed.isMine(user.getUserId())) { - throw new CustomUserException(INVALID_AUTHORIZED, - "User with ID " + user.getUserId() + " is not the owner of feed " + feed.getFeedId()); - } + // 사용자가 작성한 피드인지 확인 + Feed feed = feedService.getOwnedFeedOrException(user.getUserId(), feedId); // 기존 이미지를 임시 저장 List oldImages = new ArrayList<>(feed.getImages()); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 1a0cedc0..81af2c08 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -2,6 +2,7 @@ import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; +import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import java.util.Comparator; import java.util.List; @@ -18,6 +19,7 @@ import org.websoso.WSSServer.domain.common.FeedGetOption; import org.websoso.WSSServer.exception.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomGenreException; +import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.domain.PopularFeed; @@ -50,6 +52,17 @@ public Feed getFeedOrException(Long feedId) { .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); } + @Transactional(readOnly = true) + public Feed getOwnedFeedOrException(Long feedId, Long userId) { + Feed feed = getFeedOrException(feedId); + + if (!feed.isMine(userId)) { + throw new CustomUserException(INVALID_AUTHORIZED, "User with ID " + userId + " is not the owner of feed " + feed.getFeedId()); + } + + return feed; + } + @Transactional(readOnly = true) public Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, FeedGetOption feedGetOption, List preferenceGenres) { From 252c873251bd6946a7ce200fad20d6494cf31f42 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 9 Jun 2026 14:36:40 +0900 Subject: [PATCH 04/31] =?UTF-8?q?[CHORE]=20FeedFindApplication=EC=9D=84=20?= =?UTF-8?q?Feed=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/application/FeedManagementApplication.java | 9 --------- .../{ => feed}/application/FeedFindApplication.java | 2 +- .../WSSServer/feed/controller/FeedController.java | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/application/FeedManagementApplication.java rename src/main/java/org/websoso/WSSServer/{ => feed}/application/FeedFindApplication.java (99%) diff --git a/src/main/java/org/websoso/WSSServer/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/application/FeedManagementApplication.java deleted file mode 100644 index 18d0ec50..00000000 --- a/src/main/java/org/websoso/WSSServer/application/FeedManagementApplication.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.websoso.WSSServer.application; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class FeedManagementApplication { -} diff --git a/src/main/java/org/websoso/WSSServer/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java similarity index 99% rename from src/main/java/org/websoso/WSSServer/application/FeedFindApplication.java rename to src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 574f49ba..99aadbb8 100644 --- a/src/main/java/org/websoso/WSSServer/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.application; +package org.websoso.WSSServer.feed.application; import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index da430195..97c06d85 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; -import org.websoso.WSSServer.application.FeedFindApplication; +import org.websoso.WSSServer.feed.application.FeedFindApplication; import org.websoso.WSSServer.domain.common.FeedGetOption; import org.websoso.WSSServer.dto.feed.FeedCreateRequest; import org.websoso.WSSServer.dto.feed.FeedCreateResponse; From 6534217f3a9d545879f5b9e1ae8179d5bc652abc Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 9 Jun 2026 15:44:00 +0900 Subject: [PATCH 05/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20Application=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EB=B2=8C=ED=81=AC?= =?UTF-8?q?=20DELETE=EB=A1=9C=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EB=B0=8F=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EB=82=B4=EC=97=AD=EC=9D=84=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeedManagementApplication.java | 21 ++++++++ .../feed/controller/FeedController.java | 4 +- .../websoso/WSSServer/feed/domain/Feed.java | 4 +- .../feed/repository/CommentRepository.java | 5 ++ .../feed/repository/LikeRepository.java | 8 +++ .../repository/ReportedCommentRepository.java | 4 ++ .../feed/service/CommentServiceImpl.java | 9 ++++ .../feed/service/FeedLikeService.java | 19 +++++++ .../WSSServer/feed/service/FeedService.java | 50 ------------------- .../feed/service/FeedServiceImpl.java | 11 ++-- 10 files changed, 75 insertions(+), 60 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java index f1e34117..6d85f594 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -11,8 +11,11 @@ import org.websoso.WSSServer.dto.feed.FeedImageDeleteEvent; import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; +import org.websoso.WSSServer.feed.domain.Comment; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.service.CommentServiceImpl; +import org.websoso.WSSServer.feed.service.FeedLikeService; import org.websoso.WSSServer.feed.service.FeedServiceImpl; import org.websoso.WSSServer.novel.service.NovelServiceImpl; import org.websoso.WSSServer.service.ImageClient; @@ -26,6 +29,8 @@ public class FeedManagementApplication { private final FeedServiceImpl feedService; + private final FeedLikeService feedLikeService; + private final CommentServiceImpl commentService; private final NovelServiceImpl novelService; private final ImageClient imageUploader; @@ -79,6 +84,22 @@ public FeedCreateResponse update(User user, Long feedId, FeedUpdateRequest reque return FeedCreateResponse.of(feedImages); } + @Transactional + public void delete(User user, Long feedId) { + + // 사용자가 작성한 피드인지 확인 + Feed feed = feedService.getOwnedFeedOrException(user.getUserId(), feedId); + + // 댓글 삭제 (댓글 / 신고 내역) + commentService.deleteByFeedId(feed.getFeedId()); + + // 좋아요 내역 삭제 + feedLikeService.deleteByFeedId(feed.getFeedId()); + + // 피드 삭제 + feedService.delete(feed); + } + // TODO: 이미지 업로드 로직이 여기에서 관리되지 않도록 수정 예정 private List processFeedImages(List images) { List uploadedImageUrls = new ArrayList<>(); diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 97c06d85..cf2a43e5 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -84,10 +84,10 @@ public ResponseEntity updateFeed(@AuthenticationPrincipal Us } @DeleteMapping("/{feedId}") - @PreAuthorize("isAuthenticated() and @authorizationService.validate(#feedId, #user, T(org.websoso.WSSServer.feed.domain.Feed))") + @PreAuthorize("isAuthenticated()") public ResponseEntity deleteFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { - feedService.deleteFeed(feedId); + feedManagementApplication.delete(user, feedId); return ResponseEntity .status(NO_CONTENT) .build(); diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java b/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java index 039e5433..35b2e4a8 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java +++ b/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java @@ -59,10 +59,10 @@ public class Feed { @JoinColumn(name = "user_id", nullable = false) private User user; - @OneToMany(mappedBy = "feed", cascade = ALL, fetch = FetchType.LAZY) + @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY) private List likes = new ArrayList<>(); - @OneToMany(mappedBy = "feed", cascade = ALL, fetch = FetchType.LAZY) + @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY) private List comments = new ArrayList<>(); @OneToMany(cascade = ALL, fetch = FetchType.LAZY, orphanRemoval = true) diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java index 9a888d22..b6bd45e1 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java @@ -19,4 +19,9 @@ public interface CommentRepository extends JpaRepository { @Query("SELECT c FROM Comment c WHERE c.feed.feedId = :feedId") List findAllByFeedId(@Param("feedId") Long feedId); + + @Modifying + @Query("DELETE FROM Comment c WHERE c.feed.feedId = :feedId") + void deleteByFeedId(Long feedId); + } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java index 1cbd0b44..db51c62c 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java @@ -2,6 +2,8 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.Like; @@ -14,4 +16,10 @@ public interface LikeRepository extends JpaRepository { boolean existsByUserIdAndFeed(Long userId, Feed feed); long countByFeed(Feed feed); + + @Modifying + @Query("DELETE FROM Like l WHERE l.feed.feedId = :feedId") + void deleteByFeedId(Long feedId); + + Long feed(Feed feed); } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java index 71ae2b57..d8da75ad 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java @@ -21,4 +21,8 @@ public interface ReportedCommentRepository extends JpaRepository commentIds); + + @Modifying + @Query("DELETE FROM ReportedComment rc WHERE rc.comment.feed.feedId = :feedId") + void deleteByFeedId(Long feedId); } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java index 2a5934d4..ac07abd9 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.dto.comment.CommentCreateRequest; import org.websoso.WSSServer.dto.comment.CommentUpdateRequest; @@ -18,6 +19,7 @@ public class CommentServiceImpl { private final CommentRepository commentRepository; + private final ReportedCommentRepository reportedCommentRepository; @Transactional public void createComment(User user, Feed feed, CommentCreateRequest request) { @@ -39,4 +41,11 @@ public void updateComment(Comment comment, CommentUpdateRequest request) { public void deleteComment(Comment comment) { commentRepository.delete(comment); } + + @Transactional + public void deleteByFeedId(Long feedId) { + commentRepository.deleteByFeedId(feedId); + reportedCommentRepository.deleteByFeedId(feedId); + } + } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java new file mode 100644 index 00000000..a0ee4812 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java @@ -0,0 +1,19 @@ +package org.websoso.WSSServer.feed.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.repository.LikeRepository; + +@Service +@RequiredArgsConstructor +public class FeedLikeService { + + private final LikeRepository likeRepository; + + @Transactional + public void deleteByFeedId(Long feedId) { + likeRepository.deleteByFeedId(feedId); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java index c2a04388..ee939f01 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java @@ -111,56 +111,6 @@ public class FeedService { private final ImageClient imageClient; private final FCMClient fcmClient; - @Transactional - public FeedCreateResponse updateFeed(Long feedId, FeedUpdateRequest request, FeedImageUpdateRequest imagesRequest) { - Feed feed = getFeedOrException(feedId); - - List oldImages = new ArrayList<>(feed.getImages()); - - if (request.novelId() != null && feed.isNovelChanged(request.novelId())) { - novelRepository.findById(request.novelId()) - .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, - "novel with the given id is not found")); - } - - List feedImages = processFeedImages(imagesRequest.images()); - - feed.updateFeed(request.feedContent(), request.isSpoiler(), request.isPublic(), request.novelId(), feedImages); - - List oldImageUrls = oldImages.stream().map(FeedImage::getUrl).toList(); - eventPublisher.publishEvent(new FeedImageDeleteEvent(oldImageUrls)); - - return FeedCreateResponse.of(feedImages); - } - - private List processFeedImages(List images) { - List uploadedImageUrls = new ArrayList<>(); - - if (images != null && !images.isEmpty()) { - try { - for (MultipartFile image : images) { - String imageUrl = imageClient.uploadFeedImage(image); - uploadedImageUrls.add(imageUrl); - } - } catch (Exception e) { - if (!uploadedImageUrls.isEmpty()) { - imageClient.deleteImages(uploadedImageUrls); - } - - throw e; - } - } - - List feedImages = new ArrayList<>(); - if (!uploadedImageUrls.isEmpty()) { - feedImages.add(FeedImage.createThumbnail(uploadedImageUrls.get(0))); - for (int i = 1; i < uploadedImageUrls.size(); i++) { - feedImages.add(FeedImage.createCommon(uploadedImageUrls.get(i), i)); - } - } - - return feedImages; - } @Transactional public void deleteFeed(Long feedId) { diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 81af2c08..f89e2b1e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -46,6 +46,11 @@ public void createFeed(Feed feed) { feedRepository.save(feed); } + @Transactional + public void delete(Feed feed) { + feedRepository.delete(feed); + } + @Transactional(readOnly = true) public Feed getFeedOrException(Long feedId) { return feedRepository.findById(feedId) @@ -90,12 +95,6 @@ public Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRe } - private Genre findGenreByName(String genreName) { - return genreRepository.findByGenreName(genreName) - .orElseThrow(() -> new CustomGenreException(GENRE_NOT_FOUND, - "genre with the given name is not found")); - } - @Transactional(readOnly = true) public Integer countByFeedId(Long feedId) { return feedImageRepository.countByFeedId(feedId); From e2f4c1664163046c007d098f28997310c98d24b9 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Thu, 11 Jun 2026 16:20:44 +0900 Subject: [PATCH 06/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94=EB=A5=BC=20=EB=88=84=EB=A5=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedLikeApplication.java | 54 +++ .../application/PopularFeedApplication.java | 44 ++ .../feed/controller/FeedController.java | 4 +- .../websoso/WSSServer/feed/domain/Feed.java | 13 +- .../websoso/WSSServer/feed/domain/Like.java | 16 +- .../feed/event/FeedBecamePopularEvent.java | 9 + .../WSSServer/feed/event/FeedLikedEvent.java | 11 + .../feed/event/PopularFeedCheckEvent.java | 9 + .../PopularFeedCheckEventListener.java | 22 + .../feed/repository/LikeRepository.java | 2 + .../feed/service/FeedLikeService.java | 21 + .../WSSServer/feed/service/FeedService.java | 424 +++++++++--------- .../feed/service/FeedServiceImpl.java | 12 + .../feed/service/PopularFeedService.java | 23 + .../NotificationSendApplication.java | 105 +++++ .../FeedLikeNotificationListener.java | 21 + .../PopularFeedNotificationListener.java | 21 + .../repository/UserDeviceRepository.java | 4 + .../notification/service/FcmService.java | 35 ++ .../service/NotificationService.java | 96 +++- .../service/UserDeviceService.java | 8 + .../user/repository/BlockRepository.java | 8 + .../WSSServer/user/service/BlockService.java | 16 + 23 files changed, 758 insertions(+), 220 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java create mode 100644 src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java create mode 100644 src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java create mode 100644 src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java create mode 100644 src/main/java/org/websoso/WSSServer/notification/service/FcmService.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java new file mode 100644 index 00000000..7f7f95d6 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java @@ -0,0 +1,54 @@ +package org.websoso.WSSServer.feed.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.event.FeedLikedEvent; +import org.websoso.WSSServer.feed.event.PopularFeedCheckEvent; +import org.websoso.WSSServer.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.user.domain.User; +import org.websoso.WSSServer.user.service.BlockService; + +@Service +@RequiredArgsConstructor +public class FeedLikeApplication { + + private final FeedServiceImpl feedService; + private final FeedLikeService feedLikeService; + private final BlockService blockService; + + private final ApplicationEventPublisher eventPublisher; + + @Transactional + public void create(User user, Long feedId) { + + // 접근 가능한 피드인지 체크하고 조회 + Feed feed = feedService.getAccessFeedOrException(feedId, user.getUserId()); + + // 작성자와 본인이 차단 관계인지 체크 + blockService.validateNotBlocked(feed.getWriterId(),user.getUserId()); + + // 피드 좋아요 처리 (이미 좋아요 되어있는 경우, 패스) + if (!feedLikeService.create(user.getUserId(), feed)) { + return; + } + + // 좋아요 알림 발행 + eventPublisher.publishEvent( + FeedLikedEvent.of( + user.getUserId(), + feed.getFeedId(), + feed.getWriterId() + ) + ); + + // 인기 피드 발행 + eventPublisher.publishEvent( + PopularFeedCheckEvent.of(feed.getFeedId()) + ); + + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java new file mode 100644 index 00000000..9c0f588c --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java @@ -0,0 +1,44 @@ +package org.websoso.WSSServer.feed.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.event.FeedBecamePopularEvent; +import org.websoso.WSSServer.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.service.PopularFeedService; + +@Service +@RequiredArgsConstructor +public class PopularFeedApplication { + + private static final int POPULAR_FEED_LIKE_THRESHOLD = 5; + + private final PopularFeedService popularFeedService; + private final FeedServiceImpl feedService; + private final FeedLikeService feedLikeService; + + private final ApplicationEventPublisher eventPublisher; + + @Transactional + public void checkAndRegister(Long feedId) { + + Feed feed = feedService.getFeedOrException(feedId); + + if (!feed.containsNovel()) return; + + long likeCount = feedLikeService.countByFeedId(feedId); + + if (likeCount != POPULAR_FEED_LIKE_THRESHOLD) return; + + if (popularFeedService.existByFeed(feed)) return; + + popularFeedService.create(feed); + + // 지금 뜨는 글 선정 알림 전송 + eventPublisher.publishEvent(FeedBecamePopularEvent.of(feedId)); + + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index cf2a43e5..47f5c69e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -30,6 +30,7 @@ import org.websoso.WSSServer.dto.feed.FeedsGetResponse; import org.websoso.WSSServer.dto.feed.InterestFeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; +import org.websoso.WSSServer.feed.application.FeedLikeApplication; import org.websoso.WSSServer.feed.application.FeedManagementApplication; import org.websoso.WSSServer.feed.service.FeedService; import org.websoso.WSSServer.user.domain.User; @@ -42,6 +43,7 @@ public class FeedController { private final FeedService feedService; private final FeedManagementApplication feedManagementApplication; private final FeedFindApplication feedFindApplication; + private final FeedLikeApplication feedLikeApplication; @PostMapping @PreAuthorize("isAuthenticated()") @@ -97,7 +99,7 @@ public ResponseEntity deleteFeed(@AuthenticationPrincipal User user, @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user)") public ResponseEntity likeFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { - feedService.likeFeed(user, feedId); + feedLikeApplication.create(user, feedId); return ResponseEntity .status(NO_CONTENT) .build(); diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java b/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java index 35b2e4a8..89a36b7a 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java +++ b/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java @@ -11,7 +11,6 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.OrderBy; import java.time.LocalDateTime; import java.util.ArrayList; @@ -117,4 +116,16 @@ public boolean isMine(Long userId) { public boolean isVisibleTo(Long userId) { return this.isPublic || this.isMine(userId); } + + public boolean canAccess(Long userId) { + if (isMine(userId)) { + return true; + } + + return !isHidden && isPublic; + } + + public boolean containsNovel() { + return novelId != null; + } } diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Like.java b/src/main/java/org/websoso/WSSServer/feed/domain/Like.java index 09234a0d..935ae3dc 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Like.java +++ b/src/main/java/org/websoso/WSSServer/feed/domain/Like.java @@ -10,14 +10,24 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; import org.websoso.common.entity.BaseEntity; @Entity @Getter -@Table(name = "`like`") +@Table( + name = "`like`", + uniqueConstraints = { + @UniqueConstraint( + name = "uk_like_user_id_feed_id", + columnNames = {"user_id", "feed_id"} + ) + } +) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Like extends BaseEntity { @@ -26,11 +36,13 @@ public class Like extends BaseEntity { @Column(nullable = false) private Long likeId; - @Column(nullable = false) + @Column(name = "user_id", nullable = false) + @Comment("좋아요를 누른 사용자 PK") private Long userId; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "feed_id", nullable = false) + @Comment("좋아요를 받은 피드 PK") private Feed feed; diff --git a/src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java b/src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java new file mode 100644 index 00000000..f4e2e10b --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java @@ -0,0 +1,9 @@ +package org.websoso.WSSServer.feed.event; + +public record FeedBecamePopularEvent( + Long feedId +) { + public static FeedBecamePopularEvent of(Long feedId) { + return new FeedBecamePopularEvent(feedId); + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java b/src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java new file mode 100644 index 00000000..cef98953 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java @@ -0,0 +1,11 @@ +package org.websoso.WSSServer.feed.event; + +public record FeedLikedEvent( + Long userId, + Long feedId, + Long writerId +) { + public static FeedLikedEvent of(Long userId, Long feedId, Long writerId) { + return new FeedLikedEvent(userId, feedId, writerId); + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java b/src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java new file mode 100644 index 00000000..a43e3084 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java @@ -0,0 +1,9 @@ +package org.websoso.WSSServer.feed.event; + +public record PopularFeedCheckEvent( + Long feedId +) { + public static PopularFeedCheckEvent of(Long feedId) { + return new PopularFeedCheckEvent(feedId); + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java b/src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java new file mode 100644 index 00000000..b9a9c0a2 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java @@ -0,0 +1,22 @@ +package org.websoso.WSSServer.feed.listener; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; +import org.websoso.WSSServer.feed.application.PopularFeedApplication; +import org.websoso.WSSServer.feed.event.PopularFeedCheckEvent; + +@Component +@RequiredArgsConstructor +public class PopularFeedCheckEventListener { + + private final PopularFeedApplication popularFeedApplication; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(PopularFeedCheckEvent event) { + popularFeedApplication.checkAndRegister(event.feedId()); + } + +} + diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java index db51c62c..f16807f6 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java @@ -22,4 +22,6 @@ public interface LikeRepository extends JpaRepository { void deleteByFeedId(Long feedId); Long feed(Feed feed); + + long countByFeed_FeedId(Long feedFeedId); } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java index a0ee4812..383501dd 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java @@ -1,8 +1,11 @@ package org.websoso.WSSServer.feed.service; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.domain.Like; import org.websoso.WSSServer.feed.repository.LikeRepository; @Service @@ -11,9 +14,27 @@ public class FeedLikeService { private final LikeRepository likeRepository; + @Transactional + public boolean create(Long userId, Feed feed) { + if (likeRepository.existsByUserIdAndFeed(userId, feed)) return false; + + try { + likeRepository.save(Like.create(userId, feed)); + return true; + } catch (DataIntegrityViolationException e) { + // 동시 요청으로 이미 생성되어 무결성을 위반한 경우 패스 처리 + return false; + } + } + @Transactional public void deleteByFeedId(Long feedId) { likeRepository.deleteByFeedId(feedId); } + @Transactional(readOnly = true) + public long countByFeedId(Long feedId) { + return likeRepository.countByFeed_FeedId(feedId); + } + } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java index ee939f01..6fbf267f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java @@ -119,72 +119,72 @@ public void deleteFeed(Long feedId) { feedRepository.deleteById(feedId); } - @Transactional - public void likeFeed(User user, Long feedId) { - Feed feed = getFeedOrException(feedId); - - if (likeRepository.existsByUserIdAndFeed(user.getUserId(), feed)) { - throw new CustomFeedException(ALREADY_LIKED, "user already liked that feed"); - } - likeRepository.save(Like.create(user.getUserId(), feed)); - - long likeCount = likeRepository.countByFeed(feed); - - if (likeCount == POPULAR_FEED_LIKE_THRESHOLD && feed.getNovelId() != null) { - if (!popularFeedRepository.existsByFeed(feed)) { - popularFeedRepository.save(PopularFeed.create(feed)); - - sendPopularFeedPushMessage(feed); - } - } - - sendLikePushMessage(user, feed); - } - - private void sendLikePushMessage(User liker, Feed feed) { - User feedOwner = feed.getUser(); - if (liker.equals(feedOwner) || blockRepository.existsByBlockingIdAndBlockedId(feedOwner.getUserId(), - liker.getUserId())) { - return; - } - - NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("좋아요"); - - String notificationTitle = createNotificationTitle(feed); - String notificationBody = String.format("%s님이 내 글을 좋아해요.", liker.getNickname()); - Long feedId = feed.getFeedId(); - - Notification notification = Notification.create(notificationTitle, notificationBody, null, - feedOwner.getUserId(), feedId, notificationTypeComment); - notificationRepository.save(notification); - - if (!TRUE.equals(feedOwner.getIsPushEnabled())) { - return; - } - - List feedOwnerDevices = feedOwner.getUserDevices(); - if (feedOwnerDevices.isEmpty()) { - return; - } - - FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(notificationTitle, notificationBody, - String.valueOf(feedId), "feedDetail", String.valueOf(notification.getNotificationId())); - - List targetFCMTokens = feedOwnerDevices.stream().map(UserDevice::getFcmToken).toList(); - fcmClient.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest); - } - - private String createNotificationTitle(Feed feed) { - if (feed.getNovelId() == null) { - String feedContent = feed.getFeedContent(); - feedContent = feedContent.length() <= NOTIFICATION_TITLE_MAX_LENGTH ? feedContent - : feedContent.substring(NOTIFICATION_TITLE_MIN_LENGTH, NOTIFICATION_TITLE_MAX_LENGTH); - return "'" + feedContent + "...'"; - } - Novel novel = novelRepository.findById(feed.getNovelId()) - .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, "novel with the given id is not found")); - return novel.getTitle(); - } +// @Transactional +// public void likeFeed(User user, Long feedId) { +// Feed feed = getFeedOrException(feedId); +// +// if (likeRepository.existsByUserIdAndFeed(user.getUserId(), feed)) { +// throw new CustomFeedException(ALREADY_LIKED, "user already liked that feed"); +// } +// likeRepository.save(Like.create(user.getUserId(), feed)); +// +// long likeCount = likeRepository.countByFeed(feed); +// +// if (likeCount == POPULAR_FEED_LIKE_THRESHOLD && feed.getNovelId() != null) { +// if (!popularFeedRepository.existsByFeed(feed)) { +// popularFeedRepository.save(PopularFeed.create(feed)); +// +// sendPopularFeedPushMessage(feed); +// } +// } +// +// sendLikePushMessage(user, feed); +// } + +// private void sendLikePushMessage(User liker, Feed feed) { +// User feedOwner = feed.getUser(); +// if (liker.equals(feedOwner) || blockRepository.existsByBlockingIdAndBlockedId(feedOwner.getUserId(), +// liker.getUserId())) { +// return; +// } +// +// NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("좋아요"); +// +// String notificationTitle = createNotificationTitle(feed); +// String notificationBody = String.format("%s님이 내 글을 좋아해요.", liker.getNickname()); +// Long feedId = feed.getFeedId(); +// +// Notification notification = Notification.create(notificationTitle, notificationBody, null, +// feedOwner.getUserId(), feedId, notificationTypeComment); +// notificationRepository.save(notification); +// +// if (!TRUE.equals(feedOwner.getIsPushEnabled())) { +// return; +// } +// +// List feedOwnerDevices = feedOwner.getUserDevices(); +// if (feedOwnerDevices.isEmpty()) { +// return; +// } +// +// FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(notificationTitle, notificationBody, +// String.valueOf(feedId), "feedDetail", String.valueOf(notification.getNotificationId())); +// +// List targetFCMTokens = feedOwnerDevices.stream().map(UserDevice::getFcmToken).toList(); +// fcmClient.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest); +// } + +// private String createNotificationTitle(Feed feed) { +// if (feed.getNovelId() == null) { +// String feedContent = feed.getFeedContent(); +// feedContent = feedContent.length() <= NOTIFICATION_TITLE_MAX_LENGTH ? feedContent +// : feedContent.substring(NOTIFICATION_TITLE_MIN_LENGTH, NOTIFICATION_TITLE_MAX_LENGTH); +// return "'" + feedContent + "...'"; +// } +// Novel novel = novelRepository.findById(feed.getNovelId()) +// .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, "novel with the given id is not found")); +// return novel.getTitle(); +// } @Transactional public void unLikeFeed(User user, Long feedId) { @@ -195,43 +195,43 @@ public void unLikeFeed(User user, Long feedId) { likeRepository.delete(like); } - @Transactional(readOnly = true) - public FeedGetResponse getFeedById(User user, Long feedId) { - Feed feed = getFeedOrException(feedId); - UserBasicInfo feedUserBasicInfo = getUserBasicInfo(feed.getUser()); - Novel novel = getLinkedNovelOrNull(feed.getNovelId()); - Boolean isLiked = isUserLikedFeed(user, feed); - Boolean isMyFeed = isUserFeedOwner(feed.getUser(), user); - - return FeedGetResponse.of(feed, feedUserBasicInfo, novel, isLiked, isMyFeed); - } - - @Transactional(readOnly = true) - public FeedsGetResponse getFeeds(User user, String category, Long lastFeedId, int size, - FeedGetOption feedGetOption) { - Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); - - List genres = getPreferenceGenres(user); - - Slice feeds = findFeedsByCategoryLabel(lastFeedId, userIdOrNull, - PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); - - List feedGetResponses = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) - .map(feed -> createFeedInfo(feed, user)).toList(); - - return FeedsGetResponse.of(feeds.hasNext(), feedGetResponses); - } - - private List getPreferenceGenres(User user) { - if (user == null) { - return null; - } - return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); - } - - private static String getChosenCategoryOrDefault(String category) { - return Optional.ofNullable(category).orElse(DEFAULT_CATEGORY); - } +// @Transactional(readOnly = true) +// public FeedGetResponse getFeedById(User user, Long feedId) { +// Feed feed = getFeedOrException(feedId); +// UserBasicInfo feedUserBasicInfo = getUserBasicInfo(feed.getUser()); +// Novel novel = getLinkedNovelOrNull(feed.getNovelId()); +// Boolean isLiked = isUserLikedFeed(user, feed); +// Boolean isMyFeed = isUserFeedOwner(feed.getUser(), user); +// +// return FeedGetResponse.of(feed, feedUserBasicInfo, novel, isLiked, isMyFeed); +// } +// +// @Transactional(readOnly = true) +// public FeedsGetResponse getFeeds(User user, String category, Long lastFeedId, int size, +// FeedGetOption feedGetOption) { +// Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); +// +// List genres = getPreferenceGenres(user); +// +// Slice feeds = findFeedsByCategoryLabel(lastFeedId, userIdOrNull, +// PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); +// +// List feedGetResponses = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) +// .map(feed -> createFeedInfo(feed, user)).toList(); +// +// return FeedsGetResponse.of(feeds.hasNext(), feedGetResponses); +// } + +// private List getPreferenceGenres(User user) { +// if (user == null) { +// return null; +// } +// return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); +// } +// +// private static String getChosenCategoryOrDefault(String category) { +// return Optional.ofNullable(category).orElse(DEFAULT_CATEGORY); +// } private Feed getFeedOrException(Long feedId) { return feedRepository.findById(feedId) @@ -274,53 +274,53 @@ private FeedInfo createFeedInfo(Feed feed, User user) { return FeedInfo.of(feed, userBasicInfo, novel, isLiked, isMyFeed, thumbnailUrl, imageCount, user); } - private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, - FeedGetOption feedGetOption, List preferenceGenres) { - if (FeedGetOption.isAll(feedGetOption)) { - return feedRepository.findFeeds(lastFeedId, userId, pageRequest); - } - return feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres); - - } - - private Genre findGenreByName(String genreName) { - return genreRepository.findByGenreName(genreName) - .orElseThrow(() -> new CustomGenreException(GENRE_NOT_FOUND, - "genre with the given name is not found")); - } - - @Transactional(readOnly = true) - public InterestFeedsGetResponse getInterestFeeds(User user) { - List interestNovels = userNovelRepository.findByUserAndIsInterestTrue(user).stream() - .map(UserNovel::getNovel).toList(); - - if (interestNovels.isEmpty()) { - return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_INTEREST_NOVELS"); - } - - Map novelMap = interestNovels.stream() - .collect(Collectors.toMap(Novel::getNovelId, novel -> novel)); - List interestNovelIds = new ArrayList<>(novelMap.keySet()); - - List interestFeeds = feedRepository.findTop10ByNovelIdInOrderByFeedIdDesc(interestNovelIds); - - if (interestFeeds.isEmpty()) { - return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_ASSOCIATED_FEEDS"); - } - - Set avatarIds = interestFeeds.stream().map(feed -> feed.getUser().getAvatarProfileId()) - .collect(Collectors.toSet()); - Map avatarMap = avatarProfileRepository.findAllById(avatarIds).stream() - .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, avatar -> avatar)); - - List interestFeedGetResponses = interestFeeds.stream() - .filter(feed -> feed.isVisibleTo(user.getUserId())).map(feed -> { - Novel novel = novelMap.get(feed.getNovelId()); - AvatarProfile avatar = avatarMap.get(feed.getUser().getAvatarProfileId()); - return InterestFeedGetResponse.of(novel, feed.getUser(), feed, avatar); - }).toList(); - return InterestFeedsGetResponse.of(interestFeedGetResponses, ""); - } +// private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, +// FeedGetOption feedGetOption, List preferenceGenres) { +// if (FeedGetOption.isAll(feedGetOption)) { +// return feedRepository.findFeeds(lastFeedId, userId, pageRequest); +// } +// return feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres); +// +// } +// +// private Genre findGenreByName(String genreName) { +// return genreRepository.findByGenreName(genreName) +// .orElseThrow(() -> new CustomGenreException(GENRE_NOT_FOUND, +// "genre with the given name is not found")); +// } +// +// @Transactional(readOnly = true) +// public InterestFeedsGetResponse getInterestFeeds(User user) { +// List interestNovels = userNovelRepository.findByUserAndIsInterestTrue(user).stream() +// .map(UserNovel::getNovel).toList(); +// +// if (interestNovels.isEmpty()) { +// return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_INTEREST_NOVELS"); +// } +// +// Map novelMap = interestNovels.stream() +// .collect(Collectors.toMap(Novel::getNovelId, novel -> novel)); +// List interestNovelIds = new ArrayList<>(novelMap.keySet()); +// +// List interestFeeds = feedRepository.findTop10ByNovelIdInOrderByFeedIdDesc(interestNovelIds); +// +// if (interestFeeds.isEmpty()) { +// return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_ASSOCIATED_FEEDS"); +// } +// +// Set avatarIds = interestFeeds.stream().map(feed -> feed.getUser().getAvatarProfileId()) +// .collect(Collectors.toSet()); +// Map avatarMap = avatarProfileRepository.findAllById(avatarIds).stream() +// .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, avatar -> avatar)); +// +// List interestFeedGetResponses = interestFeeds.stream() +// .filter(feed -> feed.isVisibleTo(user.getUserId())).map(feed -> { +// Novel novel = novelMap.get(feed.getNovelId()); +// AvatarProfile avatar = avatarMap.get(feed.getUser().getAvatarProfileId()); +// return InterestFeedGetResponse.of(novel, feed.getUser(), feed, avatar); +// }).toList(); +// return InterestFeedsGetResponse.of(interestFeedGetResponses, ""); +// } @Transactional(readOnly = true) public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long lastFeedId, int size) { @@ -396,67 +396,67 @@ private Integer getImageCount(Feed feed) { return feedImageRepository.countByFeedId(feed.getFeedId()); } - private void sendPopularFeedPushMessage(Feed feed) { - NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("지금뜨는글"); - - User feedOwner = feed.getUser(); - Long feedId = feed.getFeedId(); - String notificationTitle = "지금 뜨는 글 등극\uD83D\uDE4C"; - String notificationBody = createNotificationBody(feed); - - Notification notification = Notification.create( - notificationTitle, - notificationBody, - null, - feedOwner.getUserId(), - feedId, - notificationTypeComment - ); - notificationRepository.save(notification); - - if (!TRUE.equals(feedOwner.getIsPushEnabled())) { - return; - } - - List feedOwnerDevices = feedOwner.getUserDevices(); - if (feedOwnerDevices.isEmpty()) { - return; - } - - FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of( - notificationTitle, - notificationBody, - String.valueOf(feedId), - "feedDetail", - String.valueOf(notification.getNotificationId()) - ); - - List targetFCMTokens = feedOwnerDevices - .stream() - .map(UserDevice::getFcmToken) - .toList(); - fcmClient.sendMulticastPushMessage( - targetFCMTokens, - fcmMessageRequest - ); - } - - private String createNotificationBody(Feed feed) { - return String.format("내가 남긴 %s 글이 관심 받고 있어요!", generateNotificationBodyFragment(feed)); - } - - private String generateNotificationBodyFragment(Feed feed) { - if (feed.getNovelId() == null) { - String feedContent = feed.getFeedContent(); - feedContent = feedContent.length() <= 12 - ? feedContent - : feedContent.substring(0, 12); - return "'" + feedContent + "...'"; - } - Novel novel = novelRepository.findById(feed.getNovelId()) - .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, - "novel with the given id is not found")); - return String.format("<%s>", novel.getTitle()); - } +// private void sendPopularFeedPushMessage(Feed feed) { +// NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("지금뜨는글"); +// +// User feedOwner = feed.getUser(); +// Long feedId = feed.getFeedId(); +// String notificationTitle = "지금 뜨는 글 등극\uD83D\uDE4C"; +// String notificationBody = createNotificationBody(feed); +// +// Notification notification = Notification.create( +// notificationTitle, +// notificationBody, +// null, +// feedOwner.getUserId(), +// feedId, +// notificationTypeComment +// ); +// notificationRepository.save(notification); +// +// if (!TRUE.equals(feedOwner.getIsPushEnabled())) { +// return; +// } +// +// List feedOwnerDevices = feedOwner.getUserDevices(); +// if (feedOwnerDevices.isEmpty()) { +// return; +// } +// +// FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of( +// notificationTitle, +// notificationBody, +// String.valueOf(feedId), +// "feedDetail", +// String.valueOf(notification.getNotificationId()) +// ); +// +// List targetFCMTokens = feedOwnerDevices +// .stream() +// .map(UserDevice::getFcmToken) +// .toList(); +// fcmClient.sendMulticastPushMessage( +// targetFCMTokens, +// fcmMessageRequest +// ); +// } + +// private String createNotificationBody(Feed feed) { +// return String.format("내가 남긴 %s 글이 관심 받고 있어요!", generateNotificationBodyFragment(feed)); +// } + +// private String generateNotificationBodyFragment(Feed feed) { +// if (feed.getNovelId() == null) { +// String feedContent = feed.getFeedContent(); +// feedContent = feedContent.length() <= 12 +// ? feedContent +// : feedContent.substring(0, 12); +// return "'" + feedContent + "...'"; +// } +// Novel novel = novelRepository.findById(feed.getNovelId()) +// .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, +// "novel with the given id is not found")); +// return String.format("<%s>", novel.getTitle()); +// } } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index f89e2b1e..47a72f21 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -1,6 +1,7 @@ package org.websoso.WSSServer.feed.service; import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.exception.error.CustomFeedError.HIDDEN_FEED_ACCESS; import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; @@ -68,6 +69,17 @@ public Feed getOwnedFeedOrException(Long feedId, Long userId) { return feed; } + @Transactional(readOnly = true) + public Feed getAccessFeedOrException(Long feedId, Long userId) { + Feed feed = getFeedOrException(feedId); + + if (!feed.canAccess(userId)) { + throw new CustomFeedException(HIDDEN_FEED_ACCESS, "Cannot access hidden feed."); + } + + return feed; + } + @Transactional(readOnly = true) public Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, FeedGetOption feedGetOption, List preferenceGenres) { diff --git a/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java new file mode 100644 index 00000000..de5cd9ee --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java @@ -0,0 +1,23 @@ +package org.websoso.WSSServer.feed.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.domain.PopularFeed; +import org.websoso.WSSServer.feed.repository.PopularFeedRepository; + +@Service +@RequiredArgsConstructor +public class PopularFeedService { + + private final PopularFeedRepository popularFeedRepository; + + public boolean existByFeed(Feed feed) { + return popularFeedRepository.existsByFeed(feed); + } + + public void create(Feed feed) { + popularFeedRepository.save(PopularFeed.create(feed)); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java b/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java new file mode 100644 index 00000000..accb191a --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java @@ -0,0 +1,105 @@ +package org.websoso.WSSServer.notification.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.notification.domain.Notification; +import org.websoso.WSSServer.notification.domain.UserDevice; +import org.websoso.WSSServer.notification.service.FcmService; +import org.websoso.WSSServer.notification.service.NotificationService; +import org.websoso.WSSServer.notification.service.UserDeviceService; +import org.websoso.WSSServer.novel.domain.Novel; +import org.websoso.WSSServer.novel.service.NovelServiceImpl; +import org.websoso.WSSServer.user.domain.User; +import org.websoso.WSSServer.user.service.BlockService; +import org.websoso.WSSServer.user.service.UserService; + +import java.util.List; + +import static java.lang.Boolean.TRUE; + +@Service +@RequiredArgsConstructor +public class NotificationSendApplication { + + private final NotificationService notificationService; + private final FeedServiceImpl feedService; + private final NovelServiceImpl novelService; + private final BlockService blockService; + private final UserService userService; + private final UserDeviceService userDeviceService; + private final FcmService fcmService; + + // 피드 좋아요 푸시 메세지 전송 + @Transactional + public void sendFeedLikedPushMessage(Long feedId, Long userId, Long writerId) { + // 알림 발송자, 대상자가 서로 차단 상태인지 체크 + blockService.validateNotBlocked(userId, writerId); + + Feed feed = feedService.getFeedOrException(feedId); + + User liker = userService.getUserOrException(userId); + + Novel novel; + if (feed.getNovelId() == null) { + novel = null; + } else { + novel = novelService.getNovelOrException(feed.getNovelId()); + } + + // Notification 엔티티 생성 및 저장 + Notification notification = notificationService.createFeedLikedNotification(feed, novel, liker.getNickname(), writerId); + + User target = userService.getUserOrException(writerId); + + // 알림 대상자의 알림 설정 여부 + if (!TRUE.equals(target.getIsPushEnabled())) { + return; + } + + // 알림 대상자에게 등록된 디바이스가 없다면 패스 + List devices = userDeviceService.findUserDevices(target.getUserId()); + if (devices.isEmpty()) { + return; + } + + // FCM 푸시 알림 전송 + fcmService.sendPushFeedNotification(notification, devices); + } + + // 인기글 등극 푸시 메세지 전송 + @Transactional + public void sendFeedBecamePopularPushMessage(Long feedId) { + + Feed feed = feedService.getFeedOrException(feedId); + + Novel novel; + if (feed.getNovelId() == null) { + novel = null; + } else { + novel = novelService.getNovelOrException(feed.getNovelId()); + } + + // Notification 엔티티 저장 + Notification notification = notificationService.createBecamePopularFeedNotification(feed, novel); + + User target = userService.getUserOrException(feed.getWriterId()); + + // 알림 대상자의 알림 설정 여부 + if (!TRUE.equals(target.getIsPushEnabled())) { + return; + } + + // 알림 대상자에게 등록된 디바이스가 없다면 패스 + List devices = userDeviceService.findUserDevices(target.getUserId()); + if (devices.isEmpty()) { + return; + } + + // FCM 푸시 알림 전송 + fcmService.sendPushFeedNotification(notification, devices); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java b/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java new file mode 100644 index 00000000..0fe6669d --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java @@ -0,0 +1,21 @@ +package org.websoso.WSSServer.notification.listener; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; +import org.websoso.WSSServer.feed.event.FeedLikedEvent; +import org.websoso.WSSServer.notification.application.NotificationSendApplication; + +@Component +@RequiredArgsConstructor +public class FeedLikeNotificationListener { + + private final NotificationSendApplication notificationSendApplication; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(FeedLikedEvent event) { + notificationSendApplication.sendFeedLikedPushMessage(event.feedId(), event.userId(), event.writerId()); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java b/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java new file mode 100644 index 00000000..50acb25e --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java @@ -0,0 +1,21 @@ +package org.websoso.WSSServer.notification.listener; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; +import org.websoso.WSSServer.feed.event.FeedBecamePopularEvent; +import org.websoso.WSSServer.notification.application.NotificationSendApplication; + +@Component +@RequiredArgsConstructor +public class PopularFeedNotificationListener { + + private final NotificationSendApplication notificationSendApplication; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(FeedBecamePopularEvent event) { + notificationSendApplication.sendFeedBecamePopularPushMessage(event.feedId()); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/notification/repository/UserDeviceRepository.java b/src/main/java/org/websoso/WSSServer/notification/repository/UserDeviceRepository.java index 255425df..b9074de2 100644 --- a/src/main/java/org/websoso/WSSServer/notification/repository/UserDeviceRepository.java +++ b/src/main/java/org/websoso/WSSServer/notification/repository/UserDeviceRepository.java @@ -8,6 +8,8 @@ import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.notification.domain.UserDevice; +import java.util.List; + @Repository public interface UserDeviceRepository extends JpaRepository { @@ -22,4 +24,6 @@ void upsertFcmToken(@Param("userId") Long userId, @Param("fcmToken") String fcmToken); void deleteByUserAndDeviceIdentifier(User user, String deviceIdentifier); + + List getUserDevicesByUser_UserId(Long userUserId); } diff --git a/src/main/java/org/websoso/WSSServer/notification/service/FcmService.java b/src/main/java/org/websoso/WSSServer/notification/service/FcmService.java new file mode 100644 index 00000000..abb3c163 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/notification/service/FcmService.java @@ -0,0 +1,35 @@ +package org.websoso.WSSServer.notification.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.websoso.WSSServer.notification.domain.Notification; +import org.websoso.WSSServer.notification.domain.UserDevice; +import org.websoso.WSSServer.notification.dto.FCMMessageRequest; +import org.websoso.WSSServer.notification.infrastructure.FCMClient; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class FcmService { + + private final FCMClient fcmClient; + + public void sendPushFeedNotification(Notification notification, List devices) { + + FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of( + notification.getNotificationTitle(), + notification.getNotificationBody(), + String.valueOf(notification.getFeedId()), + "feedDetail", + String.valueOf(notification.getNotificationId()) + ); + + List targetFCMTokens = devices.stream() + .map(UserDevice::getFcmToken) + .toList(); + + fcmClient.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java b/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java index 4d5dc9b5..a47df6cb 100644 --- a/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java +++ b/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java @@ -10,6 +10,7 @@ import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.notification.controller.response.NotificationPageResponse; import org.websoso.WSSServer.notification.domain.Notification; import org.websoso.WSSServer.notification.domain.NotificationType; @@ -17,17 +18,77 @@ import org.websoso.WSSServer.domain.common.NotificationTypeGroup; import org.websoso.WSSServer.exception.exception.CustomNotificationException; import org.websoso.WSSServer.notification.repository.NotificationRepository; +import org.websoso.WSSServer.notification.repository.NotificationTypeRepository; import org.websoso.WSSServer.notification.repository.ReadNotificationRepository; +import org.websoso.WSSServer.novel.domain.Novel; +import org.websoso.WSSServer.user.domain.User; @Service @RequiredArgsConstructor public class NotificationService { private static final int DEFAULT_PAGE_NUMBER = 0; + private static final int NOTIFICATION_TITLE_MAX_LENGTH = 12; + private static final int NOTIFICATION_TITLE_MIN_LENGTH = 0; private final NotificationRepository notificationRepository; + private final NotificationTypeRepository notificationTypeRepository; private final ReadNotificationRepository readNotificationRepository; + /** + * 알림을 생성 및 저장한다. + * + * @param feed 피드 + * @param novel 피드에 속한 소설 (없다면 null) + * @param nickname 좋아요를 누른 사용자 닉네임 + * @param writerId 피드 작성자 ID + */ + @Transactional + public Notification createFeedLikedNotification(Feed feed, Novel novel, String nickname, Long writerId) { + + NotificationType notificationType = notificationTypeRepository.findByNotificationTypeName("좋아요"); + + String notificationTitle = createNotificationTitle(feed, novel); + + String notificationBody = String.format("%s님이 내 글을 좋아해요.", nickname); + + Long feedId = feed.getFeedId(); + + Notification notification = Notification.createFeedNotification( + notificationTitle, + notificationBody, + writerId, + feedId, + notificationType + ); + + return notificationRepository.save(notification); + } + + @Transactional + public Notification createBecamePopularFeedNotification(Feed feed, Novel novel) { + + NotificationType notificationType = notificationTypeRepository.findByNotificationTypeName("지금뜨는글"); + + User feedOwner = feed.getUser(); + + Long feedId = feed.getFeedId(); + + String notificationTitle = "지금 뜨는 글 등극\uD83D\uDE4C"; + + String notificationBody = createNotificationBody(feed, novel); + + Notification notification = Notification.createFeedNotification( + notificationTitle, + notificationBody, + feedOwner.getUserId(), + feedId, + notificationType + ); + + return notificationRepository.save(notification); + } + /** * 읽지 않은 알림이 존재하는지 확인한다. * @@ -42,9 +103,9 @@ public boolean hasUnreadNotifications(Long userId) { /** * 알림 목록을 조회한다. * - * @param userId 사용자 ID + * @param userId 사용자 ID * @param lastNotificationId 마지막으로 조회한 알림 ID - * @param size 조회 사이즈 + * @param size 조회 사이즈 * @return 알림 목록 (읽기 여부 포함) */ @Transactional(readOnly = true) @@ -62,7 +123,7 @@ public NotificationPageResponse getNotifications(Long userId, Long lastNotificat /** * 알림 객체를 조회한다. * - * @param userId 사용자 ID + * @param userId 사용자 ID * @param notificationId 알림 ID * @return 알림 객체 */ @@ -93,7 +154,7 @@ public Notification getNoticeNotification(Long notificationId) { /** * 알림을 읽기 상태로 전환한다. * - * @param userId 사용자 ID + * @param userId 사용자 ID * @param notificationId 알림 ID */ @Transactional @@ -127,4 +188,31 @@ private void validateNotificationRecipient(Long userId, Long recipientUserId) { "User does not have permission to access this notification."); } + private String createNotificationTitle(Feed feed, Novel novel) { + if (feed.getNovelId() == null) { + String feedContent = feed.getFeedContent(); + feedContent = feedContent.length() <= NOTIFICATION_TITLE_MAX_LENGTH ? feedContent + : feedContent.substring(NOTIFICATION_TITLE_MIN_LENGTH, NOTIFICATION_TITLE_MAX_LENGTH); + return "'" + feedContent + "...'"; + } + + return novel.getTitle(); + } + + private String createNotificationBody(Feed feed, Novel novel) { + return String.format("내가 남긴 %s 글이 관심 받고 있어요!", generateNotificationBodyFragment(feed, novel)); + } + + private String generateNotificationBodyFragment(Feed feed, Novel novel) { + if (feed.getNovelId() == null) { + String feedContent = feed.getFeedContent(); + feedContent = feedContent.length() <= 12 + ? feedContent + : feedContent.substring(0, 12); + return "'" + feedContent + "...'"; + } + + return String.format("<%s>", novel.getTitle()); + } + } diff --git a/src/main/java/org/websoso/WSSServer/notification/service/UserDeviceService.java b/src/main/java/org/websoso/WSSServer/notification/service/UserDeviceService.java index 68800a76..e3756bcc 100644 --- a/src/main/java/org/websoso/WSSServer/notification/service/UserDeviceService.java +++ b/src/main/java/org/websoso/WSSServer/notification/service/UserDeviceService.java @@ -3,9 +3,12 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.notification.domain.UserDevice; import org.websoso.WSSServer.notification.repository.UserDeviceRepository; import org.websoso.WSSServer.user.domain.User; +import java.util.List; + @Service @RequiredArgsConstructor public class UserDeviceService { @@ -22,4 +25,9 @@ public void deleteDeviceIdentifier(User user, String deviceIdentifier) { userDeviceRepository.deleteByUserAndDeviceIdentifier(user, deviceIdentifier); } + @Transactional(readOnly = true) + public List findUserDevices(Long userId) { + return userDeviceRepository.getUserDevicesByUser_UserId(userId); + } + } diff --git a/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java b/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java index ec765a73..81e3836b 100644 --- a/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java +++ b/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java @@ -2,6 +2,7 @@ import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import org.websoso.WSSServer.user.domain.Block; @@ -12,4 +13,11 @@ public interface BlockRepository extends JpaRepository { List findByBlockingId(Long blockingId); + @Query(""" + SELECT count(b) > 0 + FROM Block b + WHERE (b.blockingId = :userId1 AND b.blockedId = :userId2) + OR (b.blockingId = :userId2 AND b.blockedId = :userId1) + """) + boolean existsBlockRelation(Long userId1, Long userId2); } diff --git a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java index e40b3357..b76b821b 100644 --- a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java +++ b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java @@ -1,14 +1,19 @@ package org.websoso.WSSServer.user.service; import java.util.List; +import java.util.Objects; + import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.exception.exception.CustomFeedException; import org.websoso.WSSServer.user.domain.Block; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.repository.BlockRepository; +import static org.websoso.WSSServer.exception.error.CustomFeedError.BLOCKED_USER_ACCESS; + @Service @RequiredArgsConstructor @Transactional @@ -34,6 +39,17 @@ public boolean exists(Long blockingId, Long blockedId) { return blockRepository.existsByBlockingIdAndBlockedId(blockingId, blockedId); } + @Transactional(readOnly = true) + public void validateNotBlocked(Long userId, Long targetUserId) { + + if (userId.equals(targetUserId)) return; + + if (blockRepository.existsBlockRelation(userId, targetUserId)) { + throw new CustomFeedException(BLOCKED_USER_ACCESS, + "cannot access this feed because either you or the feed author has blocked the other."); + } + } + @Transactional(readOnly = true) public List findByBlockerId(Long blockingId) { return blockRepository.findByBlockingId(blockingId); From 86976c76b68a92b38d07f69ae268a8ed586b14e4 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Thu, 11 Jun 2026 16:22:14 +0900 Subject: [PATCH 07/31] =?UTF-8?q?[FIX]=20/feeds/{feedId}/likes=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=94=BC=EB=93=9C=20=EC=A0=91=EA=B7=BC=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=20=EC=97=AC=EB=B6=80=EB=A5=BC=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=B2=B4=ED=81=AC=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/websoso/WSSServer/feed/controller/FeedController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 47f5c69e..0e5ccf32 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -96,7 +96,7 @@ public ResponseEntity deleteFeed(@AuthenticationPrincipal User user, } @PostMapping("/{feedId}/likes") - @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user)") + @PreAuthorize("isAuthenticated()") public ResponseEntity likeFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { feedLikeApplication.create(user, feedId); From 8fe4a826fd3027ce2a3e1358ffd543ba6b607060 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Thu, 11 Jun 2026 17:31:01 +0900 Subject: [PATCH 08/31] =?UTF-8?q?[REFACTOR]=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=B7=A8=EC=86=8C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20Applicati?= =?UTF-8?q?on=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedLikeApplication.java | 13 +++++++ .../feed/controller/FeedController.java | 6 ++-- .../feed/repository/LikeRepository.java | 4 +++ .../feed/service/FeedLikeService.java | 5 +++ .../WSSServer/feed/service/FeedService.java | 36 +++++++++---------- 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java index 7f7f95d6..39ab87c0 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java @@ -51,4 +51,17 @@ public void create(User user, Long feedId) { ); } + + @Transactional + public void delete(User user, Long feedId) { + + // 접근 가능한 피드인지 체크하고 조회 + Feed feed = feedService.getAccessFeedOrException(feedId, user.getUserId()); + + // 작성자와 본인이 차단 관계인지 체크 + blockService.validateNotBlocked(feed.getWriterId(),user.getUserId()); + + feedLikeService.delete(user.getUserId(), feed); + + } } diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 0e5ccf32..6b9a59b8 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -32,7 +32,6 @@ import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.feed.application.FeedLikeApplication; import org.websoso.WSSServer.feed.application.FeedManagementApplication; -import org.websoso.WSSServer.feed.service.FeedService; import org.websoso.WSSServer.user.domain.User; @RequestMapping("/feeds") @@ -40,7 +39,6 @@ @RequiredArgsConstructor public class FeedController { - private final FeedService feedService; private final FeedManagementApplication feedManagementApplication; private final FeedFindApplication feedFindApplication; private final FeedLikeApplication feedLikeApplication; @@ -106,10 +104,10 @@ public ResponseEntity likeFeed(@AuthenticationPrincipal User user, } @DeleteMapping("/{feedId}/likes") - @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user)") + @PreAuthorize("isAuthenticated()") public ResponseEntity unLikeFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { - feedService.unLikeFeed(user, feedId); + feedLikeApplication.delete(user, feedId); return ResponseEntity .status(NO_CONTENT) .build(); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java index f16807f6..a1a88db7 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java @@ -21,6 +21,10 @@ public interface LikeRepository extends JpaRepository { @Query("DELETE FROM Like l WHERE l.feed.feedId = :feedId") void deleteByFeedId(Long feedId); + @Modifying + @Query("DELETE FROM Like l WHERE l.userId = :userId AND l.feed = :feed") + void deleteByUserIdAndFeed(Long userId, Feed feed); + Long feed(Feed feed); long countByFeed_FeedId(Long feedFeedId); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java index 383501dd..067e31db 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java @@ -27,6 +27,11 @@ public boolean create(Long userId, Feed feed) { } } + @Transactional + public void delete(Long userId, Feed feed) { + likeRepository.deleteByUserIdAndFeed(userId, feed); + } + @Transactional public void deleteByFeedId(Long feedId) { likeRepository.deleteByFeedId(feedId); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java index 6fbf267f..e2379d6f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java @@ -112,12 +112,12 @@ public class FeedService { private final FCMClient fcmClient; - @Transactional - public void deleteFeed(Long feedId) { - List commentIds = commentRepository.findAllByFeedId(feedId).stream().map(Comment::getCommentId).toList(); - reportedCommentRepository.deleteByCommentIdsIn(commentIds); - feedRepository.deleteById(feedId); - } +// @Transactional +// public void deleteFeed(Long feedId) { +// List commentIds = commentRepository.findAllByFeedId(feedId).stream().map(Comment::getCommentId).toList(); +// reportedCommentRepository.deleteByCommentIdsIn(commentIds); +// feedRepository.deleteById(feedId); +// } // @Transactional // public void likeFeed(User user, Long feedId) { @@ -186,14 +186,14 @@ public void deleteFeed(Long feedId) { // return novel.getTitle(); // } - @Transactional - public void unLikeFeed(User user, Long feedId) { - Feed feed = getFeedOrException(feedId); - Like like = likeRepository.findByUserIdAndFeed(user.getUserId(), feed) - .orElseThrow(() -> new CustomFeedException(NOT_LIKED, - "User did not like this feed or like already deleted")); - likeRepository.delete(like); - } +// @Transactional +// public void unLikeFeed(User user, Long feedId) { +// Feed feed = getFeedOrException(feedId); +// Like like = likeRepository.findByUserIdAndFeed(user.getUserId(), feed) +// .orElseThrow(() -> new CustomFeedException(NOT_LIKED, +// "User did not like this feed or like already deleted")); +// likeRepository.delete(like); +// } // @Transactional(readOnly = true) // public FeedGetResponse getFeedById(User user, Long feedId) { @@ -233,10 +233,10 @@ public void unLikeFeed(User user, Long feedId) { // return Optional.ofNullable(category).orElse(DEFAULT_CATEGORY); // } - private Feed getFeedOrException(Long feedId) { - return feedRepository.findById(feedId) - .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); - } +// private Feed getFeedOrException(Long feedId) { +// return feedRepository.findById(feedId) +// .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); +// } private UserBasicInfo getUserBasicInfo(User user) { return user.getUserBasicInfo( From fef698d90acca3632ffba39738f71efdc67347ff Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Thu, 11 Jun 2026 17:37:07 +0900 Subject: [PATCH 09/31] =?UTF-8?q?[REFCATOR]=20Novel,=20User=EC=97=90=20?= =?UTF-8?q?=EB=B6=84=EC=82=B0=EB=90=98=EC=96=B4=20=EC=9E=88=EB=8D=98=20?= =?UTF-8?q?=ED=94=BC=EB=93=9C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/controller/NovelController.java | 11 ---- .../feed/controller/FeedController.java | 52 +++++++++++++++---- .../user/controller/UserController.java | 17 ------ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/controller/NovelController.java b/src/main/java/org/websoso/WSSServer/controller/NovelController.java index d44153cd..ec453016 100644 --- a/src/main/java/org/websoso/WSSServer/controller/NovelController.java +++ b/src/main/java/org/websoso/WSSServer/controller/NovelController.java @@ -29,7 +29,6 @@ @RequiredArgsConstructor public class NovelController { - private final FeedService feedService; private final SearchNovelApplication searchNovelApplication; /** @@ -149,14 +148,4 @@ public ResponseEntity getTasteNovels(@AuthenticationPrin .body(searchNovelApplication.getTasteNovels(user)); } - // TODO: Feed Controller로 이동해야함 - @GetMapping("/{novelId}/feeds") - public ResponseEntity getFeedsByNovel(@AuthenticationPrincipal User user, - @PathVariable Long novelId, - @RequestParam("lastFeedId") Long lastFeedId, - @RequestParam("size") int size) { - return ResponseEntity - .status(OK) - .body(feedService.getFeedsByNovel(user, novelId, lastFeedId, size)); - } } diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 6b9a59b8..38d90142 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -19,6 +19,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.websoso.WSSServer.domain.common.SortCriteria; +import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; +import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; import org.websoso.WSSServer.feed.application.FeedFindApplication; import org.websoso.WSSServer.domain.common.FeedGetOption; import org.websoso.WSSServer.dto.feed.FeedCreateRequest; @@ -32,18 +35,22 @@ import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.feed.application.FeedLikeApplication; import org.websoso.WSSServer.feed.application.FeedManagementApplication; +import org.websoso.WSSServer.feed.service.FeedService; import org.websoso.WSSServer.user.domain.User; -@RequestMapping("/feeds") +import java.util.List; + +@RequestMapping @RestController @RequiredArgsConstructor public class FeedController { + private final FeedService feedService; private final FeedManagementApplication feedManagementApplication; private final FeedFindApplication feedFindApplication; private final FeedLikeApplication feedLikeApplication; - @PostMapping + @PostMapping("/feeds") @PreAuthorize("isAuthenticated()") public ResponseEntity createFeed(@AuthenticationPrincipal User user, @Valid @RequestPart("feed") FeedCreateRequest request, @@ -53,7 +60,7 @@ public ResponseEntity createFeed(@AuthenticationPrincipal Us .body(feedManagementApplication.create(user, request, requestImage)); } - @GetMapping("/{feedId}") + @GetMapping("/feeds/{feedId}") @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user)") public ResponseEntity getFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { @@ -62,7 +69,7 @@ public ResponseEntity getFeed(@AuthenticationPrincipal User use .body(feedFindApplication.getFeedById(user, feedId)); } - @GetMapping + @GetMapping("/feeds") public ResponseEntity getFeeds(@AuthenticationPrincipal User user, @RequestParam("lastFeedId") Long lastFeedId, @RequestParam("size") int size, @@ -72,7 +79,7 @@ public ResponseEntity getFeeds(@AuthenticationPrincipal User u .body(feedFindApplication.getFeeds(user, lastFeedId, size, feedGetOption)); } - @PutMapping("/{feedId}") + @PutMapping("/feeds/{feedId}") @PreAuthorize("isAuthenticated()") public ResponseEntity updateFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId, @@ -83,7 +90,7 @@ public ResponseEntity updateFeed(@AuthenticationPrincipal Us .body(feedManagementApplication.update(user, feedId, request, requestImage)); } - @DeleteMapping("/{feedId}") + @DeleteMapping("/feeds/{feedId}") @PreAuthorize("isAuthenticated()") public ResponseEntity deleteFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { @@ -93,7 +100,7 @@ public ResponseEntity deleteFeed(@AuthenticationPrincipal User user, .build(); } - @PostMapping("/{feedId}/likes") + @PostMapping("/feeds/{feedId}/likes") @PreAuthorize("isAuthenticated()") public ResponseEntity likeFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { @@ -103,7 +110,7 @@ public ResponseEntity likeFeed(@AuthenticationPrincipal User user, .build(); } - @DeleteMapping("/{feedId}/likes") + @DeleteMapping("/feeds/{feedId}/likes") @PreAuthorize("isAuthenticated()") public ResponseEntity unLikeFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { @@ -113,7 +120,7 @@ public ResponseEntity unLikeFeed(@AuthenticationPrincipal User user, .build(); } - @GetMapping("/popular") + @GetMapping("/feeds/popular") public ResponseEntity getPopularFeeds(@AuthenticationPrincipal User user, @RequestParam(name = "size", defaultValue = "9") int size) { return ResponseEntity @@ -121,7 +128,7 @@ public ResponseEntity getPopularFeeds(@AuthenticationPr .body(feedFindApplication.getPopularFeeds(user, size)); } - @GetMapping("/interest") + @GetMapping("/feeds/interest") @PreAuthorize("isAuthenticated()") public ResponseEntity getInterestFeeds(@AuthenticationPrincipal User user) { return ResponseEntity @@ -129,4 +136,29 @@ public ResponseEntity getInterestFeeds(@Authentication .body(feedFindApplication.getInterestFeeds(user)); } + @GetMapping("/novels/{novelId}/feeds") + public ResponseEntity getFeedsByNovel(@AuthenticationPrincipal User user, + @PathVariable Long novelId, + @RequestParam("lastFeedId") Long lastFeedId, + @RequestParam("size") int size) { + return ResponseEntity + .status(OK) + .body(feedService.getFeedsByNovel(user, novelId, lastFeedId, size)); + } + + @GetMapping("/users/{userId}/feeds") + public ResponseEntity getUserFeeds(@AuthenticationPrincipal User visitor, + @PathVariable("userId") Long userId, + @RequestParam("lastFeedId") Long lastFeedId, + @RequestParam("size") int size, + @RequestParam(value = "isVisible", required = false) Boolean isVisible, + @RequestParam(value = "isUnVisible", required = false) Boolean isUnVisible, + @RequestParam(value = "genreNames", required = false) List genreNames, + @RequestParam(value = "sortCriteria", required = false) SortCriteria sortCriteria) { + return ResponseEntity + .status(OK) + // ToDo: isVisible -> isPublic으로 수정 + .body(feedService.getUserFeeds(visitor, userId, lastFeedId, size, isVisible, isUnVisible, genreNames, + sortCriteria)); + } } diff --git a/src/main/java/org/websoso/WSSServer/user/controller/UserController.java b/src/main/java/org/websoso/WSSServer/user/controller/UserController.java index b1ae01f2..3a39a991 100644 --- a/src/main/java/org/websoso/WSSServer/user/controller/UserController.java +++ b/src/main/java/org/websoso/WSSServer/user/controller/UserController.java @@ -59,7 +59,6 @@ public class UserController { private final UserService userService; private final UserNovelService userNovelService; private final AuthApplication authApplication; - private final FeedService feedService; // TODO: AUTH 패키지로 이동해야 함, 그리고 가장 위험한 보안 취약점 @PostMapping("/login") @@ -197,22 +196,6 @@ public ResponseEntity getUserNovelsAndNovel sortCriteria)); } - @GetMapping("/{userId}/feeds") - public ResponseEntity getUserFeeds(@AuthenticationPrincipal User visitor, - @PathVariable("userId") Long userId, - @RequestParam("lastFeedId") Long lastFeedId, - @RequestParam("size") int size, - @RequestParam(value = "isVisible", required = false) Boolean isVisible, - @RequestParam(value = "isUnVisible", required = false) Boolean isUnVisible, - @RequestParam(value = "genreNames", required = false) List genreNames, - @RequestParam(value = "sortCriteria", required = false) SortCriteria sortCriteria) { - return ResponseEntity - .status(OK) - // ToDo: isVisible -> isPublic으로 수정 - .body(feedService.getUserFeeds(visitor, userId, lastFeedId, size, isVisible, isUnVisible, genreNames, - sortCriteria)); - } - @GetMapping("/{userId}/preferences/genres") public ResponseEntity getUserGenrePreferences( @AuthenticationPrincipal User visitor, From 333cc984a4f77537b0935a995b7466d61c470387 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Fri, 12 Jun 2026 14:16:03 +0900 Subject: [PATCH 10/31] =?UTF-8?q?[REFACTOR]=20FeedService=EB=A5=BC=20?= =?UTF-8?q?=EC=A7=81=EC=A0=91=20=ED=98=B8=EC=B6=9C=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20Application=EC=9D=84=20=EA=B1=B0?= =?UTF-8?q?=EC=B9=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 79 ++++++++- .../feed/controller/FeedController.java | 6 +- .../feed/repository/FeedCustomRepository.java | 4 +- .../repository/FeedCustomRepositoryImpl.java | 44 ++++- .../feed/repository/FeedImageRepository.java | 2 +- .../feed/repository/FeedRepository.java | 18 +-- .../feed/service/FeedImageService.java | 28 ++++ .../WSSServer/feed/service/FeedService.java | 153 +++++------------- .../feed/service/FeedServiceImpl.java | 67 +++++--- .../novel/service/NovelServiceImpl.java | 5 + .../websoso/WSSServer/user/domain/User.java | 4 + .../WSSServer/user/service/UserService.java | 11 ++ 12 files changed, 272 insertions(+), 149 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 99aadbb8..552103ed 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -11,7 +11,13 @@ import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.domain.common.SortCriteria; +import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; +import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; +import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; +import org.websoso.WSSServer.feed.service.FeedImageService; +import org.websoso.WSSServer.novel.service.GenreServiceImpl; import org.websoso.WSSServer.user.domain.AvatarProfile; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.GenrePreference; @@ -36,16 +42,20 @@ import org.websoso.WSSServer.user.repository.AvatarProfileRepository; import org.websoso.WSSServer.repository.GenrePreferenceRepository; import org.websoso.WSSServer.user.domain.User; +import org.websoso.WSSServer.user.service.UserService; @Service @RequiredArgsConstructor public class FeedFindApplication { + private static final int DEFAULT_PAGE_NUMBER = 0; + + private final GenreServiceImpl genreService; + private final UserService userService; private final FeedServiceImpl feedServiceImpl; + private final FeedImageService feedImageService; private final NovelServiceImpl novelServiceImpl; - private static final int DEFAULT_PAGE_NUMBER = 0; - //ToDo : 의존성 제거 필요 부분 private final AvatarProfileRepository avatarRepository; private final LikeRepository likeRepository; @@ -202,4 +212,69 @@ public InterestFeedsGetResponse getInterestFeeds(User user) { }).toList(); return InterestFeedsGetResponse.of(interestFeedGetResponses, ""); } + + @Transactional(readOnly = true) + public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long lastFeedId, int size) { + + // 있는 웹소설인지 체크 + novelServiceImpl.getNovelOrException(novelId); + + Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); + + Slice feeds = feedServiceImpl.findFeedsByNovel(userIdOrNull, novelId, lastFeedId, size); + + List feedGetResponses = feeds.getContent().stream() + .map(feed -> createFeedInfo(feed, user)) + .toList(); + + return NovelGetResponseFeedTab.of(feeds.hasNext(), feedGetResponses); + } + + @Transactional(readOnly = true) + public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFeedId, int size, Boolean isVisible, + Boolean isUnVisible, List genreNames, SortCriteria sortCriteria) { + + User owner = userService.getUserOrException(ownerId); + + Long visitorId = Optional.ofNullable(visitor).map(User::getUserId).orElse(null); + + userService.validateProfileAccessible(owner, visitorId); + + boolean includeEtc = genreNames != null && genreNames.contains("etc"); + List filteredGenreNames = genreNames == null + ? null + : genreNames.stream().filter(name -> !name.equals("etc")).collect(Collectors.toList()); + List genres = genreService.getGenresOrException(filteredGenreNames); + + List visibleFeeds = feedServiceImpl.getViewableUserFeed(owner, lastFeedId, size, isVisible, + isUnVisible, sortCriteria, genres, visitorId, includeEtc); + + List novelIds = visibleFeeds.stream().map(Feed::getNovelId).filter(Objects::nonNull) + .collect(Collectors.toList()); + + // 소설 ID에 해당하는 소설 정보들 전부 불러오기 + List novels = novelServiceImpl.findAllByIds(novelIds); + + // 해당 로직 수정 + Map novelMap = novels.stream() + .collect(Collectors.toMap(Novel::getNovelId, Function.identity())); + + List userFeedGetResponseList = visibleFeeds.stream() + .map(feed -> UserFeedGetResponse.of( + feed, + novelMap.get(feed.getNovelId()), + visitorId, + feedImageService.getThumbnailUrl(feed), + feedImageService.getImageCount(feed)) + ).toList(); + + // TODO Slice의 hasNext()로 판단하도록 수정 + Boolean isLoadable = visibleFeeds.size() == size; + + long feedsCount = feedServiceImpl.getViewableUserFeedCount(owner, isVisible, isUnVisible, genres, visitorId, includeEtc); + + return UserFeedsGetResponse.of(isLoadable, feedsCount, userFeedGetResponseList); + + } + } diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 38d90142..3e364a28 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -35,7 +35,6 @@ import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.feed.application.FeedLikeApplication; import org.websoso.WSSServer.feed.application.FeedManagementApplication; -import org.websoso.WSSServer.feed.service.FeedService; import org.websoso.WSSServer.user.domain.User; import java.util.List; @@ -45,7 +44,6 @@ @RequiredArgsConstructor public class FeedController { - private final FeedService feedService; private final FeedManagementApplication feedManagementApplication; private final FeedFindApplication feedFindApplication; private final FeedLikeApplication feedLikeApplication; @@ -143,7 +141,7 @@ public ResponseEntity getFeedsByNovel(@AuthenticationPr @RequestParam("size") int size) { return ResponseEntity .status(OK) - .body(feedService.getFeedsByNovel(user, novelId, lastFeedId, size)); + .body(feedFindApplication.getFeedsByNovel(user, novelId, lastFeedId, size)); } @GetMapping("/users/{userId}/feeds") @@ -158,7 +156,7 @@ public ResponseEntity getUserFeeds(@AuthenticationPrincipa return ResponseEntity .status(OK) // ToDo: isVisible -> isPublic으로 수정 - .body(feedService.getUserFeeds(visitor, userId, lastFeedId, size, isVisible, isUnVisible, genreNames, + .body(feedFindApplication.getUserFeeds(visitor, userId, lastFeedId, size, isVisible, isUnVisible, genreNames, sortCriteria)); } } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java index 9204a68d..cab5e62a 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java @@ -23,7 +23,9 @@ List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Slice findFeedsByGenres(List genres, boolean includeEtc, Long lastFeedId, Long userId, PageRequest pageRequest); - Long countVisibleFeeds(User owner, Long lastFeedId, Boolean isVisible, + Long countVisibleFeeds(User owner, Boolean isVisible, Boolean isUnVisible, List genres, Long visitorId, boolean includeEtc); + + Slice findFeedsByNovelId(Long novelId, Long lastFeedId, Long userId, PageRequest pageRequest); } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index 8fa960df..72e732e0 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -96,7 +96,7 @@ public Optional findThumbnailFeedImageByFeedId(long feedId) { } @Override - public Long countVisibleFeeds(User owner, Long lastFeedId, Boolean isVisible, + public Long countVisibleFeeds(User owner, Boolean isVisible, Boolean isUnVisible, List genres, Long visitorId, boolean isNotNovelConnect) { return jpaQueryFactory @@ -114,6 +114,30 @@ public Long countVisibleFeeds(User owner, Long lastFeedId, Boolean isVisible, .fetchOne(); } + @Override + public Slice findFeedsByNovelId(Long novelId, Long lastFeedId, Long userId, PageRequest pageRequest) { + List feeds = jpaQueryFactory + .selectFrom(feed) + .where( + feed.novelId.eq(novelId), + ltFeedId(lastFeedId), + checkHidden(), + checkVisible(userId), + checkBlockRelation(userId) + ) + .orderBy(feed.feedId.desc()) + .limit(pageRequest.getPageSize() + 1) + .fetch(); + + boolean hasNext = feeds.size() > pageRequest.getPageSize(); + + if (hasNext) { + feeds.remove(feeds.size() - 1); + } + + return new SliceImpl<>(feeds, pageRequest, hasNext); + } + private BooleanExpression ltFeedId(Long lastFeedId) { if (lastFeedId == NO_CURSOR) { return null; @@ -253,6 +277,24 @@ private BooleanExpression checkBlocking(Long userId) { return null; } + private BooleanExpression checkBlockRelation(Long userId) { + if (userId == null) { + return null; + } + + return feed.user.userId.notIn( + JPAExpressions + .select(block.blockedId) + .from(block) + .where(block.blockingId.eq(userId)) + ).and(feed.user.userId.notIn( + JPAExpressions + .select(block.blockingId) + .from(block) + .where(block.blockedId.eq(userId)) + )); + } + private BooleanExpression checkHidden() { return feed.isHidden.eq(false); } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java index ca49ad46..9f916327 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java @@ -3,6 +3,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.websoso.WSSServer.feed.domain.FeedImage; -public interface FeedImageRepository extends JpaRepository { +public interface FeedImageRepository extends JpaRepository, FeedImageCustomRepository { Integer countByFeedId(Long feedId); } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java index 1fddaaa6..2dfbee04 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java @@ -26,15 +26,15 @@ public interface FeedRepository extends JpaRepository, FeedCustomRep List findTop10ByNovelIdInOrderByFeedIdDesc(List novelIds); - @Query(value = "SELECT f FROM Feed f WHERE " - + "(:lastFeedId = 0 OR f.feedId < :lastFeedId) " - + "AND f.novelId = :novelId " - + "AND f.isHidden = false " - + "AND (f.isPublic = true OR f.user.userId = :userId)" - + "AND (:userId IS NULL " - + "OR f.user.userId NOT IN (SELECT b.blockedId FROM Block b WHERE b.blockingId = :userId)) " - + "ORDER BY f.feedId DESC") - Slice findFeedsByNovelId(Long novelId, Long lastFeedId, Long userId, PageRequest pageRequest); +// @Query(value = "SELECT f FROM Feed f WHERE " +// + "(:lastFeedId = 0 OR f.feedId < :lastFeedId) " +// + "AND f.novelId = :novelId " +// + "AND f.isHidden = false " +// + "AND (f.isPublic = true OR f.user.userId = :userId)" +// + "AND (:userId IS NULL " +// + "OR f.user.userId NOT IN (SELECT b.blockedId FROM Block b WHERE b.blockingId = :userId)) " +// + "ORDER BY f.feedId DESC") +// Slice findFeedsByNovelId(Long novelId, Long lastFeedId, Long userId, PageRequest pageRequest); @Transactional @Modifying(clearAutomatically = true, flushAutomatically = true) diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java new file mode 100644 index 00000000..9196ff5e --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java @@ -0,0 +1,28 @@ +package org.websoso.WSSServer.feed.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.repository.FeedImageRepository; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class FeedImageService { + + private final FeedImageRepository feedImageRepository; + + @Transactional(readOnly = true) + public String getThumbnailUrl(Feed feed) { + Optional thumbnailImage = feedImageRepository.findThumbnailFeedImageByFeedId(feed.getFeedId()); + return thumbnailImage.map(FeedImage::getUrl).orElse(null); + } + + @Transactional(readOnly = true) + public Integer getImageCount(Feed feed) { + return feedImageRepository.countByFeedId(feed.getFeedId()); + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java index e2379d6f..3bf251e2 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java @@ -105,7 +105,6 @@ public class FeedService { private final UserRepository userRepository; private final AvatarProfileRepository avatarProfileRepository; private final FeedImageRepository feedImageRepository; - private final FeedImageCustomRepository feedImageCustomRepository; private final UserNovelRepository userNovelRepository; private final GenreRepository genreRepository; private final ImageClient imageClient; @@ -238,41 +237,41 @@ public class FeedService { // .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); // } - private UserBasicInfo getUserBasicInfo(User user) { - return user.getUserBasicInfo( - avatarProfileRepository.findById(user.getAvatarProfileId()).orElseThrow(() -> - new CustomAvatarException(AVATAR_NOT_FOUND, "avatar with the given id was not found")) - .getAvatarProfileImage()); - } - - private Novel getLinkedNovelOrNull(Long linkedNovelId) { - if (linkedNovelId == null) { - return null; - } - return novelRepository.findById(linkedNovelId) - .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, - "novel with the given id is not found")); - } - - private Boolean isUserLikedFeed(User user, Feed feed) { - return likeRepository.existsByUserIdAndFeed(user.getUserId(), feed); - } - - private Boolean isUserFeedOwner(User createdUser, User user) { - return createdUser.equals(user); - } - - private FeedInfo createFeedInfo(Feed feed, User user) { - UserBasicInfo userBasicInfo = getUserBasicInfo(feed.getUser()); - Novel novel = getLinkedNovelOrNull(feed.getNovelId()); - Boolean isLiked = user != null && isUserLikedFeed(user, feed); - Boolean isMyFeed = user != null && isUserFeedOwner(feed.getUser(), user); - Integer imageCount = feedImageRepository.countByFeedId(feed.getFeedId()); - Optional thumbnailImage = feedImageCustomRepository.findThumbnailFeedImageByFeedId(feed.getFeedId()); - String thumbnailUrl = thumbnailImage.map(FeedImage::getUrl).orElse(null); - - return FeedInfo.of(feed, userBasicInfo, novel, isLiked, isMyFeed, thumbnailUrl, imageCount, user); - } +// private UserBasicInfo getUserBasicInfo(User user) { +// return user.getUserBasicInfo( +// avatarProfileRepository.findById(user.getAvatarProfileId()).orElseThrow(() -> +// new CustomAvatarException(AVATAR_NOT_FOUND, "avatar with the given id was not found")) +// .getAvatarProfileImage()); +// } + +// private Novel getLinkedNovelOrNull(Long linkedNovelId) { +// if (linkedNovelId == null) { +// return null; +// } +// return novelRepository.findById(linkedNovelId) +// .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, +// "novel with the given id is not found")); +// } + +// private Boolean isUserLikedFeed(User user, Feed feed) { +// return likeRepository.existsByUserIdAndFeed(user.getUserId(), feed); +// } +// +// private Boolean isUserFeedOwner(User createdUser, User user) { +// return createdUser.equals(user); +// } + +// private FeedInfo createFeedInfo(Feed feed, User user) { +// UserBasicInfo userBasicInfo = getUserBasicInfo(feed.getUser()); +// Novel novel = getLinkedNovelOrNull(feed.getNovelId()); +// Boolean isLiked = user != null && isUserLikedFeed(user, feed); +// Boolean isMyFeed = user != null && isUserFeedOwner(feed.getUser(), user); +// Integer imageCount = feedImageRepository.countByFeedId(feed.getFeedId()); +// Optional thumbnailImage = feedImageCustomRepository.findThumbnailFeedImageByFeedId(feed.getFeedId()); +// String thumbnailUrl = thumbnailImage.map(FeedImage::getUrl).orElse(null); +// +// return FeedInfo.of(feed, userBasicInfo, novel, isLiked, isMyFeed, thumbnailUrl, imageCount, user); +// } // private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, // FeedGetOption feedGetOption, List preferenceGenres) { @@ -322,79 +321,15 @@ private FeedInfo createFeedInfo(Feed feed, User user) { // return InterestFeedsGetResponse.of(interestFeedGetResponses, ""); // } - @Transactional(readOnly = true) - public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long lastFeedId, int size) { - Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); - Slice feeds = feedRepository.findFeedsByNovelId(novelId, lastFeedId, userIdOrNull, - PageRequest.of(DEFAULT_PAGE_NUMBER, size)); - - List feedGetResponses = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) - .map(feed -> createFeedInfo(feed, user)).toList(); - - return NovelGetResponseFeedTab.of(feeds.hasNext(), feedGetResponses); - } - - @Transactional(readOnly = true) - public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFeedId, int size, Boolean isVisible, - Boolean isUnVisible, List genreNames, SortCriteria sortCriteria) { - User owner = userRepository.findById(ownerId) - .orElseThrow(() -> new CustomUserException(USER_NOT_FOUND, "user with the given id was not found")); - Long visitorId = Optional.ofNullable(visitor).map(User::getUserId).orElse(null); - - if (owner.getIsProfilePublic() || isOwner(visitor, ownerId)) { - boolean includeEtc = genreNames != null && genreNames.contains("etc"); - List filteredGenreNames = genreNames == null ? null : - genreNames.stream().filter(name -> !name.equals("etc")).collect(Collectors.toList()); - List genres = getGenres(filteredGenreNames); - - List visibleFeeds = feedRepository.findFeedsByNoOffsetPagination(owner, lastFeedId, size, isVisible, - isUnVisible, sortCriteria, genres, visitorId, includeEtc); - - List novelIds = visibleFeeds.stream().map(Feed::getNovelId).filter(Objects::nonNull) - .collect(Collectors.toList()); - Map novelMap = novelRepository.findAllById(novelIds).stream() - .collect(Collectors.toMap(Novel::getNovelId, novel -> novel)); - - List userFeedGetResponseList = visibleFeeds.stream() - .map(feed -> UserFeedGetResponse.of(feed, novelMap.get(feed.getNovelId()), visitorId, - getThumbnailUrl(feed), getImageCount(feed))).toList(); - - // TODO Slice의 hasNext()로 판단하도록 수정 - Boolean isLoadable = visibleFeeds.size() == size; - long feedsCount = feedRepository.countVisibleFeeds(owner, lastFeedId, isVisible, isUnVisible, genres, - visitorId, includeEtc); - - return UserFeedsGetResponse.of(isLoadable, feedsCount, userFeedGetResponseList); - } - - throw new CustomUserException(PRIVATE_PROFILE_STATUS, "the profile status of the user is set to private"); - } - - private static boolean isOwner(User visitor, Long ownerId) { - //TODO 현재는 비로그인 회원인 경우 - return visitor != null && visitor.getUserId().equals(ownerId); - } - - private List getGenres(List genreNames) { - if (genreNames != null && !genreNames.isEmpty()) { - List genres = genreNames.stream() - .map(genreName -> genreRepository.findByGenreName(genreName) - .orElseThrow(() -> new CustomGenreException(GENRE_NOT_FOUND, - "genre with the given name is not found")) - ).toList(); - return genres.isEmpty() ? null : genres; - } - return null; - } - - private String getThumbnailUrl(Feed feed) { - Optional thumbnailImage = feedImageCustomRepository.findThumbnailFeedImageByFeedId(feed.getFeedId()); - return thumbnailImage.map(FeedImage::getUrl).orElse(null); - } - - private Integer getImageCount(Feed feed) { - return feedImageRepository.countByFeedId(feed.getFeedId()); - } + + + + + + + + + // private void sendPopularFeedPushMessage(Feed feed) { // NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("지금뜨는글"); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 47a72f21..6e1c98ca 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -2,7 +2,6 @@ import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomFeedError.HIDDEN_FEED_ACCESS; -import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import java.util.Comparator; @@ -18,8 +17,8 @@ import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.common.FeedGetOption; +import org.websoso.WSSServer.domain.common.SortCriteria; import org.websoso.WSSServer.exception.exception.CustomFeedException; -import org.websoso.WSSServer.exception.exception.CustomGenreException; import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; @@ -29,11 +28,14 @@ import org.websoso.WSSServer.feed.repository.FeedRepository; import org.websoso.WSSServer.feed.repository.PopularFeedRepository; import org.websoso.WSSServer.repository.GenreRepository; +import org.websoso.WSSServer.user.domain.User; @Service @RequiredArgsConstructor public class FeedServiceImpl { + private static final int DEFAULT_PAGE_NUMBER = 0; + private final FeedRepository feedRepository; private final FeedImageRepository feedImageRepository; private final FeedImageCustomRepository feedImageCustomRepository; @@ -80,30 +82,51 @@ public Feed getAccessFeedOrException(Long feedId, Long userId) { return feed; } + @Transactional(readOnly = true) + public Slice findFeedsByNovel(Long userIdOrNull, Long novelId, Long lastFeedId, int size) { + return feedRepository.findFeedsByNovelId(novelId, lastFeedId, userIdOrNull, + PageRequest.of(DEFAULT_PAGE_NUMBER, size)); + } + + @Transactional(readOnly = true) + public List getViewableUserFeed(User owner, Long lastFeedId, int size, Boolean isVisible, + Boolean isUnVisible, SortCriteria sortCriteria, + List genres, Long visitorId, boolean isNotNovelConnect) { + return feedRepository.findFeedsByNoOffsetPagination(owner, lastFeedId, size, isVisible, + isUnVisible, sortCriteria, genres, visitorId, isNotNovelConnect); + } + + @Transactional(readOnly = true) + public long getViewableUserFeedCount(User owner, Boolean isVisible, + Boolean isUnVisible, List genres, + Long visitorId, boolean includeEtc) { + return feedRepository.countVisibleFeeds(owner, isVisible, isUnVisible, genres, visitorId, includeEtc); + } + @Transactional(readOnly = true) public Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, FeedGetOption feedGetOption, List preferenceGenres) { - if (FeedGetOption.isAll(feedGetOption)) { - return feedRepository.findFeeds(lastFeedId, userId, pageRequest); - } else { - // 인기 피드 - Slice recommendedFeeds = feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres); - // 내가 관심 등록한 작품의 피드 - Slice interestedNovelFeeds = feedRepository.findInterestedNovelFeeds(lastFeedId, userId, pageRequest); - int pageSize = pageRequest.getPageSize(); - List combinedFeeds = Stream.concat( - recommendedFeeds.getContent().stream(), - interestedNovelFeeds.getContent().stream()) - .distinct() - .sorted(Comparator.comparing(Feed::getFeedId).reversed()) // feedId 내림차순 정렬 - .toList(); - List resultFeeds = combinedFeeds.stream() - .limit(pageSize) - .toList(); - boolean hasNext = combinedFeeds.size() > pageSize || recommendedFeeds.hasNext() || interestedNovelFeeds.hasNext(); - return new SliceImpl<>(resultFeeds, pageRequest, hasNext); - } + if (FeedGetOption.isAll(feedGetOption)) { + return feedRepository.findFeeds(lastFeedId, userId, pageRequest); + } else { + // 인기 피드 + Slice recommendedFeeds = feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres); + // 내가 관심 등록한 작품의 피드 + Slice interestedNovelFeeds = feedRepository.findInterestedNovelFeeds(lastFeedId, userId, pageRequest); + int pageSize = pageRequest.getPageSize(); + List combinedFeeds = Stream.concat( + recommendedFeeds.getContent().stream(), + interestedNovelFeeds.getContent().stream()) + .distinct() + .sorted(Comparator.comparing(Feed::getFeedId).reversed()) // feedId 내림차순 정렬 + .toList(); + List resultFeeds = combinedFeeds.stream() + .limit(pageSize) + .toList(); + boolean hasNext = combinedFeeds.size() > pageSize || recommendedFeeds.hasNext() || interestedNovelFeeds.hasNext(); + return new SliceImpl<>(resultFeeds, pageRequest, hasNext); + } } diff --git a/src/main/java/org/websoso/WSSServer/novel/service/NovelServiceImpl.java b/src/main/java/org/websoso/WSSServer/novel/service/NovelServiceImpl.java index 92cea1f7..db659c62 100644 --- a/src/main/java/org/websoso/WSSServer/novel/service/NovelServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/novel/service/NovelServiceImpl.java @@ -42,6 +42,11 @@ public List getNovelsWithGenresByIds(List novelIds) { return novelRepository.findAllByNovelIdInWithGenres(novelIds); } + @Transactional(readOnly = true) + public List findAllByIds(List novelIds) { + return novelRepository.findAllById(novelIds); + } + public Page searchNovels(PageRequest pageRequest, String searchQuery) { return novelRepository.findSearchedNovels(pageRequest, searchQuery); } diff --git a/src/main/java/org/websoso/WSSServer/user/domain/User.java b/src/main/java/org/websoso/WSSServer/user/domain/User.java index a1d21a50..40911094 100644 --- a/src/main/java/org/websoso/WSSServer/user/domain/User.java +++ b/src/main/java/org/websoso/WSSServer/user/domain/User.java @@ -215,4 +215,8 @@ public boolean isSameUserId(Long userId) { public boolean isTemporaryNickname() { return this.nickname.contains("*"); } + + public boolean canBeViewedBy(Long visitorId) { + return this.isProfilePublic || this.userId.equals(visitorId); + } } diff --git a/src/main/java/org/websoso/WSSServer/user/service/UserService.java b/src/main/java/org/websoso/WSSServer/user/service/UserService.java index cc3943d1..d7ccaf7c 100644 --- a/src/main/java/org/websoso/WSSServer/user/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/user/service/UserService.java @@ -1,6 +1,7 @@ package org.websoso.WSSServer.user.service; import static java.lang.Boolean.FALSE; +import static org.websoso.WSSServer.exception.error.CustomUserError.PRIVATE_PROFILE_STATUS; import static org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessageType.JOIN; import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; @@ -263,4 +264,14 @@ public void updateTermsSetting(User user, Boolean serviceAgreed, Boolean privacy public List findAllByIds(List blockUserIds) { return userRepository.findAllById(blockUserIds); } + + + public void validateProfileAccessible(User owner, Long visitorId) { + if (!owner.canBeViewedBy(visitorId)) { + throw new CustomUserException( + PRIVATE_PROFILE_STATUS, + "the profile status of the user is set to private" + ); + } + } } From 6ba21e1cd90f84fea52a5df62af1ed38904a5a29 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Sat, 13 Jun 2026 16:26:37 +0900 Subject: [PATCH 11/31] =?UTF-8?q?[REFCATOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EB=8B=A8=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?Application=EC=97=90=EC=84=9C=20Repository=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 30 ++++++++++++++++--- .../feed/controller/FeedController.java | 2 +- .../feed/service/FeedLikeService.java | 5 ++++ .../feed/service/FeedServiceImpl.java | 8 +---- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 552103ed..ea3d11ec 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -17,6 +17,7 @@ import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; import org.websoso.WSSServer.feed.service.FeedImageService; +import org.websoso.WSSServer.feed.service.FeedLikeService; import org.websoso.WSSServer.novel.service.GenreServiceImpl; import org.websoso.WSSServer.user.domain.AvatarProfile; import org.websoso.WSSServer.domain.Genre; @@ -42,6 +43,8 @@ import org.websoso.WSSServer.user.repository.AvatarProfileRepository; import org.websoso.WSSServer.repository.GenrePreferenceRepository; import org.websoso.WSSServer.user.domain.User; +import org.websoso.WSSServer.user.service.AvatarService; +import org.websoso.WSSServer.user.service.BlockService; import org.websoso.WSSServer.user.service.UserService; @Service @@ -55,6 +58,9 @@ public class FeedFindApplication { private final FeedServiceImpl feedServiceImpl; private final FeedImageService feedImageService; private final NovelServiceImpl novelServiceImpl; + private final AvatarService avatarService; + private final FeedLikeService feedLikeService; + private final BlockService blockService; //ToDo : 의존성 제거 필요 부분 private final AvatarProfileRepository avatarRepository; @@ -64,11 +70,27 @@ public class FeedFindApplication { @Transactional(readOnly = true) public FeedGetResponse getFeedById(User user, Long feedId) { - Feed feed = feedServiceImpl.getFeedOrException(feedId); - UserBasicInfo feedUserBasicInfo = getUserBasicInfo(feed.getUser()); + + Feed feed = feedServiceImpl.getAccessFeedOrException(feedId, user.getUserId()); + + // 서로 차단 관계인지 체크한다. + blockService.validateNotBlocked(user.getUserId(), feed.getWriterId()); + + // 피드 작성자의 프로필 이미지를 불러온다. + AvatarProfile avatarProfile = avatarService.getAvatarProfileOrException(feed.getUser().getAvatarProfileId()); + String avatarImageUrl = avatarProfile.getAvatarProfileImage(); + + // 피드 작성자의 사용자 정보 전달 객체 생성 + UserBasicInfo feedUserBasicInfo = UserBasicInfo.of(feed.getUser().getUserId(), feed.getUser().getNickname(), avatarImageUrl); + + // 피드에 연결된 소설 정보 가져오기 Novel novel = getLinkedNovelOrNull(feed.getNovelId()); - Boolean isLiked = isUserLikedFeed(user, feed); - Boolean isMyFeed = isUserFeedOwner(feed.getUser(), user); + + // 사용자가 현재 피드에 좋아요를 했는지 여부 체크 + boolean isLiked = feedLikeService.isUserLikedFeed(user.getUserId(), feed); + + // 피드가 본인 피드인지 체크 + boolean isMyFeed = feed.isMine(user.getUserId()); return FeedGetResponse.of(feed, feedUserBasicInfo, novel, isLiked, isMyFeed); } diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 3e364a28..da489a84 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -59,7 +59,7 @@ public ResponseEntity createFeed(@AuthenticationPrincipal Us } @GetMapping("/feeds/{feedId}") - @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user)") + @PreAuthorize("isAuthenticated()") public ResponseEntity getFeed(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId) { return ResponseEntity diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java index 067e31db..5d44af31 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java @@ -42,4 +42,9 @@ public long countByFeedId(Long feedId) { return likeRepository.countByFeed_FeedId(feedId); } + @Transactional(readOnly = true) + public boolean isUserLikedFeed(Long userId, Feed feed) { + return likeRepository.existsByUserIdAndFeed(userId, feed); + } + } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 6e1c98ca..221d712e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -23,11 +23,9 @@ import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.domain.PopularFeed; -import org.websoso.WSSServer.feed.repository.FeedImageCustomRepository; import org.websoso.WSSServer.feed.repository.FeedImageRepository; import org.websoso.WSSServer.feed.repository.FeedRepository; import org.websoso.WSSServer.feed.repository.PopularFeedRepository; -import org.websoso.WSSServer.repository.GenreRepository; import org.websoso.WSSServer.user.domain.User; @Service @@ -38,11 +36,7 @@ public class FeedServiceImpl { private final FeedRepository feedRepository; private final FeedImageRepository feedImageRepository; - private final FeedImageCustomRepository feedImageCustomRepository; private final PopularFeedRepository popularFeedRepository; - private final GenreRepository genreRepository; - - private static final String DEFAULT_CATEGORY = "all"; @Transactional public void createFeed(Feed feed) { @@ -137,7 +131,7 @@ public Integer countByFeedId(Long feedId) { @Transactional(readOnly = true) public Optional findThumbnailFeedImageByFeedId(Long feedId) { - return feedImageCustomRepository.findThumbnailFeedImageByFeedId(feedId); + return feedImageRepository.findThumbnailFeedImageByFeedId(feedId); } @Transactional(readOnly = true) From a40b4229d52fe1fffa2aba1d1e2f27d19d38f9ac Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 09:35:01 +0900 Subject: [PATCH 12/31] =?UTF-8?q?[REFACTOR]=20JPQL=EB=A5=BC=20QueryDSL?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=ED=99=98=20=EB=B0=8F=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 32 ++++++++----------- .../feed/repository/FeedCustomRepository.java | 2 ++ .../repository/FeedCustomRepositoryImpl.java | 31 ++++++++++++++++++ .../feed/repository/FeedRepository.java | 9 ------ .../novel/service/GenreServiceImpl.java | 9 ++++++ .../feed/service/FeedServiceImplTest.java | 2 -- 6 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index ea3d11ec..58c45684 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -65,7 +65,6 @@ public class FeedFindApplication { //ToDo : 의존성 제거 필요 부분 private final AvatarProfileRepository avatarRepository; private final LikeRepository likeRepository; - private final GenrePreferenceRepository genrePreferenceRepository; private final UserNovelRepository userNovelRepository; @Transactional(readOnly = true) @@ -118,14 +117,12 @@ private Boolean isUserFeedOwner(User createdUser, User user) { } @Transactional(readOnly = true) - public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, - FeedGetOption feedGetOption) { + public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, FeedGetOption feedGetOption) { Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); - List genres = getPreferenceGenres(user); + List genres = user == null ? null : genreService.findUserPreferenceGenres(user); - Slice feeds = findFeedsByCategoryLabel(lastFeedId, userIdOrNull, - PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); + Slice feeds = feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userIdOrNull, PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); // TODO: feed -> feed.isVisibleTo(userIdOrNull) 해당 필터링 로직은 필요 없음 List feedGetResponses = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) @@ -134,18 +131,17 @@ public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, return FeedsGetResponse.of(feeds.hasNext(), feedGetResponses); } - private List getPreferenceGenres(User user) { - if (user == null) { - return null; - } - return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); - } - - private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, - FeedGetOption feedGetOption, List genres) { - return feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userId, pageRequest, feedGetOption, - genres); - } +// private List getPreferenceGenres(User user) { +// if (user == null) { +// return null; +// } +// return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); +// } +// +// private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, +// FeedGetOption feedGetOption, List genres) { +// return feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userId, pageRequest, feedGetOption, genres); +// } private FeedInfo createFeedInfo(Feed feed, User user) { UserBasicInfo userBasicInfo = getUserBasicInfo(feed.getUser()); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java index cab5e62a..2b428c7c 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java @@ -16,6 +16,8 @@ List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Boolean isUnVisible, SortCriteria sortCriteria, List genres, Long visitorId, boolean includeEtc); + Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest); + Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres); Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRequest pageRequest); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index 72e732e0..ccd30da0 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -82,6 +82,29 @@ public List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int .fetch(); } + @Override + public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest) { + List feeds = jpaQueryFactory + .selectFrom(feed) + .where( + ltFeedId(lastFeedId), + checkHidden(), + checkFeedListVisibility(userId), + checkBlocking(userId) + ) + .orderBy(feed.feedId.desc()) + .limit(pageRequest.getPageSize() + 1L) + .fetch(); + + boolean hasNext = feeds.size() > pageRequest.getPageSize(); + + if (hasNext) { + feeds.remove(feeds.size() - 1); + } + + return new SliceImpl<>(feeds, pageRequest, hasNext); + } + @Override public Optional findThumbnailFeedImageByFeedId(long feedId) { return Optional.ofNullable(jpaQueryFactory @@ -306,6 +329,14 @@ private BooleanExpression checkVisible(Long userId) { return null; } + private BooleanExpression checkFeedListVisibility(Long userId) { + if (userId == null) { + return feed.isPublic.isTrue(); + } + + return feed.isPublic.isTrue().or(feed.user.userId.eq(userId)); + } + private BooleanExpression checkInterestedNovels(Long userId) { if (userId != null) { return userNovel.user.userId.eq(userId).and(userNovel.isInterest.isTrue()); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java index 2dfbee04..bb9181a6 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java @@ -15,15 +15,6 @@ public interface FeedRepository extends JpaRepository, FeedCustomRep Integer countByNovelId(Long novelId); - @Query(value = "SELECT f FROM Feed f WHERE " - + "(:lastFeedId = 0 OR f.feedId < :lastFeedId) " - + "AND f.isHidden = false " - + "AND (f.isPublic = true OR f.user.userId = :userId)" - + "AND (:userId IS NULL " - + "OR f.user.userId NOT IN (SELECT b.blockedId FROM Block b WHERE b.blockingId = :userId)) " - + "ORDER BY f.feedId DESC") - Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest); - List findTop10ByNovelIdInOrderByFeedIdDesc(List novelIds); // @Query(value = "SELECT f FROM Feed f WHERE " diff --git a/src/main/java/org/websoso/WSSServer/novel/service/GenreServiceImpl.java b/src/main/java/org/websoso/WSSServer/novel/service/GenreServiceImpl.java index 69e3272a..0525b546 100644 --- a/src/main/java/org/websoso/WSSServer/novel/service/GenreServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/novel/service/GenreServiceImpl.java @@ -8,13 +8,17 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.Genre; +import org.websoso.WSSServer.domain.GenrePreference; import org.websoso.WSSServer.exception.exception.CustomGenreException; +import org.websoso.WSSServer.repository.GenrePreferenceRepository; import org.websoso.WSSServer.repository.GenreRepository; +import org.websoso.WSSServer.user.domain.User; @Service @RequiredArgsConstructor public class GenreServiceImpl { + private final GenrePreferenceRepository genrePreferenceRepository; private final GenreRepository genreRepository; @Transactional(readOnly = true) @@ -41,4 +45,9 @@ public List getGenresOrException(List names) { return genres; } + + @Transactional(readOnly = true) + public List findUserPreferenceGenres(User user) { + return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); + } } diff --git a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java index 40f7a8f6..a5df3a79 100644 --- a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java +++ b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java @@ -36,8 +36,6 @@ class FeedServiceImplTest { @Mock FeedImageRepository feedImageRepository; @Mock - FeedImageCustomRepository feedImageCustomRepository; - @Mock PopularFeedRepository popularFeedRepository; @Mock GenreRepository genreRepository; From 2a23b7fa8d2c5def99a46987005a0dfd4b12dbea Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 11:13:39 +0900 Subject: [PATCH 13/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EB=B0=9C=EC=83=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20N+1=20=EB=AC=B8=EC=A0=9C=20Bulk=20SELECT=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websoso/WSSServer/dto/feed/FeedInfo.java | 23 +- .../dto/feed/UserFeedGetResponse.java | 22 +- .../feed/application/FeedFindApplication.java | 266 +++++++++++------- .../feed/repository/CommentRepository.java | 12 +- .../feed/repository/FeedCountProjection.java | 8 + .../repository/FeedCustomRepositoryImpl.java | 6 + .../feed/repository/FeedImageRepository.java | 3 + .../feed/repository/LikeRepository.java | 15 + .../feed/service/CommentServiceImpl.java | 18 ++ .../feed/service/FeedImageService.java | 39 ++- .../feed/service/FeedLikeService.java | 40 +++ .../library/service/LibraryService.java | 7 + 12 files changed, 349 insertions(+), 110 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java b/src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java index db8bcbcf..33941c97 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java +++ b/src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java @@ -34,6 +34,23 @@ public record FeedInfo( ) { public static FeedInfo of(Feed feed, UserBasicInfo userBasicInfo, Novel novel, Boolean isLiked, Boolean isMyFeed, String thumbnailUrl, Integer imageCount, User user) { + return of( + feed, + userBasicInfo, + novel, + isLiked, + isMyFeed, + thumbnailUrl, + imageCount, + user, + feed.getLikes().size(), + feed.getComments().size() + ); + } + + public static FeedInfo of(Feed feed, UserBasicInfo userBasicInfo, Novel novel, Boolean isLiked, + Boolean isMyFeed, String thumbnailUrl, Integer imageCount, User user, + Integer likeCount, Integer commentCount) { String title = null; Integer novelRatingCount = null; Float novelRating = null; @@ -61,9 +78,9 @@ public static FeedInfo of(Feed feed, UserBasicInfo userBasicInfo, Novel novel, B userBasicInfo.avatarImage(), TimeFormatUtil.formatRelativeDateTime(feed.getCreatedDate()), feed.getFeedContent(), - feed.getLikes().size(), + likeCount, isLiked, - feed.getComments().size(), + commentCount, feed.getNovelId(), title, novelRatingCount, @@ -121,4 +138,4 @@ private static Float getFeedWriterNovelRating(Novel novel, Long feedWriterId) { .orElse(null); } -} \ No newline at end of file +} diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java index fa70d6c3..25ca3504 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java @@ -31,10 +31,24 @@ public record UserFeedGetResponse( public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, String thumbnailUrl, Integer imageCount) { + return of( + feed, + novel, + visitorId, + thumbnailUrl, + imageCount, + getLikeUsers(feed), + feed.getLikes().size(), + feed.getComments().size() + ); + } + + public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, String thumbnailUrl, + Integer imageCount, List likeUsers, Integer likeCount, + Integer commentCount) { boolean isModified = !feed.getCreatedDate().equals(feed.getModifiedDate()); Long novelRatingCount = getNovelRatingCount(novel); Float novelRating = getNovelRating(novel, novelRatingCount); - List likeUsers = getLikeUsers(feed); boolean isLiked = likeUsers.contains(visitorId); String genreName = getNovelGenreName(novel); Float userNovelRating = getUserNovelRating(novel, visitorId); @@ -48,8 +62,8 @@ public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, Str isModified, likeUsers, isLiked, - feed.getLikes().size(), - feed.getComments().size(), + likeCount, + commentCount, novel == null ? null : novel.getNovelId(), novel == null ? @@ -136,4 +150,4 @@ private static Float getFeedWriterNovelRating(Novel novel, Long feedWriterId) { .map(UserNovel::getUserNovelRating) .orElse(null); } -} \ No newline at end of file +} diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 58c45684..1831f19e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -1,7 +1,5 @@ package org.websoso.WSSServer.feed.application; -import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; - import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -21,7 +19,6 @@ import org.websoso.WSSServer.novel.service.GenreServiceImpl; import org.websoso.WSSServer.user.domain.AvatarProfile; import org.websoso.WSSServer.domain.Genre; -import org.websoso.WSSServer.domain.GenrePreference; import org.websoso.WSSServer.domain.common.FeedGetOption; import org.websoso.WSSServer.dto.feed.FeedGetResponse; import org.websoso.WSSServer.dto.feed.FeedInfo; @@ -30,18 +27,13 @@ import org.websoso.WSSServer.dto.feed.InterestFeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; -import org.websoso.WSSServer.exception.exception.CustomAvatarException; import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.domain.PopularFeed; -import org.websoso.WSSServer.feed.repository.LikeRepository; +import org.websoso.WSSServer.feed.service.CommentServiceImpl; import org.websoso.WSSServer.feed.service.FeedServiceImpl; -import org.websoso.WSSServer.library.domain.UserNovel; -import org.websoso.WSSServer.library.repository.UserNovelRepository; +import org.websoso.WSSServer.library.service.LibraryService; import org.websoso.WSSServer.novel.domain.Novel; import org.websoso.WSSServer.novel.service.NovelServiceImpl; -import org.websoso.WSSServer.user.repository.AvatarProfileRepository; -import org.websoso.WSSServer.repository.GenrePreferenceRepository; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.service.AvatarService; import org.websoso.WSSServer.user.service.BlockService; @@ -60,12 +52,9 @@ public class FeedFindApplication { private final NovelServiceImpl novelServiceImpl; private final AvatarService avatarService; private final FeedLikeService feedLikeService; + private final CommentServiceImpl commentServiceImpl; private final BlockService blockService; - - //ToDo : 의존성 제거 필요 부분 - private final AvatarProfileRepository avatarRepository; - private final LikeRepository likeRepository; - private final UserNovelRepository userNovelRepository; + private final LibraryService libraryService; @Transactional(readOnly = true) public FeedGetResponse getFeedById(User user, Long feedId) { @@ -94,65 +83,23 @@ public FeedGetResponse getFeedById(User user, Long feedId) { return FeedGetResponse.of(feed, feedUserBasicInfo, novel, isLiked, isMyFeed); } - private UserBasicInfo getUserBasicInfo(User user) { - return user.getUserBasicInfo( - avatarRepository.findById(user.getAvatarProfileId()).orElseThrow(() -> - new CustomAvatarException(AVATAR_NOT_FOUND, "avatar with the given id was not found")) - .getAvatarProfileImage()); - } - - private Novel getLinkedNovelOrNull(Long linkedNovelId) { - if (linkedNovelId == null) { - return null; - } - return novelServiceImpl.getNovelOrException(linkedNovelId); - } - - private Boolean isUserLikedFeed(User user, Feed feed) { - return likeRepository.existsByUserIdAndFeed(user.getUserId(), feed); - } - - private Boolean isUserFeedOwner(User createdUser, User user) { - return createdUser.equals(user); - } - @Transactional(readOnly = true) public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, FeedGetOption feedGetOption) { + + // 로그인 유저 여부 확인 Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); + // 사용자의 선호하는 장르 확인 List genres = user == null ? null : genreService.findUserPreferenceGenres(user); + // 피드 불러오기 Slice feeds = feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userIdOrNull, PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); // TODO: feed -> feed.isVisibleTo(userIdOrNull) 해당 필터링 로직은 필요 없음 - List feedGetResponses = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) - .map(feed -> createFeedInfo(feed, user)).toList(); - - return FeedsGetResponse.of(feeds.hasNext(), feedGetResponses); - } - -// private List getPreferenceGenres(User user) { -// if (user == null) { -// return null; -// } -// return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); -// } -// -// private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, -// FeedGetOption feedGetOption, List genres) { -// return feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userId, pageRequest, feedGetOption, genres); -// } - - private FeedInfo createFeedInfo(Feed feed, User user) { - UserBasicInfo userBasicInfo = getUserBasicInfo(feed.getUser()); - Novel novel = getLinkedNovelOrNull(feed.getNovelId()); - Boolean isLiked = user != null && isUserLikedFeed(user, feed); - Boolean isMyFeed = user != null && isUserFeedOwner(feed.getUser(), user); - Integer imageCount = feedServiceImpl.countByFeedId(feed.getFeedId()); - Optional thumbnailImage = feedServiceImpl.findThumbnailFeedImageByFeedId(feed.getFeedId()); - String thumbnailUrl = thumbnailImage.map(FeedImage::getUrl).orElse(null); + List visibleFeeds = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) + .toList(); - return FeedInfo.of(feed, userBasicInfo, novel, isLiked, isMyFeed, thumbnailUrl, imageCount, user); + return FeedsGetResponse.of(feeds.hasNext(), createFeedInfos(visibleFeeds, user)); } @Transactional(readOnly = true) @@ -180,28 +127,9 @@ public PopularFeedsGetResponse getPopularFeeds(User user, int size) { return PopularFeedsGetResponse.of(popularFeedGetResponses); } - private static List mapToPopularFeedGetResponseList( - List popularFeeds, - Map novelMap - ) { - return popularFeeds.stream() - .map(popularFeed -> { - Novel novel = novelMap.get(popularFeed.getFeed().getNovelId()); - - return PopularFeedGetResponse.of( - popularFeed, - novel == null ? null : novel.getTitle(), - novel == null ? null : novel.getNovelImage(), - novel == null ? null : novel.getFirstGenreName() - ); - }) - .toList(); - } - @Transactional(readOnly = true) public InterestFeedsGetResponse getInterestFeeds(User user) { - List interestNovels = userNovelRepository.findByUserAndIsInterestTrue(user).stream() - .map(UserNovel::getNovel).toList(); + List interestNovels = libraryService.getInterestNovels(user); if (interestNovels.isEmpty()) { return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_INTEREST_NOVELS"); @@ -219,7 +147,7 @@ public InterestFeedsGetResponse getInterestFeeds(User user) { Set avatarProfileIds = interestFeeds.stream().map(feed -> feed.getUser().getAvatarProfileId()) .collect(Collectors.toSet()); - Map avatarMap = avatarRepository.findAllById(avatarProfileIds).stream() + Map avatarMap = avatarService.findAllByIds(new ArrayList<>(avatarProfileIds)).stream() .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, avatar -> avatar)); List interestFeedGetResponses = interestFeeds.stream() @@ -241,11 +169,9 @@ public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long las Slice feeds = feedServiceImpl.findFeedsByNovel(userIdOrNull, novelId, lastFeedId, size); - List feedGetResponses = feeds.getContent().stream() - .map(feed -> createFeedInfo(feed, user)) - .toList(); + List visibleFeeds = feeds.getContent(); - return NovelGetResponseFeedTab.of(feeds.hasNext(), feedGetResponses); + return NovelGetResponseFeedTab.of(feeds.hasNext(), createFeedInfos(visibleFeeds, user)); } @Transactional(readOnly = true) @@ -271,20 +197,13 @@ public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFe .collect(Collectors.toList()); // 소설 ID에 해당하는 소설 정보들 전부 불러오기 - List novels = novelServiceImpl.findAllByIds(novelIds); + List novels = novelServiceImpl.getNovelsWithGenresByIds(novelIds); - // 해당 로직 수정 + // Map novelMap = novels.stream() .collect(Collectors.toMap(Novel::getNovelId, Function.identity())); - List userFeedGetResponseList = visibleFeeds.stream() - .map(feed -> UserFeedGetResponse.of( - feed, - novelMap.get(feed.getNovelId()), - visitorId, - feedImageService.getThumbnailUrl(feed), - feedImageService.getImageCount(feed)) - ).toList(); + List userFeedGetResponseList = createUserFeedResponses(visibleFeeds, novelMap, visitorId); // TODO Slice의 hasNext()로 판단하도록 수정 Boolean isLoadable = visibleFeeds.size() == size; @@ -295,4 +214,153 @@ public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFe } + private Novel getLinkedNovelOrNull(Long linkedNovelId) { + if (linkedNovelId == null) { + return null; + } + return novelServiceImpl.getNovelOrException(linkedNovelId); + } + + private Boolean isUserFeedOwner(User createdUser, User user) { + return createdUser.equals(user); + } + + private List createFeedInfos(List feeds, User user) { + FeedInfoContext context = getFeedInfoContext(feeds, user); + + return feeds.stream() + .map(feed -> { + Long feedId = feed.getFeedId(); + UserBasicInfo userBasicInfo = context.userBasicInfoMap().get(feed.getUser().getUserId()); + Novel novel = context.novelMap().get(feed.getNovelId()); + boolean isLiked = context.likedFeedIds().contains(feedId); + boolean isMyFeed = user != null && isUserFeedOwner(feed.getUser(), user); + String thumbnailUrl = context.thumbnailUrlMap().get(feedId); + Integer imageCount = context.imageCountMap().getOrDefault(feedId, 0); + Integer likeCount = context.likeCountMap().getOrDefault(feedId, 0); + Integer commentCount = context.commentCountMap().getOrDefault(feedId, 0); + + return FeedInfo.of( + feed, + userBasicInfo, + novel, + isLiked, + isMyFeed, + thumbnailUrl, + imageCount, + user, + likeCount, + commentCount + ); + }) + .toList(); + } + + private List mapToPopularFeedGetResponseList( + List popularFeeds, + Map novelMap + ) { + return popularFeeds.stream() + .map(popularFeed -> { + Novel novel = novelMap.get(popularFeed.getFeed().getNovelId()); + + return PopularFeedGetResponse.of( + popularFeed, + novel == null ? null : novel.getTitle(), + novel == null ? null : novel.getNovelImage(), + novel == null ? null : novel.getFirstGenreName() + ); + }) + .toList(); + } + + private FeedInfoContext getFeedInfoContext(List feeds, User user) { + List feedIds = feeds.stream() + .map(Feed::getFeedId) + .toList(); + List novelIds = feeds.stream() + .map(Feed::getNovelId) + .filter(Objects::nonNull) + .distinct() + .toList(); + List avatarProfileIds = feeds.stream() + .map(feed -> feed.getUser().getAvatarProfileId()) + .distinct() + .toList(); + + Map avatarImageMap = avatarService.findAllByIds(avatarProfileIds).stream() + .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, AvatarProfile::getAvatarProfileImage)); + Map userBasicInfoMap = feeds.stream() + .map(Feed::getUser) + .collect(Collectors.toMap( + User::getUserId, + feedUser -> feedUser.getUserBasicInfo(avatarImageMap.get(feedUser.getAvatarProfileId())), + (first, second) -> first + )); + + Map novelMap = novelServiceImpl.getNovelsWithGenresByIds(novelIds).stream() + .collect(Collectors.toMap(Novel::getNovelId, Function.identity())); + Set likedFeedIds = new HashSet<>(feedLikeService.findLikedFeedIds( + Optional.ofNullable(user).map(User::getUserId).orElse(null), + feedIds + )); + Map likeCountMap = feedLikeService.countByFeedIds(feedIds); + Map commentCountMap = commentServiceImpl.countByFeedIds(feedIds); + Map thumbnailUrlMap = feedImageService.getThumbnailUrlMap(feedIds); + Map imageCountMap = feedImageService.getImageCountMap(feedIds); + + return new FeedInfoContext( + userBasicInfoMap, + novelMap, + likedFeedIds, + likeCountMap, + commentCountMap, + thumbnailUrlMap, + imageCountMap + ); + } + + private List createUserFeedResponses( + List feeds, + Map novelMap, + Long visitorId + ) { + List feedIds = feeds.stream() + .map(Feed::getFeedId) + .toList(); + Map> likerUserIdsMap = feedLikeService.findLikerUserIdsByFeedIds(feedIds); + Map likeCountMap = feedLikeService.countByFeedIds(feedIds); + Map commentCountMap = commentServiceImpl.countByFeedIds(feedIds); + Map thumbnailUrlMap = feedImageService.getThumbnailUrlMap(feedIds); + Map imageCountMap = feedImageService.getImageCountMap(feedIds); + + return feeds.stream() + .map(feed -> { + Long feedId = feed.getFeedId(); + + return UserFeedGetResponse.of( + feed, + novelMap.get(feed.getNovelId()), + visitorId, + thumbnailUrlMap.get(feedId), + imageCountMap.getOrDefault(feedId, 0), + likerUserIdsMap.getOrDefault(feedId, Collections.emptyList()), + likeCountMap.getOrDefault(feedId, 0), + commentCountMap.getOrDefault(feedId, 0) + ); + }) + .toList(); + } + + private record FeedInfoContext( + Map userBasicInfoMap, + Map novelMap, + Set likedFeedIds, + Map likeCountMap, + Map commentCountMap, + Map thumbnailUrlMap, + Map imageCountMap + ) { + } + } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java index b6bd45e1..99792b90 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java @@ -1,10 +1,10 @@ package org.websoso.WSSServer.feed.repository; -import io.lettuce.core.dynamic.annotation.Param; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.feed.domain.Comment; @@ -18,7 +18,15 @@ public interface CommentRepository extends JpaRepository { void updateUserToUnknown(Long userId); @Query("SELECT c FROM Comment c WHERE c.feed.feedId = :feedId") - List findAllByFeedId(@Param("feedId") Long feedId); + List findAllByFeedId(Long feedId); + + @Query(""" + SELECT c.feed.feedId AS feedId, COUNT(c.commentId) AS count + FROM Comment c + WHERE c.feed.feedId IN :feedIds + GROUP BY c.feed.feedId + """) + List countByFeedIds(List feedIds); @Modifying @Query("DELETE FROM Comment c WHERE c.feed.feedId = :feedId") diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java new file mode 100644 index 00000000..653d30cb --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java @@ -0,0 +1,8 @@ +package org.websoso.WSSServer.feed.repository; + +public interface FeedCountProjection { + + Long getFeedId(); + + Long getCount(); +} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index ccd30da0..2c7e4a54 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -65,6 +65,7 @@ public List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int return jpaQueryFactory .selectFrom(feed) .distinct() + .join(feed.user).fetchJoin() .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) .leftJoin(novelGenre).on(novel.eq(novelGenre.novel)) .leftJoin(genre).on(novelGenre.genre.eq(genre)) @@ -86,6 +87,7 @@ public List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest) { List feeds = jpaQueryFactory .selectFrom(feed) + .join(feed.user).fetchJoin() .where( ltFeedId(lastFeedId), checkHidden(), @@ -141,6 +143,7 @@ public Long countVisibleFeeds(User owner, Boolean isVisible, public Slice findFeedsByNovelId(Long novelId, Long lastFeedId, Long userId, PageRequest pageRequest) { List feeds = jpaQueryFactory .selectFrom(feed) + .join(feed.user).fetchJoin() .where( feed.novelId.eq(novelId), ltFeedId(lastFeedId), @@ -195,6 +198,7 @@ private OrderSpecifier checkSortCriteria(SortCriteria sortCriteria) { public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres) { List feeds = jpaQueryFactory .selectFrom(feed) + .join(feed.user).fetchJoin() .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) .leftJoin(novelGenre).on(novel.eq(novelGenre.novel)) .leftJoin(genre).on(novelGenre.genre.eq(genre)) @@ -223,6 +227,7 @@ public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageReques public Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRequest pageRequest) { List feeds = jpaQueryFactory .selectFrom(feed) + .join(feed.user).fetchJoin() .join(novel).on(feed.novelId.eq(novel.novelId)) .join(userNovel).on(novel.eq(userNovel.novel)) .where( @@ -268,6 +273,7 @@ public Slice findFeedsByGenres(List genres, boolean isNotNovelConne List feeds = jpaQueryFactory .selectFrom(feed) .distinct() + .join(feed.user).fetchJoin() .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) .leftJoin(novelGenre).on(novel.eq(novelGenre.novel)) .leftJoin(genre).on(novelGenre.genre.eq(genre)) diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java index 9f916327..3004137e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java @@ -1,8 +1,11 @@ package org.websoso.WSSServer.feed.repository; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.websoso.WSSServer.feed.domain.FeedImage; public interface FeedImageRepository extends JpaRepository, FeedImageCustomRepository { Integer countByFeedId(Long feedId); + + List findByFeedIdIn(List feedIds); } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java index a1a88db7..c5433d52 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java @@ -1,9 +1,11 @@ package org.websoso.WSSServer.feed.repository; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.Like; @@ -15,6 +17,19 @@ public interface LikeRepository extends JpaRepository { boolean existsByUserIdAndFeed(Long userId, Feed feed); + @Query("SELECT l.feed.feedId FROM Like l WHERE l.userId = :userId AND l.feed.feedId IN :feedIds") + List findLikedFeedIds(Long userId, List feedIds); + + @Query(""" + SELECT l.feed.feedId AS feedId, COUNT(l.likeId) AS count + FROM Like l + WHERE l.feed.feedId IN :feedIds + GROUP BY l.feed.feedId + """) + List countByFeedIds(List feedIds); + + List findByFeedFeedIdIn(List feedIds); + long countByFeed(Feed feed); @Modifying diff --git a/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java index ac07abd9..283e816d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java @@ -2,9 +2,14 @@ import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_FOUND; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.repository.FeedCountProjection; import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.dto.comment.CommentCreateRequest; @@ -48,4 +53,17 @@ public void deleteByFeedId(Long feedId) { reportedCommentRepository.deleteByFeedId(feedId); } + @Transactional(readOnly = true) + public Map countByFeedIds(List feedIds) { + if (feedIds.isEmpty()) { + return Collections.emptyMap(); + } + + return commentRepository.countByFeedIds(feedIds).stream() + .collect(Collectors.toMap( + FeedCountProjection::getFeedId, + projection -> projection.getCount().intValue() + )); + } + } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java index 9196ff5e..ec8846c3 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java @@ -1,5 +1,13 @@ package org.websoso.WSSServer.feed.service; +import static org.websoso.WSSServer.domain.common.FeedImageType.FEED_THUMBNAIL; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -7,8 +15,6 @@ import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.repository.FeedImageRepository; -import java.util.Optional; - @Service @RequiredArgsConstructor public class FeedImageService { @@ -25,4 +31,33 @@ public String getThumbnailUrl(Feed feed) { public Integer getImageCount(Feed feed) { return feedImageRepository.countByFeedId(feed.getFeedId()); } + + @Transactional(readOnly = true) + public Map getThumbnailUrlMap(List feedIds) { + if (feedIds.isEmpty()) { + return Collections.emptyMap(); + } + + return feedImageRepository.findByFeedIdIn(feedIds).stream() + .filter(feedImage -> feedImage.getFeedImageType() == FEED_THUMBNAIL) + .sorted(Comparator.comparing(FeedImage::getSequence)) + .collect(Collectors.toMap( + FeedImage::getFeedId, + FeedImage::getUrl, + (first, second) -> first + )); + } + + @Transactional(readOnly = true) + public Map getImageCountMap(List feedIds) { + if (feedIds.isEmpty()) { + return Collections.emptyMap(); + } + + return feedImageRepository.findByFeedIdIn(feedIds).stream() + .collect(Collectors.groupingBy( + FeedImage::getFeedId, + Collectors.collectingAndThen(Collectors.counting(), Long::intValue) + )); + } } diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java index 5d44af31..2a35b0ca 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java @@ -1,11 +1,16 @@ package org.websoso.WSSServer.feed.service; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.Like; +import org.websoso.WSSServer.feed.repository.FeedCountProjection; import org.websoso.WSSServer.feed.repository.LikeRepository; @Service @@ -47,4 +52,39 @@ public boolean isUserLikedFeed(Long userId, Feed feed) { return likeRepository.existsByUserIdAndFeed(userId, feed); } + @Transactional(readOnly = true) + public List findLikedFeedIds(Long userId, List feedIds) { + if (userId == null || feedIds.isEmpty()) { + return Collections.emptyList(); + } + + return likeRepository.findLikedFeedIds(userId, feedIds); + } + + @Transactional(readOnly = true) + public Map countByFeedIds(List feedIds) { + if (feedIds.isEmpty()) { + return Collections.emptyMap(); + } + + return likeRepository.countByFeedIds(feedIds).stream() + .collect(Collectors.toMap( + FeedCountProjection::getFeedId, + projection -> projection.getCount().intValue() + )); + } + + @Transactional(readOnly = true) + public Map> findLikerUserIdsByFeedIds(List feedIds) { + if (feedIds.isEmpty()) { + return Collections.emptyMap(); + } + + return likeRepository.findByFeedFeedIdIn(feedIds).stream() + .collect(Collectors.groupingBy( + like -> like.getFeed().getFeedId(), + Collectors.mapping(Like::getUserId, Collectors.toList()) + )); + } + } diff --git a/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java b/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java index 784c48c4..b716218a 100644 --- a/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java +++ b/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java @@ -135,6 +135,13 @@ public List getTasteNovels(List preferGenres) { return userNovelRepository.findTasteNovels(preferGenres); } + @Transactional(readOnly = true) + public List getInterestNovels(User user) { + return userNovelRepository.findByUserAndIsInterestTrue(user).stream() + .map(UserNovel::getNovel) + .toList(); + } + public List getTodayPopularNovelIds(PageRequest pageRequest) { return userNovelRepository.findTodayPopularNovelsId(pageRequest); From f8ffb28fa58024a2e2580b69b2baf81c9e6be7d2 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 13:14:40 +0900 Subject: [PATCH 14/31] =?UTF-8?q?[REFACTOR]=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=8C=93=EA=B8=80=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/service/CommentService.java | 219 ------------------ 1 file changed, 219 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/service/CommentService.java diff --git a/src/main/java/org/websoso/WSSServer/feed/service/CommentService.java b/src/main/java/org/websoso/WSSServer/feed/service/CommentService.java deleted file mode 100644 index ae7d8a06..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/service/CommentService.java +++ /dev/null @@ -1,219 +0,0 @@ -package org.websoso.WSSServer.feed.service; - -import static java.lang.Boolean.TRUE; -import static org.websoso.WSSServer.domain.common.Action.DELETE; -import static org.websoso.WSSServer.domain.common.Action.UPDATE; -import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomNovelError.NOVEL_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomUserError.USER_NOT_FOUND; - -import java.util.AbstractMap; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.notification.domain.Notification; -import org.websoso.WSSServer.notification.domain.NotificationType; -import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.notification.domain.UserDevice; -import org.websoso.WSSServer.dto.comment.CommentCreateRequest; -import org.websoso.WSSServer.dto.comment.CommentGetResponse; -import org.websoso.WSSServer.dto.comment.CommentUpdateRequest; -import org.websoso.WSSServer.dto.comment.CommentsGetResponse; -import org.websoso.WSSServer.dto.user.UserBasicInfo; -import org.websoso.WSSServer.exception.exception.CustomAvatarException; -import org.websoso.WSSServer.exception.exception.CustomCommentException; -import org.websoso.WSSServer.exception.exception.CustomFeedException; -import org.websoso.WSSServer.exception.exception.CustomNovelException; -import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.repository.CommentRepository; -import org.websoso.WSSServer.feed.repository.FeedRepository; -import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; -import org.websoso.WSSServer.notification.infrastructure.FCMClient; -import org.websoso.WSSServer.notification.dto.FCMMessageRequest; -import org.websoso.WSSServer.novel.domain.Novel; -import org.websoso.WSSServer.novel.repository.NovelRepository; -import org.websoso.WSSServer.user.repository.AvatarRepository; -import org.websoso.WSSServer.user.repository.BlockRepository; -import org.websoso.WSSServer.notification.repository.NotificationRepository; -import org.websoso.WSSServer.notification.repository.NotificationTypeRepository; -import org.websoso.WSSServer.user.repository.UserRepository; -import org.websoso.WSSServer.infrastructure.discord.DiscordMessageClient; - -@Service -@Transactional -@RequiredArgsConstructor -public class CommentService { - - private final CommentRepository commentRepository; - private final FeedRepository feedRepository; - private final NotificationRepository notificationRepository; - private final NotificationTypeRepository notificationTypeRepository; - private final BlockRepository blockRepository; - private final NovelRepository novelRepository; - private final UserRepository userRepository; - private final ReportedCommentRepository reportedCommentRepository; - private final AvatarRepository avatarRepository; - private final DiscordMessageClient discordMessageClient; - private final FCMClient fcmClient; - - private static final int NOTIFICATION_TITLE_MAX_LENGTH = 12; - private static final int NOTIFICATION_TITLE_MIN_LENGTH = 0; - - @Transactional - public void createComment(User user, Long feedId, CommentCreateRequest request) { - Feed feed = getFeedOrException(feedId); - commentRepository.save(Comment.create(user.getUserId(), feed, request.commentContent())); - sendCommentPushMessageToFeedOwner(user, feed); - sendCommentPushMessageToCommenters(user, feed); - } - - private Feed getFeedOrException(Long feedId) { - return feedRepository.findById(feedId) - .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); - } - - private void sendCommentPushMessageToFeedOwner(User user, Feed feed) { - User feedOwner = feed.getUser(); - if (user.equals(feedOwner) || blockRepository.existsByBlockingIdAndBlockedId(feedOwner.getUserId(), - user.getUserId())) { - return; - } - - NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("댓글"); - - String notificationTitle = createNotificationTitle(feed); - String notificationBody = String.format("%s님이 내 글에 댓글을 남겼어요.", user.getNickname()); - Long feedId = feed.getFeedId(); - - Notification notification = Notification.create(notificationTitle, notificationBody, null, - feedOwner.getUserId(), feedId, notificationTypeComment); - notificationRepository.save(notification); - - if (!TRUE.equals(feedOwner.getIsPushEnabled())) { - return; - } - - List feedOwnerDevices = feedOwner.getUserDevices(); - if (feedOwnerDevices.isEmpty()) { - return; - } - - FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(notificationTitle, notificationBody, - String.valueOf(feedId), "feedDetail", String.valueOf(notification.getNotificationId())); - - List targetFCMTokens = feedOwnerDevices.stream().map(UserDevice::getFcmToken).toList(); - - fcmClient.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest); - } - - //ToDo : CommentService와 중복되는 부분 추출 필요 - private void sendCommentPushMessageToCommenters(User user, Feed feed) { - User feedOwner = feed.getUser(); - - List commenters = feed.getComments().stream().map(Comment::getUserId) - .filter(userId -> !userId.equals(user.getUserId())) - .filter(userId -> !userId.equals(feedOwner.getUserId())) - .filter(userId -> !blockRepository.existsByBlockingIdAndBlockedId(userId, user.getUserId()) - && !blockRepository.existsByBlockingIdAndBlockedId(userId, feed.getUser().getUserId())) - .distinct().map(userId -> userRepository.findById(userId).orElseThrow( - () -> new CustomUserException(USER_NOT_FOUND, "user with the given id was not found"))) - .toList(); - - if (commenters.isEmpty()) { - return; - } - - NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("댓글"); - - String notificationTitle = createNotificationTitle(feed); - String notificationBody = "내가 댓글 단 글에 또 다른 댓글이 달렸어요."; - Long feedId = feed.getFeedId(); - - commenters.forEach(commenter -> { - Notification notification = Notification.create(notificationTitle, notificationBody, null, - commenter.getUserId(), feedId, notificationTypeComment); - notificationRepository.save(notification); - - if (!TRUE.equals(commenter.getIsPushEnabled())) { - return; - } - - List commenterDevices = commenter.getUserDevices(); - if (commenterDevices.isEmpty()) { - return; - } - - List targetFCMTokens = commenterDevices.stream().map(UserDevice::getFcmToken).distinct().toList(); - - FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(notificationTitle, notificationBody, - String.valueOf(feedId), "feedDetail", String.valueOf(notification.getNotificationId())); - fcmClient.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest); - }); - } - - private String createNotificationTitle(Feed feed) { - if (feed.getNovelId() == null) { - String feedContent = feed.getFeedContent(); - feedContent = feedContent.length() <= NOTIFICATION_TITLE_MAX_LENGTH ? feedContent - : feedContent.substring(NOTIFICATION_TITLE_MIN_LENGTH, NOTIFICATION_TITLE_MAX_LENGTH); - return "'" + feedContent + "...'"; - } - Novel novel = novelRepository.findById(feed.getNovelId()) - .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, "novel with the given id is not found")); - return novel.getTitle(); - } - - @Transactional(readOnly = true) - public CommentsGetResponse getComments(User user, Long feedId) { - Feed feed = getFeedOrException(feedId); - List responses = feed.getComments().stream() - .map(comment -> new AbstractMap.SimpleEntry<>(comment, userRepository.findById(comment.getUserId()) - .orElseThrow( - () -> new CustomUserException(USER_NOT_FOUND, "user with the given id was not found")))) - .map(entry -> CommentGetResponse.of(getUserBasicInfo(entry.getValue()), entry.getKey(), - isUserCommentOwner(entry.getValue(), user), entry.getKey().getIsSpoiler(), - isBlocked(user, entry.getValue()), entry.getKey().getIsHidden())).toList(); - - return CommentsGetResponse.of(responses); - } - - private Boolean isUserCommentOwner(User createdUser, User user) { - return createdUser.equals(user); - } - - private Boolean isBlocked(User user, User createdFeedUser) { - return blockRepository.existsByBlockingIdAndBlockedId(user.getUserId(), createdFeedUser.getUserId()); - } - - private UserBasicInfo getUserBasicInfo(User user) { - return user.getUserBasicInfo( - avatarRepository.findById(user.getAvatarId()).orElseThrow(() -> - new CustomAvatarException(AVATAR_NOT_FOUND, "avatar with the given id was not found")) - .getAvatarImage()); - } - - @Transactional - public void updateComment(User user, Long feedId, Long commentId, CommentUpdateRequest request) { - Feed feed = getFeedOrException(feedId); - Comment comment = commentRepository.findById(commentId).orElseThrow( - () -> new CustomCommentException(COMMENT_NOT_FOUND, "comment with the given id was not found")); - comment.validateFeedAssociation(feed); - comment.validateUserAuthorization(user.getUserId(), UPDATE); - comment.updateContent(request.commentContent()); - } - - @Transactional - public void deleteComment(User user, Long feedId, Long commentId) { - Feed feed = getFeedOrException(feedId); - Comment comment = commentRepository.findById(commentId).orElseThrow( - () -> new CustomCommentException(COMMENT_NOT_FOUND, "comment with the given id was not found")); - comment.validateFeedAssociation(feed); - comment.validateUserAuthorization(user.getUserId(), DELETE); - commentRepository.delete(comment); - } -} From c6bc55b4276f0ce4ee0d82e79b4c1be9a0d23289 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 13:34:16 +0900 Subject: [PATCH 15/31] =?UTF-8?q?[REFACTOR]=20Projection=EC=9D=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=EC=BF=BC=EB=A6=AC=20Rep?= =?UTF-8?q?ository=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 106 +--------- .../WSSServer/feed/domain/FeedImage.java | 5 + .../feed/repository/FeedInfoRow.java | 70 +++++++ .../feed/repository/FeedQueryRepository.java | 9 + .../repository/FeedQueryRepositoryImpl.java | 191 ++++++++++++++++++ .../feed/service/FeedImageService.java | 6 +- .../feed/service/FeedQueryService.java | 38 ++++ 7 files changed, 323 insertions(+), 102 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java create mode 100644 src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 1831f19e..9662a540 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -16,6 +16,7 @@ import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; import org.websoso.WSSServer.feed.service.FeedImageService; import org.websoso.WSSServer.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.service.FeedQueryService; import org.websoso.WSSServer.novel.service.GenreServiceImpl; import org.websoso.WSSServer.user.domain.AvatarProfile; import org.websoso.WSSServer.domain.Genre; @@ -48,6 +49,7 @@ public class FeedFindApplication { private final GenreServiceImpl genreService; private final UserService userService; private final FeedServiceImpl feedServiceImpl; + private final FeedQueryService feedQueryService; private final FeedImageService feedImageService; private final NovelServiceImpl novelServiceImpl; private final AvatarService avatarService; @@ -59,6 +61,7 @@ public class FeedFindApplication { @Transactional(readOnly = true) public FeedGetResponse getFeedById(User user, Long feedId) { + // 접근 가능한 피드인지 체크 및 피드 불러오기 Feed feed = feedServiceImpl.getAccessFeedOrException(feedId, user.getUserId()); // 서로 차단 관계인지 체크한다. @@ -95,11 +98,11 @@ public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, FeedGetOp // 피드 불러오기 Slice feeds = feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userIdOrNull, PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); - // TODO: feed -> feed.isVisibleTo(userIdOrNull) 해당 필터링 로직은 필요 없음 - List visibleFeeds = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) - .toList(); + // FeedInfo에 필요한 정보들을 JOIN 및 서브 쿼리로 불러오기 + List visibleFeeds = feeds.getContent().stream().toList(); + List feedInfos = feedQueryService.createFeedInfos(visibleFeeds, userIdOrNull); - return FeedsGetResponse.of(feeds.hasNext(), createFeedInfos(visibleFeeds, user)); + return FeedsGetResponse.of(feeds.hasNext(), feedInfos); } @Transactional(readOnly = true) @@ -170,8 +173,9 @@ public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long las Slice feeds = feedServiceImpl.findFeedsByNovel(userIdOrNull, novelId, lastFeedId, size); List visibleFeeds = feeds.getContent(); + List feedInfos = feedQueryService.createFeedInfos(visibleFeeds, userIdOrNull); - return NovelGetResponseFeedTab.of(feeds.hasNext(), createFeedInfos(visibleFeeds, user)); + return NovelGetResponseFeedTab.of(feeds.hasNext(), feedInfos); } @Transactional(readOnly = true) @@ -221,41 +225,6 @@ private Novel getLinkedNovelOrNull(Long linkedNovelId) { return novelServiceImpl.getNovelOrException(linkedNovelId); } - private Boolean isUserFeedOwner(User createdUser, User user) { - return createdUser.equals(user); - } - - private List createFeedInfos(List feeds, User user) { - FeedInfoContext context = getFeedInfoContext(feeds, user); - - return feeds.stream() - .map(feed -> { - Long feedId = feed.getFeedId(); - UserBasicInfo userBasicInfo = context.userBasicInfoMap().get(feed.getUser().getUserId()); - Novel novel = context.novelMap().get(feed.getNovelId()); - boolean isLiked = context.likedFeedIds().contains(feedId); - boolean isMyFeed = user != null && isUserFeedOwner(feed.getUser(), user); - String thumbnailUrl = context.thumbnailUrlMap().get(feedId); - Integer imageCount = context.imageCountMap().getOrDefault(feedId, 0); - Integer likeCount = context.likeCountMap().getOrDefault(feedId, 0); - Integer commentCount = context.commentCountMap().getOrDefault(feedId, 0); - - return FeedInfo.of( - feed, - userBasicInfo, - novel, - isLiked, - isMyFeed, - thumbnailUrl, - imageCount, - user, - likeCount, - commentCount - ); - }) - .toList(); - } - private List mapToPopularFeedGetResponseList( List popularFeeds, Map novelMap @@ -274,52 +243,6 @@ private List mapToPopularFeedGetResponseList( .toList(); } - private FeedInfoContext getFeedInfoContext(List feeds, User user) { - List feedIds = feeds.stream() - .map(Feed::getFeedId) - .toList(); - List novelIds = feeds.stream() - .map(Feed::getNovelId) - .filter(Objects::nonNull) - .distinct() - .toList(); - List avatarProfileIds = feeds.stream() - .map(feed -> feed.getUser().getAvatarProfileId()) - .distinct() - .toList(); - - Map avatarImageMap = avatarService.findAllByIds(avatarProfileIds).stream() - .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, AvatarProfile::getAvatarProfileImage)); - Map userBasicInfoMap = feeds.stream() - .map(Feed::getUser) - .collect(Collectors.toMap( - User::getUserId, - feedUser -> feedUser.getUserBasicInfo(avatarImageMap.get(feedUser.getAvatarProfileId())), - (first, second) -> first - )); - - Map novelMap = novelServiceImpl.getNovelsWithGenresByIds(novelIds).stream() - .collect(Collectors.toMap(Novel::getNovelId, Function.identity())); - Set likedFeedIds = new HashSet<>(feedLikeService.findLikedFeedIds( - Optional.ofNullable(user).map(User::getUserId).orElse(null), - feedIds - )); - Map likeCountMap = feedLikeService.countByFeedIds(feedIds); - Map commentCountMap = commentServiceImpl.countByFeedIds(feedIds); - Map thumbnailUrlMap = feedImageService.getThumbnailUrlMap(feedIds); - Map imageCountMap = feedImageService.getImageCountMap(feedIds); - - return new FeedInfoContext( - userBasicInfoMap, - novelMap, - likedFeedIds, - likeCountMap, - commentCountMap, - thumbnailUrlMap, - imageCountMap - ); - } - private List createUserFeedResponses( List feeds, Map novelMap, @@ -352,15 +275,4 @@ private List createUserFeedResponses( .toList(); } - private record FeedInfoContext( - Map userBasicInfoMap, - Map novelMap, - Set likedFeedIds, - Map likeCountMap, - Map commentCountMap, - Map thumbnailUrlMap, - Map imageCountMap - ) { - } - } diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java b/src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java index 7fef910f..a78ea3a8 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java +++ b/src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java @@ -2,6 +2,7 @@ import static jakarta.persistence.GenerationType.IDENTITY; +import static org.websoso.WSSServer.domain.common.FeedImageType.FEED_THUMBNAIL; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -48,4 +49,8 @@ public static FeedImage createThumbnail(String url) { public static FeedImage createCommon(String url, int sequence) { return new FeedImage(url, FeedImageType.FEED_COMMON, sequence); } + + public boolean isThumbnail() { + return feedImageType == FEED_THUMBNAIL; + } } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java new file mode 100644 index 00000000..960d27f9 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java @@ -0,0 +1,70 @@ +package org.websoso.WSSServer.feed.repository; + +import java.time.LocalDateTime; +import org.websoso.WSSServer.dto.feed.FeedInfo; +import org.websoso.WSSServer.util.TimeFormatUtil; + +public record FeedInfoRow( + Long feedId, + Long userId, + String nickname, + String avatarImage, + LocalDateTime createdDate, + String feedContent, + Long likeCount, + Boolean isLiked, + Long commentCount, + Long novelId, + String title, + Long novelRatingCount, + Double novelRating, + Boolean isSpoiler, + Boolean isModified, + Boolean isMyFeed, + Boolean isPublic, + String thumbnailUrl, + Long imageCount, + String genreName, + Float userNovelRating, + Float feedWriterNovelRating +) { + + public FeedInfo toResponse() { + return new FeedInfo( + feedId, + userId, + nickname, + avatarImage, + TimeFormatUtil.formatRelativeDateTime(createdDate), + feedContent, + toInteger(likeCount), + isLiked, + toInteger(commentCount), + novelId, + title, + toInteger(novelRatingCount), + roundToFirstDecimal(novelRating), + isSpoiler, + isModified, + isMyFeed, + isPublic, + thumbnailUrl, + toInteger(imageCount), + genreName, + userNovelRating, + feedWriterNovelRating + ); + } + + private static Integer toInteger(Long value) { + return value == null ? 0 : value.intValue(); + } + + private static Float roundToFirstDecimal(Double value) { + if (value == null) { + return null; + } + + return Math.round(value.floatValue() * 10) / 10.0f; + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java new file mode 100644 index 00000000..0e27b304 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java @@ -0,0 +1,9 @@ +package org.websoso.WSSServer.feed.repository; + +import java.util.List; + +public interface FeedQueryRepository { + + List findFeedInfoRows(List feedIds, Long userId); + +} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java new file mode 100644 index 00000000..0f60d4b3 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java @@ -0,0 +1,191 @@ +package org.websoso.WSSServer.feed.repository; + +import static org.websoso.WSSServer.feed.domain.QFeed.feed; +import static org.websoso.WSSServer.novel.domain.QNovel.novel; +import static org.websoso.WSSServer.user.domain.QAvatarProfile.avatarProfile; + +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.JPQLQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import org.websoso.WSSServer.domain.QGenre; +import org.websoso.WSSServer.domain.common.FeedImageType; +import org.websoso.WSSServer.feed.domain.QComment; +import org.websoso.WSSServer.feed.domain.QFeedImage; +import org.websoso.WSSServer.feed.domain.QLike; +import org.websoso.WSSServer.library.domain.QUserNovel; +import org.websoso.WSSServer.novel.domain.QNovelGenre; + +@Repository +@RequiredArgsConstructor +public class FeedQueryRepositoryImpl implements FeedQueryRepository { + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public List findFeedInfoRows(List feedIds, Long userId) { + if (feedIds.isEmpty()) { + return List.of(); + } + + QFeedImage thumbnailImage = new QFeedImage("thumbnailImage"); + + return jpaQueryFactory + .select(Projections.constructor( + FeedInfoRow.class, + feed.feedId, + feed.user.userId, + feed.user.nickname, + avatarProfile.avatarProfileImage, + feed.createdDate, + feed.feedContent, + likeCount(), + isLiked(userId), + commentCount(), + feed.novelId, + novel.title, + novelRatingCount(), + novelRating(), + feed.isSpoiler, + feed.createdDate.ne(feed.modifiedDate), + isMyFeed(userId), + feed.isPublic, + thumbnailImage.url, + imageCount(), + firstGenreName(), + userNovelRating(userId), + feedWriterNovelRating() + )) + .from(feed) + .join(feed.user) + .leftJoin(avatarProfile).on(feed.user.avatarProfileId.eq(avatarProfile.avatarProfileId)) + .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) + .leftJoin(thumbnailImage).on( + thumbnailImage.feedId.eq(feed.feedId), + thumbnailImage.feedImageType.eq(FeedImageType.FEED_THUMBNAIL) + ) + .where(feed.feedId.in(feedIds)) + .fetch(); + } + + private JPQLQuery likeCount() { + QLike likeSub = new QLike("likeCountSub"); + + return JPAExpressions + .select(likeSub.likeId.count()) + .from(likeSub) + .where(likeSub.feed.eq(feed)); + } + + private JPQLQuery commentCount() { + QComment commentSub = new QComment("commentCountSub"); + + return JPAExpressions + .select(commentSub.commentId.count()) + .from(commentSub) + .where(commentSub.feed.eq(feed)); + } + + private JPQLQuery imageCount() { + QFeedImage imageSub = new QFeedImage("imageCountSub"); + + return JPAExpressions + .select(imageSub.feedImageId.count()) + .from(imageSub) + .where(imageSub.feedId.eq(feed.feedId)); + } + + private Expression isLiked(Long userId) { + if (userId == null) { + return Expressions.FALSE; + } + + QLike likeSub = new QLike("likedSub"); + + return JPAExpressions + .selectOne() + .from(likeSub) + .where( + likeSub.userId.eq(userId), + likeSub.feed.eq(feed) + ) + .exists(); + } + + private Expression isMyFeed(Long userId) { + if (userId == null) { + return Expressions.FALSE; + } + + return feed.user.userId.eq(userId); + } + + private JPQLQuery novelRatingCount() { + QUserNovel ratingSub = new QUserNovel("ratingCountSub"); + + return JPAExpressions + .select(ratingSub.userNovelId.count()) + .from(ratingSub) + .where( + ratingSub.novel.eq(novel), + ratingSub.userNovelRating.gt(0.0f) + ); + } + + private JPQLQuery novelRating() { + QUserNovel ratingSub = new QUserNovel("ratingAvgSub"); + + return JPAExpressions + .select(ratingSub.userNovelRating.avg()) + .from(ratingSub) + .where( + ratingSub.novel.eq(novel), + ratingSub.userNovelRating.gt(0.0f) + ); + } + + private JPQLQuery firstGenreName() { + QNovelGenre novelGenreSub = new QNovelGenre("firstGenreSub"); + QGenre genreSub = new QGenre("genreSub"); + + return JPAExpressions + .select(genreSub.genreName.min()) + .from(novelGenreSub) + .join(novelGenreSub.genre, genreSub) + .where(novelGenreSub.novel.eq(novel)); + } + + private Expression userNovelRating(Long userId) { + if (userId == null) { + return Expressions.nullExpression(Float.class); + } + + QUserNovel userNovelSub = new QUserNovel("userNovelRatingSub"); + + return JPAExpressions + .select(userNovelSub.userNovelRating) + .from(userNovelSub) + .where( + userNovelSub.novel.eq(novel), + userNovelSub.user.userId.eq(userId) + ); + } + + private JPQLQuery feedWriterNovelRating() { + QUserNovel writerNovelSub = new QUserNovel("writerNovelRatingSub"); + + return JPAExpressions + .select(writerNovelSub.userNovelRating) + .from(writerNovelSub) + .where( + writerNovelSub.novel.eq(novel), + writerNovelSub.user.userId.eq(feed.user.userId), + writerNovelSub.status.isNotNull() + ); + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java index ec8846c3..534d0b12 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java @@ -1,9 +1,6 @@ package org.websoso.WSSServer.feed.service; -import static org.websoso.WSSServer.domain.common.FeedImageType.FEED_THUMBNAIL; - import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -39,8 +36,7 @@ public Map getThumbnailUrlMap(List feedIds) { } return feedImageRepository.findByFeedIdIn(feedIds).stream() - .filter(feedImage -> feedImage.getFeedImageType() == FEED_THUMBNAIL) - .sorted(Comparator.comparing(FeedImage::getSequence)) + .filter(FeedImage::isThumbnail) .collect(Collectors.toMap( FeedImage::getFeedId, FeedImage::getUrl, diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java new file mode 100644 index 00000000..ad1e6725 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java @@ -0,0 +1,38 @@ +package org.websoso.WSSServer.feed.service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.dto.feed.FeedInfo; +import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.repository.FeedInfoRow; +import org.websoso.WSSServer.feed.repository.FeedQueryRepository; + +@Service +@RequiredArgsConstructor +public class FeedQueryService { + + private final FeedQueryRepository feedQueryRepository; + + @Transactional(readOnly = true) + public List createFeedInfos(List feeds, Long userId) { + List feedIds = feeds.stream() + .map(Feed::getFeedId) + .toList(); + + Map feedInfoRowMap = feedQueryRepository.findFeedInfoRows(feedIds, userId).stream() + .collect(Collectors.toMap(FeedInfoRow::feedId, Function.identity())); + + return feedIds.stream() + .map(feedInfoRowMap::get) + .filter(Objects::nonNull) + .map(FeedInfoRow::toResponse) + .toList(); + } + +} From 5f0301cb453283bb9db0f34784c37cdf1b7d5adc Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 15:21:03 +0900 Subject: [PATCH 16/31] =?UTF-8?q?[REFACTOR]=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=ED=94=BC=EB=93=9C=20=EC=A1=B0=ED=9A=8C=EC=8B=9C,=20JOIN=20?= =?UTF-8?q?=EB=B0=8F=20=EC=84=9C=EB=B8=8C=EC=BF=BC=EB=A6=AC=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/feed/UserFeedGetResponse.java | 17 +---- .../feed/application/FeedFindApplication.java | 40 +----------- .../feed/repository/FeedQueryRepository.java | 2 + .../repository/FeedQueryRepositoryImpl.java | 41 ++++++++++++ .../feed/repository/UserFeedInfoRow.java | 62 +++++++++++++++++++ .../feed/service/FeedQueryService.java | 20 +++++- 6 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java index 25ca3504..3d906bbd 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java @@ -1,8 +1,6 @@ package org.websoso.WSSServer.dto.feed; -import java.util.List; import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.Like; import org.websoso.WSSServer.novel.domain.Novel; import org.websoso.WSSServer.library.domain.UserNovel; import org.websoso.WSSServer.util.TimeFormatUtil; @@ -13,7 +11,6 @@ public record UserFeedGetResponse( String createdDate, Boolean isSpoiler, Boolean isModified, - List likerUsers, Boolean isLiked, Integer likeCount, Integer commentCount, @@ -37,19 +34,19 @@ public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, Str visitorId, thumbnailUrl, imageCount, - getLikeUsers(feed), + visitorId != null && feed.getLikes().stream() + .anyMatch(like -> like.getUserId().equals(visitorId)), feed.getLikes().size(), feed.getComments().size() ); } public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, String thumbnailUrl, - Integer imageCount, List likeUsers, Integer likeCount, + Integer imageCount, Boolean isLiked, Integer likeCount, Integer commentCount) { boolean isModified = !feed.getCreatedDate().equals(feed.getModifiedDate()); Long novelRatingCount = getNovelRatingCount(novel); Float novelRating = getNovelRating(novel, novelRatingCount); - boolean isLiked = likeUsers.contains(visitorId); String genreName = getNovelGenreName(novel); Float userNovelRating = getUserNovelRating(novel, visitorId); Float feedWriterNovelRating = getFeedWriterNovelRating(novel, feed.getUser().getUserId()); @@ -60,7 +57,6 @@ public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, Str TimeFormatUtil.formatRelativeDateTime(feed.getCreatedDate()), feed.getIsSpoiler(), isModified, - likeUsers, isLiked, likeCount, commentCount, @@ -79,13 +75,6 @@ public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, Str ); } - private static List getLikeUsers(Feed feed) { - return feed.getLikes() - .stream() - .map(Like::getUserId) - .toList(); - } - private static Long getNovelRatingCount(Novel novel) { if (novel == null) { return null; diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 9662a540..f75b72c2 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -50,11 +50,9 @@ public class FeedFindApplication { private final UserService userService; private final FeedServiceImpl feedServiceImpl; private final FeedQueryService feedQueryService; - private final FeedImageService feedImageService; private final NovelServiceImpl novelServiceImpl; private final AvatarService avatarService; private final FeedLikeService feedLikeService; - private final CommentServiceImpl commentServiceImpl; private final BlockService blockService; private final LibraryService libraryService; @@ -100,7 +98,7 @@ public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, FeedGetOp // FeedInfo에 필요한 정보들을 JOIN 및 서브 쿼리로 불러오기 List visibleFeeds = feeds.getContent().stream().toList(); - List feedInfos = feedQueryService.createFeedInfos(visibleFeeds, userIdOrNull); + List feedInfos = feedQueryService.findFeedInfoRows(visibleFeeds, userIdOrNull); return FeedsGetResponse.of(feeds.hasNext(), feedInfos); } @@ -173,7 +171,7 @@ public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long las Slice feeds = feedServiceImpl.findFeedsByNovel(userIdOrNull, novelId, lastFeedId, size); List visibleFeeds = feeds.getContent(); - List feedInfos = feedQueryService.createFeedInfos(visibleFeeds, userIdOrNull); + List feedInfos = feedQueryService.findFeedInfoRows(visibleFeeds, userIdOrNull); return NovelGetResponseFeedTab.of(feeds.hasNext(), feedInfos); } @@ -207,7 +205,7 @@ public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFe Map novelMap = novels.stream() .collect(Collectors.toMap(Novel::getNovelId, Function.identity())); - List userFeedGetResponseList = createUserFeedResponses(visibleFeeds, novelMap, visitorId); + List userFeedGetResponseList = feedQueryService.findUserFeedRows(visibleFeeds, visitorId); // TODO Slice의 hasNext()로 판단하도록 수정 Boolean isLoadable = visibleFeeds.size() == size; @@ -243,36 +241,4 @@ private List mapToPopularFeedGetResponseList( .toList(); } - private List createUserFeedResponses( - List feeds, - Map novelMap, - Long visitorId - ) { - List feedIds = feeds.stream() - .map(Feed::getFeedId) - .toList(); - Map> likerUserIdsMap = feedLikeService.findLikerUserIdsByFeedIds(feedIds); - Map likeCountMap = feedLikeService.countByFeedIds(feedIds); - Map commentCountMap = commentServiceImpl.countByFeedIds(feedIds); - Map thumbnailUrlMap = feedImageService.getThumbnailUrlMap(feedIds); - Map imageCountMap = feedImageService.getImageCountMap(feedIds); - - return feeds.stream() - .map(feed -> { - Long feedId = feed.getFeedId(); - - return UserFeedGetResponse.of( - feed, - novelMap.get(feed.getNovelId()), - visitorId, - thumbnailUrlMap.get(feedId), - imageCountMap.getOrDefault(feedId, 0), - likerUserIdsMap.getOrDefault(feedId, Collections.emptyList()), - likeCountMap.getOrDefault(feedId, 0), - commentCountMap.getOrDefault(feedId, 0) - ); - }) - .toList(); - } - } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java index 0e27b304..96fbca3c 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java @@ -6,4 +6,6 @@ public interface FeedQueryRepository { List findFeedInfoRows(List feedIds, Long userId); + List findUserFeedInfoRows(List feedIds, Long visitorId); + } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java index 0f60d4b3..28124702 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java @@ -73,6 +73,47 @@ public List findFeedInfoRows(List feedIds, Long userId) { .fetch(); } + @Override + public List findUserFeedInfoRows(List feedIds, Long visitorId) { + if (feedIds.isEmpty()) { + return List.of(); + } + + QFeedImage thumbnailImage = new QFeedImage("userFeedThumbnailImage"); + + return jpaQueryFactory + .select(Projections.constructor( + UserFeedInfoRow.class, + feed.feedId, + feed.feedContent, + feed.createdDate, + feed.isSpoiler, + feed.createdDate.ne(feed.modifiedDate), + isLiked(visitorId), + likeCount(), + commentCount(), + feed.novelId, + novel.title, + novelRating(), + novelRatingCount(), + feed.isPublic, + firstGenreName(), + userNovelRating(visitorId), + thumbnailImage.url, + imageCount(), + feedWriterNovelRating() + )) + .from(feed) + .join(feed.user) + .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) + .leftJoin(thumbnailImage).on( + thumbnailImage.feedId.eq(feed.feedId), + thumbnailImage.feedImageType.eq(FeedImageType.FEED_THUMBNAIL) + ) + .where(feed.feedId.in(feedIds)) + .fetch(); + } + private JPQLQuery likeCount() { QLike likeSub = new QLike("likeCountSub"); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java new file mode 100644 index 00000000..fd1a609b --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java @@ -0,0 +1,62 @@ +package org.websoso.WSSServer.feed.repository; + +import java.time.LocalDateTime; +import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; +import org.websoso.WSSServer.util.TimeFormatUtil; + +public record UserFeedInfoRow( + Long feedId, + String feedContent, + LocalDateTime createdDate, + Boolean isSpoiler, + Boolean isModified, + Boolean isLiked, + Long likeCount, + Long commentCount, + Long novelId, + String title, + Double novelRating, + Long novelRatingCount, + Boolean isPublic, + String genre, + Float userNovelRating, + String thumbnailUrl, + Long imageCount, + Float feedWriterNovelRating +) { + + public UserFeedGetResponse toResponse() { + return new UserFeedGetResponse( + feedId, + feedContent, + TimeFormatUtil.formatRelativeDateTime(createdDate), + isSpoiler, + isModified, + isLiked, + toInteger(likeCount), + toInteger(commentCount), + novelId, + title, + roundToFirstDecimal(novelRating), + novelRatingCount, + isPublic, + genre, + userNovelRating, + thumbnailUrl, + toInteger(imageCount), + feedWriterNovelRating + ); + } + + private static Integer toInteger(Long value) { + return value == null ? 0 : value.intValue(); + } + + private static Float roundToFirstDecimal(Double value) { + if (value == null) { + return null; + } + + return Math.round(value.floatValue() * 10) / 10.0f; + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java index ad1e6725..7dbe95cf 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java @@ -9,9 +9,11 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.dto.feed.FeedInfo; +import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.repository.FeedInfoRow; import org.websoso.WSSServer.feed.repository.FeedQueryRepository; +import org.websoso.WSSServer.feed.repository.UserFeedInfoRow; @Service @RequiredArgsConstructor @@ -20,7 +22,7 @@ public class FeedQueryService { private final FeedQueryRepository feedQueryRepository; @Transactional(readOnly = true) - public List createFeedInfos(List feeds, Long userId) { + public List findFeedInfoRows(List feeds, Long userId) { List feedIds = feeds.stream() .map(Feed::getFeedId) .toList(); @@ -35,4 +37,20 @@ public List createFeedInfos(List feeds, Long userId) { .toList(); } + @Transactional(readOnly = true) + public List findUserFeedRows(List feeds, Long visitorId) { + List feedIds = feeds.stream() + .map(Feed::getFeedId) + .toList(); + + Map userFeedInfoRowMap = feedQueryRepository.findUserFeedInfoRows(feedIds, visitorId).stream() + .collect(Collectors.toMap(UserFeedInfoRow::feedId, Function.identity())); + + return feedIds.stream() + .map(userFeedInfoRowMap::get) + .filter(Objects::nonNull) + .map(UserFeedInfoRow::toResponse) + .toList(); + } + } From dfb36e5ee20a9c979e6e20aba649ddd9247d6637 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 15:29:01 +0900 Subject: [PATCH 17/31] =?UTF-8?q?[REFACTOR]=20FeedFIndAPplication=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20Novel=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index f75b72c2..e31fa76f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -195,16 +195,6 @@ public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFe List visibleFeeds = feedServiceImpl.getViewableUserFeed(owner, lastFeedId, size, isVisible, isUnVisible, sortCriteria, genres, visitorId, includeEtc); - List novelIds = visibleFeeds.stream().map(Feed::getNovelId).filter(Objects::nonNull) - .collect(Collectors.toList()); - - // 소설 ID에 해당하는 소설 정보들 전부 불러오기 - List novels = novelServiceImpl.getNovelsWithGenresByIds(novelIds); - - // - Map novelMap = novels.stream() - .collect(Collectors.toMap(Novel::getNovelId, Function.identity())); - List userFeedGetResponseList = feedQueryService.findUserFeedRows(visibleFeeds, visitorId); // TODO Slice의 hasNext()로 판단하도록 수정 From 11bb69c1095f4357abbb5b45ae986a226666ac4b Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 15:33:38 +0900 Subject: [PATCH 18/31] =?UTF-8?q?[REMOVE]=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20FeedService=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/controller/NovelController.java | 1 - .../WSSServer/feed/service/FeedService.java | 397 ------------------ .../user/controller/UserController.java | 1 - 3 files changed, 399 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/service/FeedService.java diff --git a/src/main/java/org/websoso/WSSServer/controller/NovelController.java b/src/main/java/org/websoso/WSSServer/controller/NovelController.java index ec453016..f723925d 100644 --- a/src/main/java/org/websoso/WSSServer/controller/NovelController.java +++ b/src/main/java/org/websoso/WSSServer/controller/NovelController.java @@ -22,7 +22,6 @@ import org.websoso.WSSServer.dto.novel.NovelGetResponseInfoTab; import org.websoso.WSSServer.dto.popularNovel.PopularNovelsGetResponse; import org.websoso.WSSServer.dto.userNovel.TasteNovelsGetResponse; -import org.websoso.WSSServer.feed.service.FeedService; @RestController @RequestMapping("/novels") diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java deleted file mode 100644 index 3bf251e2..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedService.java +++ /dev/null @@ -1,397 +0,0 @@ -package org.websoso.WSSServer.feed.service; - -import static java.lang.Boolean.TRUE; -import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.ALREADY_LIKED; -import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.NOT_LIKED; -import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomNovelError.NOVEL_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomUserError.PRIVATE_PROFILE_STATUS; -import static org.websoso.WSSServer.exception.error.CustomUserError.USER_NOT_FOUND; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; -import org.websoso.WSSServer.user.domain.AvatarProfile; -import org.websoso.WSSServer.domain.Genre; -import org.websoso.WSSServer.domain.GenrePreference; -import org.websoso.WSSServer.notification.domain.Notification; -import org.websoso.WSSServer.notification.domain.NotificationType; -import org.websoso.WSSServer.user.repository.AvatarProfileRepository; -import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.notification.domain.UserDevice; -import org.websoso.WSSServer.domain.common.FeedGetOption; -import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.dto.feed.FeedCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedCreateResponse; -import org.websoso.WSSServer.dto.feed.FeedGetResponse; -import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedImageDeleteEvent; -import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; -import org.websoso.WSSServer.dto.feed.FeedInfo; -import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; -import org.websoso.WSSServer.dto.feed.FeedsGetResponse; -import org.websoso.WSSServer.dto.feed.InterestFeedGetResponse; -import org.websoso.WSSServer.dto.feed.InterestFeedsGetResponse; -import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; -import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; -import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; -import org.websoso.WSSServer.dto.user.UserBasicInfo; -import org.websoso.WSSServer.exception.exception.CustomAvatarException; -import org.websoso.WSSServer.exception.exception.CustomFeedException; -import org.websoso.WSSServer.exception.exception.CustomGenreException; -import org.websoso.WSSServer.exception.exception.CustomNovelException; -import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.FeedImage; -import org.websoso.WSSServer.feed.domain.Like; -import org.websoso.WSSServer.feed.domain.PopularFeed; -import org.websoso.WSSServer.feed.repository.CommentRepository; -import org.websoso.WSSServer.feed.repository.FeedImageCustomRepository; -import org.websoso.WSSServer.feed.repository.FeedImageRepository; -import org.websoso.WSSServer.feed.repository.FeedRepository; -import org.websoso.WSSServer.feed.repository.LikeRepository; -import org.websoso.WSSServer.feed.repository.PopularFeedRepository; -import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; -import org.websoso.WSSServer.library.domain.UserNovel; -import org.websoso.WSSServer.library.repository.UserNovelRepository; -import org.websoso.WSSServer.notification.infrastructure.FCMClient; -import org.websoso.WSSServer.notification.dto.FCMMessageRequest; -import org.websoso.WSSServer.novel.domain.Novel; -import org.websoso.WSSServer.novel.repository.NovelRepository; -import org.websoso.WSSServer.user.repository.BlockRepository; -import org.websoso.WSSServer.repository.GenrePreferenceRepository; -import org.websoso.WSSServer.repository.GenreRepository; -import org.websoso.WSSServer.notification.repository.NotificationRepository; -import org.websoso.WSSServer.notification.repository.NotificationTypeRepository; -import org.websoso.WSSServer.user.repository.UserRepository; -import org.websoso.WSSServer.service.ImageClient; - -@Service -@RequiredArgsConstructor -public class FeedService { - - private static final String DEFAULT_CATEGORY = "all"; - private static final int DEFAULT_PAGE_NUMBER = 0; - private static final int POPULAR_FEED_LIKE_THRESHOLD = 5; - private static final int NOTIFICATION_TITLE_MAX_LENGTH = 12; - private static final int NOTIFICATION_TITLE_MIN_LENGTH = 0; - - private final NovelRepository novelRepository; - private final FeedRepository feedRepository; - private final CommentRepository commentRepository; - private final ReportedCommentRepository reportedCommentRepository; - private final ApplicationEventPublisher eventPublisher; - private final LikeRepository likeRepository; - private final PopularFeedRepository popularFeedRepository; - private final NotificationRepository notificationRepository; - private final NotificationTypeRepository notificationTypeRepository; - private final BlockRepository blockRepository; - private final GenrePreferenceRepository genrePreferenceRepository; - private final UserRepository userRepository; - private final AvatarProfileRepository avatarProfileRepository; - private final FeedImageRepository feedImageRepository; - private final UserNovelRepository userNovelRepository; - private final GenreRepository genreRepository; - private final ImageClient imageClient; - private final FCMClient fcmClient; - - -// @Transactional -// public void deleteFeed(Long feedId) { -// List commentIds = commentRepository.findAllByFeedId(feedId).stream().map(Comment::getCommentId).toList(); -// reportedCommentRepository.deleteByCommentIdsIn(commentIds); -// feedRepository.deleteById(feedId); -// } - -// @Transactional -// public void likeFeed(User user, Long feedId) { -// Feed feed = getFeedOrException(feedId); -// -// if (likeRepository.existsByUserIdAndFeed(user.getUserId(), feed)) { -// throw new CustomFeedException(ALREADY_LIKED, "user already liked that feed"); -// } -// likeRepository.save(Like.create(user.getUserId(), feed)); -// -// long likeCount = likeRepository.countByFeed(feed); -// -// if (likeCount == POPULAR_FEED_LIKE_THRESHOLD && feed.getNovelId() != null) { -// if (!popularFeedRepository.existsByFeed(feed)) { -// popularFeedRepository.save(PopularFeed.create(feed)); -// -// sendPopularFeedPushMessage(feed); -// } -// } -// -// sendLikePushMessage(user, feed); -// } - -// private void sendLikePushMessage(User liker, Feed feed) { -// User feedOwner = feed.getUser(); -// if (liker.equals(feedOwner) || blockRepository.existsByBlockingIdAndBlockedId(feedOwner.getUserId(), -// liker.getUserId())) { -// return; -// } -// -// NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("좋아요"); -// -// String notificationTitle = createNotificationTitle(feed); -// String notificationBody = String.format("%s님이 내 글을 좋아해요.", liker.getNickname()); -// Long feedId = feed.getFeedId(); -// -// Notification notification = Notification.create(notificationTitle, notificationBody, null, -// feedOwner.getUserId(), feedId, notificationTypeComment); -// notificationRepository.save(notification); -// -// if (!TRUE.equals(feedOwner.getIsPushEnabled())) { -// return; -// } -// -// List feedOwnerDevices = feedOwner.getUserDevices(); -// if (feedOwnerDevices.isEmpty()) { -// return; -// } -// -// FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(notificationTitle, notificationBody, -// String.valueOf(feedId), "feedDetail", String.valueOf(notification.getNotificationId())); -// -// List targetFCMTokens = feedOwnerDevices.stream().map(UserDevice::getFcmToken).toList(); -// fcmClient.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest); -// } - -// private String createNotificationTitle(Feed feed) { -// if (feed.getNovelId() == null) { -// String feedContent = feed.getFeedContent(); -// feedContent = feedContent.length() <= NOTIFICATION_TITLE_MAX_LENGTH ? feedContent -// : feedContent.substring(NOTIFICATION_TITLE_MIN_LENGTH, NOTIFICATION_TITLE_MAX_LENGTH); -// return "'" + feedContent + "...'"; -// } -// Novel novel = novelRepository.findById(feed.getNovelId()) -// .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, "novel with the given id is not found")); -// return novel.getTitle(); -// } - -// @Transactional -// public void unLikeFeed(User user, Long feedId) { -// Feed feed = getFeedOrException(feedId); -// Like like = likeRepository.findByUserIdAndFeed(user.getUserId(), feed) -// .orElseThrow(() -> new CustomFeedException(NOT_LIKED, -// "User did not like this feed or like already deleted")); -// likeRepository.delete(like); -// } - -// @Transactional(readOnly = true) -// public FeedGetResponse getFeedById(User user, Long feedId) { -// Feed feed = getFeedOrException(feedId); -// UserBasicInfo feedUserBasicInfo = getUserBasicInfo(feed.getUser()); -// Novel novel = getLinkedNovelOrNull(feed.getNovelId()); -// Boolean isLiked = isUserLikedFeed(user, feed); -// Boolean isMyFeed = isUserFeedOwner(feed.getUser(), user); -// -// return FeedGetResponse.of(feed, feedUserBasicInfo, novel, isLiked, isMyFeed); -// } -// -// @Transactional(readOnly = true) -// public FeedsGetResponse getFeeds(User user, String category, Long lastFeedId, int size, -// FeedGetOption feedGetOption) { -// Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); -// -// List genres = getPreferenceGenres(user); -// -// Slice feeds = findFeedsByCategoryLabel(lastFeedId, userIdOrNull, -// PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); -// -// List feedGetResponses = feeds.getContent().stream().filter(feed -> feed.isVisibleTo(userIdOrNull)) -// .map(feed -> createFeedInfo(feed, user)).toList(); -// -// return FeedsGetResponse.of(feeds.hasNext(), feedGetResponses); -// } - -// private List getPreferenceGenres(User user) { -// if (user == null) { -// return null; -// } -// return genrePreferenceRepository.findByUser(user).stream().map(GenrePreference::getGenre).toList(); -// } -// -// private static String getChosenCategoryOrDefault(String category) { -// return Optional.ofNullable(category).orElse(DEFAULT_CATEGORY); -// } - -// private Feed getFeedOrException(Long feedId) { -// return feedRepository.findById(feedId) -// .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); -// } - -// private UserBasicInfo getUserBasicInfo(User user) { -// return user.getUserBasicInfo( -// avatarProfileRepository.findById(user.getAvatarProfileId()).orElseThrow(() -> -// new CustomAvatarException(AVATAR_NOT_FOUND, "avatar with the given id was not found")) -// .getAvatarProfileImage()); -// } - -// private Novel getLinkedNovelOrNull(Long linkedNovelId) { -// if (linkedNovelId == null) { -// return null; -// } -// return novelRepository.findById(linkedNovelId) -// .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, -// "novel with the given id is not found")); -// } - -// private Boolean isUserLikedFeed(User user, Feed feed) { -// return likeRepository.existsByUserIdAndFeed(user.getUserId(), feed); -// } -// -// private Boolean isUserFeedOwner(User createdUser, User user) { -// return createdUser.equals(user); -// } - -// private FeedInfo createFeedInfo(Feed feed, User user) { -// UserBasicInfo userBasicInfo = getUserBasicInfo(feed.getUser()); -// Novel novel = getLinkedNovelOrNull(feed.getNovelId()); -// Boolean isLiked = user != null && isUserLikedFeed(user, feed); -// Boolean isMyFeed = user != null && isUserFeedOwner(feed.getUser(), user); -// Integer imageCount = feedImageRepository.countByFeedId(feed.getFeedId()); -// Optional thumbnailImage = feedImageCustomRepository.findThumbnailFeedImageByFeedId(feed.getFeedId()); -// String thumbnailUrl = thumbnailImage.map(FeedImage::getUrl).orElse(null); -// -// return FeedInfo.of(feed, userBasicInfo, novel, isLiked, isMyFeed, thumbnailUrl, imageCount, user); -// } - -// private Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, -// FeedGetOption feedGetOption, List preferenceGenres) { -// if (FeedGetOption.isAll(feedGetOption)) { -// return feedRepository.findFeeds(lastFeedId, userId, pageRequest); -// } -// return feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres); -// -// } -// -// private Genre findGenreByName(String genreName) { -// return genreRepository.findByGenreName(genreName) -// .orElseThrow(() -> new CustomGenreException(GENRE_NOT_FOUND, -// "genre with the given name is not found")); -// } -// -// @Transactional(readOnly = true) -// public InterestFeedsGetResponse getInterestFeeds(User user) { -// List interestNovels = userNovelRepository.findByUserAndIsInterestTrue(user).stream() -// .map(UserNovel::getNovel).toList(); -// -// if (interestNovels.isEmpty()) { -// return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_INTEREST_NOVELS"); -// } -// -// Map novelMap = interestNovels.stream() -// .collect(Collectors.toMap(Novel::getNovelId, novel -> novel)); -// List interestNovelIds = new ArrayList<>(novelMap.keySet()); -// -// List interestFeeds = feedRepository.findTop10ByNovelIdInOrderByFeedIdDesc(interestNovelIds); -// -// if (interestFeeds.isEmpty()) { -// return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_ASSOCIATED_FEEDS"); -// } -// -// Set avatarIds = interestFeeds.stream().map(feed -> feed.getUser().getAvatarProfileId()) -// .collect(Collectors.toSet()); -// Map avatarMap = avatarProfileRepository.findAllById(avatarIds).stream() -// .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, avatar -> avatar)); -// -// List interestFeedGetResponses = interestFeeds.stream() -// .filter(feed -> feed.isVisibleTo(user.getUserId())).map(feed -> { -// Novel novel = novelMap.get(feed.getNovelId()); -// AvatarProfile avatar = avatarMap.get(feed.getUser().getAvatarProfileId()); -// return InterestFeedGetResponse.of(novel, feed.getUser(), feed, avatar); -// }).toList(); -// return InterestFeedsGetResponse.of(interestFeedGetResponses, ""); -// } - - - - - - - - - - - -// private void sendPopularFeedPushMessage(Feed feed) { -// NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("지금뜨는글"); -// -// User feedOwner = feed.getUser(); -// Long feedId = feed.getFeedId(); -// String notificationTitle = "지금 뜨는 글 등극\uD83D\uDE4C"; -// String notificationBody = createNotificationBody(feed); -// -// Notification notification = Notification.create( -// notificationTitle, -// notificationBody, -// null, -// feedOwner.getUserId(), -// feedId, -// notificationTypeComment -// ); -// notificationRepository.save(notification); -// -// if (!TRUE.equals(feedOwner.getIsPushEnabled())) { -// return; -// } -// -// List feedOwnerDevices = feedOwner.getUserDevices(); -// if (feedOwnerDevices.isEmpty()) { -// return; -// } -// -// FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of( -// notificationTitle, -// notificationBody, -// String.valueOf(feedId), -// "feedDetail", -// String.valueOf(notification.getNotificationId()) -// ); -// -// List targetFCMTokens = feedOwnerDevices -// .stream() -// .map(UserDevice::getFcmToken) -// .toList(); -// fcmClient.sendMulticastPushMessage( -// targetFCMTokens, -// fcmMessageRequest -// ); -// } - -// private String createNotificationBody(Feed feed) { -// return String.format("내가 남긴 %s 글이 관심 받고 있어요!", generateNotificationBodyFragment(feed)); -// } - -// private String generateNotificationBodyFragment(Feed feed) { -// if (feed.getNovelId() == null) { -// String feedContent = feed.getFeedContent(); -// feedContent = feedContent.length() <= 12 -// ? feedContent -// : feedContent.substring(0, 12); -// return "'" + feedContent + "...'"; -// } -// Novel novel = novelRepository.findById(feed.getNovelId()) -// .orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, -// "novel with the given id is not found")); -// return String.format("<%s>", novel.getTitle()); -// } - -} diff --git a/src/main/java/org/websoso/WSSServer/user/controller/UserController.java b/src/main/java/org/websoso/WSSServer/user/controller/UserController.java index 3a39a991..49448825 100644 --- a/src/main/java/org/websoso/WSSServer/user/controller/UserController.java +++ b/src/main/java/org/websoso/WSSServer/user/controller/UserController.java @@ -45,7 +45,6 @@ import org.websoso.WSSServer.dto.userNovel.UserNovelAndNovelsGetResponse; import org.websoso.WSSServer.dto.userNovel.UserNovelAndNovelsGetResponseLegacy; import org.websoso.WSSServer.dto.userNovel.UserTasteAttractivePointPreferencesAndKeywordsGetResponse; -import org.websoso.WSSServer.feed.service.FeedService; import org.websoso.WSSServer.library.service.UserNovelService; import org.websoso.WSSServer.user.service.UserService; import org.websoso.WSSServer.validation.NicknameConstraint; From 6e2889ac7e56aac25b9586c64923cd5ec10d9e0c Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 15:35:02 +0900 Subject: [PATCH 19/31] =?UTF-8?q?[REMOVE]=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20ReportService=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/feed/service/ReportService.java | 115 ------------------ 1 file changed, 115 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/service/ReportService.java diff --git a/src/main/java/org/websoso/WSSServer/feed/service/ReportService.java b/src/main/java/org/websoso/WSSServer/feed/service/ReportService.java deleted file mode 100644 index 7271e57e..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/service/ReportService.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.websoso.WSSServer.feed.service; - -import static org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessageType.REPORT; -import static org.websoso.WSSServer.domain.common.ReportedType.IMPERTINENCE; -import static org.websoso.WSSServer.domain.common.ReportedType.SPOILER; -import static org.websoso.WSSServer.exception.error.CustomCommentError.ALREADY_REPORTED_COMMENT; -import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.ALREADY_REPORTED_FEED; -import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.SELF_REPORT_NOT_ALLOWED; -import static org.websoso.WSSServer.exception.error.CustomUserError.USER_NOT_FOUND; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessage; -import org.websoso.WSSServer.domain.common.ReportedType; -import org.websoso.WSSServer.exception.error.CustomCommentError; -import org.websoso.WSSServer.exception.exception.CustomCommentException; -import org.websoso.WSSServer.exception.exception.CustomFeedException; -import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.ReportedComment; -import org.websoso.WSSServer.feed.domain.ReportedFeed; -import org.websoso.WSSServer.feed.repository.CommentRepository; -import org.websoso.WSSServer.feed.repository.FeedRepository; -import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; -import org.websoso.WSSServer.feed.repository.ReportedFeedRepository; -import org.websoso.WSSServer.infrastructure.discord.DiscordMessageClient; -import org.websoso.WSSServer.notification.service.MessageFormatter; -import org.websoso.WSSServer.user.repository.UserRepository; - -@Service -@RequiredArgsConstructor -public class ReportService { - - private final ReportedCommentRepository reportedCommentRepository; - private final CommentRepository commentRepository; - private final UserRepository userRepository; - private final FeedRepository feedRepository; - private final ReportedFeedRepository reportedFeedRepository; - private final DiscordMessageClient discordMessageClient; - - @Transactional - public void reportComment(User user, Long feedId, Long commentId, ReportedType reportedType) { - Feed feed = getFeedOrException(feedId); - Comment comment = commentRepository.findById(commentId).orElseThrow( - () -> new CustomCommentException(COMMENT_NOT_FOUND, "comment with the given id was not found")); - comment.validateFeedAssociation(feed); - - User commentCreatedUser = userRepository.findById(comment.getUserId()) - .orElseThrow(() -> new CustomUserException(USER_NOT_FOUND, "user with the given id was not found")); - - if (commentCreatedUser.equals(user)) { - throw new CustomCommentException(CustomCommentError.SELF_REPORT_NOT_ALLOWED, "cannot report own comment"); - } - - if (reportedCommentRepository.existsByCommentAndUserAndReportedType(comment, user, reportedType)) { - throw new CustomCommentException(ALREADY_REPORTED_COMMENT, "comment has already been reported by the user"); - } - - reportedCommentRepository.save(ReportedComment.create(comment, user, reportedType)); - - int reportedCount = reportedCommentRepository.countByCommentAndReportedType(comment, reportedType); - boolean shouldHide = reportedType.isExceedingLimit(reportedCount); - - if (shouldHide) { - if (reportedType.equals(SPOILER)) { - comment.spoiler(); - } else if (reportedType.equals(IMPERTINENCE)) { - comment.hideComment(); - } - } - - discordMessageClient.sendDiscordWebhookMessage(DiscordWebhookMessage.of( - MessageFormatter.formatCommentReportMessage(user, feed, comment, reportedType, commentCreatedUser, - reportedCount, shouldHide), REPORT)); - } - - private Feed getFeedOrException(Long feedId) { - return feedRepository.findById(feedId) - .orElseThrow(() -> new CustomFeedException(FEED_NOT_FOUND, "feed with the given id was not found")); - } - - @Transactional - public void reportFeed(User user, Long feedId, ReportedType reportedType) { - Feed feed = getFeedOrException(feedId); - - if (isUserFeedOwner(feed.getUser(), user)) { - throw new CustomFeedException(SELF_REPORT_NOT_ALLOWED, "cannot report own feed"); - } - - if (reportedFeedRepository.existsByFeedAndUserAndReportedType(feed, user, reportedType)) { - throw new CustomFeedException(ALREADY_REPORTED_FEED, "feed has already been reported by the user"); - } - - reportedFeedRepository.save(ReportedFeed.create(feed, user, reportedType)); - - int reportedCount = reportedFeedRepository.countByFeedAndReportedType(feed, reportedType); - boolean shouldHide = reportedType.isExceedingLimit(reportedCount); - - if (shouldHide) { - feed.hideFeed(); - } - - discordMessageClient.sendDiscordWebhookMessage(DiscordWebhookMessage.of( - MessageFormatter.formatFeedReportMessage(user, feed, reportedType, reportedCount, shouldHide), REPORT)); - } - - private Boolean isUserFeedOwner(User createdUser, User user) { - return createdUser.equals(user); - } -} From 76ac14ff8f61f8fdecb2984823b792862b15ceed Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 15:36:07 +0900 Subject: [PATCH 20/31] =?UTF-8?q?[FIX]=20PopularFeedService=20Transactiona?= =?UTF-8?q?l=20=EB=88=84=EB=9D=BD=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/feed/service/PopularFeedService.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java b/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java index de5cd9ee..c4b05632 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.PopularFeed; import org.websoso.WSSServer.feed.repository.PopularFeedRepository; @@ -12,12 +13,14 @@ public class PopularFeedService { private final PopularFeedRepository popularFeedRepository; - public boolean existByFeed(Feed feed) { - return popularFeedRepository.existsByFeed(feed); - } - + @Transactional public void create(Feed feed) { popularFeedRepository.save(PopularFeed.create(feed)); } + @Transactional(readOnly = true) + public boolean existByFeed(Feed feed) { + return popularFeedRepository.existsByFeed(feed); + } + } From f59711a3c56ce3808b5189ab889ad0275bce7639 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 15:44:49 +0900 Subject: [PATCH 21/31] =?UTF-8?q?[FIX]=20=EB=B9=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=EC=9D=98=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=EC=97=90=EC=84=9C=20=EB=B9=84=EA=B3=B5?= =?UTF-8?q?=EA=B0=9C=20=EA=B2=8C=EC=8B=9C=EA=B8=80=EC=9D=84=20=EB=B3=BC=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/repository/FeedCustomRepositoryImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index 2c7e4a54..a9761cf1 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -329,10 +329,11 @@ private BooleanExpression checkHidden() { } private BooleanExpression checkVisible(Long userId) { - if (userId != null) { - return feed.isPublic.isTrue().or(feed.user.userId.eq(userId)); + if (userId == null) { + return feed.isPublic.isTrue(); } - return null; + + return feed.isPublic.isTrue().or(feed.user.userId.eq(userId)); } private BooleanExpression checkFeedListVisibility(Long userId) { From af8e0ce638dd85f98c669dabf52f089801132f8d Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 16:22:23 +0900 Subject: [PATCH 22/31] =?UTF-8?q?[REFACTOR]=20=EC=9D=B8=EA=B8=B0=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=ED=94=BC=EB=93=9C=20=EC=A1=B0=ED=9A=8C=EC=8B=9C,?= =?UTF-8?q?=20Novel=EA=B9=8C=EC=A7=80=20=ED=95=9C=EB=B2=88=EC=97=90=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 50 +++---------------- .../feed/repository/FeedQueryRepository.java | 2 + .../repository/FeedQueryRepositoryImpl.java | 38 ++++++++++++++ .../feed/repository/PopularFeedInfoRow.java | 34 +++++++++++++ .../feed/service/FeedQueryService.java | 9 ++++ .../user/repository/BlockRepository.java | 9 ++++ .../WSSServer/user/service/BlockService.java | 5 ++ 7 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index e31fa76f..de6773c6 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -1,7 +1,6 @@ package org.websoso.WSSServer.feed.application; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -13,7 +12,6 @@ import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; -import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; import org.websoso.WSSServer.feed.service.FeedImageService; import org.websoso.WSSServer.feed.service.FeedLikeService; import org.websoso.WSSServer.feed.service.FeedQueryService; @@ -29,7 +27,6 @@ import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.PopularFeed; import org.websoso.WSSServer.feed.service.CommentServiceImpl; import org.websoso.WSSServer.feed.service.FeedServiceImpl; import org.websoso.WSSServer.library.service.LibraryService; @@ -105,27 +102,14 @@ public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, FeedGetOp @Transactional(readOnly = true) public PopularFeedsGetResponse getPopularFeeds(User user, int size) { - List popularFeeds = Optional.ofNullable(user) - .map(u -> feedServiceImpl.findPopularFeedsWithUser(u.getUserId(), size)) - .orElseGet(() -> feedServiceImpl.findPopularFeedsWithoutUser(size)); - - // TODO: PopularFeeds에 이런 메서드들이 더 많으면 일급 함수 객체 만들어도 괜찮을듯 - List novelIds = popularFeeds.stream() - .map(f -> f.getFeed().getNovelId()) - .filter(Objects::nonNull) - .distinct() - .toList(); - - Map novelMap = novelServiceImpl.getNovelsWithGenresByIds(novelIds) - .stream() - .collect(Collectors.toMap( - Novel::getNovelId, - Function.identity() - )); - - List popularFeedGetResponses = mapToPopularFeedGetResponseList(popularFeeds, novelMap); - - return PopularFeedsGetResponse.of(popularFeedGetResponses); + + // 사용자의 차단 목록 조회 + List blockedUserIds = Optional.ofNullable(user) + .map(User::getUserId) + .map(blockService::findBlockRelationUserIds) + .orElseGet(Collections::emptyList); + + return PopularFeedsGetResponse.of(feedQueryService.findPopularFeedRows(blockedUserIds, size)); } @Transactional(readOnly = true) @@ -213,22 +197,4 @@ private Novel getLinkedNovelOrNull(Long linkedNovelId) { return novelServiceImpl.getNovelOrException(linkedNovelId); } - private List mapToPopularFeedGetResponseList( - List popularFeeds, - Map novelMap - ) { - return popularFeeds.stream() - .map(popularFeed -> { - Novel novel = novelMap.get(popularFeed.getFeed().getNovelId()); - - return PopularFeedGetResponse.of( - popularFeed, - novel == null ? null : novel.getTitle(), - novel == null ? null : novel.getNovelImage(), - novel == null ? null : novel.getFirstGenreName() - ); - }) - .toList(); - } - } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java index 96fbca3c..defbd864 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java @@ -8,4 +8,6 @@ public interface FeedQueryRepository { List findUserFeedInfoRows(List feedIds, Long visitorId); + List findPopularFeedInfoRows(List blockedUserIds, int size); + } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java index 28124702..41391d1a 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java @@ -1,11 +1,13 @@ package org.websoso.WSSServer.feed.repository; import static org.websoso.WSSServer.feed.domain.QFeed.feed; +import static org.websoso.WSSServer.feed.domain.QPopularFeed.popularFeed; import static org.websoso.WSSServer.novel.domain.QNovel.novel; import static org.websoso.WSSServer.user.domain.QAvatarProfile.avatarProfile; import com.querydsl.core.types.Expression; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPQLQuery; @@ -114,6 +116,34 @@ public List findUserFeedInfoRows(List feedIds, Long visit .fetch(); } + @Override + public List findPopularFeedInfoRows(List blockedUserIds, int size) { + return jpaQueryFactory + .select(Projections.constructor( + PopularFeedInfoRow.class, + feed.feedId, + feed.feedContent, + likeCount(), + commentCount(), + feed.isSpoiler, + feed.isPublic, + novel.title, + novel.novelImage, + firstGenreName() + )) + .from(popularFeed) + .join(popularFeed.feed, feed) + .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) + .where( + feed.isPublic.isTrue(), + feed.isHidden.isFalse(), + excludeBlockedUsers(blockedUserIds) + ) + .orderBy(popularFeed.popularFeedId.desc()) + .limit(size) + .fetch(); + } + private JPQLQuery likeCount() { QLike likeSub = new QLike("likeCountSub"); @@ -229,4 +259,12 @@ private JPQLQuery feedWriterNovelRating() { writerNovelSub.status.isNotNull() ); } + + private BooleanExpression excludeBlockedUsers(List blockedUserIds) { + if (blockedUserIds == null || blockedUserIds.isEmpty()) { + return null; + } + + return feed.user.userId.notIn(blockedUserIds); + } } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java new file mode 100644 index 00000000..646df61c --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java @@ -0,0 +1,34 @@ +package org.websoso.WSSServer.feed.repository; + +import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; + +public record PopularFeedInfoRow( + Long feedId, + String feedContent, + Long likeCount, + Long commentCount, + Boolean isSpoiler, + Boolean isPublic, + String novelTitle, + String novelImage, + String novelGenre +) { + + public PopularFeedGetResponse toResponse() { + return new PopularFeedGetResponse( + feedId, + feedContent, + toInteger(likeCount), + toInteger(commentCount), + isSpoiler, + isPublic, + novelTitle, + novelImage, + novelGenre + ); + } + + private static Integer toInteger(Long value) { + return value == null ? 0 : value.intValue(); + } +} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java index 7dbe95cf..8364350f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java @@ -10,9 +10,11 @@ import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.dto.feed.FeedInfo; import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; +import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.repository.FeedInfoRow; import org.websoso.WSSServer.feed.repository.FeedQueryRepository; +import org.websoso.WSSServer.feed.repository.PopularFeedInfoRow; import org.websoso.WSSServer.feed.repository.UserFeedInfoRow; @Service @@ -53,4 +55,11 @@ public List findUserFeedRows(List feeds, Long visitor .toList(); } + @Transactional(readOnly = true) + public List findPopularFeedRows(List blockedUserIds, int size) { + return feedQueryRepository.findPopularFeedInfoRows(blockedUserIds, size).stream() + .map(PopularFeedInfoRow::toResponse) + .toList(); + } + } diff --git a/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java b/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java index 81e3836b..deb5d4bd 100644 --- a/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java +++ b/src/main/java/org/websoso/WSSServer/user/repository/BlockRepository.java @@ -20,4 +20,13 @@ SELECT count(b) > 0 OR (b.blockingId = :userId2 AND b.blockedId = :userId1) """) boolean existsBlockRelation(Long userId1, Long userId2); + + @Query(""" + SELECT DISTINCT CASE WHEN b.blockingId = :userId THEN b.blockedId + ELSE b.blockingId END + FROM Block b + WHERE b.blockingId = :userId + OR b.blockedId = :userId + """) + List findBlockRelationUserIds(Long userId); } diff --git a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java index b76b821b..a927625b 100644 --- a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java +++ b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java @@ -54,4 +54,9 @@ public void validateNotBlocked(Long userId, Long targetUserId) { public List findByBlockerId(Long blockingId) { return blockRepository.findByBlockingId(blockingId); } + + @Transactional(readOnly = true) + public List findBlockRelationUserIds(Long userId) { + return blockRepository.findBlockRelationUserIds(userId); + } } From 782a3766b6597dd62ed22945b438c75b332a01c9 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 17:24:54 +0900 Subject: [PATCH 23/31] =?UTF-8?q?[REFACTOR]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20/=20=EC=9D=B8?= =?UTF-8?q?=EA=B8=B0=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC=EB=A5=BC=20Service?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Application=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/common/FeedGetOption.java | 4 + .../feed/application/FeedFindApplication.java | 23 ++++-- .../feed/controller/FeedController.java | 2 +- .../feed/repository/FeedCustomRepository.java | 11 ++- .../repository/FeedCustomRepositoryImpl.java | 53 +++---------- .../feed/repository/FeedRepository.java | 10 --- .../feed/service/FeedServiceImpl.java | 75 +++++++------------ .../feed/service/FeedServiceImplTest.java | 52 ++++++------- 8 files changed, 89 insertions(+), 141 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/domain/common/FeedGetOption.java b/src/main/java/org/websoso/WSSServer/domain/common/FeedGetOption.java index bdafe2cd..58e40b1a 100644 --- a/src/main/java/org/websoso/WSSServer/domain/common/FeedGetOption.java +++ b/src/main/java/org/websoso/WSSServer/domain/common/FeedGetOption.java @@ -18,6 +18,10 @@ public static FeedGetOption of(final String feedGetOption) { "given feed option does not exist"); } + public boolean isAll() { + return this == ALL; + } + public static boolean isAll(FeedGetOption feedGetOption) { if (feedGetOption == null) { return true; diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index de6773c6..6e169426 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -2,17 +2,18 @@ import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.common.SortCriteria; import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; -import org.websoso.WSSServer.feed.service.FeedImageService; import org.websoso.WSSServer.feed.service.FeedLikeService; import org.websoso.WSSServer.feed.service.FeedQueryService; import org.websoso.WSSServer.novel.service.GenreServiceImpl; @@ -27,7 +28,6 @@ import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.service.CommentServiceImpl; import org.websoso.WSSServer.feed.service.FeedServiceImpl; import org.websoso.WSSServer.library.service.LibraryService; import org.websoso.WSSServer.novel.domain.Novel; @@ -85,17 +85,26 @@ public FeedGetResponse getFeedById(User user, Long feedId) { public FeedsGetResponse getFeeds(User user, Long lastFeedId, int size, FeedGetOption feedGetOption) { // 로그인 유저 여부 확인 - Long userIdOrNull = Optional.ofNullable(user).map(User::getUserId).orElse(null); + Long userIdOrNull = user == null ? null : user.getUserId(); // 사용자의 선호하는 장르 확인 List genres = user == null ? null : genreService.findUserPreferenceGenres(user); - // 피드 불러오기 - Slice feeds = feedServiceImpl.findFeedsByCategoryLabel(lastFeedId, userIdOrNull, PageRequest.of(DEFAULT_PAGE_NUMBER, size), feedGetOption, genres); + // 사용자의 차단 목록 조회 + List blockedUserIds = Optional.ofNullable(user) + .map(User::getUserId) + .map(blockService::findBlockRelationUserIds) + .orElseGet(Collections::emptyList); + + PageRequest pageRequest = PageRequest.of(DEFAULT_PAGE_NUMBER, size); + + // 피드 불러오기 (검색 옵션에 맞게 서비스 로직 호출) + Slice feeds = feedGetOption.isAll() + ? feedServiceImpl.findFeeds(lastFeedId, userIdOrNull, pageRequest, blockedUserIds) + : feedServiceImpl.findRecommendedFeeds(lastFeedId, userIdOrNull, pageRequest, genres, blockedUserIds); // FeedInfo에 필요한 정보들을 JOIN 및 서브 쿼리로 불러오기 - List visibleFeeds = feeds.getContent().stream().toList(); - List feedInfos = feedQueryService.findFeedInfoRows(visibleFeeds, userIdOrNull); + List feedInfos = feedQueryService.findFeedInfoRows(feeds.getContent(), userIdOrNull); return FeedsGetResponse.of(feeds.hasNext(), feedInfos); } diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index da489a84..54fc4e12 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -71,7 +71,7 @@ public ResponseEntity getFeed(@AuthenticationPrincipal User use public ResponseEntity getFeeds(@AuthenticationPrincipal User user, @RequestParam("lastFeedId") Long lastFeedId, @RequestParam("size") int size, - @RequestParam(value = "feedsOption", required = false) FeedGetOption feedGetOption) { + @RequestParam(value = "feedsOption", defaultValue = "ALL") FeedGetOption feedGetOption) { return ResponseEntity .status(OK) .body(feedFindApplication.getFeeds(user, lastFeedId, size, feedGetOption)); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java index 2b428c7c..68ea2b80 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java @@ -16,14 +16,13 @@ List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Boolean isUnVisible, SortCriteria sortCriteria, List genres, Long visitorId, boolean includeEtc); - Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest); + Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List blockedUserIds); - Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres); + Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres, + List blockedUserIds); - Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRequest pageRequest); - - Slice findFeedsByGenres(List genres, boolean includeEtc, Long lastFeedId, Long userId, - PageRequest pageRequest); + Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, + List blockedUserIds); Long countVisibleFeeds(User owner, Boolean isVisible, Boolean isUnVisible, List genres, diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index a9761cf1..866d69f5 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -84,7 +84,7 @@ public List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int } @Override - public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest) { + public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List blockedUserIds) { List feeds = jpaQueryFactory .selectFrom(feed) .join(feed.user).fetchJoin() @@ -92,7 +92,7 @@ public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageReque ltFeedId(lastFeedId), checkHidden(), checkFeedListVisibility(userId), - checkBlocking(userId) + excludeBlockedUsers(blockedUserIds) ) .orderBy(feed.feedId.desc()) .limit(pageRequest.getPageSize() + 1L) @@ -195,7 +195,8 @@ private OrderSpecifier checkSortCriteria(SortCriteria sortCriteria) { } @Override - public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres) { + public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres, + List blockedUserIds) { List feeds = jpaQueryFactory .selectFrom(feed) .join(feed.user).fetchJoin() @@ -206,7 +207,7 @@ public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageReques ltFeedId(lastFeedId), checkPopularFeed(), checkGenresAndNovels(genres, true), - checkBlocking(userId), + excludeBlockedUsers(blockedUserIds), checkHidden(), checkVisible(userId) ) @@ -224,7 +225,8 @@ public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageReques } @Override - public Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRequest pageRequest) { + public Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, + List blockedUserIds) { List feeds = jpaQueryFactory .selectFrom(feed) .join(feed.user).fetchJoin() @@ -232,7 +234,7 @@ public Slice findInterestedNovelFeeds(Long lastFeedId, Long userId, PageRe .join(userNovel).on(novel.eq(userNovel.novel)) .where( ltFeedId(lastFeedId), - checkBlocking(userId), + excludeBlockedUsers(blockedUserIds), checkHidden(), checkInterestedNovels(userId), checkVisible(userId) @@ -267,43 +269,12 @@ private BooleanExpression checkGenresAndNovels(List genres, boolean isNot return null; } - @Override - public Slice findFeedsByGenres(List genres, boolean isNotNovelConnect, Long lastFeedId, Long userId, - PageRequest pageRequest) { - List feeds = jpaQueryFactory - .selectFrom(feed) - .distinct() - .join(feed.user).fetchJoin() - .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) - .leftJoin(novelGenre).on(novel.eq(novelGenre.novel)) - .leftJoin(genre).on(novelGenre.genre.eq(genre)) - .where( - ltFeedId(lastFeedId), - checkGenresAndNovels(genres, isNotNovelConnect), - checkBlocking(userId), - checkHidden() - ) - .orderBy(feed.feedId.desc()) - .limit(pageRequest.getPageSize() + 1) - .fetch(); - - boolean hasNext = feeds.size() > pageRequest.getPageSize(); - if (hasNext) { - feeds.remove(feeds.size() - 1); + private BooleanExpression excludeBlockedUsers(List blockedUserIds) { + if (blockedUserIds == null || blockedUserIds.isEmpty()) { + return null; } - return new SliceImpl<>(feeds, pageRequest, hasNext); - } - private BooleanExpression checkBlocking(Long userId) { - if (userId != null) { - return feed.user.userId.notIn( - JPAExpressions - .select(block.blockedId) - .from(block) - .where(block.blockingId.eq(userId)) // userId는 파라미터 - ); - } - return null; + return feed.user.userId.notIn(blockedUserIds); } private BooleanExpression checkBlockRelation(Long userId) { diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java index bb9181a6..339fa861 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java @@ -17,16 +17,6 @@ public interface FeedRepository extends JpaRepository, FeedCustomRep List findTop10ByNovelIdInOrderByFeedIdDesc(List novelIds); -// @Query(value = "SELECT f FROM Feed f WHERE " -// + "(:lastFeedId = 0 OR f.feedId < :lastFeedId) " -// + "AND f.novelId = :novelId " -// + "AND f.isHidden = false " -// + "AND (f.isPublic = true OR f.user.userId = :userId)" -// + "AND (:userId IS NULL " -// + "OR f.user.userId NOT IN (SELECT b.blockedId FROM Block b WHERE b.blockingId = :userId)) " -// + "ORDER BY f.feedId DESC") -// Slice findFeedsByNovelId(Long novelId, Long lastFeedId, Long userId, PageRequest pageRequest); - @Transactional @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("UPDATE Feed f SET f.user.userId = -1 WHERE f.user.userId = :userId") diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 221d712e..691ed586 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -6,7 +6,6 @@ import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -16,16 +15,11 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.Genre; -import org.websoso.WSSServer.domain.common.FeedGetOption; import org.websoso.WSSServer.domain.common.SortCriteria; import org.websoso.WSSServer.exception.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.FeedImage; -import org.websoso.WSSServer.feed.domain.PopularFeed; -import org.websoso.WSSServer.feed.repository.FeedImageRepository; import org.websoso.WSSServer.feed.repository.FeedRepository; -import org.websoso.WSSServer.feed.repository.PopularFeedRepository; import org.websoso.WSSServer.user.domain.User; @Service @@ -35,8 +29,6 @@ public class FeedServiceImpl { private static final int DEFAULT_PAGE_NUMBER = 0; private final FeedRepository feedRepository; - private final FeedImageRepository feedImageRepository; - private final PopularFeedRepository popularFeedRepository; @Transactional public void createFeed(Feed feed) { @@ -98,54 +90,37 @@ public long getViewableUserFeedCount(User owner, Boolean isVisible, } @Transactional(readOnly = true) - public Slice findFeedsByCategoryLabel(Long lastFeedId, Long userId, PageRequest pageRequest, - FeedGetOption feedGetOption, List preferenceGenres) { - - if (FeedGetOption.isAll(feedGetOption)) { - return feedRepository.findFeeds(lastFeedId, userId, pageRequest); - } else { - // 인기 피드 - Slice recommendedFeeds = feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres); - // 내가 관심 등록한 작품의 피드 - Slice interestedNovelFeeds = feedRepository.findInterestedNovelFeeds(lastFeedId, userId, pageRequest); - int pageSize = pageRequest.getPageSize(); - List combinedFeeds = Stream.concat( - recommendedFeeds.getContent().stream(), - interestedNovelFeeds.getContent().stream()) - .distinct() - .sorted(Comparator.comparing(Feed::getFeedId).reversed()) // feedId 내림차순 정렬 - .toList(); - List resultFeeds = combinedFeeds.stream() - .limit(pageSize) - .toList(); - boolean hasNext = combinedFeeds.size() > pageSize || recommendedFeeds.hasNext() || interestedNovelFeeds.hasNext(); - return new SliceImpl<>(resultFeeds, pageRequest, hasNext); - } - - } - - @Transactional(readOnly = true) - public Integer countByFeedId(Long feedId) { - return feedImageRepository.countByFeedId(feedId); - } - - @Transactional(readOnly = true) - public Optional findThumbnailFeedImageByFeedId(Long feedId) { - return feedImageRepository.findThumbnailFeedImageByFeedId(feedId); - } - - @Transactional(readOnly = true) - public List findPopularFeedsWithUser(Long userId, int size) { - return popularFeedRepository.findPopularFeedsForMember(userId, size); + public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List blockedUserIds) { + return feedRepository.findFeeds(lastFeedId, userId, pageRequest, blockedUserIds); } @Transactional(readOnly = true) - public List findPopularFeedsWithoutUser(int size) { - return popularFeedRepository.findPopularFeedsForGuest(size); + public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List preferenceGenres, List blockedUserIds) { + Slice recommendedFeeds = feedRepository.findRecommendedFeeds(lastFeedId, userId, pageRequest, preferenceGenres, blockedUserIds); + Slice interestedNovelFeeds = feedRepository.findInterestedNovelFeeds(lastFeedId, userId, pageRequest, blockedUserIds); + + int pageSize = pageRequest.getPageSize(); + List combinedFeeds = Stream.concat( + recommendedFeeds.getContent().stream(), + interestedNovelFeeds.getContent().stream() + ) + .distinct() + .sorted(Comparator.comparing(Feed::getFeedId).reversed()) + .toList(); + + List resultFeeds = combinedFeeds.stream() + .limit(pageSize) + .toList(); + + boolean hasNext = combinedFeeds.size() > pageSize + || recommendedFeeds.hasNext() + || interestedNovelFeeds.hasNext(); + + return new SliceImpl<>(resultFeeds, pageRequest, hasNext); } @Transactional(readOnly = true) public List findInterestFeeds(List interestNovelIds) { return feedRepository.findTop10ByNovelIdInOrderByFeedIdDesc(interestNovelIds); } -} \ No newline at end of file +} diff --git a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java index a5df3a79..3e92958c 100644 --- a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java +++ b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java @@ -53,31 +53,31 @@ void setUp() { pageRequest = PageRequest.of(0, SIZE); } - @Test - void ALL_옵션이면_findFeeds를_호출한다() { - feedServiceImpl.findFeedsByCategoryLabel(LAST_FEED_ID, USER_ID, pageRequest, FeedGetOption.ALL, null); +// @Test +// void ALL_옵션이면_findFeeds를_호출한다() { +// feedServiceImpl.findFeedsByCategoryLabel(LAST_FEED_ID, USER_ID, pageRequest, FeedGetOption.ALL, null); +// +// verify(feedRepository).findFeeds(LAST_FEED_ID, USER_ID, pageRequest); +// verify(feedRepository, never()).findRecommendedFeeds(any(), any(), any(), any()); +// verify(feedRepository, never()).findFeedsByGenres(any(), anyBoolean(), any(), any(), any()); +// } - verify(feedRepository).findFeeds(LAST_FEED_ID, USER_ID, pageRequest); - verify(feedRepository, never()).findRecommendedFeeds(any(), any(), any(), any()); - verify(feedRepository, never()).findFeedsByGenres(any(), anyBoolean(), any(), any(), any()); - } - - @Test - void RECOMMENDED_옵션이면_findRecommendedFeeds를_유저_선호장르로_호출한다() { - Genre prefGenre = mock(Genre.class); - List preferenceGenres = List.of(prefGenre); - - feedServiceImpl.findFeedsByCategoryLabel(LAST_FEED_ID, USER_ID, pageRequest, - FeedGetOption.RECOMMENDED, preferenceGenres); - - verify(feedRepository).findRecommendedFeeds(LAST_FEED_ID, USER_ID, pageRequest, preferenceGenres); - verify(feedRepository, never()).findFeeds(any(), any(), any()); - } - - @Test - void feedGetOption이_null이면_ALL로_동작한다() { - feedServiceImpl.findFeedsByCategoryLabel(LAST_FEED_ID, USER_ID, pageRequest, null, null); - - verify(feedRepository).findFeeds(LAST_FEED_ID, USER_ID, pageRequest); - } +// @Test +// void RECOMMENDED_옵션이면_findRecommendedFeeds를_유저_선호장르로_호출한다() { +// Genre prefGenre = mock(Genre.class); +// List preferenceGenres = List.of(prefGenre); +// +// feedServiceImpl.findFeedsByCategoryLabel(LAST_FEED_ID, USER_ID, pageRequest, +// FeedGetOption.RECOMMENDED, preferenceGenres); +// +// verify(feedRepository).findRecommendedFeeds(LAST_FEED_ID, USER_ID, pageRequest, preferenceGenres); +// verify(feedRepository, never()).findFeeds(any(), any(), any()); +// } +// +// @Test +// void feedGetOption이_null이면_ALL로_동작한다() { +// feedServiceImpl.findFeedsByCategoryLabel(LAST_FEED_ID, USER_ID, pageRequest, null, null); +// +// verify(feedRepository).findFeeds(LAST_FEED_ID, USER_ID, pageRequest); +// } } \ No newline at end of file From d3487f5e87f7d6176ed3e38492a681e80a8cb330 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 18:51:08 +0900 Subject: [PATCH 24/31] =?UTF-8?q?[REFACTOR]=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=EC=8B=9C,=20=ED=94=BC=EB=93=9C=EC=9D=98=20Us?= =?UTF-8?q?erId=EB=A5=BC=20-1=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/AccountApplication.java | 9 +++++++- .../FeedManagementApplication.java | 6 +++++- .../FeedUserToUnknownEventListener.java | 21 +++++++++++++++++++ .../feed/repository/FeedRepository.java | 1 - .../feed/service/FeedServiceImpl.java | 5 +++++ .../user/event/WithdrawUserEvent.java | 9 ++++++++ 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java create mode 100644 src/main/java/org/websoso/WSSServer/user/event/WithdrawUserEvent.java diff --git a/src/main/java/org/websoso/WSSServer/application/AccountApplication.java b/src/main/java/org/websoso/WSSServer/application/AccountApplication.java index b93ec72b..340ade12 100644 --- a/src/main/java/org/websoso/WSSServer/application/AccountApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/AccountApplication.java @@ -3,6 +3,7 @@ import static org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessageType.WITHDRAW; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.infrastructure.discord.DiscordMessageClient; @@ -16,6 +17,7 @@ import org.websoso.WSSServer.notification.service.MessageFormatter; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.domain.WithdrawalReason; +import org.websoso.WSSServer.user.event.WithdrawUserEvent; import org.websoso.WSSServer.user.repository.UserRepository; import org.websoso.WSSServer.user.repository.WithdrawalReasonRepository; @@ -35,6 +37,8 @@ public class AccountApplication { private final RefreshTokenRepository refreshTokenRepository; private final KakaoService kakaoService; + private final ApplicationEventPublisher eventPublisher; + public void withdrawUser(User user, WithdrawalRequest withdrawalRequest) { unlinkSocialAccount(user); @@ -47,6 +51,10 @@ public void withdrawUser(User user, WithdrawalRequest withdrawalRequest) { DiscordWebhookMessage.of(messageContent, WITHDRAW)); withdrawalReasonRepository.save(WithdrawalReason.create(withdrawalRequest.reason())); + + eventPublisher.publishEvent( + WithdrawUserEvent.of(user.getUserId()) + ); } private void unlinkSocialAccount(User user) { @@ -59,7 +67,6 @@ private void unlinkSocialAccount(User user) { private void cleanupUserData(Long userId) { refreshTokenRepository.deleteAll(refreshTokenRepository.findAllByUserId(userId)); - feedRepository.updateUserToUnknown(userId); commentRepository.updateUserToUnknown(userId); userRepository.deleteById(userId); } diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java index 6d85f594..07251ca1 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -11,7 +11,6 @@ import org.websoso.WSSServer.dto.feed.FeedImageDeleteEvent; import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; -import org.websoso.WSSServer.feed.domain.Comment; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.service.CommentServiceImpl; @@ -100,6 +99,11 @@ public void delete(User user, Long feedId) { feedService.delete(feed); } + @Transactional + public void updateFeedWriterToUnknown(Long userId) { + feedService.updateWriterToUnknown(userId); + } + // TODO: 이미지 업로드 로직이 여기에서 관리되지 않도록 수정 예정 private List processFeedImages(List images) { List uploadedImageUrls = new ArrayList<>(); diff --git a/src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java b/src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java new file mode 100644 index 00000000..b70b2e49 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java @@ -0,0 +1,21 @@ +package org.websoso.WSSServer.feed.listener; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; +import org.websoso.WSSServer.feed.application.FeedManagementApplication; +import org.websoso.WSSServer.user.event.WithdrawUserEvent; + +@Component +@RequiredArgsConstructor +public class FeedUserToUnknownEventListener { + + private final FeedManagementApplication feedManagementApplication; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(WithdrawUserEvent event) { + feedManagementApplication.updateFeedWriterToUnknown(event.userId()); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java index 339fa861..082e14ce 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java @@ -17,7 +17,6 @@ public interface FeedRepository extends JpaRepository, FeedCustomRep List findTop10ByNovelIdInOrderByFeedIdDesc(List novelIds); - @Transactional @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("UPDATE Feed f SET f.user.userId = -1 WHERE f.user.userId = :userId") void updateUserToUnknown(Long userId); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 691ed586..91152127 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -40,6 +40,11 @@ public void delete(Feed feed) { feedRepository.delete(feed); } + @Transactional + public void updateWriterToUnknown(Long writerId) { + feedRepository.updateUserToUnknown(writerId); + } + @Transactional(readOnly = true) public Feed getFeedOrException(Long feedId) { return feedRepository.findById(feedId) diff --git a/src/main/java/org/websoso/WSSServer/user/event/WithdrawUserEvent.java b/src/main/java/org/websoso/WSSServer/user/event/WithdrawUserEvent.java new file mode 100644 index 00000000..01fe4302 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/user/event/WithdrawUserEvent.java @@ -0,0 +1,9 @@ +package org.websoso.WSSServer.user.event; + +public record WithdrawUserEvent( + Long userId +) { + public static WithdrawUserEvent of(Long userId) { + return new WithdrawUserEvent(userId); + } +} From bbf71002558c5c5c0eb53b4f5d10c723a0594032 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Mon, 15 Jun 2026 19:01:11 +0900 Subject: [PATCH 25/31] =?UTF-8?q?[REFACTOR]=20Application=EC=97=90=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=EB=90=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20Ser?= =?UTF-8?q?vice=EB=A1=9C=20=EC=9D=B4=EC=A0=84=20=EB=B0=8F=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20Repository?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeedManagementApplication.java | 39 +--------- .../repository/FeedCustomRepositoryImpl.java | 16 +--- .../repository/FeedImageCustomRepository.java | 8 -- .../feed/repository/FeedImageRepository.java | 11 --- .../feed/service/FeedImageService.java | 74 ++++++++++--------- .../feed/service/FeedServiceImplTest.java | 4 - 6 files changed, 43 insertions(+), 109 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedImageCustomRepository.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java index 07251ca1..3fb260a2 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -4,7 +4,6 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; import org.websoso.WSSServer.dto.feed.FeedCreateRequest; import org.websoso.WSSServer.dto.feed.FeedCreateResponse; import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; @@ -14,10 +13,10 @@ import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.service.CommentServiceImpl; +import org.websoso.WSSServer.feed.service.FeedImageService; import org.websoso.WSSServer.feed.service.FeedLikeService; import org.websoso.WSSServer.feed.service.FeedServiceImpl; import org.websoso.WSSServer.novel.service.NovelServiceImpl; -import org.websoso.WSSServer.service.ImageClient; import org.websoso.WSSServer.user.domain.User; import java.util.ArrayList; @@ -31,7 +30,7 @@ public class FeedManagementApplication { private final FeedLikeService feedLikeService; private final CommentServiceImpl commentService; private final NovelServiceImpl novelService; - private final ImageClient imageUploader; + private final FeedImageService feedImageService; private final ApplicationEventPublisher eventPublisher; @@ -44,7 +43,7 @@ public FeedCreateResponse create(User user, FeedCreateRequest request, FeedImage } // 이미지 업로드 - List feedImages = processFeedImages(imagesRequest.images()); + List feedImages = feedImageService.processFeedImages(imagesRequest.images()); // 피드 객체 생성 Feed feed = Feed.create(request.feedContent(), request.novelId(), request.isSpoiler(), request.isPublic(), user, feedImages); @@ -71,7 +70,7 @@ public FeedCreateResponse update(User user, Long feedId, FeedUpdateRequest reque } // 이미지 업로드 - List feedImages = processFeedImages(imagesRequest.images()); + List feedImages = feedImageService.processFeedImages(imagesRequest.images()); // 피드 업데이트 feed.updateFeed(request.feedContent(), request.isSpoiler(), request.isPublic(), request.novelId(), feedImages); @@ -104,34 +103,4 @@ public void updateFeedWriterToUnknown(Long userId) { feedService.updateWriterToUnknown(userId); } - // TODO: 이미지 업로드 로직이 여기에서 관리되지 않도록 수정 예정 - private List processFeedImages(List images) { - List uploadedImageUrls = new ArrayList<>(); - - if (images != null && !images.isEmpty()) { - try { - for (MultipartFile image : images) { - String imageUrl = imageUploader.uploadFeedImage(image); - uploadedImageUrls.add(imageUrl); - } - } catch (Exception e) { - if (!uploadedImageUrls.isEmpty()) { - imageUploader.deleteImages(uploadedImageUrls); - } - - throw e; - } - } - - List feedImages = new ArrayList<>(); - if (!uploadedImageUrls.isEmpty()) { - feedImages.add(FeedImage.createThumbnail(uploadedImageUrls.get(0))); - for (int i = 1; i < uploadedImageUrls.size(); i++) { - feedImages.add(FeedImage.createCommon(uploadedImageUrls.get(i), i)); - } - } - - return feedImages; - } - } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index 866d69f5..ee4804c4 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -33,10 +33,9 @@ @Repository @RequiredArgsConstructor -public class FeedCustomRepositoryImpl implements FeedCustomRepository, FeedImageCustomRepository { +public class FeedCustomRepositoryImpl implements FeedCustomRepository { private static final long NO_CURSOR = 0L; - private static final int THUMBNAIL_IMAGE_COUNT = 1; private static final long POPULAR_FEED_LIKE_COUNT = 5; private final JPAQueryFactory jpaQueryFactory; @@ -107,19 +106,6 @@ public Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageReque return new SliceImpl<>(feeds, pageRequest, hasNext); } - @Override - public Optional findThumbnailFeedImageByFeedId(long feedId) { - return Optional.ofNullable(jpaQueryFactory - .selectFrom(feedImage) - .where( - feedImage.feedId.eq(feedId), - feedImage.feedImageType.eq(FeedImageType.FEED_THUMBNAIL) - ) - .orderBy(feedImage.sequence.asc()) - .limit(THUMBNAIL_IMAGE_COUNT) - .fetchOne()); - } - @Override public Long countVisibleFeeds(User owner, Boolean isVisible, Boolean isUnVisible, List genres, diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageCustomRepository.java deleted file mode 100644 index e8201d0f..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageCustomRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.Optional; -import org.websoso.WSSServer.feed.domain.FeedImage; - -public interface FeedImageCustomRepository { - Optional findThumbnailFeedImageByFeedId(long feedId); -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java deleted file mode 100644 index 3004137e..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedImageRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; -import org.websoso.WSSServer.feed.domain.FeedImage; - -public interface FeedImageRepository extends JpaRepository, FeedImageCustomRepository { - Integer countByFeedId(Long feedId); - - List findByFeedIdIn(List feedIds); -} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java index 534d0b12..a7e111bb 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java @@ -1,59 +1,61 @@ package org.websoso.WSSServer.feed.service; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; +import org.springframework.web.multipart.MultipartFile; import org.websoso.WSSServer.feed.domain.FeedImage; -import org.websoso.WSSServer.feed.repository.FeedImageRepository; +import org.websoso.WSSServer.service.ImageClient; @Service @RequiredArgsConstructor public class FeedImageService { - private final FeedImageRepository feedImageRepository; + private final ImageClient imageUploader; - @Transactional(readOnly = true) - public String getThumbnailUrl(Feed feed) { - Optional thumbnailImage = feedImageRepository.findThumbnailFeedImageByFeedId(feed.getFeedId()); - return thumbnailImage.map(FeedImage::getUrl).orElse(null); - } + public List processFeedImages(List images) { + List uploadedImageUrls = uploadFeedImages(images); - @Transactional(readOnly = true) - public Integer getImageCount(Feed feed) { - return feedImageRepository.countByFeedId(feed.getFeedId()); + return createFeedImages(uploadedImageUrls); } - @Transactional(readOnly = true) - public Map getThumbnailUrlMap(List feedIds) { - if (feedIds.isEmpty()) { - return Collections.emptyMap(); + private List uploadFeedImages(List images) { + List uploadedImageUrls = new ArrayList<>(); + + if (images == null || images.isEmpty()) { + return uploadedImageUrls; + } + + try { + for (MultipartFile image : images) { + String imageUrl = imageUploader.uploadFeedImage(image); + uploadedImageUrls.add(imageUrl); + } + } catch (Exception e) { + if (!uploadedImageUrls.isEmpty()) { + imageUploader.deleteImages(uploadedImageUrls); + } + + throw e; } - return feedImageRepository.findByFeedIdIn(feedIds).stream() - .filter(FeedImage::isThumbnail) - .collect(Collectors.toMap( - FeedImage::getFeedId, - FeedImage::getUrl, - (first, second) -> first - )); + return uploadedImageUrls; } - @Transactional(readOnly = true) - public Map getImageCountMap(List feedIds) { - if (feedIds.isEmpty()) { - return Collections.emptyMap(); + private List createFeedImages(List uploadedImageUrls) { + List feedImages = new ArrayList<>(); + + if (uploadedImageUrls.isEmpty()) { + return feedImages; + } + + feedImages.add(FeedImage.createThumbnail(uploadedImageUrls.get(0))); + for (int i = 1; i < uploadedImageUrls.size(); i++) { + feedImages.add(FeedImage.createCommon(uploadedImageUrls.get(i), i)); } - return feedImageRepository.findByFeedIdIn(feedIds).stream() - .collect(Collectors.groupingBy( - FeedImage::getFeedId, - Collectors.collectingAndThen(Collectors.counting(), Long::intValue) - )); + return feedImages; } + } diff --git a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java index 3e92958c..ab1a30ca 100644 --- a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java +++ b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java @@ -22,8 +22,6 @@ import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.common.FeedGetOption; import org.websoso.WSSServer.exception.exception.CustomGenreException; -import org.websoso.WSSServer.feed.repository.FeedImageCustomRepository; -import org.websoso.WSSServer.feed.repository.FeedImageRepository; import org.websoso.WSSServer.feed.repository.FeedRepository; import org.websoso.WSSServer.feed.repository.PopularFeedRepository; import org.websoso.WSSServer.repository.GenreRepository; @@ -34,8 +32,6 @@ class FeedServiceImplTest { @Mock FeedRepository feedRepository; @Mock - FeedImageRepository feedImageRepository; - @Mock PopularFeedRepository popularFeedRepository; @Mock GenreRepository genreRepository; From 5a7001a59a0df3aaa7fb8797f9e9362968541ab8 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 16 Jun 2026 15:48:09 +0900 Subject: [PATCH 26/31] =?UTF-8?q?[RENAME]=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EA=B4=80=EC=8B=AC=EA=B8=80=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/feed/InterestFeedGetResponse.java | 58 ------------------- .../dto/feed/InterestFeedsGetResponse.java | 14 ----- .../feed/application/FeedFindApplication.java | 36 ------------ .../feed/controller/FeedController.java | 9 --- .../feed/repository/FeedRepository.java | 2 - .../feed/service/FeedServiceImpl.java | 4 -- 6 files changed, 123 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedGetResponse.java delete mode 100644 src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedsGetResponse.java diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedGetResponse.java deleted file mode 100644 index 7fcf5e3a..00000000 --- a/src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedGetResponse.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.websoso.WSSServer.dto.feed; - -import org.websoso.WSSServer.user.domain.AvatarProfile; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.novel.domain.Novel; -import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.library.domain.UserNovel; - -public record InterestFeedGetResponse( - Long novelId, - String novelTitle, - String novelImage, - Float novelRating, - Long novelRatingCount, - Boolean isPublic, - String nickname, - String avatarImage, - String feedContent -) { - - public static InterestFeedGetResponse of(Novel novel, User user, Feed feed, AvatarProfile avatarProfile) { - Long novelRatingCount = getNovelRatingCount(novel); - Float novelRating = getNovelRating(novel, novelRatingCount); - - return new InterestFeedGetResponse( - novel.getNovelId(), - novel.getTitle(), - novel.getNovelImage(), - novelRating, - novelRatingCount, - feed.getIsPublic(), - user.getNickname(), - avatarProfile.getAvatarProfileImage(), - feed.getFeedContent() - ); - } - - private static Long getNovelRatingCount(Novel novel) { - return novel.getUserNovels() - .stream() - .filter(userNovel -> userNovel.getUserNovelRating() != 0.0f) - .count(); - } - - private static Float getNovelRatingSum(Novel novel) { - return (float) novel.getUserNovels() - .stream() - .filter(userNovel -> userNovel.getUserNovelRating() != 0.0f) - .mapToDouble(UserNovel::getUserNovelRating) - .sum(); - } - - private static float getNovelRating(Novel novel, Long novelRatingCount) { - return novelRatingCount > 0 - ? getNovelRatingSum(novel) / novelRatingCount - : 0.0f; - } -} diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedsGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedsGetResponse.java deleted file mode 100644 index d1c85d7b..00000000 --- a/src/main/java/org/websoso/WSSServer/dto/feed/InterestFeedsGetResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.websoso.WSSServer.dto.feed; - -import java.util.List; - -public record InterestFeedsGetResponse( - List recommendFeeds, - - String message -) { - - public static InterestFeedsGetResponse of(List interestFeedGetResponses, String message) { - return new InterestFeedsGetResponse(interestFeedGetResponses, message); - } -} diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 6e169426..1812380c 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -2,12 +2,10 @@ import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.common.SortCriteria; @@ -23,8 +21,6 @@ import org.websoso.WSSServer.dto.feed.FeedGetResponse; import org.websoso.WSSServer.dto.feed.FeedInfo; import org.websoso.WSSServer.dto.feed.FeedsGetResponse; -import org.websoso.WSSServer.dto.feed.InterestFeedGetResponse; -import org.websoso.WSSServer.dto.feed.InterestFeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.feed.domain.Feed; @@ -121,38 +117,6 @@ public PopularFeedsGetResponse getPopularFeeds(User user, int size) { return PopularFeedsGetResponse.of(feedQueryService.findPopularFeedRows(blockedUserIds, size)); } - @Transactional(readOnly = true) - public InterestFeedsGetResponse getInterestFeeds(User user) { - List interestNovels = libraryService.getInterestNovels(user); - - if (interestNovels.isEmpty()) { - return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_INTEREST_NOVELS"); - } - - Map novelMap = interestNovels.stream() - .collect(Collectors.toMap(Novel::getNovelId, novel -> novel)); - List interestNovelIds = new ArrayList<>(novelMap.keySet()); - - List interestFeeds = feedServiceImpl.findInterestFeeds(interestNovelIds); - - if (interestFeeds.isEmpty()) { - return InterestFeedsGetResponse.of(Collections.emptyList(), "NO_ASSOCIATED_FEEDS"); - } - - Set avatarProfileIds = interestFeeds.stream().map(feed -> feed.getUser().getAvatarProfileId()) - .collect(Collectors.toSet()); - Map avatarMap = avatarService.findAllByIds(new ArrayList<>(avatarProfileIds)).stream() - .collect(Collectors.toMap(AvatarProfile::getAvatarProfileId, avatar -> avatar)); - - List interestFeedGetResponses = interestFeeds.stream() - .filter(feed -> feed.isVisibleTo(user.getUserId())).map(feed -> { - Novel novel = novelMap.get(feed.getNovelId()); - AvatarProfile avatar = avatarMap.get(feed.getUser().getAvatarProfileId()); - return InterestFeedGetResponse.of(novel, feed.getUser(), feed, avatar); - }).toList(); - return InterestFeedsGetResponse.of(interestFeedGetResponses, ""); - } - @Transactional(readOnly = true) public NovelGetResponseFeedTab getFeedsByNovel(User user, Long novelId, Long lastFeedId, int size) { diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 54fc4e12..75b23d6d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -31,7 +31,6 @@ import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; import org.websoso.WSSServer.dto.feed.FeedsGetResponse; -import org.websoso.WSSServer.dto.feed.InterestFeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.feed.application.FeedLikeApplication; import org.websoso.WSSServer.feed.application.FeedManagementApplication; @@ -126,14 +125,6 @@ public ResponseEntity getPopularFeeds(@AuthenticationPr .body(feedFindApplication.getPopularFeeds(user, size)); } - @GetMapping("/feeds/interest") - @PreAuthorize("isAuthenticated()") - public ResponseEntity getInterestFeeds(@AuthenticationPrincipal User user) { - return ResponseEntity - .status(OK) - .body(feedFindApplication.getInterestFeeds(user)); - } - @GetMapping("/novels/{novelId}/feeds") public ResponseEntity getFeedsByNovel(@AuthenticationPrincipal User user, @PathVariable Long novelId, diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java index 082e14ce..971f26a2 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java @@ -15,8 +15,6 @@ public interface FeedRepository extends JpaRepository, FeedCustomRep Integer countByNovelId(Long novelId); - List findTop10ByNovelIdInOrderByFeedIdDesc(List novelIds); - @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("UPDATE Feed f SET f.user.userId = -1 WHERE f.user.userId = :userId") void updateUserToUnknown(Long userId); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 91152127..b4e27b27 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -124,8 +124,4 @@ public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageReques return new SliceImpl<>(resultFeeds, pageRequest, hasNext); } - @Transactional(readOnly = true) - public List findInterestFeeds(List interestNovelIds) { - return feedRepository.findTop10ByNovelIdInOrderByFeedIdDesc(interestNovelIds); - } } From d9328ed4f6b6807ad1942c2fdb2592a6ed454aa0 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 16 Jun 2026 16:48:44 +0900 Subject: [PATCH 27/31] =?UTF-8?q?[REAFCTOR]=20List=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Slice=20=EB=B0=98=ED=99=98=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=88=9C=20=EC=A0=95=EB=A0=AC=EC=8B=9C=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EC=9E=98=EB=AA=BB=20=EB=B0=98=ED=99=98=EB=90=98?= =?UTF-8?q?=EB=8D=98=20=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/application/FeedFindApplication.java | 10 +-- .../feed/repository/FeedCustomRepository.java | 6 +- .../repository/FeedCustomRepositoryImpl.java | 67 +++++++++++++++++-- .../feed/service/FeedServiceImpl.java | 6 +- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 1812380c..b44c8e56 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -149,17 +149,13 @@ public UserFeedsGetResponse getUserFeeds(User visitor, Long ownerId, Long lastFe : genreNames.stream().filter(name -> !name.equals("etc")).collect(Collectors.toList()); List genres = genreService.getGenresOrException(filteredGenreNames); - List visibleFeeds = feedServiceImpl.getViewableUserFeed(owner, lastFeedId, size, isVisible, - isUnVisible, sortCriteria, genres, visitorId, includeEtc); + Slice visibleFeeds = feedServiceImpl.getViewableUserFeed(owner, lastFeedId, size, isVisible, isUnVisible, sortCriteria, genres, visitorId, includeEtc); - List userFeedGetResponseList = feedQueryService.findUserFeedRows(visibleFeeds, visitorId); - - // TODO Slice의 hasNext()로 판단하도록 수정 - Boolean isLoadable = visibleFeeds.size() == size; + List userFeedGetResponseList = feedQueryService.findUserFeedRows(visibleFeeds.getContent(), visitorId); long feedsCount = feedServiceImpl.getViewableUserFeedCount(owner, isVisible, isUnVisible, genres, visitorId, includeEtc); - return UserFeedsGetResponse.of(isLoadable, feedsCount, userFeedGetResponseList); + return UserFeedsGetResponse.of(visibleFeeds.hasNext(), feedsCount, userFeedGetResponseList); } diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java index 68ea2b80..1defeec4 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java @@ -12,9 +12,9 @@ public interface FeedCustomRepository { List findPopularFeedsByNovelIds(List novelIds); - List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Boolean isVisible, - Boolean isUnVisible, SortCriteria sortCriteria, List genres, - Long visitorId, boolean includeEtc); + Slice findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Boolean isVisible, + Boolean isUnVisible, SortCriteria sortCriteria, List genres, + Long visitorId, boolean includeEtc); Slice findFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List blockedUserIds); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index ee4804c4..4ff4b09d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -25,6 +25,7 @@ import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Repository; import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.domain.QFeed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.user.domain.User; @@ -58,10 +59,12 @@ public List findPopularFeedsByNovelIds(List novelIds) { } @Override - public List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Boolean isVisible, - Boolean isUnVisible, SortCriteria sortCriteria, - List genres, Long visitorId, boolean isNotNovelConnect) { - return jpaQueryFactory + public Slice findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int size, Boolean isVisible, + Boolean isUnVisible, SortCriteria sortCriteria, + List genres, Long visitorId, boolean isNotNovelConnect) { + PageRequest pageRequest = PageRequest.of(0, size); + + List feeds = jpaQueryFactory .selectFrom(feed) .distinct() .join(feed.user).fetchJoin() @@ -70,16 +73,24 @@ public List findFeedsByNoOffsetPagination(User owner, Long lastFeedId, int .leftJoin(genre).on(novelGenre.genre.eq(genre)) .where( feed.user.eq(owner), - ltFeedId(lastFeedId), + userFeedCursor(lastFeedId, sortCriteria), checkVisible(visitorId), checkPublic(isVisible, isUnVisible), checkGenresAndNovels(genres, isNotNovelConnect) ) .orderBy( checkSortCriteria(sortCriteria), - feed.feedId.desc()) - .limit(size) + checkFeedIdSortCriteria(sortCriteria)) + .limit(pageRequest.getPageSize() + 1L) .fetch(); + + boolean hasNext = feeds.size() > pageRequest.getPageSize(); + + if (hasNext) { + feeds.remove(feeds.size() - 1); + } + + return new SliceImpl<>(feeds, pageRequest, hasNext); } @Override @@ -157,6 +168,41 @@ private BooleanExpression ltFeedId(Long lastFeedId) { return feed.feedId.lt(lastFeedId); } + private BooleanExpression userFeedCursor(Long lastFeedId, SortCriteria sortCriteria) { + if (lastFeedId == NO_CURSOR) { + return null; + } + + QFeed cursorFeed = new QFeed("cursorFeed"); + + BooleanExpression sameCreatedDate = feed.createdDate.eq( + JPAExpressions + .select(cursorFeed.createdDate) + .from(cursorFeed) + .where(cursorFeed.feedId.eq(lastFeedId)) + ); + + if (sortCriteria != null && sortCriteria.isOld()) { + BooleanExpression nextByFeedId = sameCreatedDate.and(feed.feedId.gt(lastFeedId)); + + return feed.createdDate.gt( + JPAExpressions + .select(cursorFeed.createdDate) + .from(cursorFeed) + .where(cursorFeed.feedId.eq(lastFeedId)) + ).or(nextByFeedId); + } + + BooleanExpression nextByFeedId = sameCreatedDate.and(feed.feedId.lt(lastFeedId)); + + return feed.createdDate.lt( + JPAExpressions + .select(cursorFeed.createdDate) + .from(cursorFeed) + .where(cursorFeed.feedId.eq(lastFeedId)) + ).or(nextByFeedId); + } + private BooleanExpression checkPublic(Boolean isVisible, Boolean isUnVisible) { if (Boolean.TRUE.equals(isVisible) && Boolean.TRUE.equals(isUnVisible)) { return null; @@ -180,6 +226,13 @@ private OrderSpecifier checkSortCriteria(SortCriteria sortCriteria) { return new OrderSpecifier<>(Order.DESC, feed.createdDate); } + private OrderSpecifier checkFeedIdSortCriteria(SortCriteria sortCriteria) { + if (sortCriteria != null && sortCriteria.equals(SortCriteria.OLD)) { + return new OrderSpecifier<>(Order.ASC, feed.feedId); + } + return new OrderSpecifier<>(Order.DESC, feed.feedId); + } + @Override public Slice findRecommendedFeeds(Long lastFeedId, Long userId, PageRequest pageRequest, List genres, List blockedUserIds) { diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index b4e27b27..06bbf47a 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -80,9 +80,9 @@ public Slice findFeedsByNovel(Long userIdOrNull, Long novelId, Long lastFe } @Transactional(readOnly = true) - public List getViewableUserFeed(User owner, Long lastFeedId, int size, Boolean isVisible, - Boolean isUnVisible, SortCriteria sortCriteria, - List genres, Long visitorId, boolean isNotNovelConnect) { + public Slice getViewableUserFeed(User owner, Long lastFeedId, int size, Boolean isVisible, + Boolean isUnVisible, SortCriteria sortCriteria, + List genres, Long visitorId, boolean isNotNovelConnect) { return feedRepository.findFeedsByNoOffsetPagination(owner, lastFeedId, size, isVisible, isUnVisible, sortCriteria, genres, visitorId, isNotNovelConnect); } From 613d31356826b2df87084e9dac8639e5e1c5f752 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Tue, 16 Jun 2026 17:24:10 +0900 Subject: [PATCH 28/31] =?UTF-8?q?[REFACTOR]=20Feed=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20Feed=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=20=EB=B0=8F=20getOwnedFeedOrException=20?= =?UTF-8?q?=EC=9D=B8=EC=9E=90=20=EC=9E=98=EB=AA=BB=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EB=90=9C=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ReportApplication.java | 6 +- .../auth/validator/FeedAccessValidator.java | 8 +- .../validator/FeedAuthorizationValidator.java | 4 +- .../dto/feed/UserFeedGetResponse.java | 142 ------------------ .../dto/novel/NovelGetResponseFeedTab.java | 2 +- .../feed/application/FeedFindApplication.java | 10 +- .../FeedManagementApplication.java | 16 +- .../feed/controller/FeedController.java | 16 +- .../controller/dto}/FeedCreateRequest.java | 2 +- .../controller/dto}/FeedCreateResponse.java | 2 +- .../controller/dto}/FeedGetResponse.java | 2 +- .../dto}/FeedImageCreateRequest.java | 2 +- .../dto}/FeedImageUpdateRequest.java | 2 +- .../controller/dto}/FeedInfo.java | 2 +- .../controller/dto}/FeedUpdateRequest.java | 2 +- .../controller/dto}/FeedsGetResponse.java | 2 +- .../controller/dto/UserFeedGetResponse.java | 24 +++ .../controller/dto}/UserFeedsGetResponse.java | 2 +- .../event}/FeedImageDeleteEvent.java | 2 +- .../exception}/CustomFeedError.java | 2 +- .../exception/CustomFeedException.java | 3 +- .../FeedCategoryCustomRepository.java | 14 -- .../FeedCategoryCustomRepositoryImpl.java | 94 ------------ .../repository/FeedCategoryRepository.java | 29 ---- .../repository/FeedCustomRepositoryImpl.java | 2 + .../feed/repository/FeedQueryRepository.java | 4 + .../repository/FeedQueryRepositoryImpl.java | 3 + .../{ => projection}/FeedInfoRow.java | 4 +- .../{ => projection}/PopularFeedInfoRow.java | 2 +- .../{ => projection}/UserFeedInfoRow.java | 4 +- .../feed/service/FeedQueryService.java | 10 +- .../feed/service/FeedServiceImpl.java | 6 +- .../WSSServer/service/ImageClient.java | 2 +- .../user/controller/UserController.java | 1 - .../WSSServer/user/service/BlockService.java | 5 +- 35 files changed, 92 insertions(+), 341 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedCreateRequest.java (92%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedCreateResponse.java (91%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedGetResponse.java (98%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedImageCreateRequest.java (85%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedImageUpdateRequest.java (85%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedInfo.java (98%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedUpdateRequest.java (92%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/FeedsGetResponse.java (85%) create mode 100644 src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/controller/dto}/UserFeedsGetResponse.java (89%) rename src/main/java/org/websoso/WSSServer/{dto/feed => feed/event}/FeedImageDeleteEvent.java (67%) rename src/main/java/org/websoso/WSSServer/{exception/error => feed/exception}/CustomFeedError.java (96%) rename src/main/java/org/websoso/WSSServer/{exception => feed}/exception/CustomFeedException.java (72%) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepository.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepositoryImpl.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryRepository.java rename src/main/java/org/websoso/WSSServer/feed/repository/{ => projection}/FeedInfoRow.java (93%) rename src/main/java/org/websoso/WSSServer/feed/repository/{ => projection}/PopularFeedInfoRow.java (93%) rename src/main/java/org/websoso/WSSServer/feed/repository/{ => projection}/UserFeedInfoRow.java (92%) diff --git a/src/main/java/org/websoso/WSSServer/application/ReportApplication.java b/src/main/java/org/websoso/WSSServer/application/ReportApplication.java index 26adf77b..b1a7336d 100644 --- a/src/main/java/org/websoso/WSSServer/application/ReportApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/ReportApplication.java @@ -4,8 +4,8 @@ import static org.websoso.WSSServer.domain.common.ReportedType.IMPERTINENCE; import static org.websoso.WSSServer.domain.common.ReportedType.SPOILER; import static org.websoso.WSSServer.exception.error.CustomCommentError.ALREADY_REPORTED_COMMENT; -import static org.websoso.WSSServer.exception.error.CustomFeedError.ALREADY_REPORTED_FEED; -import static org.websoso.WSSServer.exception.error.CustomFeedError.SELF_REPORT_NOT_ALLOWED; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.ALREADY_REPORTED_FEED; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.SELF_REPORT_NOT_ALLOWED; import static org.websoso.WSSServer.exception.error.CustomUserError.USER_NOT_FOUND; import lombok.RequiredArgsConstructor; @@ -16,7 +16,7 @@ import org.websoso.WSSServer.domain.common.ReportedType; import org.websoso.WSSServer.exception.error.CustomCommentError; import org.websoso.WSSServer.exception.exception.CustomCommentException; -import org.websoso.WSSServer.exception.exception.CustomFeedException; +import org.websoso.WSSServer.feed.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.domain.Comment; import org.websoso.WSSServer.feed.domain.Feed; diff --git a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java index 0511966e..15a508bd 100644 --- a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java +++ b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java @@ -1,14 +1,14 @@ package org.websoso.WSSServer.auth.validator; -import static org.websoso.WSSServer.exception.error.CustomFeedError.BLOCKED_USER_ACCESS; -import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.HIDDEN_FEED_ACCESS; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.BLOCKED_USER_ACCESS; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.HIDDEN_FEED_ACCESS; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.exception.exception.CustomFeedException; +import org.websoso.WSSServer.feed.exception.CustomFeedException; import org.websoso.WSSServer.feed.repository.FeedRepository; import org.websoso.WSSServer.user.service.BlockService; diff --git a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java index 30872df5..f3da7f18 100644 --- a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java +++ b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java @@ -1,13 +1,13 @@ package org.websoso.WSSServer.auth.validator; -import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.FEED_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.exception.exception.CustomFeedException; +import org.websoso.WSSServer.feed.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.repository.FeedRepository; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java deleted file mode 100644 index 3d906bbd..00000000 --- a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedGetResponse.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.websoso.WSSServer.dto.feed; - -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.novel.domain.Novel; -import org.websoso.WSSServer.library.domain.UserNovel; -import org.websoso.WSSServer.util.TimeFormatUtil; - -public record UserFeedGetResponse( - Long feedId, - String feedContent, - String createdDate, - Boolean isSpoiler, - Boolean isModified, - Boolean isLiked, - Integer likeCount, - Integer commentCount, - Long novelId, - String title, - Float novelRating, - Long novelRatingCount, - Boolean isPublic, - String genre, - Float userNovelRating, - String thumbnailUrl, - Integer imageCount, - Float feedWriterNovelRating -) { - - public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, String thumbnailUrl, - Integer imageCount) { - return of( - feed, - novel, - visitorId, - thumbnailUrl, - imageCount, - visitorId != null && feed.getLikes().stream() - .anyMatch(like -> like.getUserId().equals(visitorId)), - feed.getLikes().size(), - feed.getComments().size() - ); - } - - public static UserFeedGetResponse of(Feed feed, Novel novel, Long visitorId, String thumbnailUrl, - Integer imageCount, Boolean isLiked, Integer likeCount, - Integer commentCount) { - boolean isModified = !feed.getCreatedDate().equals(feed.getModifiedDate()); - Long novelRatingCount = getNovelRatingCount(novel); - Float novelRating = getNovelRating(novel, novelRatingCount); - String genreName = getNovelGenreName(novel); - Float userNovelRating = getUserNovelRating(novel, visitorId); - Float feedWriterNovelRating = getFeedWriterNovelRating(novel, feed.getUser().getUserId()); - - return new UserFeedGetResponse( - feed.getFeedId(), - feed.getFeedContent(), - TimeFormatUtil.formatRelativeDateTime(feed.getCreatedDate()), - feed.getIsSpoiler(), - isModified, - isLiked, - likeCount, - commentCount, - novel == null ? - null : novel.getNovelId(), - novel == null ? - null : novel.getTitle(), - novelRating, - novelRatingCount, - feed.getIsPublic(), - genreName, - userNovelRating, - thumbnailUrl, - imageCount, - feedWriterNovelRating - ); - } - - private static Long getNovelRatingCount(Novel novel) { - if (novel == null) { - return null; - } - return novel.getUserNovels() - .stream() - .filter(userNovel -> userNovel.getUserNovelRating() != 0.0f) - .count(); - } - - private static Float getNovelRatingSum(Novel novel) { - if (novel == null) { - return null; - } - - return (float) novel.getUserNovels() - .stream() - .filter(userNovel -> userNovel.getUserNovelRating() != 0.0f) - .mapToDouble(UserNovel::getUserNovelRating) - .sum(); - } - - private static Float getNovelRating(Novel novel, Long novelRatingCount) { - if (novel == null) { - return null; - } - return novelRatingCount > 0 - ? Math.round(getNovelRatingSum(novel) / novelRatingCount * 10) / 10.0f - : 0.0f; - } - - private static String getNovelGenreName(Novel novel) { - if (novel == null) { - return null; - } - - return novel.getNovelGenres().get(0).getGenre().getGenreName(); - } - - private static Float getUserNovelRating(Novel novel, Long visitorId) { - if (novel == null) { - return null; - } - - return novel.getUserNovels() - .stream() - .filter(userNovel -> userNovel.getUser().getUserId().equals(visitorId)) - .findFirst() - .map(UserNovel::getUserNovelRating) - .orElse(null); - } - - private static Float getFeedWriterNovelRating(Novel novel, Long feedWriterId) { - if (novel == null) { - return null; - } - - return novel.getUserNovels() - .stream() - .filter(userNovel -> userNovel.getUser().getUserId().equals(feedWriterId)) - .findFirst() - .map(UserNovel::getUserNovelRating) - .orElse(null); - } -} diff --git a/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java b/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java index 77b72fe7..17347025 100644 --- a/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java +++ b/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java @@ -1,7 +1,7 @@ package org.websoso.WSSServer.dto.novel; import java.util.List; -import org.websoso.WSSServer.dto.feed.FeedInfo; +import org.websoso.WSSServer.feed.controller.dto.FeedInfo; public record NovelGetResponseFeedTab( Boolean isLoadable, diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index b44c8e56..05a390e3 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -9,8 +9,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; -import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; +import org.websoso.WSSServer.feed.controller.dto.UserFeedGetResponse; +import org.websoso.WSSServer.feed.controller.dto.UserFeedsGetResponse; import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; import org.websoso.WSSServer.feed.service.FeedLikeService; import org.websoso.WSSServer.feed.service.FeedQueryService; @@ -18,9 +18,9 @@ import org.websoso.WSSServer.user.domain.AvatarProfile; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.common.FeedGetOption; -import org.websoso.WSSServer.dto.feed.FeedGetResponse; -import org.websoso.WSSServer.dto.feed.FeedInfo; -import org.websoso.WSSServer.dto.feed.FeedsGetResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedGetResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedInfo; +import org.websoso.WSSServer.feed.controller.dto.FeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.feed.domain.Feed; diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java index 3fb260a2..072e013e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java @@ -4,12 +4,12 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.dto.feed.FeedCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedCreateResponse; -import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedImageDeleteEvent; -import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; -import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedCreateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedCreateResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedImageCreateRequest; +import org.websoso.WSSServer.feed.event.FeedImageDeleteEvent; +import org.websoso.WSSServer.feed.controller.dto.FeedImageUpdateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedUpdateRequest; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.domain.FeedImage; import org.websoso.WSSServer.feed.service.CommentServiceImpl; @@ -59,7 +59,7 @@ public FeedCreateResponse create(User user, FeedCreateRequest request, FeedImage public FeedCreateResponse update(User user, Long feedId, FeedUpdateRequest request, FeedImageUpdateRequest imagesRequest) { // 사용자가 작성한 피드인지 확인 - Feed feed = feedService.getOwnedFeedOrException(user.getUserId(), feedId); + Feed feed = feedService.getOwnedFeedOrException(feedId, user.getUserId()); // 기존 이미지를 임시 저장 List oldImages = new ArrayList<>(feed.getImages()); @@ -86,7 +86,7 @@ public FeedCreateResponse update(User user, Long feedId, FeedUpdateRequest reque public void delete(User user, Long feedId) { // 사용자가 작성한 피드인지 확인 - Feed feed = feedService.getOwnedFeedOrException(user.getUserId(), feedId); + Feed feed = feedService.getOwnedFeedOrException(feedId, user.getUserId()); // 댓글 삭제 (댓글 / 신고 내역) commentService.deleteByFeedId(feed.getFeedId()); diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java index 75b23d6d..a13f9692 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java @@ -20,17 +20,17 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; +import org.websoso.WSSServer.feed.controller.dto.UserFeedsGetResponse; import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; import org.websoso.WSSServer.feed.application.FeedFindApplication; import org.websoso.WSSServer.domain.common.FeedGetOption; -import org.websoso.WSSServer.dto.feed.FeedCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedCreateResponse; -import org.websoso.WSSServer.dto.feed.FeedGetResponse; -import org.websoso.WSSServer.dto.feed.FeedImageCreateRequest; -import org.websoso.WSSServer.dto.feed.FeedImageUpdateRequest; -import org.websoso.WSSServer.dto.feed.FeedUpdateRequest; -import org.websoso.WSSServer.dto.feed.FeedsGetResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedCreateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedCreateResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedGetResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedImageCreateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedImageUpdateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedUpdateRequest; +import org.websoso.WSSServer.feed.controller.dto.FeedsGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; import org.websoso.WSSServer.feed.application.FeedLikeApplication; import org.websoso.WSSServer.feed.application.FeedManagementApplication; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedCreateRequest.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateRequest.java similarity index 92% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedCreateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateRequest.java index 8f380396..4ef9b1aa 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedCreateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedCreateResponse.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateResponse.java similarity index 91% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedCreateResponse.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateResponse.java index 93b84aac..1298434b 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedCreateResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import org.websoso.WSSServer.feed.domain.FeedImage; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedGetResponse.java similarity index 98% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedGetResponse.java index a96d87e9..8c12f71b 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import java.util.List; import org.websoso.WSSServer.feed.domain.Feed; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedImageCreateRequest.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageCreateRequest.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedImageCreateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageCreateRequest.java index 3b9a1e89..9206f318 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedImageCreateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageCreateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import jakarta.validation.constraints.Size; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedImageUpdateRequest.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageUpdateRequest.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedImageUpdateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageUpdateRequest.java index 8bedafaf..a373c8d0 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedImageUpdateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageUpdateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import jakarta.validation.constraints.Size; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedInfo.java similarity index 98% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedInfo.java index 33941c97..b8cb6880 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedInfo.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedInfo.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import java.util.List; import org.websoso.WSSServer.feed.domain.Feed; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedUpdateRequest.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedUpdateRequest.java similarity index 92% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedUpdateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedUpdateRequest.java index 784a1c8a..f3b5f4f5 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedUpdateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedUpdateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedsGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedsGetResponse.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedsGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedsGetResponse.java index 7d4cc2f1..9d48b0dd 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedsGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java new file mode 100644 index 00000000..4fbd809c --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java @@ -0,0 +1,24 @@ +package org.websoso.WSSServer.feed.controller.dto; + +public record UserFeedGetResponse( + Long feedId, + String feedContent, + String createdDate, + Boolean isSpoiler, + Boolean isModified, + Boolean isLiked, + Integer likeCount, + Integer commentCount, + Long novelId, + String title, + Float novelRating, + Long novelRatingCount, + Boolean isPublic, + String genre, + Float userNovelRating, + String thumbnailUrl, + Integer imageCount, + Float feedWriterNovelRating +) { + +} diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedsGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedsGetResponse.java similarity index 89% rename from src/main/java/org/websoso/WSSServer/dto/feed/UserFeedsGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedsGetResponse.java index c37650db..58a7a9a1 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/UserFeedsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedsGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.controller.dto; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/dto/feed/FeedImageDeleteEvent.java b/src/main/java/org/websoso/WSSServer/feed/event/FeedImageDeleteEvent.java similarity index 67% rename from src/main/java/org/websoso/WSSServer/dto/feed/FeedImageDeleteEvent.java rename to src/main/java/org/websoso/WSSServer/feed/event/FeedImageDeleteEvent.java index 77e5240c..81586bb5 100644 --- a/src/main/java/org/websoso/WSSServer/dto/feed/FeedImageDeleteEvent.java +++ b/src/main/java/org/websoso/WSSServer/feed/event/FeedImageDeleteEvent.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.feed; +package org.websoso.WSSServer.feed.event; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/exception/error/CustomFeedError.java b/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedError.java similarity index 96% rename from src/main/java/org/websoso/WSSServer/exception/error/CustomFeedError.java rename to src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedError.java index 06e864f9..da175eaa 100644 --- a/src/main/java/org/websoso/WSSServer/exception/error/CustomFeedError.java +++ b/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedError.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.exception.error; +package org.websoso.WSSServer.feed.exception; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CONFLICT; diff --git a/src/main/java/org/websoso/WSSServer/exception/exception/CustomFeedException.java b/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedException.java similarity index 72% rename from src/main/java/org/websoso/WSSServer/exception/exception/CustomFeedException.java rename to src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedException.java index 36a6e627..fcbba792 100644 --- a/src/main/java/org/websoso/WSSServer/exception/exception/CustomFeedException.java +++ b/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedException.java @@ -1,8 +1,7 @@ -package org.websoso.WSSServer.exception.exception; +package org.websoso.WSSServer.feed.exception; import lombok.Getter; import org.websoso.common.exception.AbstractCustomException; -import org.websoso.WSSServer.exception.error.CustomFeedError; @Getter public class CustomFeedException extends AbstractCustomException { diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepository.java deleted file mode 100644 index c9cc97dc..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.List; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; -import org.websoso.WSSServer.feed.domain.Category; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.domain.Genre; - -public interface FeedCategoryCustomRepository { - - Slice findRecommendedFeedsByCategoryLabel(Category category, Long lastFeedId, Long userId, - PageRequest pageRequest, List genres); -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepositoryImpl.java deleted file mode 100644 index 0167da04..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryCustomRepositoryImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import static org.websoso.WSSServer.domain.QGenre.genre; -import static org.websoso.WSSServer.feed.domain.QFeed.feed; -import static org.websoso.WSSServer.feed.domain.QFeedCategory.feedCategory; -import static org.websoso.WSSServer.novel.domain.QNovel.novel; -import static org.websoso.WSSServer.novel.domain.QNovelGenre.novelGenre; -import static org.websoso.WSSServer.user.domain.QBlock.block; - -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; -import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Category; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.domain.Genre; - -@Repository -@RequiredArgsConstructor -public class FeedCategoryCustomRepositoryImpl implements FeedCategoryCustomRepository { - - private static final long NO_CURSOR = 0L; - - private final JPAQueryFactory jpaQueryFactory; - - @Override - public Slice findRecommendedFeedsByCategoryLabel(Category category, Long lastFeedId, Long userId, - PageRequest pageRequest, List genres) { - - List results = jpaQueryFactory - .select(feedCategory.feed) - .from(feedCategory) - .leftJoin(feedCategory.feed, feed) - .leftJoin(novel).on(feed.novelId.eq(novel.novelId)) - .leftJoin(novelGenre).on(novel.eq(novelGenre.novel)) - .leftJoin(genre).on(novelGenre.genre.eq(genre)) - .where( - ltFeedId(lastFeedId), - checkCategory(category), - checkHidden(), - checkBlocking(userId), - checkGenres(genres) - ) - .orderBy(feed.feedId.desc()) - .limit(pageRequest.getPageSize() + 1) - .fetch(); - - boolean hasNext = results.size() > pageRequest.getPageSize(); - if (hasNext) { - results.remove(results.size() - 1); - } - - return new SliceImpl<>(results, pageRequest, hasNext); - } - - private BooleanExpression ltFeedId(Long lastFeedId) { - if (lastFeedId == NO_CURSOR) { - return null; - } - return feed.feedId.lt(lastFeedId); - } - - private BooleanExpression checkCategory(Category category) { - return feedCategory.category.eq(category); - } - - private BooleanExpression checkHidden() { - return feed.isHidden.eq(false); - } - - private BooleanExpression checkBlocking(Long userId) { - if (userId != null) { - return feed.user.userId.notIn( - JPAExpressions - .select(block.blockedId) - .from(block) - .where(block.blockingId.eq(userId)) // userId는 파라미터 - ); - } - return null; - } - - private BooleanExpression checkGenres(List genres) { - if (genres != null && !genres.isEmpty()) { - return genre.in(genres).or(feed.novelId.isNull()); - } - return null; - } -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryRepository.java deleted file mode 100644 index a2fa226f..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCategoryRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.List; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Category; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.FeedCategory; - -@Repository -public interface FeedCategoryRepository extends JpaRepository, FeedCategoryCustomRepository { - - List findByFeed(Feed feed); - - void deleteByCategoryAndFeed(Category category, Feed feed); - - @Query(value = "SELECT fc.feed FROM FeedCategory fc " - + "WHERE fc.category = :category " - + "AND (:lastFeedId = 0 OR fc.feed.feedId < :lastFeedId) " - + "AND fc.feed.isHidden = false " - + "AND (:lastFeedId IS NULL " - + "OR fc.feed.user.userId NOT IN (SELECT b.blockedId FROM Block b WHERE b.blockingId = :userId)) " - + "ORDER BY fc.feed.feedId DESC") - Slice findFeedsByCategory(Category category, Long lastFeedId, Long userId, PageRequest pageRequest); - -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java index 4ff4b09d..d5a50d6c 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java @@ -76,6 +76,7 @@ public Slice findFeedsByNoOffsetPagination(User owner, Long lastFeedId, in userFeedCursor(lastFeedId, sortCriteria), checkVisible(visitorId), checkPublic(isVisible, isUnVisible), + checkHidden(), checkGenresAndNovels(genres, isNotNovelConnect) ) .orderBy( @@ -131,6 +132,7 @@ public Long countVisibleFeeds(User owner, Boolean isVisible, feed.user.eq(owner), checkVisible(visitorId), checkPublic(isVisible, isUnVisible), + checkHidden(), checkGenresAndNovels(genres, isNotNovelConnect) ) .fetchOne(); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java index defbd864..584a488b 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java @@ -1,5 +1,9 @@ package org.websoso.WSSServer.feed.repository; +import org.websoso.WSSServer.feed.repository.projection.FeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.PopularFeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.UserFeedInfoRow; + import java.util.List; public interface FeedQueryRepository { diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java index 41391d1a..2c451448 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java @@ -20,6 +20,9 @@ import org.websoso.WSSServer.feed.domain.QComment; import org.websoso.WSSServer.feed.domain.QFeedImage; import org.websoso.WSSServer.feed.domain.QLike; +import org.websoso.WSSServer.feed.repository.projection.FeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.PopularFeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.UserFeedInfoRow; import org.websoso.WSSServer.library.domain.QUserNovel; import org.websoso.WSSServer.novel.domain.QNovelGenre; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/repository/projection/FeedInfoRow.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java rename to src/main/java/org/websoso/WSSServer/feed/repository/projection/FeedInfoRow.java index 960d27f9..539465ba 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedInfoRow.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/projection/FeedInfoRow.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.repository.projection; import java.time.LocalDateTime; -import org.websoso.WSSServer.dto.feed.FeedInfo; +import org.websoso.WSSServer.feed.controller.dto.FeedInfo; import org.websoso.WSSServer.util.TimeFormatUtil; public record FeedInfoRow( diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/repository/projection/PopularFeedInfoRow.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java rename to src/main/java/org/websoso/WSSServer/feed/repository/projection/PopularFeedInfoRow.java index 646df61c..3b4b641b 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedInfoRow.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/projection/PopularFeedInfoRow.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.repository.projection; import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/repository/projection/UserFeedInfoRow.java similarity index 92% rename from src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java rename to src/main/java/org/websoso/WSSServer/feed/repository/projection/UserFeedInfoRow.java index fd1a609b..cb5e71c3 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/UserFeedInfoRow.java +++ b/src/main/java/org/websoso/WSSServer/feed/repository/projection/UserFeedInfoRow.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.repository.projection; import java.time.LocalDateTime; -import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; +import org.websoso.WSSServer.feed.controller.dto.UserFeedGetResponse; import org.websoso.WSSServer.util.TimeFormatUtil; public record UserFeedInfoRow( diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java index 8364350f..b050327e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java @@ -8,14 +8,14 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.dto.feed.FeedInfo; -import org.websoso.WSSServer.dto.feed.UserFeedGetResponse; +import org.websoso.WSSServer.feed.controller.dto.FeedInfo; +import org.websoso.WSSServer.feed.controller.dto.UserFeedGetResponse; import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.repository.FeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.FeedInfoRow; import org.websoso.WSSServer.feed.repository.FeedQueryRepository; -import org.websoso.WSSServer.feed.repository.PopularFeedInfoRow; -import org.websoso.WSSServer.feed.repository.UserFeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.PopularFeedInfoRow; +import org.websoso.WSSServer.feed.repository.projection.UserFeedInfoRow; @Service @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java index 06bbf47a..e906c17f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java @@ -1,7 +1,7 @@ package org.websoso.WSSServer.feed.service; -import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.exception.error.CustomFeedError.HIDDEN_FEED_ACCESS; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.HIDDEN_FEED_ACCESS; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import java.util.Comparator; @@ -16,7 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.exception.exception.CustomFeedException; +import org.websoso.WSSServer.feed.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.repository.FeedRepository; diff --git a/src/main/java/org/websoso/WSSServer/service/ImageClient.java b/src/main/java/org/websoso/WSSServer/service/ImageClient.java index 0104246a..512412cd 100644 --- a/src/main/java/org/websoso/WSSServer/service/ImageClient.java +++ b/src/main/java/org/websoso/WSSServer/service/ImageClient.java @@ -16,7 +16,7 @@ import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.web.multipart.MultipartFile; -import org.websoso.WSSServer.dto.feed.FeedImageDeleteEvent; +import org.websoso.WSSServer.feed.event.FeedImageDeleteEvent; import org.websoso.WSSServer.exception.exception.CustomImageException; import org.websoso.s3.core.S3FileService; import org.websoso.s3.modle.S3UploadResult; diff --git a/src/main/java/org/websoso/WSSServer/user/controller/UserController.java b/src/main/java/org/websoso/WSSServer/user/controller/UserController.java index 49448825..47d527ea 100644 --- a/src/main/java/org/websoso/WSSServer/user/controller/UserController.java +++ b/src/main/java/org/websoso/WSSServer/user/controller/UserController.java @@ -24,7 +24,6 @@ import org.websoso.WSSServer.application.AuthApplication; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse; import org.websoso.WSSServer.dto.user.PushSettingGetResponse; import org.websoso.WSSServer.dto.user.PushSettingRequest; import org.websoso.WSSServer.dto.user.EditMyInfoRequest; diff --git a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java index a927625b..74160266 100644 --- a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java +++ b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java @@ -1,18 +1,17 @@ package org.websoso.WSSServer.user.service; import java.util.List; -import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.exception.exception.CustomFeedException; +import org.websoso.WSSServer.feed.exception.CustomFeedException; import org.websoso.WSSServer.user.domain.Block; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.repository.BlockRepository; -import static org.websoso.WSSServer.exception.error.CustomFeedError.BLOCKED_USER_ACCESS; +import static org.websoso.WSSServer.feed.exception.CustomFeedError.BLOCKED_USER_ACCESS; @Service @RequiredArgsConstructor From e4ec49aedb1e882c80e8ab89cd1234c6224d36f4 Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Wed, 17 Jun 2026 13:34:26 +0900 Subject: [PATCH 29/31] =?UTF-8?q?[RENAME]=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=94=BC=EB=93=9C=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/feed/domain/Category.java | 30 ------------- .../WSSServer/feed/domain/FeedCategory.java | 42 ------------------- .../feed/repository/CategoryRepository.java | 13 ------ 3 files changed, 85 deletions(-) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/domain/Category.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/domain/FeedCategory.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/CategoryRepository.java diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Category.java b/src/main/java/org/websoso/WSSServer/feed/domain/Category.java deleted file mode 100644 index 01e54ffa..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Category.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.websoso.WSSServer.feed.domain; - -import static jakarta.persistence.GenerationType.IDENTITY; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.websoso.WSSServer.domain.common.CategoryName; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Category { - - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(nullable = false) - private Byte categoryId; - - @Enumerated(EnumType.STRING) - @Column(columnDefinition = "varchar(14)", nullable = false) - private CategoryName categoryName; - -} diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/FeedCategory.java b/src/main/java/org/websoso/WSSServer/feed/domain/FeedCategory.java deleted file mode 100644 index cfc77c08..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/domain/FeedCategory.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.websoso.WSSServer.feed.domain; - -import static jakarta.persistence.GenerationType.IDENTITY; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class FeedCategory { - - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(nullable = false) - private Long feedCategoryId; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "feed_id", nullable = false) - private Feed feed; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "category_id", nullable = false) - private Category category; - - private FeedCategory(Feed feed, Category category) { - this.feed = feed; - this.category = category; - } - - public static FeedCategory create(Feed feed, Category category) { - return new FeedCategory(feed, category); - } -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/CategoryRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/CategoryRepository.java deleted file mode 100644 index 4a404d98..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/CategoryRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Category; -import org.websoso.WSSServer.domain.common.CategoryName; - -@Repository -public interface CategoryRepository extends JpaRepository { - - Optional findByCategoryName(CategoryName categoryName); -} From ace3ce7a9e05e53b6547e2fc5e866cec7eb877ab Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Wed, 17 Jun 2026 13:38:23 +0900 Subject: [PATCH 30/31] =?UTF-8?q?[REFACTOR]=20FeedFindApplication=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EC=84=9C=EC=9E=AC=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websoso/WSSServer/feed/application/FeedFindApplication.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java index 05a390e3..c50b6eed 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java @@ -25,7 +25,6 @@ import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.feed.domain.Feed; import org.websoso.WSSServer.feed.service.FeedServiceImpl; -import org.websoso.WSSServer.library.service.LibraryService; import org.websoso.WSSServer.novel.domain.Novel; import org.websoso.WSSServer.novel.service.NovelServiceImpl; import org.websoso.WSSServer.user.domain.User; @@ -47,7 +46,6 @@ public class FeedFindApplication { private final AvatarService avatarService; private final FeedLikeService feedLikeService; private final BlockService blockService; - private final LibraryService libraryService; @Transactional(readOnly = true) public FeedGetResponse getFeedById(User user, Long feedId) { From 851ff80ad31af31e5b20a3ea352e704687aa85cf Mon Sep 17 00:00:00 2001 From: GiJungPark Date: Wed, 17 Jun 2026 15:23:41 +0900 Subject: [PATCH 31/31] =?UTF-8?q?[REMOVE]=20feed=20=EB=82=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=94=BC=EB=93=9C,=20=EB=8C=93=EA=B8=80,=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/AccountApplication.java | 4 +- .../application/SearchNovelApplication.java | 4 +- .../CommentAuthorizationValidator.java | 8 +- .../auth/validator/FeedAccessValidator.java | 12 +-- .../validator/FeedAuthorizationValidator.java | 8 +- .../dto/novel/NovelGetResponseFeedTab.java | 2 +- .../popularNovel/PopularNovelGetResponse.java | 2 +- .../PopularNovelsGetResponse.java | 3 +- .../application/CommentFindApplication.java | 10 +-- .../CommentManagementApplication.java | 14 +-- .../controller/CommentController.java | 16 ++-- .../controller/dto}/CommentCreateRequest.java | 2 +- .../controller/dto}/CommentGetResponse.java | 4 +- .../controller/dto}/CommentUpdateRequest.java | 2 +- .../controller/dto}/CommentsGetResponse.java | 2 +- .../feed/{ => comment}/domain/Comment.java | 7 +- .../exception}/CustomCommentError.java | 2 +- .../exception/CustomCommentException.java | 3 +- .../repository/CommentRepository.java | 17 +--- .../service/CommentServiceImpl.java | 36 ++------ .../application/FeedFindApplication.java | 22 ++--- .../application/FeedLikeApplication.java | 12 +-- .../FeedManagementApplication.java | 26 +++--- .../application/PopularFeedApplication.java | 12 +-- .../{ => feed}/controller/FeedController.java | 26 +++--- .../controller/dto/FeedCreateRequest.java | 2 +- .../controller/dto/FeedCreateResponse.java | 4 +- .../controller/dto/FeedGetResponse.java | 6 +- .../dto/FeedImageCreateRequest.java | 2 +- .../dto/FeedImageUpdateRequest.java | 2 +- .../{ => feed}/controller/dto/FeedInfo.java | 4 +- .../controller/dto/FeedUpdateRequest.java | 2 +- .../controller/dto/FeedsGetResponse.java | 2 +- .../dto}/PopularFeedGetResponse.java | 4 +- .../dto}/PopularFeedsGetResponse.java | 2 +- .../controller/dto/UserFeedGetResponse.java | 2 +- .../controller/dto/UserFeedsGetResponse.java | 2 +- .../feed/{ => feed}/domain/Feed.java | 4 +- .../feed/{ => feed}/domain/FeedImage.java | 2 +- .../feed/{ => feed}/domain/Like.java | 2 +- .../feed/{ => feed}/domain/PopularFeed.java | 2 +- .../event/FeedBecamePopularEvent.java | 2 +- .../event/FeedImageDeleteEvent.java | 2 +- .../feed/{ => feed}/event/FeedLikedEvent.java | 2 +- .../event/PopularFeedCheckEvent.java | 2 +- .../{ => feed}/exception/CustomFeedError.java | 2 +- .../exception/CustomFeedException.java | 2 +- .../FeedUserToUnknownEventListener.java | 4 +- .../PopularFeedCheckEventListener.java | 6 +- .../repository/FeedCustomRepository.java | 4 +- .../repository/FeedCustomRepositoryImpl.java | 16 ++-- .../repository/FeedQueryRepository.java | 8 +- .../repository/FeedQueryRepositoryImpl.java | 21 +++-- .../{ => feed}/repository/FeedRepository.java | 8 +- .../feed/feed/repository/LikeRepository.java | 24 +++++ .../repository/PopularFeedRepository.java | 9 +- .../repository/projection/FeedInfoRow.java | 4 +- .../projection/PopularFeedInfoRow.java | 4 +- .../projection/UserFeedInfoRow.java | 4 +- .../{ => feed}/service/FeedImageService.java | 4 +- .../feed/feed/service/FeedLikeService.java | 50 +++++++++++ .../{ => feed}/service/FeedQueryService.java | 18 ++-- .../{ => feed}/service/FeedServiceImpl.java | 12 +-- .../service/PopularFeedService.java | 8 +- .../application/ReportApplication.java | 24 ++--- .../controller/ReportController.java | 4 +- .../{ => report}/domain/ReportedComment.java | 3 +- .../{ => report}/domain/ReportedFeed.java | 3 +- .../repository/ReportedCommentRepository.java | 6 +- .../repository/ReportedFeedRepository.java | 6 +- .../service/ReportServiceImpl.java | 14 +-- .../feed/repository/FeedCountProjection.java | 8 -- .../feed/repository/LikeRepository.java | 46 ---------- .../PopularFeedCustomRepository.java | 18 ---- .../PopularFeedCustomRepositoryImpl.java | 78 ---------------- .../feed/service/FeedLikeService.java | 90 ------------------- .../library/service/UserNovelService.java | 4 +- .../NotificationSendApplication.java | 4 +- .../FeedLikeNotificationListener.java | 2 +- .../PopularFeedNotificationListener.java | 2 +- .../service/MessageFormatter.java | 4 +- .../service/NotificationService.java | 2 +- .../WSSServer/service/ImageClient.java | 2 +- .../websoso/WSSServer/user/domain/User.java | 4 +- .../WSSServer/user/service/BlockService.java | 4 +- .../feed/service/FeedServiceImplTest.java | 14 +-- .../service/MessageFormatterTest.java | 4 +- 87 files changed, 329 insertions(+), 533 deletions(-) rename src/main/java/org/websoso/WSSServer/{ => feed/comment}/application/CommentFindApplication.java (89%) rename src/main/java/org/websoso/WSSServer/{ => feed/comment}/application/CommentManagementApplication.java (94%) rename src/main/java/org/websoso/WSSServer/feed/{ => comment}/controller/CommentController.java (85%) rename src/main/java/org/websoso/WSSServer/{dto/comment => feed/comment/controller/dto}/CommentCreateRequest.java (85%) rename src/main/java/org/websoso/WSSServer/{dto/comment => feed/comment/controller/dto}/CommentGetResponse.java (90%) rename src/main/java/org/websoso/WSSServer/{dto/comment => feed/comment/controller/dto}/CommentUpdateRequest.java (85%) rename src/main/java/org/websoso/WSSServer/{dto/comment => feed/comment/controller/dto}/CommentsGetResponse.java (85%) rename src/main/java/org/websoso/WSSServer/feed/{ => comment}/domain/Comment.java (91%) rename src/main/java/org/websoso/WSSServer/{exception/error => feed/comment/exception}/CustomCommentError.java (94%) rename src/main/java/org/websoso/WSSServer/{exception => feed/comment}/exception/CustomCommentException.java (72%) rename src/main/java/org/websoso/WSSServer/feed/{ => comment}/repository/CommentRepository.java (55%) rename src/main/java/org/websoso/WSSServer/feed/{ => comment}/service/CommentServiceImpl.java (53%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/application/FeedFindApplication.java (90%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/application/FeedLikeApplication.java (84%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/application/FeedManagementApplication.java (80%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/application/PopularFeedApplication.java (74%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/FeedController.java (88%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedCreateRequest.java (91%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedCreateResponse.java (81%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedGetResponse.java (96%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedImageCreateRequest.java (84%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedImageUpdateRequest.java (84%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedInfo.java (97%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedUpdateRequest.java (91%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/FeedsGetResponse.java (84%) rename src/main/java/org/websoso/WSSServer/{dto/popularFeed => feed/feed/controller/dto}/PopularFeedGetResponse.java (88%) rename src/main/java/org/websoso/WSSServer/{dto/popularFeed => feed/feed/controller/dto}/PopularFeedsGetResponse.java (84%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/UserFeedGetResponse.java (90%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/controller/dto/UserFeedsGetResponse.java (88%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/domain/Feed.java (95%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/domain/FeedImage.java (96%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/domain/Like.java (96%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/domain/PopularFeed.java (94%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/event/FeedBecamePopularEvent.java (79%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/event/FeedImageDeleteEvent.java (65%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/event/FeedLikedEvent.java (84%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/event/PopularFeedCheckEvent.java (79%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/exception/CustomFeedError.java (96%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/exception/CustomFeedException.java (85%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/listener/FeedUserToUnknownEventListener.java (83%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/listener/PopularFeedCheckEventListener.java (74%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/FeedCustomRepository.java (93%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/FeedCustomRepositoryImpl.java (96%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/FeedQueryRepository.java (52%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/FeedQueryRepositoryImpl.java (93%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/FeedRepository.java (77%) create mode 100644 src/main/java/org/websoso/WSSServer/feed/feed/repository/LikeRepository.java rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/PopularFeedRepository.java (51%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/projection/FeedInfoRow.java (93%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/projection/PopularFeedInfoRow.java (84%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/repository/projection/UserFeedInfoRow.java (92%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/service/FeedImageService.java (94%) create mode 100644 src/main/java/org/websoso/WSSServer/feed/feed/service/FeedLikeService.java rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/service/FeedQueryService.java (76%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/service/FeedServiceImpl.java (91%) rename src/main/java/org/websoso/WSSServer/feed/{ => feed}/service/PopularFeedService.java (70%) rename src/main/java/org/websoso/WSSServer/{ => feed/report}/application/ReportApplication.java (82%) rename src/main/java/org/websoso/WSSServer/feed/{ => report}/controller/ReportController.java (95%) rename src/main/java/org/websoso/WSSServer/feed/{ => report}/domain/ReportedComment.java (93%) rename src/main/java/org/websoso/WSSServer/feed/{ => report}/domain/ReportedFeed.java (93%) rename src/main/java/org/websoso/WSSServer/feed/{ => report}/repository/ReportedCommentRepository.java (85%) rename src/main/java/org/websoso/WSSServer/feed/{ => report}/repository/ReportedFeedRepository.java (74%) rename src/main/java/org/websoso/WSSServer/feed/{ => report}/service/ReportServiceImpl.java (80%) delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepository.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepositoryImpl.java delete mode 100644 src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java diff --git a/src/main/java/org/websoso/WSSServer/application/AccountApplication.java b/src/main/java/org/websoso/WSSServer/application/AccountApplication.java index 340ade12..8eb22387 100644 --- a/src/main/java/org/websoso/WSSServer/application/AccountApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/AccountApplication.java @@ -9,8 +9,8 @@ import org.websoso.WSSServer.infrastructure.discord.DiscordMessageClient; import org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessage; import org.websoso.WSSServer.dto.user.WithdrawalRequest; -import org.websoso.WSSServer.feed.repository.CommentRepository; -import org.websoso.WSSServer.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.comment.repository.CommentRepository; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; import org.websoso.WSSServer.oauth2.service.AppleService; import org.websoso.WSSServer.oauth2.service.KakaoService; import org.websoso.WSSServer.oauth2.repository.RefreshTokenRepository; diff --git a/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java b/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java index 039587fa..7b858927 100644 --- a/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java @@ -36,8 +36,8 @@ import org.websoso.WSSServer.dto.userNovel.TasteNovelGetResponse; import org.websoso.WSSServer.dto.userNovel.TasteNovelsGetResponse; import org.websoso.WSSServer.exception.exception.CustomGenreException; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; import org.websoso.WSSServer.library.domain.Keyword; import org.websoso.WSSServer.library.domain.UserNovel; import org.websoso.WSSServer.library.service.LibraryService; diff --git a/src/main/java/org/websoso/WSSServer/auth/validator/CommentAuthorizationValidator.java b/src/main/java/org/websoso/WSSServer/auth/validator/CommentAuthorizationValidator.java index 60cd7f30..a55ed041 100644 --- a/src/main/java/org/websoso/WSSServer/auth/validator/CommentAuthorizationValidator.java +++ b/src/main/java/org/websoso/WSSServer/auth/validator/CommentAuthorizationValidator.java @@ -1,15 +1,15 @@ package org.websoso.WSSServer.auth.validator; -import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_FOUND; +import static org.websoso.WSSServer.feed.comment.exception.CustomCommentError.COMMENT_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.websoso.WSSServer.feed.domain.Comment; +import org.websoso.WSSServer.feed.comment.domain.Comment; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.exception.exception.CustomCommentException; +import org.websoso.WSSServer.feed.comment.exception.CustomCommentException; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.repository.CommentRepository; +import org.websoso.WSSServer.feed.comment.repository.CommentRepository; @Component @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java index 15a508bd..51699f41 100644 --- a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java +++ b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAccessValidator.java @@ -1,15 +1,15 @@ package org.websoso.WSSServer.auth.validator; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.BLOCKED_USER_ACCESS; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.HIDDEN_FEED_ACCESS; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.BLOCKED_USER_ACCESS; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.HIDDEN_FEED_ACCESS; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.feed.exception.CustomFeedException; -import org.websoso.WSSServer.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.feed.exception.CustomFeedException; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; import org.websoso.WSSServer.user.service.BlockService; @Component diff --git a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java index f3da7f18..7e32f0c5 100644 --- a/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java +++ b/src/main/java/org/websoso/WSSServer/auth/validator/FeedAuthorizationValidator.java @@ -1,15 +1,15 @@ package org.websoso.WSSServer.auth.validator; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.FEED_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.feed.exception.CustomFeedException; +import org.websoso.WSSServer.feed.feed.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; @Component @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java b/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java index 17347025..a273b983 100644 --- a/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java +++ b/src/main/java/org/websoso/WSSServer/dto/novel/NovelGetResponseFeedTab.java @@ -1,7 +1,7 @@ package org.websoso.WSSServer.dto.novel; import java.util.List; -import org.websoso.WSSServer.feed.controller.dto.FeedInfo; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedInfo; public record NovelGetResponseFeedTab( Boolean isLoadable, diff --git a/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelGetResponse.java index 8565cbcf..a60774c4 100644 --- a/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelGetResponse.java @@ -1,7 +1,7 @@ package org.websoso.WSSServer.dto.popularNovel; import org.websoso.WSSServer.user.domain.AvatarProfile; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.novel.domain.Novel; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelsGetResponse.java b/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelsGetResponse.java index a6272b6a..aa2d0c8c 100644 --- a/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/dto/popularNovel/PopularNovelsGetResponse.java @@ -3,10 +3,9 @@ import java.util.List; import java.util.Map; -import org.websoso.WSSServer.dto.keyword.KeywordGetResponse; import org.websoso.WSSServer.library.domain.Keyword; import org.websoso.WSSServer.user.domain.AvatarProfile; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.novel.domain.Novel; public record PopularNovelsGetResponse( diff --git a/src/main/java/org/websoso/WSSServer/application/CommentFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/comment/application/CommentFindApplication.java similarity index 89% rename from src/main/java/org/websoso/WSSServer/application/CommentFindApplication.java rename to src/main/java/org/websoso/WSSServer/feed/comment/application/CommentFindApplication.java index 3a3204bf..5613c221 100644 --- a/src/main/java/org/websoso/WSSServer/application/CommentFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/application/CommentFindApplication.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.application; +package org.websoso.WSSServer.feed.comment.application; import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.USER_NOT_FOUND; @@ -10,13 +10,13 @@ import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.user.repository.AvatarProfileRepository; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.dto.comment.CommentGetResponse; -import org.websoso.WSSServer.dto.comment.CommentsGetResponse; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentGetResponse; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.exception.exception.CustomAvatarException; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; import org.websoso.WSSServer.user.repository.BlockRepository; import org.websoso.WSSServer.user.repository.UserRepository; diff --git a/src/main/java/org/websoso/WSSServer/application/CommentManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/comment/application/CommentManagementApplication.java similarity index 94% rename from src/main/java/org/websoso/WSSServer/application/CommentManagementApplication.java rename to src/main/java/org/websoso/WSSServer/feed/comment/application/CommentManagementApplication.java index 51fc9197..ab68ca9a 100644 --- a/src/main/java/org/websoso/WSSServer/application/CommentManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/application/CommentManagementApplication.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.application; +package org.websoso.WSSServer.feed.comment.application; import static java.lang.Boolean.TRUE; import static org.websoso.WSSServer.domain.common.Action.DELETE; @@ -13,13 +13,13 @@ import org.websoso.WSSServer.notification.domain.NotificationType; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.notification.domain.UserDevice; -import org.websoso.WSSServer.dto.comment.CommentCreateRequest; -import org.websoso.WSSServer.dto.comment.CommentUpdateRequest; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentCreateRequest; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentUpdateRequest; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.service.CommentServiceImpl; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.comment.service.CommentServiceImpl; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; import org.websoso.WSSServer.notification.infrastructure.FCMClient; import org.websoso.WSSServer.notification.dto.FCMMessageRequest; import org.websoso.WSSServer.novel.domain.Novel; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/CommentController.java b/src/main/java/org/websoso/WSSServer/feed/comment/controller/CommentController.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/feed/controller/CommentController.java rename to src/main/java/org/websoso/WSSServer/feed/comment/controller/CommentController.java index a8f2ad89..c6c83a93 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/CommentController.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/controller/CommentController.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller; +package org.websoso.WSSServer.feed.comment.controller; import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.OK; @@ -16,11 +16,11 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.websoso.WSSServer.application.CommentFindApplication; -import org.websoso.WSSServer.application.CommentManagementApplication; -import org.websoso.WSSServer.dto.comment.CommentCreateRequest; -import org.websoso.WSSServer.dto.comment.CommentUpdateRequest; -import org.websoso.WSSServer.dto.comment.CommentsGetResponse; +import org.websoso.WSSServer.feed.comment.application.CommentFindApplication; +import org.websoso.WSSServer.feed.comment.application.CommentManagementApplication; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentCreateRequest; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentUpdateRequest; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentsGetResponse; import org.websoso.WSSServer.user.domain.User; @RequestMapping("/feeds") @@ -50,7 +50,7 @@ public ResponseEntity getComments(@AuthenticationPrincipal @PutMapping("/{feedId}/comments/{commentId}") @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user) " - + "and @authorizationService.validate(#commentId, #user, T(org.websoso.WSSServer.feed.domain.Comment))") + + "and @authorizationService.validate(#commentId, #user, T(org.websoso.WSSServer.feed.comment.domain.Comment))") public ResponseEntity updateComment(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId, @PathVariable("commentId") Long commentId, @@ -63,7 +63,7 @@ public ResponseEntity updateComment(@AuthenticationPrincipal User user, @DeleteMapping("/{feedId}/comments/{commentId}") @PreAuthorize("isAuthenticated() and @feedAccessValidator.canAccess(#feedId, #user) " - + "and @authorizationService.validate(#commentId, #user, T(org.websoso.WSSServer.feed.domain.Comment))") + + "and @authorizationService.validate(#commentId, #user, T(org.websoso.WSSServer.feed.comment.domain.Comment))") public ResponseEntity deleteComment(@AuthenticationPrincipal User user, @PathVariable("feedId") Long feedId, @PathVariable("commentId") Long commentId) { diff --git a/src/main/java/org/websoso/WSSServer/dto/comment/CommentCreateRequest.java b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentCreateRequest.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/dto/comment/CommentCreateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentCreateRequest.java index 4741d2bb..9000dced 100644 --- a/src/main/java/org/websoso/WSSServer/dto/comment/CommentCreateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentCreateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.comment; +package org.websoso.WSSServer.feed.comment.controller.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; diff --git a/src/main/java/org/websoso/WSSServer/dto/comment/CommentGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentGetResponse.java similarity index 90% rename from src/main/java/org/websoso/WSSServer/dto/comment/CommentGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentGetResponse.java index 2ccd83a8..774b45b3 100644 --- a/src/main/java/org/websoso/WSSServer/dto/comment/CommentGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentGetResponse.java @@ -1,6 +1,6 @@ -package org.websoso.WSSServer.dto.comment; +package org.websoso.WSSServer.feed.comment.controller.dto; -import org.websoso.WSSServer.feed.domain.Comment; +import org.websoso.WSSServer.feed.comment.domain.Comment; import org.websoso.WSSServer.dto.user.UserBasicInfo; import org.websoso.WSSServer.util.TimeFormatUtil; diff --git a/src/main/java/org/websoso/WSSServer/dto/comment/CommentUpdateRequest.java b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentUpdateRequest.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/dto/comment/CommentUpdateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentUpdateRequest.java index bf897c0d..744666d3 100644 --- a/src/main/java/org/websoso/WSSServer/dto/comment/CommentUpdateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentUpdateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.comment; +package org.websoso.WSSServer.feed.comment.controller.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; diff --git a/src/main/java/org/websoso/WSSServer/dto/comment/CommentsGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentsGetResponse.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/dto/comment/CommentsGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentsGetResponse.java index f39c45c4..838df1a9 100644 --- a/src/main/java/org/websoso/WSSServer/dto/comment/CommentsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/controller/dto/CommentsGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.comment; +package org.websoso.WSSServer.feed.comment.controller.dto; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Comment.java b/src/main/java/org/websoso/WSSServer/feed/comment/domain/Comment.java similarity index 91% rename from src/main/java/org/websoso/WSSServer/feed/domain/Comment.java rename to src/main/java/org/websoso/WSSServer/feed/comment/domain/Comment.java index c8a34efa..da346fe5 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Comment.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/domain/Comment.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.comment.domain; import static jakarta.persistence.GenerationType.IDENTITY; -import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_BELONG_TO_FEED; +import static org.websoso.WSSServer.feed.comment.exception.CustomCommentError.COMMENT_NOT_BELONG_TO_FEED; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import jakarta.persistence.Column; @@ -18,8 +18,9 @@ import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicInsert; import org.websoso.WSSServer.domain.common.Action; -import org.websoso.WSSServer.exception.exception.CustomCommentException; +import org.websoso.WSSServer.feed.comment.exception.CustomCommentException; import org.websoso.WSSServer.exception.exception.CustomUserException; +import org.websoso.WSSServer.feed.feed.domain.Feed; @Entity @Getter diff --git a/src/main/java/org/websoso/WSSServer/exception/error/CustomCommentError.java b/src/main/java/org/websoso/WSSServer/feed/comment/exception/CustomCommentError.java similarity index 94% rename from src/main/java/org/websoso/WSSServer/exception/error/CustomCommentError.java rename to src/main/java/org/websoso/WSSServer/feed/comment/exception/CustomCommentError.java index fb1f4dd5..375cb987 100644 --- a/src/main/java/org/websoso/WSSServer/exception/error/CustomCommentError.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/exception/CustomCommentError.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.exception.error; +package org.websoso.WSSServer.feed.comment.exception; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CONFLICT; diff --git a/src/main/java/org/websoso/WSSServer/exception/exception/CustomCommentException.java b/src/main/java/org/websoso/WSSServer/feed/comment/exception/CustomCommentException.java similarity index 72% rename from src/main/java/org/websoso/WSSServer/exception/exception/CustomCommentException.java rename to src/main/java/org/websoso/WSSServer/feed/comment/exception/CustomCommentException.java index 705d190b..5ff795a4 100644 --- a/src/main/java/org/websoso/WSSServer/exception/exception/CustomCommentException.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/exception/CustomCommentException.java @@ -1,8 +1,7 @@ -package org.websoso.WSSServer.exception.exception; +package org.websoso.WSSServer.feed.comment.exception; import lombok.Getter; import org.websoso.common.exception.AbstractCustomException; -import org.websoso.WSSServer.exception.error.CustomCommentError; @Getter public class CustomCommentException extends AbstractCustomException { diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java b/src/main/java/org/websoso/WSSServer/feed/comment/repository/CommentRepository.java similarity index 55% rename from src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java rename to src/main/java/org/websoso/WSSServer/feed/comment/repository/CommentRepository.java index 99792b90..fd2b7c45 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/CommentRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/repository/CommentRepository.java @@ -1,13 +1,11 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.comment.repository; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Comment; +import org.websoso.WSSServer.feed.comment.domain.Comment; @Repository public interface CommentRepository extends JpaRepository { @@ -17,17 +15,6 @@ public interface CommentRepository extends JpaRepository { @Query("UPDATE Comment c SET c.userId = -1 WHERE c.userId = :userId") void updateUserToUnknown(Long userId); - @Query("SELECT c FROM Comment c WHERE c.feed.feedId = :feedId") - List findAllByFeedId(Long feedId); - - @Query(""" - SELECT c.feed.feedId AS feedId, COUNT(c.commentId) AS count - FROM Comment c - WHERE c.feed.feedId IN :feedIds - GROUP BY c.feed.feedId - """) - List countByFeedIds(List feedIds); - @Modifying @Query("DELETE FROM Comment c WHERE c.feed.feedId = :feedId") void deleteByFeedId(Long feedId); diff --git a/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/comment/service/CommentServiceImpl.java similarity index 53% rename from src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java rename to src/main/java/org/websoso/WSSServer/feed/comment/service/CommentServiceImpl.java index 283e816d..8911c891 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/CommentServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/comment/service/CommentServiceImpl.java @@ -1,23 +1,18 @@ -package org.websoso.WSSServer.feed.service; +package org.websoso.WSSServer.feed.comment.service; -import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_FOUND; +import static org.websoso.WSSServer.feed.comment.exception.CustomCommentError.COMMENT_NOT_FOUND; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.repository.FeedCountProjection; -import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; +import org.websoso.WSSServer.feed.report.repository.ReportedCommentRepository; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.dto.comment.CommentCreateRequest; -import org.websoso.WSSServer.dto.comment.CommentUpdateRequest; -import org.websoso.WSSServer.exception.exception.CustomCommentException; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.repository.CommentRepository; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentCreateRequest; +import org.websoso.WSSServer.feed.comment.controller.dto.CommentUpdateRequest; +import org.websoso.WSSServer.feed.comment.exception.CustomCommentException; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.comment.repository.CommentRepository; @Service @RequiredArgsConstructor @@ -53,17 +48,4 @@ public void deleteByFeedId(Long feedId) { reportedCommentRepository.deleteByFeedId(feedId); } - @Transactional(readOnly = true) - public Map countByFeedIds(List feedIds) { - if (feedIds.isEmpty()) { - return Collections.emptyMap(); - } - - return commentRepository.countByFeedIds(feedIds).stream() - .collect(Collectors.toMap( - FeedCountProjection::getFeedId, - projection -> projection.getCount().intValue() - )); - } - } diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java b/src/main/java/org/websoso/WSSServer/feed/feed/application/FeedFindApplication.java similarity index 90% rename from src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java rename to src/main/java/org/websoso/WSSServer/feed/feed/application/FeedFindApplication.java index c50b6eed..e4ce8352 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedFindApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/application/FeedFindApplication.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.application; +package org.websoso.WSSServer.feed.feed.application; import java.util.*; import java.util.stream.Collectors; @@ -9,22 +9,22 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.feed.controller.dto.UserFeedGetResponse; -import org.websoso.WSSServer.feed.controller.dto.UserFeedsGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.UserFeedGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.UserFeedsGetResponse; import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; -import org.websoso.WSSServer.feed.service.FeedLikeService; -import org.websoso.WSSServer.feed.service.FeedQueryService; +import org.websoso.WSSServer.feed.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.feed.service.FeedQueryService; import org.websoso.WSSServer.novel.service.GenreServiceImpl; import org.websoso.WSSServer.user.domain.AvatarProfile; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.common.FeedGetOption; -import org.websoso.WSSServer.feed.controller.dto.FeedGetResponse; -import org.websoso.WSSServer.feed.controller.dto.FeedInfo; -import org.websoso.WSSServer.feed.controller.dto.FeedsGetResponse; -import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedInfo; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedsGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.PopularFeedsGetResponse; import org.websoso.WSSServer.dto.user.UserBasicInfo; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; import org.websoso.WSSServer.novel.domain.Novel; import org.websoso.WSSServer.novel.service.NovelServiceImpl; import org.websoso.WSSServer.user.domain.User; diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java b/src/main/java/org/websoso/WSSServer/feed/feed/application/FeedLikeApplication.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java rename to src/main/java/org/websoso/WSSServer/feed/feed/application/FeedLikeApplication.java index 39ab87c0..073107f8 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedLikeApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/application/FeedLikeApplication.java @@ -1,14 +1,14 @@ -package org.websoso.WSSServer.feed.application; +package org.websoso.WSSServer.feed.feed.application; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.event.FeedLikedEvent; -import org.websoso.WSSServer.feed.event.PopularFeedCheckEvent; -import org.websoso.WSSServer.feed.service.FeedLikeService; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.event.FeedLikedEvent; +import org.websoso.WSSServer.feed.feed.event.PopularFeedCheckEvent; +import org.websoso.WSSServer.feed.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.service.BlockService; diff --git a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java b/src/main/java/org/websoso/WSSServer/feed/feed/application/FeedManagementApplication.java similarity index 80% rename from src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java rename to src/main/java/org/websoso/WSSServer/feed/feed/application/FeedManagementApplication.java index 072e013e..ca814798 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/FeedManagementApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/application/FeedManagementApplication.java @@ -1,21 +1,21 @@ -package org.websoso.WSSServer.feed.application; +package org.websoso.WSSServer.feed.feed.application; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.controller.dto.FeedCreateRequest; -import org.websoso.WSSServer.feed.controller.dto.FeedCreateResponse; -import org.websoso.WSSServer.feed.controller.dto.FeedImageCreateRequest; -import org.websoso.WSSServer.feed.event.FeedImageDeleteEvent; -import org.websoso.WSSServer.feed.controller.dto.FeedImageUpdateRequest; -import org.websoso.WSSServer.feed.controller.dto.FeedUpdateRequest; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.FeedImage; -import org.websoso.WSSServer.feed.service.CommentServiceImpl; -import org.websoso.WSSServer.feed.service.FeedImageService; -import org.websoso.WSSServer.feed.service.FeedLikeService; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedCreateRequest; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedCreateResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedImageCreateRequest; +import org.websoso.WSSServer.feed.feed.event.FeedImageDeleteEvent; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedImageUpdateRequest; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedUpdateRequest; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.comment.service.CommentServiceImpl; +import org.websoso.WSSServer.feed.feed.service.FeedImageService; +import org.websoso.WSSServer.feed.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; import org.websoso.WSSServer.novel.service.NovelServiceImpl; import org.websoso.WSSServer.user.domain.User; diff --git a/src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java b/src/main/java/org/websoso/WSSServer/feed/feed/application/PopularFeedApplication.java similarity index 74% rename from src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java rename to src/main/java/org/websoso/WSSServer/feed/feed/application/PopularFeedApplication.java index 9c0f588c..6aee1ebb 100644 --- a/src/main/java/org/websoso/WSSServer/feed/application/PopularFeedApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/application/PopularFeedApplication.java @@ -1,14 +1,14 @@ -package org.websoso.WSSServer.feed.application; +package org.websoso.WSSServer.feed.feed.application; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.event.FeedBecamePopularEvent; -import org.websoso.WSSServer.feed.service.FeedLikeService; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; -import org.websoso.WSSServer.feed.service.PopularFeedService; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.event.FeedBecamePopularEvent; +import org.websoso.WSSServer.feed.feed.service.FeedLikeService; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.service.PopularFeedService; @Service @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/FeedController.java similarity index 88% rename from src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/FeedController.java index a13f9692..5dd76419 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/FeedController.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/FeedController.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller; +package org.websoso.WSSServer.feed.feed.controller; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.NO_CONTENT; @@ -20,20 +20,20 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.feed.controller.dto.UserFeedsGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.UserFeedsGetResponse; import org.websoso.WSSServer.dto.novel.NovelGetResponseFeedTab; -import org.websoso.WSSServer.feed.application.FeedFindApplication; +import org.websoso.WSSServer.feed.feed.application.FeedFindApplication; import org.websoso.WSSServer.domain.common.FeedGetOption; -import org.websoso.WSSServer.feed.controller.dto.FeedCreateRequest; -import org.websoso.WSSServer.feed.controller.dto.FeedCreateResponse; -import org.websoso.WSSServer.feed.controller.dto.FeedGetResponse; -import org.websoso.WSSServer.feed.controller.dto.FeedImageCreateRequest; -import org.websoso.WSSServer.feed.controller.dto.FeedImageUpdateRequest; -import org.websoso.WSSServer.feed.controller.dto.FeedUpdateRequest; -import org.websoso.WSSServer.feed.controller.dto.FeedsGetResponse; -import org.websoso.WSSServer.dto.popularFeed.PopularFeedsGetResponse; -import org.websoso.WSSServer.feed.application.FeedLikeApplication; -import org.websoso.WSSServer.feed.application.FeedManagementApplication; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedCreateRequest; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedCreateResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedImageCreateRequest; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedImageUpdateRequest; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedUpdateRequest; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedsGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.PopularFeedsGetResponse; +import org.websoso.WSSServer.feed.feed.application.FeedLikeApplication; +import org.websoso.WSSServer.feed.feed.application.FeedManagementApplication; import org.websoso.WSSServer.user.domain.User; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateRequest.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedCreateRequest.java similarity index 91% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedCreateRequest.java index 4ef9b1aa..7915ca7f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedCreateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedCreateResponse.java similarity index 81% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedCreateResponse.java index 1298434b..b581b3c6 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedCreateResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedCreateResponse.java @@ -1,6 +1,6 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; -import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.feed.domain.FeedImage; import java.util.Comparator; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedGetResponse.java similarity index 96% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedGetResponse.java index 8c12f71b..bd30db16 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedGetResponse.java @@ -1,8 +1,8 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import java.util.List; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.FeedImage; import org.websoso.WSSServer.novel.domain.Novel; import org.websoso.WSSServer.library.domain.UserNovel; import org.websoso.WSSServer.dto.user.UserBasicInfo; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageCreateRequest.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedImageCreateRequest.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageCreateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedImageCreateRequest.java index 9206f318..50175ab4 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageCreateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedImageCreateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import jakarta.validation.constraints.Size; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageUpdateRequest.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedImageUpdateRequest.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageUpdateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedImageUpdateRequest.java index a373c8d0..62af6203 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedImageUpdateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedImageUpdateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import jakarta.validation.constraints.Size; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedInfo.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedInfo.java similarity index 97% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedInfo.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedInfo.java index b8cb6880..70087793 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedInfo.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedInfo.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import java.util.List; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.novel.domain.Novel; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.library.domain.UserNovel; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedUpdateRequest.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedUpdateRequest.java similarity index 91% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedUpdateRequest.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedUpdateRequest.java index f3b5f4f5..f2faa4d7 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedUpdateRequest.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedUpdateRequest.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedsGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedsGetResponse.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedsGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedsGetResponse.java index 9d48b0dd..973e9da7 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/FeedsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/FeedsGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/dto/popularFeed/PopularFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/PopularFeedGetResponse.java similarity index 88% rename from src/main/java/org/websoso/WSSServer/dto/popularFeed/PopularFeedGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/PopularFeedGetResponse.java index ff9faa17..36cf5a8a 100644 --- a/src/main/java/org/websoso/WSSServer/dto/popularFeed/PopularFeedGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/PopularFeedGetResponse.java @@ -1,6 +1,6 @@ -package org.websoso.WSSServer.dto.popularFeed; +package org.websoso.WSSServer.feed.feed.controller.dto; -import org.websoso.WSSServer.feed.domain.PopularFeed; +import org.websoso.WSSServer.feed.feed.domain.PopularFeed; public record PopularFeedGetResponse( Long feedId, diff --git a/src/main/java/org/websoso/WSSServer/dto/popularFeed/PopularFeedsGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/PopularFeedsGetResponse.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/dto/popularFeed/PopularFeedsGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/PopularFeedsGetResponse.java index 7ac5f2c1..6dd7a57d 100644 --- a/src/main/java/org/websoso/WSSServer/dto/popularFeed/PopularFeedsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/PopularFeedsGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.dto.popularFeed; +package org.websoso.WSSServer.feed.feed.controller.dto; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/UserFeedGetResponse.java similarity index 90% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/UserFeedGetResponse.java index 4fbd809c..e6aa4fe6 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/UserFeedGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; public record UserFeedGetResponse( Long feedId, diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedsGetResponse.java b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/UserFeedsGetResponse.java similarity index 88% rename from src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedsGetResponse.java rename to src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/UserFeedsGetResponse.java index 58a7a9a1..570c4a5d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/dto/UserFeedsGetResponse.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/controller/dto/UserFeedsGetResponse.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller.dto; +package org.websoso.WSSServer.feed.feed.controller.dto; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java b/src/main/java/org/websoso/WSSServer/feed/feed/domain/Feed.java similarity index 95% rename from src/main/java/org/websoso/WSSServer/feed/domain/Feed.java rename to src/main/java/org/websoso/WSSServer/feed/feed/domain/Feed.java index 89a36b7a..1a8c2f4d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Feed.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/domain/Feed.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.feed.domain; import static jakarta.persistence.CascadeType.ALL; import static jakarta.persistence.GenerationType.IDENTITY; @@ -20,6 +20,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicInsert; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.report.domain.ReportedFeed; import org.websoso.WSSServer.user.domain.User; @Getter diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java b/src/main/java/org/websoso/WSSServer/feed/feed/domain/FeedImage.java similarity index 96% rename from src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java rename to src/main/java/org/websoso/WSSServer/feed/feed/domain/FeedImage.java index a78ea3a8..e4919d18 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/FeedImage.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/domain/FeedImage.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.feed.domain; import static jakarta.persistence.GenerationType.IDENTITY; diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/Like.java b/src/main/java/org/websoso/WSSServer/feed/feed/domain/Like.java similarity index 96% rename from src/main/java/org/websoso/WSSServer/feed/domain/Like.java rename to src/main/java/org/websoso/WSSServer/feed/feed/domain/Like.java index 935ae3dc..98d61b8a 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/Like.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/domain/Like.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.feed.domain; import static jakarta.persistence.GenerationType.IDENTITY; diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/PopularFeed.java b/src/main/java/org/websoso/WSSServer/feed/feed/domain/PopularFeed.java similarity index 94% rename from src/main/java/org/websoso/WSSServer/feed/domain/PopularFeed.java rename to src/main/java/org/websoso/WSSServer/feed/feed/domain/PopularFeed.java index d9152418..51f4620d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/PopularFeed.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/domain/PopularFeed.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.feed.domain; import static jakarta.persistence.GenerationType.IDENTITY; diff --git a/src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java b/src/main/java/org/websoso/WSSServer/feed/feed/event/FeedBecamePopularEvent.java similarity index 79% rename from src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java rename to src/main/java/org/websoso/WSSServer/feed/feed/event/FeedBecamePopularEvent.java index f4e2e10b..d2769e8d 100644 --- a/src/main/java/org/websoso/WSSServer/feed/event/FeedBecamePopularEvent.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/event/FeedBecamePopularEvent.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.event; +package org.websoso.WSSServer.feed.feed.event; public record FeedBecamePopularEvent( Long feedId diff --git a/src/main/java/org/websoso/WSSServer/feed/event/FeedImageDeleteEvent.java b/src/main/java/org/websoso/WSSServer/feed/feed/event/FeedImageDeleteEvent.java similarity index 65% rename from src/main/java/org/websoso/WSSServer/feed/event/FeedImageDeleteEvent.java rename to src/main/java/org/websoso/WSSServer/feed/feed/event/FeedImageDeleteEvent.java index 81586bb5..65f28a29 100644 --- a/src/main/java/org/websoso/WSSServer/feed/event/FeedImageDeleteEvent.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/event/FeedImageDeleteEvent.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.event; +package org.websoso.WSSServer.feed.feed.event; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java b/src/main/java/org/websoso/WSSServer/feed/feed/event/FeedLikedEvent.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java rename to src/main/java/org/websoso/WSSServer/feed/feed/event/FeedLikedEvent.java index cef98953..a0b6eb60 100644 --- a/src/main/java/org/websoso/WSSServer/feed/event/FeedLikedEvent.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/event/FeedLikedEvent.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.event; +package org.websoso.WSSServer.feed.feed.event; public record FeedLikedEvent( Long userId, diff --git a/src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java b/src/main/java/org/websoso/WSSServer/feed/feed/event/PopularFeedCheckEvent.java similarity index 79% rename from src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java rename to src/main/java/org/websoso/WSSServer/feed/feed/event/PopularFeedCheckEvent.java index a43e3084..c26acd1e 100644 --- a/src/main/java/org/websoso/WSSServer/feed/event/PopularFeedCheckEvent.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/event/PopularFeedCheckEvent.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.event; +package org.websoso.WSSServer.feed.feed.event; public record PopularFeedCheckEvent( Long feedId diff --git a/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedError.java b/src/main/java/org/websoso/WSSServer/feed/feed/exception/CustomFeedError.java similarity index 96% rename from src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedError.java rename to src/main/java/org/websoso/WSSServer/feed/feed/exception/CustomFeedError.java index da175eaa..b97d84f7 100644 --- a/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedError.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/exception/CustomFeedError.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.exception; +package org.websoso.WSSServer.feed.feed.exception; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CONFLICT; diff --git a/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedException.java b/src/main/java/org/websoso/WSSServer/feed/feed/exception/CustomFeedException.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedException.java rename to src/main/java/org/websoso/WSSServer/feed/feed/exception/CustomFeedException.java index fcbba792..8d5481bc 100644 --- a/src/main/java/org/websoso/WSSServer/feed/exception/CustomFeedException.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/exception/CustomFeedException.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.exception; +package org.websoso.WSSServer.feed.feed.exception; import lombok.Getter; import org.websoso.common.exception.AbstractCustomException; diff --git a/src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java b/src/main/java/org/websoso/WSSServer/feed/feed/listener/FeedUserToUnknownEventListener.java similarity index 83% rename from src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java rename to src/main/java/org/websoso/WSSServer/feed/feed/listener/FeedUserToUnknownEventListener.java index b70b2e49..42bb7e93 100644 --- a/src/main/java/org/websoso/WSSServer/feed/listener/FeedUserToUnknownEventListener.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/listener/FeedUserToUnknownEventListener.java @@ -1,10 +1,10 @@ -package org.websoso.WSSServer.feed.listener; +package org.websoso.WSSServer.feed.feed.listener; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; -import org.websoso.WSSServer.feed.application.FeedManagementApplication; +import org.websoso.WSSServer.feed.feed.application.FeedManagementApplication; import org.websoso.WSSServer.user.event.WithdrawUserEvent; @Component diff --git a/src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java b/src/main/java/org/websoso/WSSServer/feed/feed/listener/PopularFeedCheckEventListener.java similarity index 74% rename from src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java rename to src/main/java/org/websoso/WSSServer/feed/feed/listener/PopularFeedCheckEventListener.java index b9a9c0a2..51819032 100644 --- a/src/main/java/org/websoso/WSSServer/feed/listener/PopularFeedCheckEventListener.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/listener/PopularFeedCheckEventListener.java @@ -1,11 +1,11 @@ -package org.websoso.WSSServer.feed.listener; +package org.websoso.WSSServer.feed.feed.listener; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; -import org.websoso.WSSServer.feed.application.PopularFeedApplication; -import org.websoso.WSSServer.feed.event.PopularFeedCheckEvent; +import org.websoso.WSSServer.feed.feed.application.PopularFeedApplication; +import org.websoso.WSSServer.feed.feed.event.PopularFeedCheckEvent; @Component @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedCustomRepository.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedCustomRepository.java index 1defeec4..fe0b53f7 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedCustomRepository.java @@ -1,9 +1,9 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.feed.repository; import java.util.List; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.SortCriteria; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedCustomRepositoryImpl.java similarity index 96% rename from src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedCustomRepositoryImpl.java index d5a50d6c..6459c8c8 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCustomRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedCustomRepositoryImpl.java @@ -1,9 +1,8 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.feed.repository; import static org.websoso.WSSServer.domain.QGenre.genre; -import static org.websoso.WSSServer.feed.domain.QFeed.feed; -import static org.websoso.WSSServer.feed.domain.QFeedImage.feedImage; -import static org.websoso.WSSServer.feed.domain.QLike.like; +import static org.websoso.WSSServer.feed.feed.domain.QFeed.feed; +import static org.websoso.WSSServer.feed.feed.domain.QLike.like; import static org.websoso.WSSServer.library.domain.QUserNovel.userNovel; import static org.websoso.WSSServer.novel.domain.QNovel.novel; import static org.websoso.WSSServer.novel.domain.QNovelGenre.novelGenre; @@ -15,21 +14,20 @@ import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; + import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.stream.Collectors; + import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.QFeed; -import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.domain.Genre; +import org.websoso.WSSServer.feed.feed.domain.QFeed; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.domain.common.FeedImageType; import org.websoso.WSSServer.domain.common.SortCriteria; @Repository diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedQueryRepository.java similarity index 52% rename from src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedQueryRepository.java index 584a488b..22092735 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedQueryRepository.java @@ -1,8 +1,8 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.feed.repository; -import org.websoso.WSSServer.feed.repository.projection.FeedInfoRow; -import org.websoso.WSSServer.feed.repository.projection.PopularFeedInfoRow; -import org.websoso.WSSServer.feed.repository.projection.UserFeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.projection.FeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.projection.PopularFeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.projection.UserFeedInfoRow; import java.util.List; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedQueryRepositoryImpl.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedQueryRepositoryImpl.java index 2c451448..e22f9e83 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedQueryRepositoryImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedQueryRepositoryImpl.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.feed.repository; -import static org.websoso.WSSServer.feed.domain.QFeed.feed; -import static org.websoso.WSSServer.feed.domain.QPopularFeed.popularFeed; +import static org.websoso.WSSServer.feed.feed.domain.QFeed.feed; +import static org.websoso.WSSServer.feed.feed.domain.QPopularFeed.popularFeed; import static org.websoso.WSSServer.novel.domain.QNovel.novel; import static org.websoso.WSSServer.user.domain.QAvatarProfile.avatarProfile; @@ -12,17 +12,20 @@ import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.impl.JPAQueryFactory; + import java.util.List; + import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.websoso.WSSServer.domain.QGenre; import org.websoso.WSSServer.domain.common.FeedImageType; -import org.websoso.WSSServer.feed.domain.QComment; -import org.websoso.WSSServer.feed.domain.QFeedImage; -import org.websoso.WSSServer.feed.domain.QLike; -import org.websoso.WSSServer.feed.repository.projection.FeedInfoRow; -import org.websoso.WSSServer.feed.repository.projection.PopularFeedInfoRow; -import org.websoso.WSSServer.feed.repository.projection.UserFeedInfoRow; + +import org.websoso.WSSServer.feed.comment.domain.QComment; +import org.websoso.WSSServer.feed.feed.domain.QFeedImage; +import org.websoso.WSSServer.feed.feed.domain.QLike; +import org.websoso.WSSServer.feed.feed.repository.projection.FeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.projection.PopularFeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.projection.UserFeedInfoRow; import org.websoso.WSSServer.library.domain.QUserNovel; import org.websoso.WSSServer.novel.domain.QNovelGenre; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedRepository.java similarity index 77% rename from src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedRepository.java index 971f26a2..0138de45 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/FeedRepository.java @@ -1,14 +1,12 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.feed.repository; import java.util.List; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; @Repository public interface FeedRepository extends JpaRepository, FeedCustomRepository { diff --git a/src/main/java/org/websoso/WSSServer/feed/feed/repository/LikeRepository.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/LikeRepository.java new file mode 100644 index 00000000..3d996654 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/LikeRepository.java @@ -0,0 +1,24 @@ +package org.websoso.WSSServer.feed.feed.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Like; + +@Repository +public interface LikeRepository extends JpaRepository { + + boolean existsByUserIdAndFeed(Long userId, Feed feed); + + @Modifying + @Query("DELETE FROM Like l WHERE l.feed.feedId = :feedId") + void deleteByFeedId(Long feedId); + + @Modifying + @Query("DELETE FROM Like l WHERE l.userId = :userId AND l.feed = :feed") + void deleteByUserIdAndFeed(Long userId, Feed feed); + + long countByFeed_FeedId(Long feedFeedId); +} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/PopularFeedRepository.java similarity index 51% rename from src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedRepository.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/PopularFeedRepository.java index b8caaebc..02ccbec4 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/PopularFeedRepository.java @@ -1,13 +1,12 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.feed.repository; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.PopularFeed; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.PopularFeed; @Repository -public interface PopularFeedRepository extends JpaRepository, PopularFeedCustomRepository { +public interface PopularFeedRepository extends JpaRepository { Boolean existsByFeed(Feed feed); diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/projection/FeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/FeedInfoRow.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/repository/projection/FeedInfoRow.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/FeedInfoRow.java index 539465ba..4a522ba8 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/projection/FeedInfoRow.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/FeedInfoRow.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.repository.projection; +package org.websoso.WSSServer.feed.feed.repository.projection; import java.time.LocalDateTime; -import org.websoso.WSSServer.feed.controller.dto.FeedInfo; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedInfo; import org.websoso.WSSServer.util.TimeFormatUtil; public record FeedInfoRow( diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/projection/PopularFeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/PopularFeedInfoRow.java similarity index 84% rename from src/main/java/org/websoso/WSSServer/feed/repository/projection/PopularFeedInfoRow.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/PopularFeedInfoRow.java index 3b4b641b..729037e9 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/projection/PopularFeedInfoRow.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/PopularFeedInfoRow.java @@ -1,6 +1,6 @@ -package org.websoso.WSSServer.feed.repository.projection; +package org.websoso.WSSServer.feed.feed.repository.projection; -import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.PopularFeedGetResponse; public record PopularFeedInfoRow( Long feedId, diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/projection/UserFeedInfoRow.java b/src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/UserFeedInfoRow.java similarity index 92% rename from src/main/java/org/websoso/WSSServer/feed/repository/projection/UserFeedInfoRow.java rename to src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/UserFeedInfoRow.java index cb5e71c3..b8e289f2 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/projection/UserFeedInfoRow.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/repository/projection/UserFeedInfoRow.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.repository.projection; +package org.websoso.WSSServer.feed.feed.repository.projection; import java.time.LocalDateTime; -import org.websoso.WSSServer.feed.controller.dto.UserFeedGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.UserFeedGetResponse; import org.websoso.WSSServer.util.TimeFormatUtil; public record UserFeedInfoRow( diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedImageService.java similarity index 94% rename from src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java rename to src/main/java/org/websoso/WSSServer/feed/feed/service/FeedImageService.java index a7e111bb..89922a23 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedImageService.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedImageService.java @@ -1,11 +1,11 @@ -package org.websoso.WSSServer.feed.service; +package org.websoso.WSSServer.feed.feed.service; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import org.websoso.WSSServer.feed.domain.FeedImage; +import org.websoso.WSSServer.feed.feed.domain.FeedImage; import org.websoso.WSSServer.service.ImageClient; @Service diff --git a/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedLikeService.java new file mode 100644 index 00000000..8eded581 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedLikeService.java @@ -0,0 +1,50 @@ +package org.websoso.WSSServer.feed.feed.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Like; +import org.websoso.WSSServer.feed.feed.repository.LikeRepository; + +@Service +@RequiredArgsConstructor +public class FeedLikeService { + + private final LikeRepository likeRepository; + + @Transactional + public boolean create(Long userId, Feed feed) { + if (likeRepository.existsByUserIdAndFeed(userId, feed)) return false; + + try { + likeRepository.save(Like.create(userId, feed)); + return true; + } catch (DataIntegrityViolationException e) { + // 동시 요청으로 이미 생성되어 무결성을 위반한 경우 패스 처리 + return false; + } + } + + @Transactional + public void delete(Long userId, Feed feed) { + likeRepository.deleteByUserIdAndFeed(userId, feed); + } + + @Transactional + public void deleteByFeedId(Long feedId) { + likeRepository.deleteByFeedId(feedId); + } + + @Transactional(readOnly = true) + public long countByFeedId(Long feedId) { + return likeRepository.countByFeed_FeedId(feedId); + } + + @Transactional(readOnly = true) + public boolean isUserLikedFeed(Long userId, Feed feed) { + return likeRepository.existsByUserIdAndFeed(userId, feed); + } + +} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedQueryService.java similarity index 76% rename from src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java rename to src/main/java/org/websoso/WSSServer/feed/feed/service/FeedQueryService.java index b050327e..a795eb0f 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedQueryService.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedQueryService.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.service; +package org.websoso.WSSServer.feed.feed.service; import java.util.List; import java.util.Map; @@ -8,14 +8,14 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.controller.dto.FeedInfo; -import org.websoso.WSSServer.feed.controller.dto.UserFeedGetResponse; -import org.websoso.WSSServer.dto.popularFeed.PopularFeedGetResponse; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.repository.projection.FeedInfoRow; -import org.websoso.WSSServer.feed.repository.FeedQueryRepository; -import org.websoso.WSSServer.feed.repository.projection.PopularFeedInfoRow; -import org.websoso.WSSServer.feed.repository.projection.UserFeedInfoRow; +import org.websoso.WSSServer.feed.feed.controller.dto.FeedInfo; +import org.websoso.WSSServer.feed.feed.controller.dto.UserFeedGetResponse; +import org.websoso.WSSServer.feed.feed.controller.dto.PopularFeedGetResponse; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.repository.projection.FeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.FeedQueryRepository; +import org.websoso.WSSServer.feed.feed.repository.projection.PopularFeedInfoRow; +import org.websoso.WSSServer.feed.feed.repository.projection.UserFeedInfoRow; @Service @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedServiceImpl.java similarity index 91% rename from src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java rename to src/main/java/org/websoso/WSSServer/feed/feed/service/FeedServiceImpl.java index e906c17f..d98e40d1 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/service/FeedServiceImpl.java @@ -1,7 +1,7 @@ -package org.websoso.WSSServer.feed.service; +package org.websoso.WSSServer.feed.feed.service; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.FEED_NOT_FOUND; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.HIDDEN_FEED_ACCESS; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.FEED_NOT_FOUND; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.HIDDEN_FEED_ACCESS; import static org.websoso.WSSServer.exception.error.CustomUserError.INVALID_AUTHORIZED; import java.util.Comparator; @@ -16,10 +16,10 @@ import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.common.SortCriteria; -import org.websoso.WSSServer.feed.exception.CustomFeedException; +import org.websoso.WSSServer.feed.feed.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; import org.websoso.WSSServer.user.domain.User; @Service diff --git a/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java b/src/main/java/org/websoso/WSSServer/feed/feed/service/PopularFeedService.java similarity index 70% rename from src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java rename to src/main/java/org/websoso/WSSServer/feed/feed/service/PopularFeedService.java index c4b05632..d2fbfbaa 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/PopularFeedService.java +++ b/src/main/java/org/websoso/WSSServer/feed/feed/service/PopularFeedService.java @@ -1,11 +1,11 @@ -package org.websoso.WSSServer.feed.service; +package org.websoso.WSSServer.feed.feed.service; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.PopularFeed; -import org.websoso.WSSServer.feed.repository.PopularFeedRepository; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.PopularFeed; +import org.websoso.WSSServer.feed.feed.repository.PopularFeedRepository; @Service @RequiredArgsConstructor diff --git a/src/main/java/org/websoso/WSSServer/application/ReportApplication.java b/src/main/java/org/websoso/WSSServer/feed/report/application/ReportApplication.java similarity index 82% rename from src/main/java/org/websoso/WSSServer/application/ReportApplication.java rename to src/main/java/org/websoso/WSSServer/feed/report/application/ReportApplication.java index b1a7336d..8a0c6d76 100644 --- a/src/main/java/org/websoso/WSSServer/application/ReportApplication.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/application/ReportApplication.java @@ -1,11 +1,11 @@ -package org.websoso.WSSServer.application; +package org.websoso.WSSServer.feed.report.application; import static org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessageType.REPORT; import static org.websoso.WSSServer.domain.common.ReportedType.IMPERTINENCE; import static org.websoso.WSSServer.domain.common.ReportedType.SPOILER; -import static org.websoso.WSSServer.exception.error.CustomCommentError.ALREADY_REPORTED_COMMENT; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.ALREADY_REPORTED_FEED; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.SELF_REPORT_NOT_ALLOWED; +import static org.websoso.WSSServer.feed.comment.exception.CustomCommentError.ALREADY_REPORTED_COMMENT; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.ALREADY_REPORTED_FEED; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.SELF_REPORT_NOT_ALLOWED; import static org.websoso.WSSServer.exception.error.CustomUserError.USER_NOT_FOUND; import lombok.RequiredArgsConstructor; @@ -14,15 +14,15 @@ import org.websoso.WSSServer.infrastructure.discord.DiscordMessageClient; import org.websoso.WSSServer.infrastructure.discord.DiscordWebhookMessage; import org.websoso.WSSServer.domain.common.ReportedType; -import org.websoso.WSSServer.exception.error.CustomCommentError; -import org.websoso.WSSServer.exception.exception.CustomCommentException; -import org.websoso.WSSServer.feed.exception.CustomFeedException; +import org.websoso.WSSServer.feed.comment.exception.CustomCommentError; +import org.websoso.WSSServer.feed.comment.exception.CustomCommentException; +import org.websoso.WSSServer.feed.feed.exception.CustomFeedException; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.service.CommentServiceImpl; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; -import org.websoso.WSSServer.feed.service.ReportServiceImpl; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.comment.service.CommentServiceImpl; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.report.service.ReportServiceImpl; import org.websoso.WSSServer.notification.service.MessageFormatter; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.repository.UserRepository; diff --git a/src/main/java/org/websoso/WSSServer/feed/controller/ReportController.java b/src/main/java/org/websoso/WSSServer/feed/report/controller/ReportController.java similarity index 95% rename from src/main/java/org/websoso/WSSServer/feed/controller/ReportController.java rename to src/main/java/org/websoso/WSSServer/feed/report/controller/ReportController.java index 5810d0dd..dcc2e1fd 100644 --- a/src/main/java/org/websoso/WSSServer/feed/controller/ReportController.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/controller/ReportController.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.controller; +package org.websoso.WSSServer.feed.report.controller; import static org.springframework.http.HttpStatus.CREATED; import static org.websoso.WSSServer.domain.common.ReportedType.IMPERTINENCE; @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.websoso.WSSServer.application.ReportApplication; +import org.websoso.WSSServer.feed.report.application.ReportApplication; import org.websoso.WSSServer.user.domain.User; @RequestMapping("/feeds") diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/ReportedComment.java b/src/main/java/org/websoso/WSSServer/feed/report/domain/ReportedComment.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/domain/ReportedComment.java rename to src/main/java/org/websoso/WSSServer/feed/report/domain/ReportedComment.java index 1e90cdcb..42ff343a 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/ReportedComment.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/domain/ReportedComment.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.report.domain; import static jakarta.persistence.GenerationType.IDENTITY; @@ -14,6 +14,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.websoso.WSSServer.feed.comment.domain.Comment; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.ReportedType; diff --git a/src/main/java/org/websoso/WSSServer/feed/domain/ReportedFeed.java b/src/main/java/org/websoso/WSSServer/feed/report/domain/ReportedFeed.java similarity index 93% rename from src/main/java/org/websoso/WSSServer/feed/domain/ReportedFeed.java rename to src/main/java/org/websoso/WSSServer/feed/report/domain/ReportedFeed.java index 6639480d..c44a7e34 100644 --- a/src/main/java/org/websoso/WSSServer/feed/domain/ReportedFeed.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/domain/ReportedFeed.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.domain; +package org.websoso.WSSServer.feed.report.domain; import static jakarta.persistence.GenerationType.IDENTITY; @@ -14,6 +14,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.ReportedType; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java b/src/main/java/org/websoso/WSSServer/feed/report/repository/ReportedCommentRepository.java similarity index 85% rename from src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java rename to src/main/java/org/websoso/WSSServer/feed/report/repository/ReportedCommentRepository.java index d8da75ad..0b1eb065 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/ReportedCommentRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/repository/ReportedCommentRepository.java @@ -1,4 +1,4 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.report.repository; import io.lettuce.core.dynamic.annotation.Param; import java.util.List; @@ -6,8 +6,8 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.ReportedComment; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.report.domain.ReportedComment; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.ReportedType; diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/ReportedFeedRepository.java b/src/main/java/org/websoso/WSSServer/feed/report/repository/ReportedFeedRepository.java similarity index 74% rename from src/main/java/org/websoso/WSSServer/feed/repository/ReportedFeedRepository.java rename to src/main/java/org/websoso/WSSServer/feed/report/repository/ReportedFeedRepository.java index 5611ee86..da8d235b 100644 --- a/src/main/java/org/websoso/WSSServer/feed/repository/ReportedFeedRepository.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/repository/ReportedFeedRepository.java @@ -1,9 +1,9 @@ -package org.websoso.WSSServer.feed.repository; +package org.websoso.WSSServer.feed.report.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.ReportedFeed; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.report.domain.ReportedFeed; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.ReportedType; diff --git a/src/main/java/org/websoso/WSSServer/feed/service/ReportServiceImpl.java b/src/main/java/org/websoso/WSSServer/feed/report/service/ReportServiceImpl.java similarity index 80% rename from src/main/java/org/websoso/WSSServer/feed/service/ReportServiceImpl.java rename to src/main/java/org/websoso/WSSServer/feed/report/service/ReportServiceImpl.java index 0b30326f..0dfa43be 100644 --- a/src/main/java/org/websoso/WSSServer/feed/service/ReportServiceImpl.java +++ b/src/main/java/org/websoso/WSSServer/feed/report/service/ReportServiceImpl.java @@ -1,15 +1,15 @@ -package org.websoso.WSSServer.feed.service; +package org.websoso.WSSServer.feed.report.service; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.common.ReportedType; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.ReportedComment; -import org.websoso.WSSServer.feed.domain.ReportedFeed; -import org.websoso.WSSServer.feed.repository.ReportedCommentRepository; -import org.websoso.WSSServer.feed.repository.ReportedFeedRepository; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.report.domain.ReportedComment; +import org.websoso.WSSServer.feed.report.domain.ReportedFeed; +import org.websoso.WSSServer.feed.report.repository.ReportedCommentRepository; +import org.websoso.WSSServer.feed.report.repository.ReportedFeedRepository; import org.websoso.WSSServer.user.domain.User; @Service diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java b/src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java deleted file mode 100644 index 653d30cb..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/FeedCountProjection.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -public interface FeedCountProjection { - - Long getFeedId(); - - Long getCount(); -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java deleted file mode 100644 index c5433d52..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/LikeRepository.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.Like; - -@Repository -public interface LikeRepository extends JpaRepository { - - Optional findByUserIdAndFeed(Long userId, Feed feed); - - boolean existsByUserIdAndFeed(Long userId, Feed feed); - - @Query("SELECT l.feed.feedId FROM Like l WHERE l.userId = :userId AND l.feed.feedId IN :feedIds") - List findLikedFeedIds(Long userId, List feedIds); - - @Query(""" - SELECT l.feed.feedId AS feedId, COUNT(l.likeId) AS count - FROM Like l - WHERE l.feed.feedId IN :feedIds - GROUP BY l.feed.feedId - """) - List countByFeedIds(List feedIds); - - List findByFeedFeedIdIn(List feedIds); - - long countByFeed(Feed feed); - - @Modifying - @Query("DELETE FROM Like l WHERE l.feed.feedId = :feedId") - void deleteByFeedId(Long feedId); - - @Modifying - @Query("DELETE FROM Like l WHERE l.userId = :userId AND l.feed = :feed") - void deleteByUserIdAndFeed(Long userId, Feed feed); - - Long feed(Feed feed); - - long countByFeed_FeedId(Long feedFeedId); -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepository.java b/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepository.java deleted file mode 100644 index a7458282..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import java.util.List; -import org.websoso.WSSServer.feed.domain.PopularFeed; - -public interface PopularFeedCustomRepository { - - /** - * 로그인 사용자가 조회 가능한 인기 피드를 조회한다. - * 차단한 사용자 및 차단당한 사용자의 피드는 제외한다. - */ - List findPopularFeedsForMember(Long userId, int size); - - /** - * 비회원이 조회 가능한 인기 피드를 조회한다. - */ - List findPopularFeedsForGuest(int size); -} diff --git a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepositoryImpl.java b/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepositoryImpl.java deleted file mode 100644 index fb462bb8..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/repository/PopularFeedCustomRepositoryImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.websoso.WSSServer.feed.repository; - -import static org.websoso.WSSServer.feed.domain.QFeed.feed; -import static org.websoso.WSSServer.feed.domain.QPopularFeed.popularFeed; -import static org.websoso.WSSServer.user.domain.QBlock.block; -import static org.websoso.WSSServer.user.domain.QUser.user; - - -import com.querydsl.core.types.dsl.CaseBuilder; -import com.querydsl.core.types.dsl.NumberExpression; -import com.querydsl.jpa.impl.JPAQueryFactory; - -import java.util.List; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; -import org.websoso.WSSServer.feed.domain.PopularFeed; - -@Repository -@RequiredArgsConstructor -public class PopularFeedCustomRepositoryImpl implements PopularFeedCustomRepository { - - private final JPAQueryFactory jpaQueryFactory; - - @Override - public List findPopularFeedsForMember(Long userId, int size) { - List blockIds = getBlockIds(userId); - - return jpaQueryFactory - .selectFrom(popularFeed) - .join(popularFeed.feed, feed) - .join(feed.user, user) - .where( - feed.isPublic.isTrue(), - feed.isHidden.isFalse(), - user.userId.notIn(blockIds) - ) - .orderBy(popularFeed.popularFeedId.desc()) - .limit(size) - .fetch(); - } - - @Override - public List findPopularFeedsForGuest(int size) { - return jpaQueryFactory - .selectFrom(popularFeed) - .join(popularFeed.feed, feed) - .where( - feed.isPublic.isTrue(), - feed.isHidden.isFalse() - ) - .orderBy(popularFeed.popularFeedId.desc()) - .limit(size) - .fetch(); - } - - /** - * 본인이 차단했거나, 본인을 차단한 사용자의 ID를 조회한다. - * - * @param userId 사용자 ID - * @return 차단 사용자 ID - */ - private List getBlockIds(Long userId) { - NumberExpression blockUserId = new CaseBuilder() - .when(block.blockingId.eq(userId)).then(block.blockedId) - .otherwise(block.blockingId); - - return jpaQueryFactory - .select(blockUserId) - .from(block) - .where( - block.blockingId.eq(userId) - .or(block.blockedId.eq(userId)) - ) - .distinct() - .fetch(); - } -} diff --git a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java b/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java deleted file mode 100644 index 2a35b0ca..00000000 --- a/src/main/java/org/websoso/WSSServer/feed/service/FeedLikeService.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.websoso.WSSServer.feed.service; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.domain.Like; -import org.websoso.WSSServer.feed.repository.FeedCountProjection; -import org.websoso.WSSServer.feed.repository.LikeRepository; - -@Service -@RequiredArgsConstructor -public class FeedLikeService { - - private final LikeRepository likeRepository; - - @Transactional - public boolean create(Long userId, Feed feed) { - if (likeRepository.existsByUserIdAndFeed(userId, feed)) return false; - - try { - likeRepository.save(Like.create(userId, feed)); - return true; - } catch (DataIntegrityViolationException e) { - // 동시 요청으로 이미 생성되어 무결성을 위반한 경우 패스 처리 - return false; - } - } - - @Transactional - public void delete(Long userId, Feed feed) { - likeRepository.deleteByUserIdAndFeed(userId, feed); - } - - @Transactional - public void deleteByFeedId(Long feedId) { - likeRepository.deleteByFeedId(feedId); - } - - @Transactional(readOnly = true) - public long countByFeedId(Long feedId) { - return likeRepository.countByFeed_FeedId(feedId); - } - - @Transactional(readOnly = true) - public boolean isUserLikedFeed(Long userId, Feed feed) { - return likeRepository.existsByUserIdAndFeed(userId, feed); - } - - @Transactional(readOnly = true) - public List findLikedFeedIds(Long userId, List feedIds) { - if (userId == null || feedIds.isEmpty()) { - return Collections.emptyList(); - } - - return likeRepository.findLikedFeedIds(userId, feedIds); - } - - @Transactional(readOnly = true) - public Map countByFeedIds(List feedIds) { - if (feedIds.isEmpty()) { - return Collections.emptyMap(); - } - - return likeRepository.countByFeedIds(feedIds).stream() - .collect(Collectors.toMap( - FeedCountProjection::getFeedId, - projection -> projection.getCount().intValue() - )); - } - - @Transactional(readOnly = true) - public Map> findLikerUserIdsByFeedIds(List feedIds) { - if (feedIds.isEmpty()) { - return Collections.emptyMap(); - } - - return likeRepository.findByFeedFeedIdIn(feedIds).stream() - .collect(Collectors.groupingBy( - like -> like.getFeed().getFeedId(), - Collectors.mapping(Like::getUserId, Collectors.toList()) - )); - } - -} diff --git a/src/main/java/org/websoso/WSSServer/library/service/UserNovelService.java b/src/main/java/org/websoso/WSSServer/library/service/UserNovelService.java index e61216cc..5c8142fe 100644 --- a/src/main/java/org/websoso/WSSServer/library/service/UserNovelService.java +++ b/src/main/java/org/websoso/WSSServer/library/service/UserNovelService.java @@ -14,7 +14,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.library.domain.AttractivePoint; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.library.domain.Keyword; import org.websoso.WSSServer.novel.domain.Novel; @@ -36,7 +36,7 @@ import org.websoso.WSSServer.dto.userNovel.UserTasteAttractivePointPreferencesAndKeywordsGetResponse; import org.websoso.WSSServer.exception.exception.CustomGenreException; import org.websoso.WSSServer.exception.exception.CustomUserException; -import org.websoso.WSSServer.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; import org.websoso.WSSServer.repository.GenreRepository; import org.websoso.WSSServer.library.repository.UserNovelRepository; import org.websoso.WSSServer.user.service.UserService; diff --git a/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java b/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java index accb191a..a96b2a30 100644 --- a/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java +++ b/src/main/java/org/websoso/WSSServer/notification/application/NotificationSendApplication.java @@ -3,8 +3,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; -import org.websoso.WSSServer.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; import org.websoso.WSSServer.notification.domain.Notification; import org.websoso.WSSServer.notification.domain.UserDevice; import org.websoso.WSSServer.notification.service.FcmService; diff --git a/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java b/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java index 0fe6669d..c070620e 100644 --- a/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java +++ b/src/main/java/org/websoso/WSSServer/notification/listener/FeedLikeNotificationListener.java @@ -4,7 +4,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; -import org.websoso.WSSServer.feed.event.FeedLikedEvent; +import org.websoso.WSSServer.feed.feed.event.FeedLikedEvent; import org.websoso.WSSServer.notification.application.NotificationSendApplication; @Component diff --git a/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java b/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java index 50acb25e..9a852857 100644 --- a/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java +++ b/src/main/java/org/websoso/WSSServer/notification/listener/PopularFeedNotificationListener.java @@ -4,7 +4,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; -import org.websoso.WSSServer.feed.event.FeedBecamePopularEvent; +import org.websoso.WSSServer.feed.feed.event.FeedBecamePopularEvent; import org.websoso.WSSServer.notification.application.NotificationSendApplication; @Component diff --git a/src/main/java/org/websoso/WSSServer/notification/service/MessageFormatter.java b/src/main/java/org/websoso/WSSServer/notification/service/MessageFormatter.java index d475b1db..d5cb5d6a 100644 --- a/src/main/java/org/websoso/WSSServer/notification/service/MessageFormatter.java +++ b/src/main/java/org/websoso/WSSServer/notification/service/MessageFormatter.java @@ -7,8 +7,8 @@ import static org.websoso.WSSServer.domain.common.ReportedType.IMPERTINENCE; import static org.websoso.WSSServer.domain.common.ReportedType.SPOILER; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.infrastructure.discord.DiscordMessageTemplate; import org.websoso.WSSServer.domain.common.ReportedType; diff --git a/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java b/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java index a47df6cb..9c6d83ed 100644 --- a/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java +++ b/src/main/java/org/websoso/WSSServer/notification/service/NotificationService.java @@ -10,7 +10,7 @@ import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.notification.controller.response.NotificationPageResponse; import org.websoso.WSSServer.notification.domain.Notification; import org.websoso.WSSServer.notification.domain.NotificationType; diff --git a/src/main/java/org/websoso/WSSServer/service/ImageClient.java b/src/main/java/org/websoso/WSSServer/service/ImageClient.java index 512412cd..ff52f11f 100644 --- a/src/main/java/org/websoso/WSSServer/service/ImageClient.java +++ b/src/main/java/org/websoso/WSSServer/service/ImageClient.java @@ -16,7 +16,7 @@ import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.web.multipart.MultipartFile; -import org.websoso.WSSServer.feed.event.FeedImageDeleteEvent; +import org.websoso.WSSServer.feed.feed.event.FeedImageDeleteEvent; import org.websoso.WSSServer.exception.exception.CustomImageException; import org.websoso.s3.core.S3FileService; import org.websoso.s3.modle.S3UploadResult; diff --git a/src/main/java/org/websoso/WSSServer/user/domain/User.java b/src/main/java/org/websoso/WSSServer/user/domain/User.java index 40911094..bb027823 100644 --- a/src/main/java/org/websoso/WSSServer/user/domain/User.java +++ b/src/main/java/org/websoso/WSSServer/user/domain/User.java @@ -23,8 +23,8 @@ import org.hibernate.annotations.ColumnDefault; import org.websoso.WSSServer.domain.GenrePreference; import org.websoso.WSSServer.notification.domain.ReadNotification; -import org.websoso.WSSServer.feed.domain.ReportedComment; -import org.websoso.WSSServer.feed.domain.ReportedFeed; +import org.websoso.WSSServer.feed.report.domain.ReportedComment; +import org.websoso.WSSServer.feed.report.domain.ReportedFeed; import org.websoso.WSSServer.library.domain.UserNovel; import org.websoso.WSSServer.notification.domain.UserDevice; import org.websoso.common.entity.BaseEntity; diff --git a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java index 74160266..f76e3ccb 100644 --- a/src/main/java/org/websoso/WSSServer/user/service/BlockService.java +++ b/src/main/java/org/websoso/WSSServer/user/service/BlockService.java @@ -6,12 +6,12 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.websoso.WSSServer.feed.exception.CustomFeedException; +import org.websoso.WSSServer.feed.feed.exception.CustomFeedException; import org.websoso.WSSServer.user.domain.Block; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.user.repository.BlockRepository; -import static org.websoso.WSSServer.feed.exception.CustomFeedError.BLOCKED_USER_ACCESS; +import static org.websoso.WSSServer.feed.feed.exception.CustomFeedError.BLOCKED_USER_ACCESS; @Service @RequiredArgsConstructor diff --git a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java index ab1a30ca..e24e7436 100644 --- a/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java +++ b/src/test/java/org/websoso/WSSServer/feed/service/FeedServiceImplTest.java @@ -2,28 +2,20 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import java.util.List; -import java.util.Optional; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.PageRequest; -import org.websoso.WSSServer.domain.Genre; -import org.websoso.WSSServer.domain.common.FeedGetOption; -import org.websoso.WSSServer.exception.exception.CustomGenreException; -import org.websoso.WSSServer.feed.repository.FeedRepository; -import org.websoso.WSSServer.feed.repository.PopularFeedRepository; +import org.websoso.WSSServer.feed.feed.service.FeedServiceImpl; +import org.websoso.WSSServer.feed.feed.repository.FeedRepository; +import org.websoso.WSSServer.feed.feed.repository.PopularFeedRepository; import org.websoso.WSSServer.repository.GenreRepository; @ExtendWith(MockitoExtension.class) diff --git a/src/test/java/org/websoso/WSSServer/notification/service/MessageFormatterTest.java b/src/test/java/org/websoso/WSSServer/notification/service/MessageFormatterTest.java index 9d09b0d5..86faa7c0 100644 --- a/src/test/java/org/websoso/WSSServer/notification/service/MessageFormatterTest.java +++ b/src/test/java/org/websoso/WSSServer/notification/service/MessageFormatterTest.java @@ -9,8 +9,8 @@ import org.springframework.test.util.ReflectionTestUtils; import org.websoso.WSSServer.domain.common.ReportedType; import org.websoso.WSSServer.domain.common.SocialLoginType; -import org.websoso.WSSServer.feed.domain.Comment; -import org.websoso.WSSServer.feed.domain.Feed; +import org.websoso.WSSServer.feed.comment.domain.Comment; +import org.websoso.WSSServer.feed.feed.domain.Feed; import org.websoso.WSSServer.user.domain.User; class MessageFormatterTest {