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
2 changes: 1 addition & 1 deletion server-config
10 changes: 10 additions & 0 deletions src/main/java/com/chooz/common/config/CommonConfig.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
package com.chooz.common.config;

import jakarta.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.time.Clock;
import java.time.LocalDateTime;
import java.util.TimeZone;

@Configuration
@EnableScheduling
@ConfigurationPropertiesScan(basePackages = "com.chooz")
public class CommonConfig {

@PostConstruct
public void init() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
}

@Bean
public Clock clock() {
System.out.println("LocalDateTime.now() = " + LocalDateTime.now());
return Clock.systemDefaultZone();
}

}
10 changes: 10 additions & 0 deletions src/main/java/com/chooz/common/dev/DataInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Profile({"!prod", "!test"})
Expand Down Expand Up @@ -62,6 +63,15 @@ public void init() {
"shareUrl",
PollOption.create(PollType.SINGLE, Scope.PUBLIC, CommentActive.OPEN),
CloseOption.create(CloseType.VOTER, null, 2)));
postRepository.save(Post.create(
user.getId(),
"title",
"description",
"imageUrl",
List.of(PollChoice.create("title1", "imageUrl1"), PollChoice.create("title1", "imageUrl1")),
"shareUrl",
PollOption.create(PollType.SINGLE, Scope.PUBLIC, CommentActive.OPEN),
new CloseOption(CloseType.DATE, LocalDateTime.now().plusMinutes(5), null)));
// TokenResponse tokenResponse = jwtService.createToken(new JwtClaim(testUser.getId(), testUser.getRole()));
// TokenPair tokenPair = tokenResponse.tokenPair();
// System.out.println("accessToken = " + tokenPair.accessToken());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand Down
39 changes: 15 additions & 24 deletions src/main/java/com/chooz/post/domain/CloseOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,30 @@ public CloseOption(CloseType closeType, LocalDateTime closedAt, Integer maxVoter
}

public static CloseOption create(CloseType closeType, LocalDateTime closedAt, Integer maxVoterCount) {
validateCloseOption(closeType, closedAt, maxVoterCount);
return new CloseOption(closeType, closedAt, maxVoterCount);
}

private static void validateCloseOption(CloseType closeType, LocalDateTime closedAt, Integer maxVoterCount) {
switch (closeType) {
case SELF -> validateSelfCloseType(closedAt, maxVoterCount);
case DATE -> validateDateCloseType(closedAt, maxVoterCount);
case VOTER -> validateVoterCloseType(closedAt, maxVoterCount);
case SELF -> {
return new CloseOption(closeType, null, null);
}
case DATE -> {
validateDateCloseType(closedAt);
return new CloseOption(closeType, closedAt, null);
}
case VOTER -> {
validateVoterCloseType(maxVoterCount);
return new CloseOption(closeType, closedAt, maxVoterCount);
}
default -> throw new BadRequestException(ErrorCode.INVALID_CLOSE_OPTION);
}
}

private static void validateSelfCloseType(LocalDateTime closedAt, Integer maxVoterCount) {
if (Objects.nonNull(closedAt) || Objects.nonNull(maxVoterCount)) {
throw new BadRequestException(ErrorCode.INVALID_SELF_CLOSE_OPTION);
}
}

