Skip to content

Commit

Permalink
feat: 댓글 좋아요 기능 구현 (#5)
Browse files Browse the repository at this point in the history
* feat: 댓글생성 기능 추가

* feat: 댓글 조회, 수정, 삭제 기능 추가

* test: CommentServiceTest 댓글생성 테스트 추가

* test: CommentServiceTest 댓글 조회 추가

* test: CommentServiceTest 댓글 수정 테스트

* test: CommentServiceTest 댓글 삭제 테스트

* refactor: 코드컨벤션 적용

* 댓글 좋아요

* refactor: 댓글 좋아요 기능 구현

---------

Co-authored-by: Lee gun woo <139452702+gunnu3226@users.noreply.github.com>
Co-authored-by: yiyaaa <65538799+RamuneOrch@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 26, 2024
1 parent 64b21c5 commit 56eaa7a
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,4 +85,30 @@ public ResponseEntity<CommonResponse<CommentResponseDto>> deleteComment(
.build()
);
}

@PostMapping("/{commentId}/likes")
public ResponseEntity<CommonResponse<CommentLikesResponseDto>> createCommentLikes(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable(name = "commentId") Long commentId) {
return ResponseEntity.status(HttpStatus.OK).body(
CommonResponse.<CommentLikesResponseDto>builder()
.status(HttpStatus.OK.value())
.message("댓글 좋아요 성공")
.data(commentService.createCommentLikes(new User(), commentId))
.build()
);
}

@DeleteMapping("/{commentId}/likes")
public ResponseEntity<CommonResponse<CommentLikesResponseDto>> deleteCommentLikes(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable(name = "commentId") Long commentId) {
return ResponseEntity.status(HttpStatus.OK).body(
CommonResponse.<CommentLikesResponseDto>builder()
.status(HttpStatus.OK.value())
.message("댓글 좋아요 취소 성공")
.data(commentService.deleteCommentLikes(new User(), commentId))
.build()
);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/project/webtoonzoa/entity/CommentLikes.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<CommentLikes, Long> {

boolean existsByUserAndComment(User user, Comment comment);
}
Original file line number Diff line number Diff line change
@@ -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<WebtoonLikes, Long> {

}
44 changes: 44 additions & 0 deletions src/main/java/com/project/webtoonzoa/service/CommentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<CommentDetailResponseDto> responseDtos = commentService.readComment(TEST_WEBTOON_ID);
Expand All @@ -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(
Expand All @@ -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)
);
}
}
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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));
}
}
}
4 changes: 2 additions & 2 deletions src/test/java/com/project/webtoonzoa/service/CommentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 56eaa7a

Please sign in to comment.