diff --git a/src/main/java/com/kkokkomu/short_news/comment/repository/CommentRepository.java b/src/main/java/com/kkokkomu/short_news/comment/repository/CommentRepository.java index 2a510a1..607fd50 100644 --- a/src/main/java/com/kkokkomu/short_news/comment/repository/CommentRepository.java +++ b/src/main/java/com/kkokkomu/short_news/comment/repository/CommentRepository.java @@ -1,6 +1,7 @@ package com.kkokkomu.short_news.comment.repository; import com.kkokkomu.short_news.comment.domain.Comment; +import com.kkokkomu.short_news.news.domain.NewsReaction; import com.kkokkomu.short_news.user.domain.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -228,4 +229,16 @@ List findByUserAndCommentLike( @Param("user") User user ); + // 최신순 댓글 단 댓글 조회 + @Query("SELECT c FROM Comment c " + + "WHERE c.user.id = :userId " + + "ORDER BY c.id") + Page findAllByUserAndCorsorFirst(Long userId, Pageable pageable); + + // 최신순 댓글 단 댓글 조회 초기화 + @Query("SELECT c FROM Comment c " + + "WHERE c.user.id = :userId " + + "AND c.id > :cursorId " + + "ORDER BY c.id") + Page findAllByUserAndCorsor(@Param("userId") Long userId, @Param("cursorId") Long cursorId, Pageable pageable); } diff --git a/src/main/java/com/kkokkomu/short_news/comment/service/CommentService.java b/src/main/java/com/kkokkomu/short_news/comment/service/CommentService.java index 14faac7..45fcfa1 100644 --- a/src/main/java/com/kkokkomu/short_news/comment/service/CommentService.java +++ b/src/main/java/com/kkokkomu/short_news/comment/service/CommentService.java @@ -4,6 +4,7 @@ import com.kkokkomu.short_news.comment.dto.comment.response.*; import com.kkokkomu.short_news.core.config.service.RedisService; import com.kkokkomu.short_news.news.domain.News; +import com.kkokkomu.short_news.news.domain.NewsReaction; import com.kkokkomu.short_news.news.service.NewsLookupService; import com.kkokkomu.short_news.user.domain.User; import com.kkokkomu.short_news.comment.dto.comment.request.CreateCommentDto; @@ -470,5 +471,26 @@ public CursorResponseDto> guestReadOldestReply(Long parentId, return CursorResponseDto.fromEntityAndPageInfo(replyListDtos, cursorInfoDto); } // 비로그인 오래된순 대댓글 조회 - /* 관리자 */ + /* 유틸 */ + @Transactional(readOnly = true) + public Page getNewsCommentByCursor(Long userId, Long cursorId, int size) { + log.info("getNewsCommentByCursor service"); + + PageRequest pageRequest = PageRequest.of(0, size); + + Page results; + if (cursorId == null) { + // 최초 + results = commentRepository.findAllByUserAndCorsorFirst(userId, pageRequest); + } else { + // 그 이후 + if (!commentRepository.existsById(cursorId)) { + throw new CommonException(ErrorCode.NOT_FOUND_CURSOR); + } + + results = commentRepository.findAllByUserAndCorsor(userId, cursorId, pageRequest); + } + + return results; + } } diff --git a/src/main/java/com/kkokkomu/short_news/news/controller/NewsLogController.java b/src/main/java/com/kkokkomu/short_news/news/controller/NewsLogController.java index ed62e01..91506fa 100644 --- a/src/main/java/com/kkokkomu/short_news/news/controller/NewsLogController.java +++ b/src/main/java/com/kkokkomu/short_news/news/controller/NewsLogController.java @@ -5,6 +5,7 @@ import com.kkokkomu.short_news.core.dto.ResponseDto; import com.kkokkomu.short_news.news.dto.news.response.NewsInfoDto; import com.kkokkomu.short_news.news.dto.news.response.SearchNewsDto; +import com.kkokkomu.short_news.news.dto.newsHist.response.CommentHistInfoDto; import com.kkokkomu.short_news.news.dto.newsHist.response.NewsHistInfoDto; import com.kkokkomu.short_news.news.service.NewsLogService; import io.swagger.v3.oas.annotations.Operation; @@ -26,7 +27,7 @@ public class NewsLogController { @Operation(summary = "댓글 달았던 뉴스 조회") @GetMapping("/commented") - public ResponseDto>> readCommentedNews( + public ResponseDto>> readCommentedNews( @UserId Long userId, @RequestParam(value = "cursorId", required = false) Long cursorId, @RequestParam("size") int size @@ -37,7 +38,7 @@ public ResponseDto>> readCommentedNews( @Operation(summary = "감정표현한 뉴스 조회") @GetMapping("/reaction") - public ResponseDto>> readReactionNews( + public ResponseDto>> readReactionNews( @UserId Long userId, @RequestParam(value = "cursorId", required = false) Long cursorId, @RequestParam("size") int size diff --git a/src/main/java/com/kkokkomu/short_news/news/dto/newsHist/response/CommentHistInfoDto.java b/src/main/java/com/kkokkomu/short_news/news/dto/newsHist/response/CommentHistInfoDto.java new file mode 100644 index 0000000..4be11f4 --- /dev/null +++ b/src/main/java/com/kkokkomu/short_news/news/dto/newsHist/response/CommentHistInfoDto.java @@ -0,0 +1,12 @@ +package com.kkokkomu.short_news.news.dto.newsHist.response; + +import com.kkokkomu.short_news.comment.dto.comment.response.CommentDto; +import com.kkokkomu.short_news.news.dto.news.response.NewsInfoDto; +import lombok.Builder; + +@Builder +public record CommentHistInfoDto( + CommentDto comment, + NewsInfoDto news +) { +} diff --git a/src/main/java/com/kkokkomu/short_news/news/repository/NewsReactionRepository.java b/src/main/java/com/kkokkomu/short_news/news/repository/NewsReactionRepository.java index 9347873..b05e519 100644 --- a/src/main/java/com/kkokkomu/short_news/news/repository/NewsReactionRepository.java +++ b/src/main/java/com/kkokkomu/short_news/news/repository/NewsReactionRepository.java @@ -2,9 +2,14 @@ import com.kkokkomu.short_news.news.domain.News; import com.kkokkomu.short_news.news.domain.NewsReaction; +import com.kkokkomu.short_news.news.domain.NewsViewHist; import com.kkokkomu.short_news.user.domain.User; import com.kkokkomu.short_news.core.type.ENewsReaction; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.Optional; @@ -19,5 +24,16 @@ public interface NewsReactionRepository extends JpaRepository findByNewsAndUser(News news, User user); + @Query("SELECT nr FROM NewsReaction nr " + + "WHERE nr.user.id = :userId " + + "ORDER BY nr.id DESC") + Page findAllByUserAndCorsorFirst(Long userId, Pageable pageable); + + @Query("SELECT nr FROM NewsReaction nr " + + "WHERE nr.user.id = :userId " + + "AND nr.id > :cursorId " + + "ORDER BY nr.id DESC") + Page findAllByUserAndCorsor(@Param("userId") Long userId, @Param("cursorId") Long cursorId, Pageable pageable); + void deleteByNewsAndUserAndReaction(News news, User user, ENewsReaction reaction); } diff --git a/src/main/java/com/kkokkomu/short_news/news/repository/NewsViewHistRepository.java b/src/main/java/com/kkokkomu/short_news/news/repository/NewsViewHistRepository.java index 18f5c13..03de6cb 100644 --- a/src/main/java/com/kkokkomu/short_news/news/repository/NewsViewHistRepository.java +++ b/src/main/java/com/kkokkomu/short_news/news/repository/NewsViewHistRepository.java @@ -21,15 +21,17 @@ public interface NewsViewHistRepository extends JpaRepository findAllByUserAndCorsorFirst(Long userId, Pageable pageable); + // 뉴스 시청기록 최신순 조회 초기화 @Query("SELECT nh FROM NewsViewHist nh " + "WHERE nh.user.id = :userId " + - "AND nh.id < :cursorId " + - "ORDER BY nh.id DESC") + "AND nh.id > :cursorId " + + "ORDER BY nh.id ") Page findAllByUserAndCorsor(@Param("userId") Long userId, @Param("cursorId") Long cursorId, Pageable pageable); // 중복 체크를 위한 쿼리 diff --git a/src/main/java/com/kkokkomu/short_news/news/service/AdminNewsService.java b/src/main/java/com/kkokkomu/short_news/news/service/AdminNewsService.java index f6214d7..5c175d6 100644 --- a/src/main/java/com/kkokkomu/short_news/news/service/AdminNewsService.java +++ b/src/main/java/com/kkokkomu/short_news/news/service/AdminNewsService.java @@ -118,7 +118,7 @@ public List generateNewsList(CreateGenerateNewsDto createGenera List newsListAll = newsRepository.findAll(); for (News news : newsListAll) { - news.addScore(-1 * topScore); + news.addScore(topScore); } newsRepository.saveAll(newsListAll); @@ -256,11 +256,12 @@ public List generateNews(CreateGenerateNewsDto createGenerateNe // 랭킹 초기화 log.info("news ranking rese"); News topNews = newsRepository.findTopByOrderByScoreDesc(); + log.info("top news {}", topNews.getId()); Double topScore = topNews.getScore() * -1; List newsListAll = newsRepository.findAll(); for (News news : newsListAll) { - news.addScore(-1 * topScore); + news.addScore(topScore); } newsRepository.saveAll(newsListAll); @@ -333,7 +334,7 @@ public List generateNews(CreateGenerateNewsDto createGenerateNe for (GenerateNewsDto generateNewsDto : generateNewsDtos) { if (generateNewsDto.newsDto() != null) { content.append("

