From baade14a66eca3efe727706e0350607a819a3dd7 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Thu, 13 Jul 2023 16:31:06 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20(#36)=20Vote=20=EB=A6=AC=ED=8C=8C?= =?UTF-8?q?=EC=A7=80=ED=84=B0=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../votogether/domain/vote/repository/VoteRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java diff --git a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java new file mode 100644 index 000000000..a1c6a8453 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java @@ -0,0 +1,7 @@ +package com.votogether.domain.vote.repository; + +import com.votogether.domain.vote.entity.Vote; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface VoteRepository extends JpaRepository { +} From ca33f0374fca79d2b611c497d43972fe78ac9af6 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Thu, 13 Jul 2023 16:35:00 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20(#36)=20=ED=88=AC=ED=91=9C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5,=20=ED=8F=AC=EC=9D=B8?= =?UTF-8?q?=ED=8A=B8=ED=9A=8D=EB=93=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../votogether/domain/member/entity/Member.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backend/src/main/java/com/votogether/domain/member/entity/Member.java b/backend/src/main/java/com/votogether/domain/member/entity/Member.java index 2f3611c43..131caa53d 100644 --- a/backend/src/main/java/com/votogether/domain/member/entity/Member.java +++ b/backend/src/main/java/com/votogether/domain/member/entity/Member.java @@ -1,6 +1,9 @@ package com.votogether.domain.member.entity; import com.votogether.domain.common.BaseEntity; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.PostOption; +import com.votogether.domain.vote.entity.Vote; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -60,4 +63,16 @@ private Member( this.point = point; } + public Vote vote(final Post post, final Long postOptionId) { + PostOption postOption = post.findPostOptionById(postOptionId); + return Vote.builder() + .member(this) + .postOption(postOption) + .build(); + } + + public void plusPoint(int point) { + this.point = this.point + point; + } + } From 06747f7e25e906adde6ca703d4379726cc0d11bd Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Thu, 13 Jul 2023 16:37:48 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20(#36)=20=EC=8B=9D=EB=B3=84?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20=ED=86=B5=ED=95=B4=20PostOption=20?= =?UTF-8?q?=EC=B0=BE=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PostOption과 양뱡향 매핑 관계 등록 --- .../com/votogether/domain/post/entity/Post.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index 2f5643a94..7201ba4d9 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -10,7 +10,10 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -38,6 +41,9 @@ public class Post extends BaseEntity { @Column(columnDefinition = "datetime(2)", nullable = false) private LocalDateTime deadline; + @OneToMany(mappedBy = "post") + private List postOptions = new ArrayList<>(); + @Builder private Post( final Member member, @@ -51,4 +57,11 @@ private Post( this.deadline = deadline; } + public PostOption findPostOptionById(final Long PostOptionId) { + return postOptions.stream() + .filter(postOption -> postOption.getId().equals(PostOptionId)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("해당 게시글에서 존재하지 않는 선택지 입니다.")); + } + } From 52179ba66ea63114081e3aebcf8dce59d8d30ed4 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Thu, 13 Jul 2023 16:54:33 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20(#36)=20=EC=9E=90=EC=8B=A0?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=91=EC=84=B1=ED=95=9C=20=EA=B8=80=EC=97=90?= =?UTF-8?q?=EB=8A=94=20=ED=88=AC=ED=91=9C=ED=95=98=EC=A7=80=20=EB=AA=BB?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/votogether/domain/member/entity/Member.java | 7 +++++++ .../main/java/com/votogether/domain/post/entity/Post.java | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/backend/src/main/java/com/votogether/domain/member/entity/Member.java b/backend/src/main/java/com/votogether/domain/member/entity/Member.java index 131caa53d..3eddaee57 100644 --- a/backend/src/main/java/com/votogether/domain/member/entity/Member.java +++ b/backend/src/main/java/com/votogether/domain/member/entity/Member.java @@ -65,12 +65,19 @@ private Member( public Vote vote(final Post post, final Long postOptionId) { PostOption postOption = post.findPostOptionById(postOptionId); + validateWriter(post); return Vote.builder() .member(this) .postOption(postOption) .build(); } + private void validateWriter(Post post) { + if (post.isWriter(this)) { + throw new IllegalArgumentException("자기자신은 투표할 수 없습니다."); + } + } + public void plusPoint(int point) { this.point = this.point + point; } diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index 7201ba4d9..efef3cc92 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -64,4 +64,8 @@ public PostOption findPostOptionById(final Long PostOptionId) { .orElseThrow(() -> new IllegalArgumentException("해당 게시글에서 존재하지 않는 선택지 입니다.")); } + public boolean isWriter(Member member) { + return this.member == member; + } + } From 9a2ac6fc8433b6775d0295d5484fa6caa16a47a2 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Thu, 13 Jul 2023 16:56:36 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20(#36)=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=EC=97=90=20=ED=88=AC=ED=91=9C=ED=95=98=EB=8A=94=20API?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/controller/VoteController.java | 29 ++++++++++++++++ .../domain/vote/service/VoteService.java | 34 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java create mode 100644 backend/src/main/java/com/votogether/domain/vote/service/VoteService.java diff --git a/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java b/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java new file mode 100644 index 000000000..1f79f8c4b --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java @@ -0,0 +1,29 @@ +package com.votogether.domain.vote.controller; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.vote.service.VoteService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +public class VoteController { + + private final VoteService voteService; + + @PostMapping("/posts/{postId}/options/{optionId}") + public ResponseEntity vote( + @PathVariable final Long postId, + @PathVariable("optionId") final Long postOptionId, + final Member member + ) { + voteService.vote(member, postId, postOptionId); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + +} diff --git a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java new file mode 100644 index 000000000..df553194b --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java @@ -0,0 +1,34 @@ +package com.votogether.domain.vote.service; + +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.repository.PostRepository; +import com.votogether.domain.vote.entity.Vote; +import com.votogether.domain.vote.repository.VoteRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +@Transactional +public class VoteService { + + public final VoteRepository voteRepository; + public final PostRepository postRepository; + + public void vote( + final Member member, + final Long postId, + final Long postOptionId + ) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + + Vote vote = member.vote(post, postOptionId); + member.plusPoint(1); + + voteRepository.save(vote); + } + +} From bb44a75ac03bcbbe252679064f23a5bf2a836e73 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Fri, 14 Jul 2023 02:06:48 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20(#36)=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=EC=97=90=20=ED=88=AC=ED=91=9C=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=ED=95=98=EB=8A=94=20API=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/vote/controller/VoteController.java | 12 ++++++++++++ .../com/votogether/domain/vote/entity/Vote.java | 4 ++++ .../domain/vote/repository/VoteRepository.java | 3 +++ .../domain/vote/service/VoteService.java | 17 +++++++++++++++++ 4 files changed, 36 insertions(+) diff --git a/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java b/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java index 1f79f8c4b..b9304f2e5 100644 --- a/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java +++ b/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java @@ -5,8 +5,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @@ -25,5 +27,15 @@ public ResponseEntity vote( return ResponseEntity.status(HttpStatus.CREATED).build(); } + @PatchMapping("/posts/{postId}/options") + public ResponseEntity changeVote( + @PathVariable final Long postId, + @RequestParam("source") final Long originPostOptionId, + @RequestParam("target") final Long newPostOptionId, + final Member member + ) { + voteService.changeVote(member, postId, originPostOptionId, newPostOptionId); + return ResponseEntity.status(HttpStatus.OK).build(); + } } diff --git a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java index a5a4afdb8..781e73b95 100644 --- a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java +++ b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java @@ -38,4 +38,8 @@ private Vote(final Member member, final PostOption postOption) { this.postOption = postOption; } + public void changePostOption(PostOption postOption) { + this.postOption = postOption; + } + } diff --git a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java index a1c6a8453..8a918c526 100644 --- a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java +++ b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java @@ -4,4 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface VoteRepository extends JpaRepository { + + Vote findByMemberIdAndPostOptionId(Long memberId, Long postOptionId); + } diff --git a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java index df553194b..caf6dc462 100644 --- a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java +++ b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java @@ -2,6 +2,7 @@ import com.votogether.domain.member.entity.Member; import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.PostOption; import com.votogether.domain.post.repository.PostRepository; import com.votogether.domain.vote.entity.Vote; import com.votogether.domain.vote.repository.VoteRepository; @@ -31,4 +32,20 @@ public void vote( voteRepository.save(vote); } + public void changeVote( + final Member member, + final Long postId, + final Long originPostOptionId, + final Long newPostOptionId + ) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + + Vote vote = voteRepository.findByMemberIdAndPostOptionId( + member.getId(), originPostOptionId); + PostOption postOption = post.findPostOptionById(newPostOptionId); + + vote.changePostOption(postOption); + } + } From 62824b5d3d975021404c580cc3f3d66fa57af321 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Fri, 14 Jul 2023 10:30:05 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20(#36)=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EB=A7=88=EA=B0=90=EA=B8=B0=EA=B0=84=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/votogether/domain/post/entity/Post.java | 4 ++++ .../com/votogether/domain/vote/service/VoteService.java | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index efef3cc92..5a6470c08 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -68,4 +68,8 @@ public boolean isWriter(Member member) { return this.member == member; } + public boolean isClosed() { + return deadline.isBefore(LocalDateTime.now()); + } + } diff --git a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java index caf6dc462..bcb14f8fb 100644 --- a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java +++ b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java @@ -25,6 +25,7 @@ public void vote( ) { Post post = postRepository.findById(postId) .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + validateDeadLine(post); Vote vote = member.vote(post, postOptionId); member.plusPoint(1); @@ -32,6 +33,12 @@ public void vote( voteRepository.save(vote); } + private void validateDeadLine(Post post) { + if (post.isClosed()) { + throw new IllegalStateException("게시글이 마감되었습니다."); + } + } + public void changeVote( final Member member, final Long postId, @@ -40,6 +47,7 @@ public void changeVote( ) { Post post = postRepository.findById(postId) .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + validateDeadLine(post); Vote vote = voteRepository.findByMemberIdAndPostOptionId( member.getId(), originPostOptionId); From 5a499c1e8b307cf09395c390e9d1649d940ab8db Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Fri, 14 Jul 2023 10:32:39 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20(#36)=20=ED=88=AC=ED=91=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=8B=9C=20=EA=B0=99=EC=9D=80=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EA=B8=80=EC=9D=B8=EC=A7=80=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/votogether/domain/post/entity/PostOption.java | 4 ++++ .../main/java/com/votogether/domain/vote/entity/Vote.java | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java b/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java index c0728fb14..e9bcdc025 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java @@ -44,4 +44,8 @@ private PostOption( this.content = content; } + public boolean isFromSamePost(PostOption postOption) { + return this.post.equals(postOption.getPost()); + } + } diff --git a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java index 781e73b95..01972c22f 100644 --- a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java +++ b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java @@ -39,7 +39,14 @@ private Vote(final Member member, final PostOption postOption) { } public void changePostOption(PostOption postOption) { + validatePostOption(postOption); this.postOption = postOption; } + private void validatePostOption(PostOption postOption) { + if (!this.postOption.isFromSamePost(postOption)) { + throw new IllegalArgumentException("같은 게시글이어야 합니다."); + } + } + } From 6b3ee41ecbed1ae79553353523073ec725ef26ab Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Fri, 14 Jul 2023 10:33:52 +0900 Subject: [PATCH 09/16] =?UTF-8?q?test:=20(#36)=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/entity/PostTest.java | 55 +++++++++ .../domain/vote/entity/VoteTest.java | 56 +++++++++ .../vote/repository/VoteRepositoryTest.java | 112 ++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 backend/src/test/java/com/votogether/domain/post/entity/PostTest.java create mode 100644 backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java create mode 100644 backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java diff --git a/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java b/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java new file mode 100644 index 000000000..88f33e7b3 --- /dev/null +++ b/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java @@ -0,0 +1,55 @@ +package com.votogether.domain.post.entity; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.votogether.domain.member.entity.Gender; +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.member.entity.SocialType; +import java.time.LocalDateTime; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PostTest { + + @Test + @DisplayName("게시글의 작성자 여부를 확인한다.") + void isWriter() { + // given + Member member = Member.builder() + .gender(Gender.MALE) + .point(0) + .socialType(SocialType.GOOGLE) + .nickname("user1") + .socialId("kakao@gmail.com") + .birthDate( + LocalDateTime.of(1995, 07, 12, 00, 00)) + .build(); + + Post post = Post.builder() + .member(member) + .build(); + + // when + boolean result = post.isWriter(member); + + // then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("게시글의 마감 여부를 확인한다.") + void isClosed() { + // given + Post post = Post.builder() + .deadline( + LocalDateTime.of(2022, 01, 01, 0, 0)) + .build(); + + // when + boolean result = post.isClosed(); + + // then + assertThat(result).isTrue(); + } + +} diff --git a/backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java b/backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java new file mode 100644 index 000000000..474c449f3 --- /dev/null +++ b/backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java @@ -0,0 +1,56 @@ +package com.votogether.domain.vote.entity; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.PostOption; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class VoteTest { + + @Test + @DisplayName("투표선택지의 값을 변경 할 수 있다.") + void changePostOption() { + // given + Post post = Post.builder().build(); + PostOption postOption1 = PostOption.builder() + .post(post) + .build(); + PostOption postOption2 = PostOption.builder() + .post(post) + .build(); + Vote vote = Vote.builder() + .postOption(postOption1) + .build(); + + // when + vote.changePostOption(postOption2); + + // then + assertThat(vote.getPostOption()).isEqualTo(postOption2); + } + + @Test + @DisplayName("다른 게시글의 투표선택지로 값을 변경 하는 경우 에러가 발생한다.") + void changePostOption1() { + // given + Post post1 = Post.builder().build(); + Post post2 = Post.builder().build(); + PostOption postOption1 = PostOption.builder() + .post(post1) + .build(); + PostOption postOption2 = PostOption.builder() + .post(post2) + .build(); + Vote vote = Vote.builder() + .postOption(postOption1) + .build(); + + // when & then + assertThatThrownBy(() -> vote.changePostOption(postOption2)) + .isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java b/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java new file mode 100644 index 000000000..2ec42f7cc --- /dev/null +++ b/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java @@ -0,0 +1,112 @@ +package com.votogether.domain.vote.repository; + +import com.votogether.config.JpaConfig; +import com.votogether.domain.member.entity.Gender; +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.member.entity.SocialType; +import com.votogether.domain.member.repository.MemberRepository; +import com.votogether.domain.post.entity.Post; +import com.votogether.domain.post.entity.PostOption; +import com.votogether.domain.post.repository.PostRepository; +import com.votogether.domain.vote.entity.Vote; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; + +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@DataJpaTest +@Import(JpaConfig.class) +class VoteRepositoryTest { + + @Autowired + MemberRepository memberRepository; + + @Autowired + PostRepository postRepository; + + @Autowired + VoteRepository voteRepository; + + @PersistenceContext + EntityManager entityManager; + + Member member = Member.builder() + .gender(Gender.MALE) + .point(0) + .socialType(SocialType.GOOGLE) + .nickname("user1") + .socialId("kakao@gmail.com") + .birthDate( + LocalDateTime.of(1995, 07, 12, 00, 00)) + .build(); + + Post post = Post.builder() + .title("title") + .deadline( + LocalDateTime.of(3023, 07, 12, 00, 00)) + .content("content") + .member(member) + .build(); + + PostOption postOption1 = PostOption.builder() + .post(post) + .sequence(1) + .content("content1") + .build(); + + PostOption postOption2 = PostOption.builder() + .post(post) + .sequence(2) + .content("content2") + .build(); + + @Test + @DisplayName("투표를 저장한다.") + void save() { + // given + Vote vote = Vote.builder() + .postOption(postOption1) + .member(member) + .build(); + memberRepository.save(member); + postRepository.save(post); + entityManager.persist(postOption1); + + // when + voteRepository.save(vote); + + // then + assertThat(vote.getId()).isNotNull(); + } + + @Test + @DisplayName("멤버아이디와 투표선택지아이디를 통해 투표를 찾는다.") + void findVoteByMemberIdAndPostOptionId() { + // given + Vote vote = Vote.builder() + .postOption(postOption1) + .member(member) + .build(); + memberRepository.save(member); + postRepository.save(post); + entityManager.persist(postOption1); + voteRepository.save(vote); + + // when + Vote findVote = voteRepository.findByMemberIdAndPostOptionId( + member.getId(), postOption1.getId()); + + // then + assertThat(findVote).isSameAs(vote); + } + +} From 32bc3602dbd9a22b7d486cc4b3e6aa4f6127bdee Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Fri, 14 Jul 2023 22:18:19 +0900 Subject: [PATCH 10/16] =?UTF-8?q?style:=20(#36)=20final=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/votogether/domain/member/entity/Member.java | 4 ++-- .../main/java/com/votogether/domain/post/entity/Post.java | 2 +- .../java/com/votogether/domain/post/entity/PostOption.java | 2 +- .../main/java/com/votogether/domain/vote/entity/Vote.java | 6 +++--- .../votogether/domain/vote/repository/VoteRepository.java | 2 +- .../com/votogether/domain/vote/service/VoteService.java | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/com/votogether/domain/member/entity/Member.java b/backend/src/main/java/com/votogether/domain/member/entity/Member.java index 3eddaee57..41463b560 100644 --- a/backend/src/main/java/com/votogether/domain/member/entity/Member.java +++ b/backend/src/main/java/com/votogether/domain/member/entity/Member.java @@ -72,13 +72,13 @@ public Vote vote(final Post post, final Long postOptionId) { .build(); } - private void validateWriter(Post post) { + private void validateWriter(final Post post) { if (post.isWriter(this)) { throw new IllegalArgumentException("자기자신은 투표할 수 없습니다."); } } - public void plusPoint(int point) { + public void plusPoint(final int point) { this.point = this.point + point; } diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index 5a6470c08..2ad4d367e 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -64,7 +64,7 @@ public PostOption findPostOptionById(final Long PostOptionId) { .orElseThrow(() -> new IllegalArgumentException("해당 게시글에서 존재하지 않는 선택지 입니다.")); } - public boolean isWriter(Member member) { + public boolean isWriter(final Member member) { return this.member == member; } diff --git a/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java b/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java index e9bcdc025..6b43fdd7c 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java @@ -44,7 +44,7 @@ private PostOption( this.content = content; } - public boolean isFromSamePost(PostOption postOption) { + public boolean isFromSamePost(final PostOption postOption) { return this.post.equals(postOption.getPost()); } diff --git a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java index 01972c22f..c9d20c4cc 100644 --- a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java +++ b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java @@ -15,9 +15,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; +@Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@Entity public class Vote extends BaseEntity { @Id @@ -38,12 +38,12 @@ private Vote(final Member member, final PostOption postOption) { this.postOption = postOption; } - public void changePostOption(PostOption postOption) { + public void changePostOption(final PostOption postOption) { validatePostOption(postOption); this.postOption = postOption; } - private void validatePostOption(PostOption postOption) { + private void validatePostOption(final PostOption postOption) { if (!this.postOption.isFromSamePost(postOption)) { throw new IllegalArgumentException("같은 게시글이어야 합니다."); } diff --git a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java index 8a918c526..ec9011d65 100644 --- a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java +++ b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java @@ -5,6 +5,6 @@ public interface VoteRepository extends JpaRepository { - Vote findByMemberIdAndPostOptionId(Long memberId, Long postOptionId); + Vote findByMemberIdAndPostOptionId(final Long memberId, final Long postOptionId); } diff --git a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java index bcb14f8fb..11b6f8761 100644 --- a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java +++ b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java @@ -10,9 +10,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -@RequiredArgsConstructor @Service @Transactional +@RequiredArgsConstructor public class VoteService { public final VoteRepository voteRepository; @@ -33,7 +33,7 @@ public void vote( voteRepository.save(vote); } - private void validateDeadLine(Post post) { + private void validateDeadLine(final Post post) { if (post.isClosed()) { throw new IllegalStateException("게시글이 마감되었습니다."); } From b141ebf7584734ff91c9eeb72880021dec3cecf0 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Mon, 17 Jul 2023 16:40:55 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20swagger=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/vote/controller/VoteController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java b/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java index b9304f2e5..de3178d34 100644 --- a/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java +++ b/backend/src/main/java/com/votogether/domain/vote/controller/VoteController.java @@ -2,6 +2,9 @@ import com.votogether.domain.member.entity.Member; import com.votogether.domain.vote.service.VoteService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -11,12 +14,15 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -@RequiredArgsConstructor @RestController +@Tag(name = "투표", description = "투표 API") +@RequiredArgsConstructor public class VoteController { private final VoteService voteService; + @Operation(summary = "투표하기", description = "게시글의 선택지에 투표를 한다.") + @ApiResponse(responseCode = "201", description = "투표 성공") @PostMapping("/posts/{postId}/options/{optionId}") public ResponseEntity vote( @PathVariable final Long postId, @@ -27,6 +33,9 @@ public ResponseEntity vote( return ResponseEntity.status(HttpStatus.CREATED).build(); } + + @Operation(summary = "투표 수정하기", description = "게시글의 선택지의 투표를 수정한다.") + @ApiResponse(responseCode = "200", description = "투표 수정 성공") @PatchMapping("/posts/{postId}/options") public ResponseEntity changeVote( @PathVariable final Long postId, From 2b4aa11ec35ac647233f2ad8b21674f9c06c32e5 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Mon, 17 Jul 2023 18:02:19 +0900 Subject: [PATCH 12/16] =?UTF-8?q?feat:=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 투표 할때 이미 게시글에 투표를 했는지 검증하는 기능 - 투표 수정할때 기존에 투표가 존재하는지 검증하는 기능 --- .../vote/repository/VoteRepository.java | 6 ++++- .../domain/vote/service/VoteService.java | 23 ++++++++++++++----- .../vote/repository/VoteRepositoryTest.java | 9 ++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java index ec9011d65..590a9cac6 100644 --- a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java +++ b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java @@ -1,10 +1,14 @@ package com.votogether.domain.vote.repository; import com.votogether.domain.vote.entity.Vote; +import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface VoteRepository extends JpaRepository { - Vote findByMemberIdAndPostOptionId(final Long memberId, final Long postOptionId); + Optional findByMemberIdAndPostOptionId(final Long memberId, final Long postOptionId); + + List findByMemberIdAndPostOptionIdIn(final Long memberId, final List postOptionIds); } diff --git a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java index 11b6f8761..9c1802478 100644 --- a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java +++ b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java @@ -6,6 +6,7 @@ import com.votogether.domain.post.repository.PostRepository; import com.votogether.domain.vote.entity.Vote; import com.votogether.domain.vote.repository.VoteRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,8 +16,8 @@ @RequiredArgsConstructor public class VoteService { - public final VoteRepository voteRepository; - public final PostRepository postRepository; + private final VoteRepository voteRepository; + private final PostRepository postRepository; public void vote( final Member member, @@ -27,10 +28,19 @@ public void vote( .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); validateDeadLine(post); - Vote vote = member.vote(post, postOptionId); + List postOptionIdsInPost = post.getPostOptions().stream() + .map(postOption -> postOption.getId()) + .toList(); + + List alreadyVoted = voteRepository.findByMemberIdAndPostOptionIdIn(member.getId(), postOptionIdsInPost); + if (!alreadyVoted.isEmpty()) { + throw new IllegalStateException("해당 게시물에는 이미 투표하였습니다."); + } + + Vote newVote = member.vote(post, postOptionId); member.plusPoint(1); - voteRepository.save(vote); + voteRepository.save(newVote); } private void validateDeadLine(final Post post) { @@ -49,8 +59,9 @@ public void changeVote( .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); validateDeadLine(post); - Vote vote = voteRepository.findByMemberIdAndPostOptionId( - member.getId(), originPostOptionId); + Vote vote = voteRepository.findByMemberIdAndPostOptionId(member.getId(), originPostOptionId) + .orElseThrow(() -> new IllegalArgumentException("선택지에 해당되는 투표가 존재하지 않습니다.")); + PostOption postOption = post.findPostOptionById(newPostOptionId); vote.changePostOption(postOption); diff --git a/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java b/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java index 2ec42f7cc..69110fd62 100644 --- a/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java +++ b/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java @@ -1,5 +1,7 @@ package com.votogether.domain.vote.repository; +import static org.assertj.core.api.Assertions.assertThat; + import com.votogether.config.JpaConfig; import com.votogether.domain.member.entity.Gender; import com.votogether.domain.member.entity.Member; @@ -11,6 +13,7 @@ import com.votogether.domain.vote.entity.Vote; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import java.time.LocalDateTime; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -18,10 +21,6 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; -import java.time.LocalDateTime; - -import static org.assertj.core.api.Assertions.assertThat; - @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @DataJpaTest @Import(JpaConfig.class) @@ -103,7 +102,7 @@ void findVoteByMemberIdAndPostOptionId() { // when Vote findVote = voteRepository.findByMemberIdAndPostOptionId( - member.getId(), postOption1.getId()); + member.getId(), postOption1.getId()).get(); // then assertThat(findVote).isSameAs(vote); From e364be98fedb95eb6cdf4ce9e81d7ba282f6fd0d Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Mon, 17 Jul 2023 22:03:08 +0900 Subject: [PATCH 13/16] =?UTF-8?q?refactor:=20(#36)=20=EB=A9=A4=EB=B2=84?= =?UTF-8?q?=EA=B0=80=20=EC=95=84=EB=8B=8C=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=B4=20=ED=88=AC=ED=91=9C=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EB=A7=8C=EB=93=A4=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/entity/Member.java | 18 --------- .../votogether/domain/post/entity/Post.java | 37 ++++++++++++++++--- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/backend/src/main/java/com/votogether/domain/member/entity/Member.java b/backend/src/main/java/com/votogether/domain/member/entity/Member.java index 41463b560..d21e3dc94 100644 --- a/backend/src/main/java/com/votogether/domain/member/entity/Member.java +++ b/backend/src/main/java/com/votogether/domain/member/entity/Member.java @@ -1,9 +1,6 @@ package com.votogether.domain.member.entity; import com.votogether.domain.common.BaseEntity; -import com.votogether.domain.post.entity.Post; -import com.votogether.domain.post.entity.PostOption; -import com.votogether.domain.vote.entity.Vote; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -63,21 +60,6 @@ private Member( this.point = point; } - public Vote vote(final Post post, final Long postOptionId) { - PostOption postOption = post.findPostOptionById(postOptionId); - validateWriter(post); - return Vote.builder() - .member(this) - .postOption(postOption) - .build(); - } - - private void validateWriter(final Post post) { - if (post.isWriter(this)) { - throw new IllegalArgumentException("자기자신은 투표할 수 없습니다."); - } - } - public void plusPoint(final int point) { this.point = this.point + point; } diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index 2ad4d367e..9995058fe 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -2,6 +2,7 @@ import com.votogether.domain.common.BaseEntity; import com.votogether.domain.member.entity.Member; +import com.votogether.domain.vote.entity.Vote; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -57,11 +58,8 @@ private Post( this.deadline = deadline; } - public PostOption findPostOptionById(final Long PostOptionId) { - return postOptions.stream() - .filter(postOption -> postOption.getId().equals(PostOptionId)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("해당 게시글에서 존재하지 않는 선택지 입니다.")); + public boolean hasPostOption(final PostOption postOption) { + return postOptions.contains(postOption); } public boolean isWriter(final Member member) { @@ -72,4 +70,33 @@ public boolean isClosed() { return deadline.isBefore(LocalDateTime.now()); } + public Vote makeVote(Member member, PostOption postOption) { + validateDeadLine(); + validateWriter(member); + validatePostOption(postOption); + + return Vote.builder() + .member(member) + .postOption(postOption) + .build(); + } + + private void validateDeadLine() { + if (isClosed()) { + throw new IllegalStateException("게시글이 이미 마감되었습니다."); + } + } + + private void validateWriter(Member member) { + if (isWriter(member)) { + throw new IllegalArgumentException("작성자는 투표할 수 없습니다."); + } + } + + private void validatePostOption(PostOption postOption) { + if (!hasPostOption(postOption)) { + throw new IllegalArgumentException("해당 게시글에서 존재하지 않는 선택지 입니다."); + } + } + } From 426cf072bec0ca1ed22f6606338ba8c6f4aa5ae2 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Mon, 17 Jul 2023 22:04:33 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor:=20(#36)=20PostOptionRepository?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80,=20=ED=88=AC=ED=91=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=8B=9C=20=ED=88=AC=ED=91=9C=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=82=AD=EC=A0=9C=ED=9B=84=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/entity/PostOption.java | 4 -- .../post/repository/PostOptionRepository.java | 7 ++ .../votogether/domain/vote/entity/Vote.java | 11 ---- .../vote/repository/VoteRepository.java | 6 +- .../domain/vote/service/VoteService.java | 42 ++++++------ .../domain/vote/entity/VoteTest.java | 56 ---------------- .../vote/repository/VoteRepositoryTest.java | 65 ++++++++++++++----- 7 files changed, 83 insertions(+), 108 deletions(-) create mode 100644 backend/src/main/java/com/votogether/domain/post/repository/PostOptionRepository.java delete mode 100644 backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java diff --git a/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java b/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java index 6b43fdd7c..c0728fb14 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/PostOption.java @@ -44,8 +44,4 @@ private PostOption( this.content = content; } - public boolean isFromSamePost(final PostOption postOption) { - return this.post.equals(postOption.getPost()); - } - } diff --git a/backend/src/main/java/com/votogether/domain/post/repository/PostOptionRepository.java b/backend/src/main/java/com/votogether/domain/post/repository/PostOptionRepository.java new file mode 100644 index 000000000..c208de238 --- /dev/null +++ b/backend/src/main/java/com/votogether/domain/post/repository/PostOptionRepository.java @@ -0,0 +1,7 @@ +package com.votogether.domain.post.repository; + +import com.votogether.domain.post.entity.PostOption; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostOptionRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java index c9d20c4cc..f4bf1bbbc 100644 --- a/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java +++ b/backend/src/main/java/com/votogether/domain/vote/entity/Vote.java @@ -38,15 +38,4 @@ private Vote(final Member member, final PostOption postOption) { this.postOption = postOption; } - public void changePostOption(final PostOption postOption) { - validatePostOption(postOption); - this.postOption = postOption; - } - - private void validatePostOption(final PostOption postOption) { - if (!this.postOption.isFromSamePost(postOption)) { - throw new IllegalArgumentException("같은 게시글이어야 합니다."); - } - } - } diff --git a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java index 590a9cac6..d896c8dbe 100644 --- a/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java +++ b/backend/src/main/java/com/votogether/domain/vote/repository/VoteRepository.java @@ -1,5 +1,7 @@ package com.votogether.domain.vote.repository; +import com.votogether.domain.member.entity.Member; +import com.votogether.domain.post.entity.PostOption; import com.votogether.domain.vote.entity.Vote; import java.util.List; import java.util.Optional; @@ -7,8 +9,8 @@ public interface VoteRepository extends JpaRepository { - Optional findByMemberIdAndPostOptionId(final Long memberId, final Long postOptionId); + Optional findByMemberAndPostOption(final Member member, final PostOption postOption); - List findByMemberIdAndPostOptionIdIn(final Long memberId, final List postOptionIds); + List findByMemberAndPostOptionIn(final Member member, final List postOptions); } diff --git a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java index 9c1802478..f8a502f3f 100644 --- a/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java +++ b/backend/src/main/java/com/votogether/domain/vote/service/VoteService.java @@ -3,6 +3,7 @@ import com.votogether.domain.member.entity.Member; import com.votogether.domain.post.entity.Post; import com.votogether.domain.post.entity.PostOption; +import com.votogether.domain.post.repository.PostOptionRepository; import com.votogether.domain.post.repository.PostRepository; import com.votogether.domain.vote.entity.Vote; import com.votogether.domain.vote.repository.VoteRepository; @@ -18,6 +19,7 @@ public class VoteService { private final VoteRepository voteRepository; private final PostRepository postRepository; + private final PostOptionRepository postOptionRepository; public void vote( final Member member, @@ -25,27 +27,22 @@ public void vote( final Long postOptionId ) { Post post = postRepository.findById(postId) - .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); - validateDeadLine(post); + .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다.")); - List postOptionIdsInPost = post.getPostOptions().stream() - .map(postOption -> postOption.getId()) - .toList(); + validateAlreadyVoted(member, post); - List alreadyVoted = voteRepository.findByMemberIdAndPostOptionIdIn(member.getId(), postOptionIdsInPost); - if (!alreadyVoted.isEmpty()) { - throw new IllegalStateException("해당 게시물에는 이미 투표하였습니다."); - } + PostOption postOption = postOptionRepository.findById(postOptionId) + .orElseThrow(() -> new IllegalArgumentException("해당 선택지가 존재하지 않습니다.")); - Vote newVote = member.vote(post, postOptionId); + Vote vote = post.makeVote(member, postOption); member.plusPoint(1); - - voteRepository.save(newVote); + voteRepository.save(vote); } - private void validateDeadLine(final Post post) { - if (post.isClosed()) { - throw new IllegalStateException("게시글이 마감되었습니다."); + private void validateAlreadyVoted(Member member, Post post) { + List alreadyVoted = voteRepository.findByMemberAndPostOptionIn(member, post.getPostOptions()); + if (!alreadyVoted.isEmpty()) { + throw new IllegalStateException("해당 게시물에는 이미 투표하였습니다."); } } @@ -56,15 +53,20 @@ public void changeVote( final Long newPostOptionId ) { Post post = postRepository.findById(postId) - .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); - validateDeadLine(post); + .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다.")); + + PostOption originPostOption = postOptionRepository.findById(originPostOptionId) + .orElseThrow(() -> new IllegalArgumentException("헤당 선택지가 존재하지 않습니다.")); - Vote vote = voteRepository.findByMemberIdAndPostOptionId(member.getId(), originPostOptionId) + Vote originVote = voteRepository.findByMemberAndPostOption(member, originPostOption) .orElseThrow(() -> new IllegalArgumentException("선택지에 해당되는 투표가 존재하지 않습니다.")); - PostOption postOption = post.findPostOptionById(newPostOptionId); + PostOption newPostOption = postOptionRepository.findById(newPostOptionId) + .orElseThrow(() -> new IllegalArgumentException("헤당 선택지가 존재하지 않습니다.")); - vote.changePostOption(postOption); + voteRepository.delete(originVote); + Vote vote = post.makeVote(member, newPostOption); + voteRepository.save(vote); } } diff --git a/backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java b/backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java deleted file mode 100644 index 474c449f3..000000000 --- a/backend/src/test/java/com/votogether/domain/vote/entity/VoteTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.votogether.domain.vote.entity; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.votogether.domain.post.entity.Post; -import com.votogether.domain.post.entity.PostOption; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class VoteTest { - - @Test - @DisplayName("투표선택지의 값을 변경 할 수 있다.") - void changePostOption() { - // given - Post post = Post.builder().build(); - PostOption postOption1 = PostOption.builder() - .post(post) - .build(); - PostOption postOption2 = PostOption.builder() - .post(post) - .build(); - Vote vote = Vote.builder() - .postOption(postOption1) - .build(); - - // when - vote.changePostOption(postOption2); - - // then - assertThat(vote.getPostOption()).isEqualTo(postOption2); - } - - @Test - @DisplayName("다른 게시글의 투표선택지로 값을 변경 하는 경우 에러가 발생한다.") - void changePostOption1() { - // given - Post post1 = Post.builder().build(); - Post post2 = Post.builder().build(); - PostOption postOption1 = PostOption.builder() - .post(post1) - .build(); - PostOption postOption2 = PostOption.builder() - .post(post2) - .build(); - Vote vote = Vote.builder() - .postOption(postOption1) - .build(); - - // when & then - assertThatThrownBy(() -> vote.changePostOption(postOption2)) - .isInstanceOf(IllegalArgumentException.class); - } - -} diff --git a/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java b/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java index 69110fd62..318d61898 100644 --- a/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java +++ b/backend/src/test/java/com/votogether/domain/vote/repository/VoteRepositoryTest.java @@ -9,11 +9,11 @@ import com.votogether.domain.member.repository.MemberRepository; import com.votogether.domain.post.entity.Post; import com.votogether.domain.post.entity.PostOption; +import com.votogether.domain.post.repository.PostOptionRepository; import com.votogether.domain.post.repository.PostRepository; import com.votogether.domain.vote.entity.Vote; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -35,8 +35,8 @@ class VoteRepositoryTest { @Autowired VoteRepository voteRepository; - @PersistenceContext - EntityManager entityManager; + @Autowired + PostOptionRepository postOptionRepository; Member member = Member.builder() .gender(Gender.MALE) @@ -48,7 +48,7 @@ class VoteRepositoryTest { LocalDateTime.of(1995, 07, 12, 00, 00)) .build(); - Post post = Post.builder() + Post post1 = Post.builder() .title("title") .deadline( LocalDateTime.of(3023, 07, 12, 00, 00)) @@ -56,14 +56,21 @@ class VoteRepositoryTest { .member(member) .build(); + Post post2 = Post.builder() + .title("title2") + .deadline( + LocalDateTime.of(3023, 07, 12, 00, 00)) + .content("content2") + .member(member) + .build(); PostOption postOption1 = PostOption.builder() - .post(post) + .post(post1) .sequence(1) .content("content1") .build(); PostOption postOption2 = PostOption.builder() - .post(post) + .post(post2) .sequence(2) .content("content2") .build(); @@ -77,8 +84,8 @@ void save() { .member(member) .build(); memberRepository.save(member); - postRepository.save(post); - entityManager.persist(postOption1); + postRepository.save(post1); + postOptionRepository.save(postOption1); // when voteRepository.save(vote); @@ -88,24 +95,52 @@ void save() { } @Test - @DisplayName("멤버아이디와 투표선택지아이디를 통해 투표를 찾는다.") - void findVoteByMemberIdAndPostOptionId() { + @DisplayName("멤버와 투표선택지를 통해 투표를 찾는다.") + void findByMemberAndPostOption() { // given Vote vote = Vote.builder() .postOption(postOption1) .member(member) .build(); memberRepository.save(member); - postRepository.save(post); - entityManager.persist(postOption1); + postRepository.save(post1); + postOptionRepository.save(postOption1); voteRepository.save(vote); // when - Vote findVote = voteRepository.findByMemberIdAndPostOptionId( - member.getId(), postOption1.getId()).get(); + Vote findVote = voteRepository.findByMemberAndPostOption(member, postOption1).get(); // then assertThat(findVote).isSameAs(vote); } + @Test + @DisplayName("멤버와 여러 투표선택지를 통해 투표를 찾는다.") + void findByMemberAndPostOptionIn() { + // given + memberRepository.save(member); + postRepository.save(post1); + postRepository.save(post2); + postOptionRepository.save(postOption1); + postOptionRepository.save(postOption2); + + Vote vote1 = Vote.builder() + .postOption(postOption1) + .member(member) + .build(); + voteRepository.save(vote1); + + Vote vote2 = Vote.builder() + .postOption(postOption2) + .member(member) + .build(); + voteRepository.save(vote2); + + // when + List votes = voteRepository.findByMemberAndPostOptionIn(member, List.of(postOption1, postOption2)); + + // then + assertThat(votes).hasSize(2); + } + } From baa804b179840953a806e8898a5aca4fd57030fc Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Mon, 17 Jul 2023 22:59:18 +0900 Subject: [PATCH 15/16] =?UTF-8?q?test:=20(#36)=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=9D=BC=EB=B6=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../votogether/domain/post/entity/Post.java | 4 ++- .../domain/post/entity/PostTest.java | 32 +++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index 9995058fe..2e32fea73 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -50,12 +50,14 @@ private Post( final Member member, final String title, final String content, - final LocalDateTime deadline + final LocalDateTime deadline, + final List postOptions ) { this.member = member; this.title = title; this.content = content; this.deadline = deadline; + this.postOptions = new ArrayList<>(postOptions); } public boolean hasPostOption(final PostOption postOption) { diff --git a/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java b/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java index 88f33e7b3..39cdeb8da 100644 --- a/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java +++ b/backend/src/test/java/com/votogether/domain/post/entity/PostTest.java @@ -1,6 +1,7 @@ package com.votogether.domain.post.entity; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import com.votogether.domain.member.entity.Gender; import com.votogether.domain.member.entity.Member; @@ -15,7 +16,7 @@ class PostTest { @DisplayName("게시글의 작성자 여부를 확인한다.") void isWriter() { // given - Member member = Member.builder() + Member member1 = Member.builder() .gender(Gender.MALE) .point(0) .socialType(SocialType.GOOGLE) @@ -25,31 +26,48 @@ void isWriter() { LocalDateTime.of(1995, 07, 12, 00, 00)) .build(); + Member member2 = Member.builder() + .nickname("s") + .build(); + Post post = Post.builder() - .member(member) + .member(member1) .build(); // when - boolean result = post.isWriter(member); + boolean result1 = post.isWriter(member1); + boolean result2 = post.isWriter(member2); // then - assertThat(result).isTrue(); + assertAll( + () -> assertThat(result1).isTrue(), + () -> assertThat(result2).isFalse() + ); } @Test @DisplayName("게시글의 마감 여부를 확인한다.") void isClosed() { // given - Post post = Post.builder() + Post post1 = Post.builder() .deadline( LocalDateTime.of(2022, 01, 01, 0, 0)) .build(); + Post post2 = Post.builder() + .deadline( + LocalDateTime.of(3222, 01, 01, 0, 0)) + .build(); + // when - boolean result = post.isClosed(); + boolean result1 = post1.isClosed(); + boolean result2 = post2.isClosed(); // then - assertThat(result).isTrue(); + assertAll( + () -> assertThat(result1).isTrue(), + () -> assertThat(result2).isFalse() + ); } } From 2d02f3983521964d61a9f9bd1db971c8cb9c9bd6 Mon Sep 17 00:00:00 2001 From: aiaiaiai1 Date: Mon, 17 Jul 2023 23:35:27 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20(#36)=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/votogether/domain/post/entity/Post.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/main/java/com/votogether/domain/post/entity/Post.java b/backend/src/main/java/com/votogether/domain/post/entity/Post.java index 2e32fea73..9995058fe 100644 --- a/backend/src/main/java/com/votogether/domain/post/entity/Post.java +++ b/backend/src/main/java/com/votogether/domain/post/entity/Post.java @@ -50,14 +50,12 @@ private Post( final Member member, final String title, final String content, - final LocalDateTime deadline, - final List postOptions + final LocalDateTime deadline ) { this.member = member; this.title = title; this.content = content; this.deadline = deadline; - this.postOptions = new ArrayList<>(postOptions); } public boolean hasPostOption(final PostOption postOption) {