Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

열정 유저 랭킹 목록 조회 기능 #569

Merged
merged 4 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.votogether.domain.ranking.dto.response.RankingResponse;
import com.votogether.domain.ranking.service.RankingService;
import com.votogether.global.jwt.Auth;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -21,4 +22,10 @@ public ResponseEntity<RankingResponse> getRanking(@Auth final Member member) {
return ResponseEntity.ok(response);
}

@GetMapping("/members/ranking/passion/guest")
public ResponseEntity<List<RankingResponse>> getPassionRankings() {
final List<RankingResponse> response = rankingService.getPassionRanking();
return ResponseEntity.ok(response);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.http.ResponseEntity;

@Tag(name = "랭킹", description = "랭킹 API")
Expand All @@ -14,4 +15,8 @@ public interface RankingControllerDocs {
@ApiResponse(responseCode = "200", description = "나의 열정 랭킹 정보 조회 성공")
ResponseEntity<RankingResponse> getRanking(final Member member);

@Operation(summary = "상위 10명의 열정 랭킹 조회", description = "상위 10명의 열정 랭킹 정보를 조회한다.")
@ApiResponse(responseCode = "200", description = "상위 10명의 열정 랭킹 조회 성공")
ResponseEntity<List<RankingResponse>> getPassionRankings();

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.votogether.domain.ranking.dto.response;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.ranking.entity.PassionRankings;
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "랭킹 정보 응답")
Expand All @@ -19,5 +21,14 @@ public record RankingResponse(
@Schema(description = "점수", example = "31")
long score
) {

public static RankingResponse of(final PassionRankings passionRankings, final Member member) {
return new RankingResponse(
passionRankings.getRanking(member),
member.getNickname(),
passionRankings.getPassionRecord(member).getPostCount(),
passionRankings.getPassionRecord(member).getVoteCount(),
passionRankings.getScore(member));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.ranking.entity.vo.PassionRecord;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class PassionRankings {
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3
해당 클래스를 entity로 보기는 어려울 것 같은데, entity에 패키지를 두신 이유가 있으신가요 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

작성됐던 클래스를 그대로 사용했어서 생각은 안해봤는데 어떻게 변경하는게 좋을까요?
저도 엔티티는 아닌 것 같다고 판단이 되네요.


private final Map<Member, Integer> rankings = new HashMap<>();
private final Map<Member, Integer> rankings = new LinkedHashMap<>();
private final Map<Member, PassionRecord> passionBoard;

public PassionRankings(final Map<Member, PassionRecord> passionBoard) {
Expand All @@ -20,13 +22,13 @@ public PassionRankings(final Map<Member, PassionRecord> passionBoard) {
private void calculateRanking() {
final List<Member> members = passionBoard.entrySet().stream()
.sorted(Comparator.comparingLong(entry -> -entry.getValue().calculateScore()))
.map(s -> s.getKey())
.map(Entry::getKey)
.toList();

int currentRanking = 1;
int previousRanking = -1;
long previousScore = -1;
for (Member member : members) {
for (final Member member : members) {
long currentScore = passionBoard.get(member).calculateScore();
int ranking = (currentScore == previousScore) ? previousRanking : currentRanking;

Expand All @@ -38,16 +40,20 @@ private void calculateRanking() {
}
}

public long getScore(Member member) {
public List<Member> getTop10Members() {
return new ArrayList<>(rankings.keySet())
.subList(0, Math.min(10, rankings.size()));
}

public long getScore(final Member member) {
return passionBoard.get(member).calculateScore();
}

public int getRanking(Member member) {
public int getRanking(final Member member) {
return rankings.get(member);
}

public PassionRecord getActivityRecord(Member member) {
public PassionRecord getPassionRecord(final Member member) {
return passionBoard.get(member);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.votogether.domain.ranking.entity.PassionRankings;
import com.votogether.domain.ranking.entity.vo.PassionRecord;
import com.votogether.domain.vote.repository.VoteRepository;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
Expand All @@ -25,21 +25,24 @@ public class RankingService {
@Transactional(readOnly = true)
public RankingResponse getPassionRanking(final Member member) {
final PassionRankings passionRankings = getPassionRankings();
return new RankingResponse(
passionRankings.getRanking(member),
member.getNickname(),
passionRankings.getActivityRecord(member).getPostCount(),
passionRankings.getActivityRecord(member).getVoteCount(),
passionRankings.getScore(member)
);
return RankingResponse.of(passionRankings, member);
}

@Transactional(readOnly = true)
public List<RankingResponse> getPassionRanking() {
final PassionRankings passionRankings = getPassionRankings();
return passionRankings.getTop10Members()
.stream()
.map(member -> RankingResponse.of(passionRankings, member)
).toList();
}

private PassionRankings getPassionRankings() {
final List<Member> members = memberRepository.findAll();
final List<Integer> postCounts = postRepository.findCountsByMembers(members);
final List<Integer> voteCounts = voteRepository.findCountsByMembers(members);

final Map<Member, PassionRecord> passionBoard = new HashMap<>();
final Map<Member, PassionRecord> passionBoard = new LinkedHashMap<>();

for (int i = 0; i < members.size(); i++) {
passionBoard.put(members.get(i), new PassionRecord(postCounts.get(i), voteCounts.get(i)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@
import com.votogether.global.jwt.TokenPayload;
import com.votogether.global.jwt.TokenProcessor;
import com.votogether.test.fixtures.MemberFixtures;
import io.restassured.common.mapper.TypeRef;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.context.WebApplicationContext;

@WebMvcTest(RankingController.class)
Expand Down Expand Up @@ -67,4 +71,45 @@ void getRanking() throws Exception {
assertThat(result).isEqualTo(response);
}

@Test
@DisplayName("상위 10명의 랭킹을 조회한다.")
void getPassionRankingTop10() {
// given
Member memberA = MemberFixtures.MALE_20.get();
Member memberB = MemberFixtures.MALE_30.get();
Member memberC = MemberFixtures.MALE_40.get();
Member memberD = MemberFixtures.MALE_50.get();
Member memberE = MemberFixtures.MALE_60.get();
Member memberF = MemberFixtures.FEMALE_20.get();
Member memberG = MemberFixtures.FEMALE_30.get();
Member memberH = MemberFixtures.FEMALE_30.get();
Member memberI = MemberFixtures.FEMALE_30.get();
Member memberJ = MemberFixtures.FEMALE_30.get();

List<Member> members = List.of(
memberA, memberB, memberC, memberD, memberE,
memberF, memberG, memberH, memberI, memberJ
);
List<RankingResponse> response = new ArrayList<>();
for (final Member member : members) {
response.add(new RankingResponse(1, member.getNickname(), 0, 0, 0));
}

given(rankingService.getPassionRanking()).willReturn(response);

// when
List<RankingResponse> responses = RestAssuredMockMvc
.given().log().all()
.contentType(MediaType.APPLICATION_JSON)
.when().get("/members/ranking/passion/guest")
.then().log().all()
.statusCode(HttpStatus.OK.value())
.extract()
.as(new TypeRef<>() {
});

// then
assertThat(responses).usingRecursiveComparison().isEqualTo(response);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import com.votogether.domain.ranking.dto.response.RankingResponse;
import com.votogether.test.annotation.ServiceTest;
import com.votogether.test.persister.MemberTestPersister;
import com.votogether.test.persister.PostOptionTestPersister;
import com.votogether.test.persister.PostTestPersister;
import com.votogether.test.persister.VoteTestPersister;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -25,32 +27,35 @@ class RankingServiceTest {
@Autowired
PostTestPersister postTestPersister;

@Autowired
PostOptionTestPersister postOptionTestPersister;

@Autowired
VoteTestPersister voteTestPersister;

@Test
@DisplayName("회원의 랭킹을 조회한다.")
void getRanking() {
@DisplayName("회원의 열정 랭킹을 조회한다.")
void getMemberPassionRanking() {
// given
Member member = memberTestPersister.builder().save();
Member member1 = memberTestPersister.builder().save();
Member member2 = memberTestPersister.builder().save();
Member member3 = memberTestPersister.builder().save();
Member member4 = memberTestPersister.builder().save();

postTestPersister.builder().writer(member).save();
postTestPersister.builder().writer(member1).save();
postTestPersister.builder().writer(member2).save();
postTestPersister.builder().writer(member3).save();

voteTestPersister.builder().member(member).save();
voteTestPersister.builder().member(member2).save();
voteTestPersister.builder().member(member3).save();
voteTestPersister.builder().member(member3).save();
voteTestPersister.builder().member(member4).save();
Member memberA = memberTestPersister.builder().save();
Member memberB = memberTestPersister.builder().save();
Member memberC = memberTestPersister.builder().save();
Member memberD = memberTestPersister.builder().save();
Member memberE = memberTestPersister.builder().save();

postTestPersister.builder().writer(memberA).save();
postTestPersister.builder().writer(memberB).save();
postTestPersister.builder().writer(memberC).save();
postTestPersister.builder().writer(memberD).save();

voteTestPersister.builder().member(memberA).save();
voteTestPersister.builder().member(memberC).save();
voteTestPersister.builder().member(memberD).save();
voteTestPersister.builder().member(memberD).save();
voteTestPersister.builder().member(memberE).save();

// when
RankingResponse response = rankingService.getPassionRanking(member);
RankingResponse response = rankingService.getPassionRanking(memberA);

// then (score: 6,5,7,6,1)
assertAll(
Expand All @@ -61,4 +66,44 @@ void getRanking() {
);
}

@Test
@DisplayName("상위10명의 열정 유저 랭킹을 조회한다.")
void getPassionRankingTop10() {
// given
Member memberA = memberTestPersister.builder().save();
Member memberB = memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();
memberTestPersister.builder().save();

postTestPersister.builder().writer(memberA).save();
postTestPersister.builder().writer(memberB).save();

// when
final List<RankingResponse> rankings = rankingService.getPassionRanking();

// then
assertAll(
() -> assertThat(rankings.get(0).ranking()).isEqualTo(1),
() -> assertThat(rankings.get(0).postCount()).isEqualTo(1),
() -> assertThat(rankings.get(0).score()).isEqualTo(5),
() -> assertThat(rankings.get(1).ranking()).isEqualTo(1),
() -> assertThat(rankings.get(1).postCount()).isEqualTo(1),
() -> assertThat(rankings.get(1).score()).isEqualTo(5),
() -> assertThat(rankings.get(2).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(3).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(4).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(5).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(6).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(7).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(8).ranking()).isEqualTo(3),
() -> assertThat(rankings.get(9).ranking()).isEqualTo(3)
);
}

}