Title: ").append(generateNewsDto.newsDto().title()).append("

"); - content.append("

URL: ").append(generateNewsDto.newsDto().shortformUrl()).append("

"); + content.append("

URL: ").append(generateNewsDto.newsDto().shortformUrl().replace("kkm-shortform", "kkm-shortform-withad")).append("

"); content.append("

origin: ").append(generateNewsDto.newsDto().relatedUrl()).append("

"); } diff --git a/src/main/java/com/kkokkomu/short_news/news/service/NewsLogService.java b/src/main/java/com/kkokkomu/short_news/news/service/NewsLogService.java index e5dfa16..0d40f85 100644 --- a/src/main/java/com/kkokkomu/short_news/news/service/NewsLogService.java +++ b/src/main/java/com/kkokkomu/short_news/news/service/NewsLogService.java @@ -1,13 +1,18 @@ package com.kkokkomu.short_news.news.service; +import com.kkokkomu.short_news.comment.domain.Comment; +import com.kkokkomu.short_news.comment.dto.comment.response.CommentDto; +import com.kkokkomu.short_news.comment.service.CommentService; import com.kkokkomu.short_news.core.dto.CursorInfoDto; import com.kkokkomu.short_news.core.dto.CursorResponseDto; import com.kkokkomu.short_news.core.exception.CommonException; import com.kkokkomu.short_news.core.exception.ErrorCode; import com.kkokkomu.short_news.news.domain.News; +import com.kkokkomu.short_news.news.domain.NewsReaction; import com.kkokkomu.short_news.news.domain.NewsViewHist; import com.kkokkomu.short_news.news.dto.news.response.NewsInfoDto; import com.kkokkomu.short_news.news.dto.news.response.SearchNewsDto; +import com.kkokkomu.short_news.news.dto.newsHist.response.CommentHistInfoDto; import com.kkokkomu.short_news.news.dto.newsHist.response.NewsHistInfoDto; import com.kkokkomu.short_news.news.repository.NewsRepository; import com.kkokkomu.short_news.news.repository.NewsViewHistRepository; @@ -28,88 +33,68 @@ @RequiredArgsConstructor @Slf4j public class NewsLogService { - private final NewsRepository newsRepository; + private final NewsViewHistRepository newsViewHistRepository; private final NewsViewHistService newsViewHistService; private final UserLookupService userLookupService; private final SearchNewsService searchNewsService; - private final NewsViewHistRepository newsViewHistRepository; + private final NewsReactionService newsReactionService; + private final NewsLookupService newsLookupService; + private final CommentService commentService; - public CursorResponseDto> getNewsWithComment(Long userId, Long cursorId, int size) { + @Transactional(readOnly = true) + public CursorResponseDto> getNewsWithComment(Long userId, Long cursorId, int size) { log.info("getNewsWithComment service"); User user = userLookupService.findUserById(userId); - // 커서 아이디에 해당하는 뉴스가 있는지 검사 - if (cursorId != null && !newsRepository.existsById(cursorId)) { - throw new CommonException(ErrorCode.NOT_FOUND_CURSOR); - } - - PageRequest pageRequest = PageRequest.of(0, size); - // 캐싱 히스토리 db 동기화 newsViewHistService.updateNewsHist(userId); - List news; - Page results; - if (cursorId == null) { - // 최초 - results = newsRepository.findFirstPageNewsByUserCommentsOrderByIdDesc(user, pageRequest); - } else { - // 그 이후 - results = newsRepository.findNewsByUserCommentsAndIdLessThanOrderByIdDesc(user, cursorId, pageRequest); - } - news = results.getContent(); + // 유저가 감정표현한 뉴스들 조회 by cursot + Page reactionsByCursor = commentService.getNewsCommentByCursor(userId, cursorId, size); + List comments = reactionsByCursor.getContent(); - List searchNewsDtos = SearchNewsDto.of(news); + List searchNewsDtos = getCommentHistInfo(userId, comments); - CursorInfoDto cursorInfoDto = CursorInfoDto.fromPageInfo(results); + CursorInfoDto cursorInfoDto = CursorInfoDto.fromPageInfo(reactionsByCursor); return CursorResponseDto.fromEntityAndPageInfo(searchNewsDtos, cursorInfoDto); - } // 댓글 달았던 뉴스 조회 + } // 댓글 단 뉴스 조회 @Transactional(readOnly = true) - public CursorResponseDto> getNewsWithReaction(Long userId, Long cursorId, int size) { + public CursorResponseDto> getNewsWithReaction(Long userId, Long cursorId, int size) { log.info("getNewsWithReaction service"); User user = userLookupService.findUserById(userId); - // 커서 아이디에 해당하는 뉴스가 있는지 검사 - if (cursorId != null && !newsRepository.existsById(cursorId)) { - throw new CommonException(ErrorCode.NOT_FOUND_CURSOR); - } - - PageRequest pageRequest = PageRequest.of(0, size); - // 캐싱 히스토리 db 동기화 newsViewHistService.updateNewsHist(userId); - List news; - Page results; - if (cursorId == null) { - // 최초 - results = newsViewHistRepository.findAllByUserAndCorsorFirst(userId, pageRequest); - } else { - // 그 이후 - results = newsViewHistRepository.findAllByUserAndCorsor(userId, cursorId, pageRequest); - } - news = results.getContent(); + // 유저가 감정표현한 뉴스들 조회 by cursot + Page reactionsByCursor = newsReactionService.getNewsReactionsByCursor(userId, cursorId, size); - List searchNewsDtos = getNewsHistInfo(news); + List newsList = reactionsByCursor.stream() + .map(r -> r.getNews()) + .toList(); - CursorInfoDto cursorInfoDto = CursorInfoDto.fromPageInfo(results); + // 뉴스들 기반 시청기록 조회 + List newsHistList = searchNewsService.getNewsInfo(newsList, userId); - return CursorResponseDto.fromEntityAndPageInfo(searchNewsDtos, cursorInfoDto); + CursorInfoDto cursorInfoDto = CursorInfoDto.fromPageInfo(reactionsByCursor); + + return CursorResponseDto.fromEntityAndPageInfo(newsHistList, cursorInfoDto); } // 감정표현한 뉴스 조회 @Transactional(readOnly = true) public CursorResponseDto> getNewsWithHist(Long userId, Long cursorId, int size) { log.info("getNewsWithHist service"); + log.info("getNewsWithReaction service"); User user = userLookupService.findUserById(userId); // 커서 아이디에 해당하는 뉴스가 있는지 검사 - if (cursorId != null && !newsRepository.existsById(cursorId)) { + if (cursorId != null && !newsLookupService.existNewsById(cursorId)) { throw new CommonException(ErrorCode.NOT_FOUND_CURSOR); } @@ -118,7 +103,7 @@ public CursorResponseDto> getNewsWithHist(Long userId, Lon // 캐싱 히스토리 db 동기화 newsViewHistService.updateNewsHist(userId); - List news; + List hist; Page results; if (cursorId == null) { // 최초 @@ -127,14 +112,14 @@ public CursorResponseDto> getNewsWithHist(Long userId, Lon // 그 이후 results = newsViewHistRepository.findAllByUserAndCorsor(userId, cursorId, pageRequest); } - news = results.getContent(); + hist = results.getContent(); - List searchNewsDtos = getNewsHistInfo(news); + List searchNewsDtos = getNewsHistInfo(hist); CursorInfoDto cursorInfoDto = CursorInfoDto.fromPageInfo(results); return CursorResponseDto.fromEntityAndPageInfo(searchNewsDtos, cursorInfoDto); - } // 감정표현한 뉴스 조회 + } // 최신순 시청기록 조회 @Transactional public String deleteNewsHist(String newsHistIdList) { @@ -177,4 +162,19 @@ private List getNewsHistInfo(List newsViewHists) } return newsHistInfoDtos; } + + private CommentHistInfoDto getCommentHistInfo(Long userId, Comment comment) { + return CommentHistInfoDto.builder() + .news(searchNewsService.getNewsInfo(comment.getNews(), userId)) + .comment(CommentDto.of(comment)) + .build(); + } + + private List getCommentHistInfo(Long userId, List comments) { + List newsHistInfoDtos = new ArrayList<>(); + for (Comment comment : comments) { + newsHistInfoDtos.add(getCommentHistInfo(userId, comment)); + } + return newsHistInfoDtos; + } } diff --git a/src/main/java/com/kkokkomu/short_news/news/service/NewsReactionService.java b/src/main/java/com/kkokkomu/short_news/news/service/NewsReactionService.java index c953154..e6ad46f 100644 --- a/src/main/java/com/kkokkomu/short_news/news/service/NewsReactionService.java +++ b/src/main/java/com/kkokkomu/short_news/news/service/NewsReactionService.java @@ -1,8 +1,12 @@ package com.kkokkomu.short_news.news.service; import com.kkokkomu.short_news.core.config.service.RedisService; +import com.kkokkomu.short_news.core.dto.CursorInfoDto; +import com.kkokkomu.short_news.core.dto.CursorResponseDto; import com.kkokkomu.short_news.news.domain.News; import com.kkokkomu.short_news.news.domain.NewsReaction; +import com.kkokkomu.short_news.news.domain.NewsViewHist; +import com.kkokkomu.short_news.news.dto.newsHist.response.NewsHistInfoDto; import com.kkokkomu.short_news.user.domain.User; import com.kkokkomu.short_news.news.dto.newsReaction.request.CreateNewsReactionDto; import com.kkokkomu.short_news.news.dto.newsReaction.response.NewReactionByUserDto; @@ -16,9 +20,13 @@ import com.kkokkomu.short_news.user.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @RequiredArgsConstructor @Slf4j @@ -94,6 +102,28 @@ public String deleteNewsReaction(Long userId, Long newsId, String reaction) { } } // 뉴스 감정표현 삭제 + @Transactional(readOnly = true) + public Page getNewsReactionsByCursor(Long userId, Long cursorId, int size) { + log.info("getNewsWithReaction service"); + + PageRequest pageRequest = PageRequest.of(0, size); + + Page results; + if (cursorId == null) { + // 최초 + results = newsReactionRepository.findAllByUserAndCorsorFirst(userId, pageRequest); + } else { + // 그 이후 + if (!newsReactionRepository.existsById(cursorId)) { + throw new CommonException(ErrorCode.NOT_FOUND_CURSOR); + } + + results = newsReactionRepository.findAllByUserAndCorsor(userId, cursorId, pageRequest); + } + + return results; + } + // 각 감정표현 별 갯수 카운드 public ReactionCntDto countNewsReaction(Long newsId) { return ReactionCntDto.builder()