From 84101284b77d3ae7d18fb6d1f53b649abd47500c Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 23 Jan 2025 20:42:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20commentLike=20auditingEntityListener?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/maeilwiki/comment/CommentLike.java | 7 ++++--- .../java/maeilwiki/support/IntegrationTestSupport.java | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/maeil-wiki/src/main/java/maeilwiki/comment/CommentLike.java b/maeil-wiki/src/main/java/maeilwiki/comment/CommentLike.java index fc79577..4632630 100644 --- a/maeil-wiki/src/main/java/maeilwiki/comment/CommentLike.java +++ b/maeil-wiki/src/main/java/maeilwiki/comment/CommentLike.java @@ -1,11 +1,9 @@ package maeilwiki.comment; import java.time.LocalDateTime; - -import org.springframework.data.annotation.CreatedDate; - import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -16,10 +14,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; import maeilwiki.member.Member; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) public class CommentLike { @Id diff --git a/maeil-wiki/src/test/java/maeilwiki/support/IntegrationTestSupport.java b/maeil-wiki/src/test/java/maeilwiki/support/IntegrationTestSupport.java index 3486101..57faa40 100644 --- a/maeil-wiki/src/test/java/maeilwiki/support/IntegrationTestSupport.java +++ b/maeil-wiki/src/test/java/maeilwiki/support/IntegrationTestSupport.java @@ -2,9 +2,18 @@ import maeilwiki.WikiConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Import; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.transaction.annotation.Transactional; @Transactional @SpringBootTest(classes = WikiConfiguration.class) +@Import(IntegrationTestSupport.TestConfig.class) public abstract class IntegrationTestSupport { + + @EnableJpaAuditing + @TestConfiguration + public static class TestConfig { + } } From 8bd2fc62cd7afe611c9f656a79843c74163c9ec7 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 23 Jan 2025 20:43:35 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EB=8B=B5=EB=B3=80=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/maeilwiki/comment/CommentApi.java | 7 +++ .../comment/CommentLikeRepository.java | 9 +++ .../maeilwiki/comment/CommentService.java | 40 ++++++++++++ .../maeilwiki/comment/CommentServiceTest.java | 61 +++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 maeil-wiki/src/main/java/maeilwiki/comment/CommentLikeRepository.java diff --git a/maeil-wiki/src/main/java/maeilwiki/comment/CommentApi.java b/maeil-wiki/src/main/java/maeilwiki/comment/CommentApi.java index 3be7287..08d7c08 100644 --- a/maeil-wiki/src/main/java/maeilwiki/comment/CommentApi.java +++ b/maeil-wiki/src/main/java/maeilwiki/comment/CommentApi.java @@ -19,4 +19,11 @@ public ResponseEntity createComment(@RequestBody CommentRequest request, @ return ResponseEntity.noContent().build(); } + + @PostMapping("/wiki/{wikiId}/comment/{id}/like") + public ResponseEntity createCommentLike(@PathVariable Long wikiId, @PathVariable Long id) { + commentService.toggleLike(id); + + return ResponseEntity.noContent().build(); + } } diff --git a/maeil-wiki/src/main/java/maeilwiki/comment/CommentLikeRepository.java b/maeil-wiki/src/main/java/maeilwiki/comment/CommentLikeRepository.java new file mode 100644 index 0000000..f325a3f --- /dev/null +++ b/maeil-wiki/src/main/java/maeilwiki/comment/CommentLikeRepository.java @@ -0,0 +1,9 @@ +package maeilwiki.comment; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +interface CommentLikeRepository extends JpaRepository { + + Optional findByCommentIdAndMemberId(Long commentId, Long memberId); +} diff --git a/maeil-wiki/src/main/java/maeilwiki/comment/CommentService.java b/maeil-wiki/src/main/java/maeilwiki/comment/CommentService.java index f3b3a42..bdcfd80 100644 --- a/maeil-wiki/src/main/java/maeilwiki/comment/CommentService.java +++ b/maeil-wiki/src/main/java/maeilwiki/comment/CommentService.java @@ -1,7 +1,9 @@ package maeilwiki.comment; +import java.util.Map; import java.util.NoSuchElementException; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import lombok.RequiredArgsConstructor; import maeilwiki.member.Member; import maeilwiki.member.MemberRepository; @@ -9,6 +11,7 @@ import maeilwiki.wiki.WikiRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronizationManager; @Service @RequiredArgsConstructor @@ -17,6 +20,9 @@ class CommentService { private final WikiRepository wikiRepository; private final CommentRepository commentRepository; private final MemberRepository memberRepository; + private final CommentLikeRepository commentLikeRepository; + + private final Map transactionTmpMemberMap = new ConcurrentHashMap<>(); @Transactional public void comment(CommentRequest request, Long wikiId) { @@ -29,4 +35,38 @@ public void comment(CommentRequest request, Long wikiId) { commentRepository.save(comment); } + + @Transactional + public void toggleLike(Long id) { + Member member = memberSetting(); + Comment comment = commentRepository.findById(id) + .orElseThrow(NoSuchElementException::new); + + commentLikeRepository.findByCommentIdAndMemberId(comment.getId(), member.getId()) + .ifPresentOrElse(this::unlike, () -> like(member, comment)); + } + + // TODO: 인가 적용 시 제거 + private Member memberSetting() { + String key = TransactionSynchronizationManager.getCurrentTransactionName(); + Member member = transactionTmpMemberMap.get(key); + if (member == null) { + String uuid = UUID.randomUUID().toString(); + Member newMember = new Member(uuid, uuid, "GITHUB"); + memberRepository.save(newMember); + transactionTmpMemberMap.put(key, newMember); + return newMember; + } + + return member; + } + + private void unlike(CommentLike commentLike) { + commentLikeRepository.delete(commentLike); + } + + private void like(Member member, Comment comment) { + CommentLike commentLike = new CommentLike(member, comment); + commentLikeRepository.save(commentLike); + } } diff --git a/maeil-wiki/src/test/java/maeilwiki/comment/CommentServiceTest.java b/maeil-wiki/src/test/java/maeilwiki/comment/CommentServiceTest.java index a4ff382..65b948a 100644 --- a/maeil-wiki/src/test/java/maeilwiki/comment/CommentServiceTest.java +++ b/maeil-wiki/src/test/java/maeilwiki/comment/CommentServiceTest.java @@ -1,9 +1,15 @@ package maeilwiki.comment; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.List; import java.util.NoSuchElementException; +import maeilwiki.member.Member; +import maeilwiki.member.MemberRepository; import maeilwiki.support.IntegrationTestSupport; +import maeilwiki.wiki.Wiki; +import maeilwiki.wiki.WikiRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -13,6 +19,18 @@ class CommentServiceTest extends IntegrationTestSupport { @Autowired private CommentService commentService; + @Autowired + private CommentRepository commentRepository; + + @Autowired + private CommentLikeRepository commentLikeRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private WikiRepository wikiRepository; + @Test @DisplayName("존재하지 않는 위키에 답변을 작성할 수 없다.") void notfound() { @@ -22,4 +40,47 @@ void notfound() { assertThatThrownBy(() -> commentService.comment(request, unknownWikiId)) .isInstanceOf(NoSuchElementException.class); } + + @Test + @DisplayName("존재하지 않는 답변에 좋아요를 생성할 수 없다.") + void notFoundComment() { + Long unknownCommentId = -1L; + + assertThatThrownBy(() -> commentService.toggleLike(unknownCommentId)) + .isInstanceOf(NoSuchElementException.class); + } + + @Test + @DisplayName("답변에 좋아요를 생성할 수 있다.") + void like() { + Comment comment = createComment(); + + commentService.toggleLike(comment.getId()); + + List likes = commentLikeRepository.findAll(); + assertThat(likes).hasSize(1); + } + + @Test + @DisplayName("이미 해당 사용자가 좋아요를 생성한 상황에서 재요청하는 경우, 좋아요를 취소한다.") + void unlike() { + Comment comment = createComment(); + commentService.toggleLike(comment.getId()); + + commentService.toggleLike(comment.getId()); + + List likes = commentLikeRepository.findAll(); + assertThat(likes).hasSize(0); + } + + private Comment createComment() { + Member member = new Member("name", "providerId", "GITHUB"); + memberRepository.save(member); + + Wiki wiki = new Wiki("question", "backend", false, member); + wikiRepository.save(wiki); + + Comment comment = new Comment("answer", false, member, wiki); + return commentRepository.save(comment); + } }