private static void validateVoterCloseType(LocalDateTime closedAt, Integer maxVoterCount) {
if (Objects.nonNull(closedAt) || Objects.isNull(maxVoterCount)) {
throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION);
}
if (maxVoterCount < 1 || maxVoterCount > 999) {
private static void validateVoterCloseType(Integer maxVoterCount) {
if (Objects.isNull(maxVoterCount) || (maxVoterCount < 1 || maxVoterCount > 999)) {
throw new BadRequestException(ErrorCode.INVALID_VOTER_CLOSE_OPTION);
}
}

private static void validateDateCloseType(LocalDateTime closedAt, Integer maxVoterCount) {
if (Objects.isNull(closedAt) || Objects.nonNull(maxVoterCount)) {
throw new BadRequestException(ErrorCode.INVALID_DATE_CLOSE_OPTION);
}
if (closedAt.isBefore(LocalDateTime.now().plusHours(1))) {
private static void validateDateCloseType(LocalDateTime closedAt) {
if (Objects.isNull(closedAt) || closedAt.isBefore(LocalDateTime.now().plusHours(1))) {
throw new BadRequestException(ErrorCode.INVALID_DATE_CLOSE_OPTION);
}
}
Expand Down
20 changes: 0 additions & 20 deletions src/main/java/com/chooz/post/persistence/PostJpaRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,5 @@ public interface PostJpaRepository extends JpaRepository<Post, Long> {
)
Optional<CommentActive> findCommentActiveByPostId(@Param("postId") Long postId);

@Query("""
select new com.chooz.post.application.dto.PostWithVoteCount(
p,
count(distinct v2.userId)
)
from Post p
inner join Vote v on v.postId = p.id and v.userId = :userId
left join Vote v2 on v2.postId = p.id
where (:postId is null or p.id < :postId)
AND p.deleted = false
group by p
order by p.id desc
"""
)
Slice<PostWithVoteCount> findVotedPostsWithVoteCount(
@Param("userId") Long userId,
@Param("postId") Long postId,
Pageable pageable
);

Optional<Post> findByIdAndUserIdAndDeletedFalse(Long postId, Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ public Slice<FeedDto> findFeed(Long postId, Pageable pageable) {
JPAExpressions
.select(vote.userId.countDistinct())
.from(vote)
.where(vote.postId.eq(post.id)),
.where(
vote.postId.eq(post.id),
vote.deleted.isFalse()
),
JPAExpressions
.select(comment.count())
.from(comment)
Expand Down Expand Up @@ -121,7 +124,10 @@ public Slice<PostWithVoteCount> findPostsWithVoteCountByUserId(Long userId, Long
JPAExpressions
.select(vote.userId.countDistinct())
.from(vote)
.where(vote.postId.eq(post.id))
.where(
vote.postId.eq(post.id),
vote.deleted.isFalse()
)
))
.from(post)
.where(
Expand Down Expand Up @@ -153,9 +159,12 @@ public Slice<PostWithVoteCount> findVotedPostsWithVoteCount(Long userId, Long po
.select(new QPostWithVoteCount(
post,
JPAExpressions
.select(vote.userId.countDistinct())
.select(vote.userId.count())
.from(vote)
.where(vote.postId.eq(post.id))
.where(
vote.postId.eq(post.id),
vote.deleted.isFalse()
)
))
.from(post)
.where(
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/chooz/vote/application/RatioCalculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ public class RatioCalculator {

public String calculate(long totalVoteCount, long voteCount) {
if (totalVoteCount == 0) {
return "0.0";
return "0";
}
BigDecimal totalCount = new BigDecimal(totalVoteCount);
BigDecimal count = new BigDecimal(voteCount);
BigDecimal bigDecimal = count.divide(totalCount, 3, RoundingMode.HALF_UP)
BigDecimal bigDecimal = count.divide(totalCount, 2, RoundingMode.HALF_UP)
.multiply(new BigDecimal(100));
return String.format("%.1f", bigDecimal);
return String.valueOf(bigDecimal.intValue());
}

public String calculate(int totalVoteCount, long voteCount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public List<VoteResultResponse> getVoteResult(List<Vote> totalVoteList, Post pos
Map<PollChoice, Long> pollChoiceVoteCountMap = getPollChoiceVoteCountMap(totalVoteList, post);
return pollChoiceVoteCountMap.entrySet().stream()
.map(entry -> getVoteResultResponse(entry, totalVoteCount))
.sorted(Comparator.comparingLong(VoteResultResponse::voteCount).reversed())
.sorted(Comparator.comparing(VoteResultResponse::voteCount, Comparator.reverseOrder())
.thenComparing(VoteResultResponse::id))
.toList();
}

Expand Down
71 changes: 71 additions & 0 deletions src/test/java/com/chooz/post/application/PostQueryServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import com.chooz.common.dto.CursorBasePaginatedResponse;
import com.chooz.post.domain.*;
import com.chooz.post.presentation.dto.FeedResponse;
import com.chooz.post.presentation.dto.MyPagePostResponse;
import com.chooz.post.presentation.dto.PollChoiceVoteResponse;
import com.chooz.post.presentation.dto.PostResponse;
import com.chooz.support.IntegrationTest;
import com.chooz.support.fixture.PostFixture;
import com.chooz.support.fixture.UserFixture;
import com.chooz.support.fixture.VoteFixture;
import com.chooz.thumbnail.domain.ThumbnailRepository;
import com.chooz.user.domain.User;
Expand Down Expand Up @@ -178,6 +181,74 @@ void findFeed() throws Exception {
);
}

@Test
@DisplayName("투표 현황 조회 - 중복 투표")
void findVotedPosts_multiple() {
//given
User user = userRepository.save(UserFixture.createDefaultUser());
Post post = postRepository.save(PostFixture.createPostBuilder()
.userId(user.getId())
.pollOption(PostFixture.multiplePollOption())
.build());
//유저1 선택지 1, 2 복수 투표
voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(0).getId()));
voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(1).getId()));

//when
var response = postService.findVotedPosts(user.getId(), null, 10);

//then
List<MyPagePostResponse> data = response.data();
assertAll(
() -> assertThat(response.data()).hasSize(1),
() -> assertThat(response.hasNext()).isFalse(),

() -> assertThat(data.getFirst().id()).isEqualTo(post.getId()),
() -> assertThat(data.getFirst().title()).isEqualTo(post.getTitle()),

() -> assertThat(data.getFirst().postVoteInfo().mostVotedPollChoice().title()).isEqualTo(post.getPollChoices().get(0).getTitle()),
() -> assertThat(data.getFirst().postVoteInfo().totalVoterCount()).isEqualTo(2),
() -> assertThat(data.getFirst().postVoteInfo().mostVotedPollChoice().voteCount()).isEqualTo(1),
() -> assertThat(data.getFirst().postVoteInfo().mostVotedPollChoice().voteRatio()).isEqualTo("50")
);
}

@Test
@DisplayName("투표 현황 조회 - 중복 투표2")
void findVotedPosts_multiple2() {
//given
User user = userRepository.save(UserFixture.createDefaultUser());
User user2 = userRepository.save(UserFixture.createDefaultUser());
Post post = postRepository.save(PostFixture.createPostBuilder()
.userId(user.getId())
.pollOption(PostFixture.multiplePollOption())
.build());
//유저1 선택지 1, 2 복수 투표
voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(0).getId()));
voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(1).getId()));

//유저2 선택지 1 단일 투표
voteRepository.save(VoteFixture.createDefaultVote(user.getId(), post.getId(), post.getPollChoices().get(0).getId()));

//when
var response = postService.findVotedPosts(user.getId(), null, 10);

//then
List<MyPagePostResponse> data = response.data();
assertAll(
() -> assertThat(response.data()).hasSize(1),
() -> assertThat(response.hasNext()).isFalse(),

() -> assertThat(data.getFirst().id()).isEqualTo(post.getId()),
() -> assertThat(data.getFirst().title()).isEqualTo(post.getTitle()),

() -> assertThat(data.getFirst().postVoteInfo().mostVotedPollChoice().title()).isEqualTo(post.getPollChoices().get(0).getTitle()),
() -> assertThat(data.getFirst().postVoteInfo().totalVoterCount()).isEqualTo(3),
() -> assertThat(data.getFirst().postVoteInfo().mostVotedPollChoice().voteCount()).isEqualTo(2),
() -> assertThat(data.getFirst().postVoteInfo().mostVotedPollChoice().voteRatio()).isEqualTo("67")
);
}

