diff --git a/src/main/java/com/project/webtoonzoa/controller/CommentController.java b/src/main/java/com/project/webtoonzoa/controller/CommentController.java index 57a7048..47b0f0f 100644 --- a/src/main/java/com/project/webtoonzoa/controller/CommentController.java +++ b/src/main/java/com/project/webtoonzoa/controller/CommentController.java @@ -3,6 +3,7 @@ import com.project.webtoonzoa.dto.CommonResponse; import com.project.webtoonzoa.dto.request.CommentRequestDto; import com.project.webtoonzoa.dto.response.CommentDetailResponseDto; +import com.project.webtoonzoa.dto.response.CommentLikesResponseDto; import com.project.webtoonzoa.dto.response.CommentResponseDto; import com.project.webtoonzoa.entity.User; import com.project.webtoonzoa.global.util.UserDetailsImpl; @@ -84,4 +85,30 @@ public ResponseEntity> deleteComment( .build() ); } + + @PostMapping("/{commentId}/likes") + public ResponseEntity> createCommentLikes( + @AuthenticationPrincipal UserDetailsImpl userDetails, + @PathVariable(name = "commentId") Long commentId) { + return ResponseEntity.status(HttpStatus.OK).body( + CommonResponse.builder() + .status(HttpStatus.OK.value()) + .message("댓글 좋아요 성공") + .data(commentService.createCommentLikes(new User(), commentId)) + .build() + ); + } + + @DeleteMapping("/{commentId}/likes") + public ResponseEntity> deleteCommentLikes( + @AuthenticationPrincipal UserDetailsImpl userDetails, + @PathVariable(name = "commentId") Long commentId) { + return ResponseEntity.status(HttpStatus.OK).body( + CommonResponse.builder() + .status(HttpStatus.OK.value()) + .message("댓글 좋아요 취소 성공") + .data(commentService.deleteCommentLikes(new User(), commentId)) + .build() + ); + } } \ No newline at end of file diff --git a/src/main/java/com/project/webtoonzoa/dto/response/CommentLikesResponseDto.java b/src/main/java/com/project/webtoonzoa/dto/response/CommentLikesResponseDto.java new file mode 100644 index 0000000..c68c8e2 --- /dev/null +++ b/src/main/java/com/project/webtoonzoa/dto/response/CommentLikesResponseDto.java @@ -0,0 +1,14 @@ +package com.project.webtoonzoa.dto.response; + +import com.project.webtoonzoa.entity.CommentLikes; +import lombok.Getter; + +@Getter +public class CommentLikesResponseDto { + + private Long id; + + public CommentLikesResponseDto(CommentLikes commentLikes) { + this.id = commentLikes.getId(); + } +} diff --git a/src/main/java/com/project/webtoonzoa/entity/CommentLikes.java b/src/main/java/com/project/webtoonzoa/entity/CommentLikes.java index 19f4871..060cde1 100644 --- a/src/main/java/com/project/webtoonzoa/entity/CommentLikes.java +++ b/src/main/java/com/project/webtoonzoa/entity/CommentLikes.java @@ -26,4 +26,9 @@ public class CommentLikes extends TimeStamped { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "comment_id") private Comment comment; + + public CommentLikes(User user, Comment comment) { + this.user = user; + this.comment = comment; + } } diff --git a/src/main/java/com/project/webtoonzoa/repository/CommentLikesRepository.java b/src/main/java/com/project/webtoonzoa/repository/CommentLikesRepository.java new file mode 100644 index 0000000..61cddab --- /dev/null +++ b/src/main/java/com/project/webtoonzoa/repository/CommentLikesRepository.java @@ -0,0 +1,11 @@ +package com.project.webtoonzoa.repository; + +import com.project.webtoonzoa.entity.Comment; +import com.project.webtoonzoa.entity.CommentLikes; +import com.project.webtoonzoa.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentLikesRepository extends JpaRepository { + + boolean existsByUserAndComment(User user, Comment comment); +} diff --git a/src/main/java/com/project/webtoonzoa/repository/WebtoonLikesRepository.java b/src/main/java/com/project/webtoonzoa/repository/WebtoonLikesRepository.java new file mode 100644 index 0000000..816da30 --- /dev/null +++ b/src/main/java/com/project/webtoonzoa/repository/WebtoonLikesRepository.java @@ -0,0 +1,8 @@ +package com.project.webtoonzoa.repository; + +import com.project.webtoonzoa.entity.WebtoonLikes; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface WebtoonLikesRepository extends JpaRepository { + +} diff --git a/src/main/java/com/project/webtoonzoa/service/CommentService.java b/src/main/java/com/project/webtoonzoa/service/CommentService.java index 04ee74a..3c7e7d8 100644 --- a/src/main/java/com/project/webtoonzoa/service/CommentService.java +++ b/src/main/java/com/project/webtoonzoa/service/CommentService.java @@ -2,17 +2,22 @@ import com.project.webtoonzoa.dto.request.CommentRequestDto; import com.project.webtoonzoa.dto.response.CommentDetailResponseDto; +import com.project.webtoonzoa.dto.response.CommentLikesResponseDto; import com.project.webtoonzoa.dto.response.CommentResponseDto; import com.project.webtoonzoa.entity.Comment; +import com.project.webtoonzoa.entity.CommentLikes; import com.project.webtoonzoa.entity.Enum.UserRoleEnum; import com.project.webtoonzoa.entity.User; import com.project.webtoonzoa.entity.Webtoon; +import com.project.webtoonzoa.repository.CommentLikesRepository; import com.project.webtoonzoa.repository.CommentRepository; +import com.project.webtoonzoa.repository.UserRepository; import com.project.webtoonzoa.repository.WebtoonRepository; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,7 +28,9 @@ public class CommentService { private final CommentRepository commentRepository; + private final CommentLikesRepository commentLikesRepository; private final WebtoonRepository webtoonRepository; + private final UserRepository userRepository; @Transactional public CommentResponseDto createComment(User user, Long webtoonId, @@ -69,14 +76,51 @@ private Comment checkExistComment(Long commentId) { ); } + private CommentLikes checkExistCommentLikes(Long commentLikesId) { + return commentLikesRepository.findById(commentLikesId).orElseThrow( + () -> new NoSuchElementException("해당 댓글 좋아요가 존재하지 않습니다.") + ); + } + private Webtoon checkExistWebtoon(Long webtoonId) { return webtoonRepository.findById(webtoonId).orElseThrow( () -> new NoSuchElementException("웹툰이 존재하지 않습니다.")); } + private User checkExistUser(User user) { + return userRepository.findById(user.getId()).orElseThrow( + () -> new NoSuchElementException("유저가 존재하지 않습니다.")); + } + private static void validateUser(User user, Comment comment) { if (!comment.getUser().equals(user)) { throw new AccessDeniedException("댓글 작성자만 수정, 삭제할 수 있습니다."); } } + + @Transactional + public CommentLikesResponseDto createCommentLikes(User user, Long commentId) { + User savedUser = checkExistUser(user); + Comment savedComment = checkExistComment(commentId); + if (commentLikesRepository.existsByUserAndComment(savedUser, savedComment)) { + throw new DataIntegrityViolationException("이미 게시글에 좋아요를 했습니다."); + } + CommentLikes savedCommentLikes = commentLikesRepository.save( + new CommentLikes(savedUser, savedComment)); + return new CommentLikesResponseDto(savedCommentLikes); + } + + @Transactional + public CommentLikesResponseDto deleteCommentLikes(User user, Long commentId) { + User savedUser = checkExistUser(user); + Comment savedComment = checkExistComment(commentId); + CommentLikes savedCommentLikes = checkExistCommentLikes(commentId); + if (!commentLikesRepository.existsByUserAndComment(savedUser, savedComment)) { + throw new NoSuchElementException("해당 댓글 좋아요가 존재하지 않습니다."); + } + commentLikesRepository.delete(new CommentLikes(savedUser, savedComment)); + + return new CommentLikesResponseDto(savedCommentLikes); + } + } diff --git a/src/test/java/com/project/webtoonzoa/service/CommentServiceTest.java b/src/test/java/com/project/webtoonzoa/service/CommentServiceTest.java index 281ce29..4480046 100644 --- a/src/test/java/com/project/webtoonzoa/service/CommentServiceTest.java +++ b/src/test/java/com/project/webtoonzoa/service/CommentServiceTest.java @@ -46,7 +46,8 @@ void createComment() { given(commentRepository.save(any(Comment.class))).willReturn(TEST_COMMENT); //when - CommentResponseDto responseDto = commentService.createComment(TEST_USER, TEST_WEBTOON_ID, TEST_COMMENT_REQUEST_DTO); + CommentResponseDto responseDto = commentService.createComment(TEST_USER, TEST_WEBTOON_ID, + TEST_COMMENT_REQUEST_DTO); //then assertThat(responseDto.getId()).isEqualTo(TEST_COMMENT_ID); @@ -57,7 +58,8 @@ void createComment() { void readComment() { //given given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn(Optional.of(TEST_WEBTOON)); - given(commentRepository.findByWebtoonIdAndDeletedAtIsNullOrderByCreatedAtAsc(TEST_WEBTOON_ID)).willReturn(List.of(TEST_COMMENT, TEST_COMMENT_2)); + given(commentRepository.findByWebtoonIdAndDeletedAtIsNullOrderByCreatedAtAsc( + TEST_WEBTOON_ID)).willReturn(List.of(TEST_COMMENT, TEST_COMMENT_2)); //when List responseDtos = commentService.readComment(TEST_WEBTOON_ID); @@ -76,8 +78,10 @@ class updateComment { @DisplayName("댓글 수정 성공") void updateComment_success() { //given - given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn(Optional.of(TEST_WEBTOON)); - given(commentRepository.findById(TEST_COMMENT_ID)).willReturn(Optional.of(TEST_COMMENT)); + given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn( + Optional.of(TEST_WEBTOON)); + given(commentRepository.findById(TEST_COMMENT_ID)).willReturn( + Optional.of(TEST_COMMENT)); //when CommentDetailResponseDto responseDto = commentService.updateComment( @@ -93,12 +97,15 @@ void updateComment_success() { @DisplayName("댓글 수정 실패_다른 유저") void updateComment_fail_anotherUser() { //given - given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn(Optional.of(TEST_WEBTOON)); - given(commentRepository.findById(TEST_COMMENT_ID)).willReturn(Optional.of(TEST_COMMENT)); + given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn( + Optional.of(TEST_WEBTOON)); + given(commentRepository.findById(TEST_COMMENT_ID)).willReturn( + Optional.of(TEST_COMMENT)); // when, then assertThrows(AccessDeniedException.class, () -> - commentService.updateComment(TEST_ANOTHER_USER, TEST_WEBTOON_ID, TEST_COMMENT_ID, TEST_COMMENT_UPDATE_REQUEST_DTO) + commentService.updateComment(TEST_ANOTHER_USER, TEST_WEBTOON_ID, TEST_COMMENT_ID, + TEST_COMMENT_UPDATE_REQUEST_DTO) ); } } @@ -111,8 +118,10 @@ class deleteComment { @DisplayName("댓글 삭제 성공_유저") void deleteComment_success_user() { //given - given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn(Optional.of(TEST_WEBTOON)); - given(commentRepository.findById(TEST_COMMENT_ID)).willReturn(Optional.of(TEST_COMMENT)); + given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn( + Optional.of(TEST_WEBTOON)); + given(commentRepository.findById(TEST_COMMENT_ID)).willReturn( + Optional.of(TEST_COMMENT)); //when CommentResponseDto responseDto = commentService.deleteComment( @@ -126,8 +135,10 @@ void deleteComment_success_user() { @DisplayName("댓글 삭제 성공_관리자") void deleteComment_success_admin() { //given - given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn(Optional.of(TEST_WEBTOON)); - given(commentRepository.findById(TEST_COMMENT_ID)).willReturn(Optional.of(TEST_COMMENT)); + given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn( + Optional.of(TEST_WEBTOON)); + given(commentRepository.findById(TEST_COMMENT_ID)).willReturn( + Optional.of(TEST_COMMENT)); //when CommentResponseDto responseDto = commentService.deleteComment( @@ -141,12 +152,15 @@ void deleteComment_success_admin() { @DisplayName("댓글 삭제 실패_다른유저") void deleteComment_fail_anotherUser() { //given - given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn(Optional.of(TEST_WEBTOON)); - given(commentRepository.findById(TEST_COMMENT_ID)).willReturn(Optional.of(TEST_COMMENT)); + given(webtoonRepository.findById(TEST_WEBTOON_ID)).willReturn( + Optional.of(TEST_WEBTOON)); + given(commentRepository.findById(TEST_COMMENT_ID)).willReturn( + Optional.of(TEST_COMMENT)); // when, then assertThrows(AccessDeniedException.class, - () -> commentService.deleteComment(TEST_ANOTHER_USER, TEST_WEBTOON_ID, TEST_COMMENT_ID)); + () -> commentService.deleteComment(TEST_ANOTHER_USER, TEST_WEBTOON_ID, + TEST_COMMENT_ID)); } } } \ No newline at end of file diff --git a/src/test/java/com/project/webtoonzoa/service/CommentTest.java b/src/test/java/com/project/webtoonzoa/service/CommentTest.java index 8ab678d..4e44b1e 100644 --- a/src/test/java/com/project/webtoonzoa/service/CommentTest.java +++ b/src/test/java/com/project/webtoonzoa/service/CommentTest.java @@ -35,8 +35,8 @@ public interface CommentTest { Long TEST_COMMENT_ID = 1L; Long TEST_COMMENT_ID_2 = 2L; - String TEST_COMMENT_CONTENT ="content"; - String TEST_COMMENT_CONTENT_2 ="content2"; + String TEST_COMMENT_CONTENT = "content"; + String TEST_COMMENT_CONTENT_2 = "content2"; String TEST_COMMENT_UPDATE_CONTENT = "update content"; Comment TEST_COMMENT = Comment.builder()