Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/main/java/com/debatetimer/domain/poll/Vote.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.debatetimer.domain.poll;

import java.time.LocalDateTime;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

Expand All @@ -12,12 +13,13 @@ public class Vote {
private final VoteTeam team;
private final ParticipantName name;
private final ParticipateCode code;
private final LocalDateTime createdAt;

public Vote(long pollId, VoteTeam team, String name, String code) {
this(null, pollId, team, name, code);
this(null, pollId, team, name, code, LocalDateTime.now());
}

public Vote(Long id, long pollId, VoteTeam team, String name, String code) {
this(id, pollId, team, new ParticipantName(name), new ParticipateCode(code));
public Vote(Long id, long pollId, VoteTeam team, String name, String code, LocalDateTime createdAt) {
this(id, pollId, team, new ParticipantName(name), new ParticipateCode(code), createdAt);
}
}
26 changes: 22 additions & 4 deletions src/main/java/com/debatetimer/domain/poll/VoteInfo.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
package com.debatetimer.domain.poll;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.Getter;

@Getter
public class VoteInfo {

private static final Comparator<Vote> VOTE_COMPARATOR = Comparator.comparing(Vote::getCreatedAt);
private static final long INITIAL_VOTE_COUNT = 0L;

private final long pollId;
private final long totalCount;
private final long prosCount;
private final long consCount;
private final List<ParticipantName> voterNames;

public VoteInfo(long pollId, long prosCount, long consCount) {
public VoteInfo(long pollId, List<Vote> votes) {
Map<VoteTeam, Long> voteCounts = createVoteCounts(votes);
this.pollId = pollId;
this.totalCount = prosCount + consCount;
this.prosCount = prosCount;
this.consCount = consCount;
this.totalCount = votes.size();
this.prosCount = voteCounts.getOrDefault(VoteTeam.PROS, INITIAL_VOTE_COUNT);
this.consCount = voteCounts.getOrDefault(VoteTeam.CONS, INITIAL_VOTE_COUNT);
this.voterNames = votes.stream()
.sorted(VOTE_COMPARATOR)
.map(Vote::getName)
.toList();
}
Comment on lines 27 to 31
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vote의 getName() 메서드 시그니처를 String 으로 return 하게 만들면 안됨?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반대로 필드를 List으로 도메인 중심으로 짜게 하고, DTO에서 String을 반환하게 만들었어요!


private Map<VoteTeam, Long> createVoteCounts(List<Vote> votes) {
return votes.stream()
.collect(Collectors.groupingBy(Vote::getTeam, Collectors.counting()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.debatetimer.domain.poll.ParticipateCode;
import com.debatetimer.domain.poll.Vote;
import com.debatetimer.domain.poll.VoteInfo;
import com.debatetimer.domain.poll.VoteTeam;
import com.debatetimer.entity.poll.PollEntity;
import com.debatetimer.entity.poll.VoteEntity;
import com.debatetimer.exception.custom.DTClientErrorException;
Expand All @@ -12,8 +11,6 @@
import com.debatetimer.repository.poll.PollRepository;
import com.debatetimer.repository.poll.VoteRepository;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Repository;
Expand All @@ -27,16 +24,11 @@ public class VoteDomainRepository {
private final RepositoryErrorDecoder errorDecoder;

public VoteInfo findVoteInfoByPollId(long pollId) {
List<VoteEntity> pollVotes = voteRepository.findAllByPollId(pollId);
return countVotes(pollId, pollVotes);
}

private VoteInfo countVotes(long pollId, List<VoteEntity> voteEntities) {
Map<VoteTeam, Long> teamCount = voteEntities.stream()
.collect(Collectors.groupingBy(VoteEntity::getTeam, Collectors.counting()));
long prosCount = teamCount.getOrDefault(VoteTeam.PROS, 0L);
long consCount = teamCount.getOrDefault(VoteTeam.CONS, 0L);
return new VoteInfo(pollId, prosCount, consCount);
List<Vote> pollVotes = voteRepository.findAllByPollId(pollId)
.stream()
.map(VoteEntity::toDomain)
.toList();
return new VoteInfo(pollId, pollVotes);
}

public boolean isExists(long pollId, ParticipateCode code) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.debatetimer.dto.poll.response;

import com.debatetimer.domain.poll.ParticipantName;
import com.debatetimer.domain.poll.Poll;
import com.debatetimer.domain.poll.PollStatus;
import com.debatetimer.domain.poll.VoteInfo;
import java.util.List;

public record PollInfoResponse(
long id,
Expand All @@ -11,7 +13,8 @@ public record PollInfoResponse(
String consTeamName,
long totalCount,
long prosCount,
long consCount
long consCount,
List<String> voterNames
) {

public PollInfoResponse(Poll poll, VoteInfo voteInfo) {
Expand All @@ -22,7 +25,10 @@ public PollInfoResponse(Poll poll, VoteInfo voteInfo) {
poll.getConsTeamName().getValue(),
voteInfo.getTotalCount(),
voteInfo.getProsCount(),
voteInfo.getConsCount()
voteInfo.getConsCount(),
voteInfo.getVoterNames().stream()
.map(ParticipantName::getValue)
.toList()
);
}
}
5 changes: 3 additions & 2 deletions src/main/java/com/debatetimer/entity/poll/VoteEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.debatetimer.domain.poll.Vote;
import com.debatetimer.domain.poll.VoteTeam;
import com.debatetimer.entity.BaseTimeEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand All @@ -28,7 +29,7 @@
})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class VoteEntity {
public class VoteEntity extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -55,6 +56,6 @@ public VoteEntity(Vote vote, PollEntity pollEntity) {
}

public Vote toDomain() {
return new Vote(id, poll.getId(), team, name, participateCode);
return new Vote(id, poll.getId(), team, name, participateCode, getCreatedAt());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ALTER TABLE vote
ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;

ALTER TABLE vote
ADD COLUMN modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.debatetimer.dto.poll.response.PollInfoResponse;
import com.debatetimer.entity.customize.CustomizeTableEntity;
import com.debatetimer.entity.poll.PollEntity;
import com.debatetimer.entity.poll.VoteEntity;
import io.restassured.http.ContentType;
import io.restassured.http.Headers;
import org.junit.jupiter.api.Nested;
Expand Down Expand Up @@ -44,9 +45,9 @@ class GetPollInfo {
Member member = memberGenerator.generate("email@email.com");
CustomizeTableEntity table = customizeTableEntityGenerator.generate(member);
PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS);
voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리");
voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토");
voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬");
VoteEntity voter1 = voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리");
VoteEntity voter2 = voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토");
VoteEntity voter3 = voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬");
Headers headers = headerGenerator.generateAccessTokenHeader(member);

PollInfoResponse response = given()
Expand All @@ -64,7 +65,9 @@ class GetPollInfo {
() -> assertThat(response.status()).isEqualTo(pollEntity.getStatus()),
() -> assertThat(response.totalCount()).isEqualTo(3L),
() -> assertThat(response.prosCount()).isEqualTo(2L),
() -> assertThat(response.consCount()).isEqualTo(1L)
() -> assertThat(response.consCount()).isEqualTo(1L),
() -> assertThat(response.voterNames()).containsExactly(
voter1.getName(), voter2.getName(), voter3.getName())
);
Comment on lines +68 to 71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[의견 🔈 ]

음... 순서 검증이 필요한 부분이에요. 지금 voteEntites에서는 정렬이 안되어 있지만 VoteInfo에서 정렬을 해주고 있고 Controller 응답에서 투표 생성 순으로 정렬되어 응답이 온다는 점을 검증해야 하기 때문에 containsExactly가 InAnyOrder보다 맞는 검증이라 생각했습니다.

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.payload.JsonFieldType.ARRAY;
import static org.springframework.restdocs.payload.JsonFieldType.NUMBER;
import static org.springframework.restdocs.payload.JsonFieldType.STRING;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
Expand All @@ -18,6 +19,7 @@
import com.debatetimer.dto.poll.response.PollCreateResponse;
import com.debatetimer.dto.poll.response.PollInfoResponse;
import io.restassured.http.ContentType;
import java.util.List;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
Expand Down Expand Up @@ -85,7 +87,8 @@ class GetPollInfo {
fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름"),
fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"),
fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"),
fieldWithPath("consCount").type(NUMBER).description("반대 투표 수")
fieldWithPath("consCount").type(NUMBER).description("반대 투표 수"),
fieldWithPath("voterNames").type(ARRAY).description("투표자 이름 정보")
);

@Test
Expand All @@ -97,7 +100,8 @@ class GetPollInfo {
"반대",
3L,
2L,
1L
1L,
List.of("콜리", "비토", "커찬")
);
doReturn(response).when(pollService).getPollInfo(anyLong(), any(Member.class));

Expand Down Expand Up @@ -136,7 +140,8 @@ class FinishPoll {
fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름"),
fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"),
fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"),
fieldWithPath("consCount").type(NUMBER).description("반대 투표 수")
fieldWithPath("consCount").type(NUMBER).description("반대 투표 수"),
fieldWithPath("voterNames").type(ARRAY).description("투표자 이름 정보")
);

@Test
Expand All @@ -148,7 +153,9 @@ class FinishPoll {
"반대",
3L,
2L,
1L
1L,
List.of("콜리", "비토", "커찬")

);
doReturn(response).when(pollService).finishPoll(anyLong(), any(Member.class));

Expand Down
39 changes: 39 additions & 0 deletions src/test/java/com/debatetimer/domain/poll/VoteInfoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.debatetimer.domain.poll;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.LocalDateTime;
import java.util.List;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class VoteInfoTest {

@Nested
class getVoterNames {

@Test
void 생성_순으로_투표자_이름을_정렬한다() {
LocalDateTime now = LocalDateTime.now();
LocalDateTime oneMinutesAgo = now.minusMinutes(1);
LocalDateTime twoMinutesAgo = now.minusMinutes(2);
long pollId = 1L;
Vote nowVote = new Vote(1L, pollId, VoteTeam.PROS, "콜리1", "code1", now);
Vote oneMinutesAgoVote = new Vote(2L, pollId, VoteTeam.PROS, "콜리2", "code2", oneMinutesAgo);
Vote twoMinutesAgoVote = new Vote(3L, pollId, VoteTeam.PROS, "콜리3", "code3", twoMinutesAgo);

VoteInfo voteInfo = new VoteInfo(pollId, List.of(nowVote, oneMinutesAgoVote, twoMinutesAgoVote));
List<String> voterNames = voteInfo.getVoterNames()
.stream()
.map(ParticipantName::getValue)
.toList();

assertThat(voterNames)
.containsExactly(
twoMinutesAgoVote.getName().getValue(),
oneMinutesAgoVote.getName().getValue(),
nowVote.getName().getValue()
);
}
}
}
11 changes: 7 additions & 4 deletions src/test/java/com/debatetimer/service/poll/PollServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.debatetimer.dto.poll.response.PollInfoResponse;
import com.debatetimer.entity.customize.CustomizeTableEntity;
import com.debatetimer.entity.poll.PollEntity;
import com.debatetimer.entity.poll.VoteEntity;
import com.debatetimer.service.BaseServiceTest;
import java.util.Optional;
import org.junit.jupiter.api.Nested;
Expand Down Expand Up @@ -44,9 +45,9 @@ class GetPollInfo {
Member member = memberGenerator.generate("email@email.com");
CustomizeTableEntity table = customizeTableEntityGenerator.generate(member);
PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS);
voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리");
voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토");
voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬");
VoteEntity voter1 = voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리");
VoteEntity voter2 = voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토");
VoteEntity voter3 = voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬");

PollInfoResponse pollInfo = pollService.getPollInfo(table.getId(), member);

Expand All @@ -57,7 +58,9 @@ class GetPollInfo {
() -> assertThat(pollInfo.status()).isEqualTo(pollEntity.getStatus()),
() -> assertThat(pollInfo.totalCount()).isEqualTo(3L),
() -> assertThat(pollInfo.prosCount()).isEqualTo(2L),
() -> assertThat(pollInfo.consCount()).isEqualTo(1L)
() -> assertThat(pollInfo.consCount()).isEqualTo(1L),
() -> assertThat(pollInfo.voterNames()).containsExactly(
voter1.getName(), voter2.getName(), voter3.getName())
);
Comment on lines 58 to 64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}
}
Expand Down