private List<Post> createPosts(User user, int size) {
List<Post> posts = new ArrayList<>();
for (int i = 0; i < size; i ++) {
Expand Down
34 changes: 0 additions & 34 deletions src/test/java/com/chooz/post/domain/CloseOptionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,6 @@ void create() throws Exception {
assertDoesNotThrow(() -> CloseOption.create(CloseType.VOTER, null, 5));
}

@Test
@DisplayName("마감 옵션 생성 실패 - null")
void createException_null() throws Exception {
// CloseType가 SELF인 경우
assertThatThrownBy(() -> CloseOption.create(SELF, LocalDateTime.now(), null))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_SELF_CLOSE_OPTION.getMessage());
assertThatThrownBy(() -> CloseOption.create(SELF, null, 2))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_SELF_CLOSE_OPTION.getMessage());

// CloseType가 DATE인 경우
assertThatThrownBy(() -> CloseOption.create(DATE, null, null))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_DATE_CLOSE_OPTION.getMessage());
assertThatThrownBy(() -> CloseOption.create(DATE, null, 2))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_DATE_CLOSE_OPTION.getMessage());
assertThatThrownBy(() -> CloseOption.create(DATE, LocalDateTime.now().plusDays(1), 2))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_DATE_CLOSE_OPTION.getMessage());

// CloseType가 VOTER인 경우
assertThatThrownBy(() -> CloseOption.create(CloseType.VOTER, null, null))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_VOTER_CLOSE_OPTION.getMessage());
assertThatThrownBy(() -> CloseOption.create(CloseType.VOTER, LocalDateTime.now(), null))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_VOTER_CLOSE_OPTION.getMessage());
assertThatThrownBy(() -> CloseOption.create(CloseType.VOTER, LocalDateTime.now(), 2))
.isInstanceOf(BadRequestException.class)
.hasMessage(ErrorCode.INVALID_VOTER_CLOSE_OPTION.getMessage());
}

@Test
@DisplayName("시간 마감 옵션 생성 실패 - 마감시간이 1시간 이내인 경우")
void createDateCloseOptionException() throws Exception {
Expand Down
6 changes: 6 additions & 0 deletions src/test/java/com/chooz/support/fixture/PostFixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,10 @@ public static PollOption.PollOptionBuilder createPollOptionBuilder() {
.scope(Scope.PUBLIC)
.commentActive(CommentActive.OPEN);
}

public static PollOption multiplePollOption() {
return createPollOptionBuilder()
.pollType(PollType.MULTIPLE)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ void setUp() {
}

@ParameterizedTest(name = "{index}: totalVoteCount={0}, voteCount={1} => result={2}")
@CsvSource({"3, 2, 66.7", "3, 1, 33.3", "4, 2, 50.0", "4, 3, 75.0", "0, 0, 0.0", "1, 0, 0.0", "1, 1, 100.0", "10, 7, 70.0", "10, 3, 30.0"})
@CsvSource({"3, 2, 67", "3, 1, 33", "4, 2, 50", "4, 3, 75", "0, 0, 0", "1, 0, 0", "1, 1, 100", "10, 7, 70", "10, 3, 30"})
@DisplayName("비율 계산")
void calculate(int totalVoteCount, int voteCount, String result) throws Exception {
//given
Expand Down
Loading