From e670a06eaed48dc48312c05788ec77ac0b383d00 Mon Sep 17 00:00:00 2001 From: king_0417 <73704053+gywns0417@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:12:04 +0900 Subject: [PATCH] =?UTF-8?q?=08CommentService=20Duplicates=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20API=20=EB=B3=B4=EC=95=88=EC=84=B1=20=ED=96=A5?= =?UTF-8?q?=EC=83=81=20(#769)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 댓글 조회, 답글 조회 Duplicates 제거 * refactor: 베스트 댓글 조회 메서드 분리 * refactor: validateImgUrl() 메서드 분리 * feat: 비밀번호 검증 API 보안성 향상 * fix: checkStyle issue 해결 * feat: 트랜잭션 어노테이션 수정 * refactor: 불필요한 메서드 제거 --- .../comment/application/CommentService.java | 150 ++++++++---------- .../member/application/MemberService.java | 7 +- .../member/presentation/MemberController.java | 6 +- 3 files changed, 78 insertions(+), 85 deletions(-) diff --git a/src/main/java/balancetalk/comment/application/CommentService.java b/src/main/java/balancetalk/comment/application/CommentService.java index 01a75e07a..ba7e99ca4 100644 --- a/src/main/java/balancetalk/comment/application/CommentService.java +++ b/src/main/java/balancetalk/comment/application/CommentService.java @@ -116,27 +116,12 @@ public Page findAllComments(Long talkPickId, Pageable pag Page comments = commentRepository.findAllByTalkPickIdAndParentIsNull(talkPickId, pageable); - return comments.map(comment -> { //TODO : Duplicates 메서드 분리 - int likesCount = likeRepository.countByResourceIdAndLikeType(comment.getId(), LikeType.COMMENT); - boolean myLike = isCommentMyLiked(comment.getId(), guestOrApiMember); - Member member = comment.getMember(); - VoteOption option = member.getVoteOnTalkPick(talkPick) - .isPresent() ? member.getVoteOnTalkPick(talkPick).get().getVoteOption() : null; - - if (member.getProfileImgId() == null) { - return LatestCommentResponse.fromEntity(comment, option, null, likesCount, myLike); - } - - String imgUrl = fileRepository.findById(member.getProfileImgId()) - .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_FILE)) - .getImgUrl(); - - return LatestCommentResponse.fromEntity(comment, option, imgUrl, likesCount, myLike); - }); + return convertToLatestCommentPagesResponse(comments, talkPick, guestOrApiMember); } @Transactional(readOnly = true) - public List findAllReplies(Long parentId, Long talkPickId, GuestOrApiMember guestOrApiMember) { + public List findAllReplies(Long parentId, Long talkPickId, + GuestOrApiMember guestOrApiMember) { // 부모 댓글이 존재하는지 확인 validateCommentId(parentId); @@ -149,102 +134,107 @@ public List findAllReplies(Long parentId, Long talkPickId // 해당 부모 댓글의 답글 조회 List replies = commentRepository.findAllRepliesByParentIdOrderByMemberAndCreatedAt(parentId, memberId); - return replies.stream().map(reply -> { - int likesCount = likeRepository.countByResourceIdAndLikeType(reply.getId(), LikeType.COMMENT); - boolean myLike = isCommentMyLiked(reply.getId(), guestOrApiMember); - Member member = reply.getMember(); - VoteOption option = member.getVoteOnTalkPick(talkPick) - .isPresent() ? member.getVoteOnTalkPick(talkPick).get().getVoteOption() : null; - if (member.getProfileImgId() == null) { - return LatestCommentResponse.fromEntity(reply, option, null, likesCount, myLike); - } + return convertToLatestCommentResponse(replies, talkPick, guestOrApiMember); + } - String imgUrl = fileRepository.findById(member.getProfileImgId()) - .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_FILE)) - .getImgUrl(); + // Page 처리 + private Page convertToLatestCommentPagesResponse(Page comments, TalkPick talkPick, + GuestOrApiMember guestOrApiMember) { + return comments.map(comment -> mapToLatestCommentResponse(comment, talkPick, guestOrApiMember)); + } - return LatestCommentResponse.fromEntity(reply, option, imgUrl, likesCount, myLike);}) + // List 처리 + private List convertToLatestCommentResponse(List comments, TalkPick talkPick, + GuestOrApiMember guestOrApiMember) { + return comments.stream() + .map(comment -> mapToLatestCommentResponse(comment, talkPick, guestOrApiMember)) .toList(); } + // 공통 변환 로직 + private LatestCommentResponse mapToLatestCommentResponse(Comment comment, TalkPick talkPick, + GuestOrApiMember guestOrApiMember) { + int likesCount = likeRepository.countByResourceIdAndLikeType(comment.getId(), LikeType.COMMENT); + boolean myLike = isCommentMyLiked(comment.getId(), guestOrApiMember); + Member member = comment.getMember(); + VoteOption option = member.getVoteOnTalkPick(talkPick) + .isPresent() ? member.getVoteOnTalkPick(talkPick).get().getVoteOption() : null; - @Transactional(readOnly = true) + String imgUrl = (member.getProfileImgId() != null) ? fileRepository.findById(member.getProfileImgId()) + .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_FILE)) + .getImgUrl() : null; + + return LatestCommentResponse.fromEntity(comment, option, imgUrl, likesCount, myLike); + } + + @Transactional public Page findAllBestComments(Long talkPickId, Pageable pageable, GuestOrApiMember guestOrApiMember) { validateTalkPickId(talkPickId); TalkPick talkPick = talkPickRepository.findById(talkPickId) .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_TALK_PICK)); - List allComments = commentRepository.findByTalkPickIdAndParentIsNullOrderByLikesCountDescCreatedAtAsc(talkPickId, - LikeType.COMMENT); - List bestComments = new ArrayList<>(); - List otherComments = new ArrayList<>(); + List allComments = commentRepository.findByTalkPickIdAndParentIsNullOrderByLikesCountDescCreatedAtAsc( + talkPickId, LikeType.COMMENT); - // 최대 좋아요 수 구하기 + // 최대 좋아요 수 계산 int maxLikes = allComments.stream() .mapToInt(comment -> likeRepository.countByResourceIdAndLikeType(comment.getId(), LikeType.COMMENT)) .max() .orElse(0); - // 좋아요 10개 이상인 댓글이 있는 경우 - if (maxLikes >= MIN_COUNT_FOR_BEST_COMMENT) { - for (Comment comment : allComments) { - boolean myLike = isCommentMyLiked(comment.getId(), guestOrApiMember); - int likeCount = likeRepository.countByResourceIdAndLikeType(comment.getId(), LikeType.COMMENT); - Member member = comment.getMember(); - VoteOption option = member.getVoteOnTalkPick(talkPick) - .isPresent() ? member.getVoteOnTalkPick(talkPick).get().getVoteOption() : null; - comment.setIsBest(likeCount >= MIN_COUNT_FOR_BEST_COMMENT); - BestCommentResponse response = validateImgUrl(member, comment, option, likeCount, myLike); - - if (comment.getIsBest()) { - bestComments.add(response); - } else { - otherComments.add(response); - } - } - } else { - for (Comment comment : allComments) { - boolean myLike = isCommentMyLiked(comment.getId(), guestOrApiMember); - int likeCount = likeRepository.countByResourceIdAndLikeType(comment.getId(), LikeType.COMMENT); - Member member = comment.getMember(); - VoteOption option = member.getVoteOnTalkPick(talkPick) - .isPresent() ? member.getVoteOnTalkPick(talkPick).get().getVoteOption() : null; - comment.setIsBest(likeCount == maxLikes); - - BestCommentResponse response = validateImgUrl(member, comment, option, likeCount, myLike); - - if (comment.getIsBest()) { - bestComments.add(response); - } else { - otherComments.add(response); - } + List bestComments = new ArrayList<>(); + List otherComments = new ArrayList<>(); + + for (Comment comment : allComments) { + BestCommentResponse response = processFindBestComments(comment, talkPick, guestOrApiMember, maxLikes); + if (comment.getIsBest()) { + bestComments.add(response); + } else { + otherComments.add(response); } } + // 정렬 bestComments.sort(Comparator.comparing(BestCommentResponse::getCreatedAt).reversed()); otherComments.sort(Comparator.comparing(BestCommentResponse::getCreatedAt).reversed()); + // 결과 병합 List result = new ArrayList<>(); result.addAll(bestComments); result.addAll(otherComments); + // 페이징 처리 int start = (int) pageable.getOffset(); int end = Math.min((start + pageable.getPageSize()), result.size()); - return new PageImpl<>(result.subList(start, end), pageable, result.size()); } - private BestCommentResponse validateImgUrl(Member member, Comment comment, VoteOption option, - int likeCount, boolean myLike) { - if (member.getProfileImgId() != null) { - String imgUrl = fileRepository.findById(member.getProfileImgId()) - .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_FILE)) - .getImgUrl(); - return BestCommentResponse.fromEntity(comment, option, imgUrl, likeCount, myLike); + private BestCommentResponse processFindBestComments(Comment comment, TalkPick talkPick, + GuestOrApiMember guestOrApiMember, int maxLikes) { + boolean myLike = isCommentMyLiked(comment.getId(), guestOrApiMember); + int likeCount = likeRepository.countByResourceIdAndLikeType(comment.getId(), LikeType.COMMENT); + Member member = comment.getMember(); + VoteOption option = member.getVoteOnTalkPick(talkPick) + .isPresent() ? member.getVoteOnTalkPick(talkPick).get().getVoteOption() : null; + + // isBest 여부 설정 + comment.setIsBest(likeCount >= MIN_COUNT_FOR_BEST_COMMENT || likeCount == maxLikes); + String imgUrl = fetchProfileImgUrl(member); + + // BestCommentResponse 생성 + return BestCommentResponse.fromEntity(comment, option, imgUrl, likeCount, myLike); + } + + // 프로필 이미지 URL 조회 + private String fetchProfileImgUrl(Member member) { + if (member.getProfileImgId() == null) { + return null; } - return BestCommentResponse.fromEntity(comment, option, null, likeCount, myLike); + return fileRepository.findById(member.getProfileImgId()) + .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_FILE)) + .getImgUrl(); } public void updateComment(Long commentId, Long talkPickId, String content, ApiMember apiMember) { @@ -336,7 +326,7 @@ private void sendReplyNotification(Comment parentComment) { // 첫 답글 알림 if ((isFirstReplyFromOther && !parentComment.getIsNotifiedForFirstReply()) && !notificationHistory.getOrDefault(firstReplyKey, false)) { - notificationService.sendTalkPickNotification(parentCommentAuthor,talkPick, + notificationService.sendTalkPickNotification(parentCommentAuthor, talkPick, category, FIRST_COMMENT_REPLY.getMessage()); parentComment.setIsNotifiedForFirstReplyTrue(); // 50, 100개 답글 알림 diff --git a/src/main/java/balancetalk/member/application/MemberService.java b/src/main/java/balancetalk/member/application/MemberService.java index 14efd45ef..fecb0d240 100644 --- a/src/main/java/balancetalk/member/application/MemberService.java +++ b/src/main/java/balancetalk/member/application/MemberService.java @@ -196,8 +196,11 @@ private void validateSameNickname(MemberUpdateRequest memberUpdateRequest, Membe } } - public boolean verifyPassword(String password, ApiMember apiMember) { + public void verifyPassword(String password, ApiMember apiMember) { Member member = apiMember.toMember(memberRepository); - return passwordEncoder.matches(password, member.getPassword()); + + if (!passwordEncoder.matches(password, member.getPassword())) { + throw new BalanceTalkException(PASSWORD_MISMATCH); + } } } diff --git a/src/main/java/balancetalk/member/presentation/MemberController.java b/src/main/java/balancetalk/member/presentation/MemberController.java index 7208efd64..0c266849d 100644 --- a/src/main/java/balancetalk/member/presentation/MemberController.java +++ b/src/main/java/balancetalk/member/presentation/MemberController.java @@ -85,10 +85,10 @@ public void updateMemberInfo(@RequestBody @Valid MemberUpdateRequest memberUpdat memberService.updateMemberInformation(memberUpdateRequest, apiMember); } - @GetMapping("/verify-password") + @PostMapping("/verify-password") @Operation(summary = "비밀번호 검증", description = "회원의 비밀번호를 검증한다.") - public boolean validatePassword(@RequestParam @NotBlank String password, + public void validatePassword(@RequestParam @NotBlank String password, @Parameter(hidden = true) @AuthPrincipal ApiMember apiMember) { - return memberService.verifyPassword(password, apiMember); + memberService.verifyPassword(password, apiMember); } }