Skip to content

Commit

Permalink
열정 유저 랭킹 목록 조회 기능 (#569)
Browse files Browse the repository at this point in the history
* feat: (#565) 상위 10명의 열정 유저 랭킹 목록 조회 기능

* refactor: (#565) 컨벤션 수정 및 dto 정적 팩터리 메서드 적용

* refactor: (#565) test final 키워드 삭제

* chore: (#565) 게시글 이미지 관련 크기 설정 수정
  • Loading branch information
jeomxon authored Sep 12, 2023
1 parent 6f0a2dc commit 6ff5b38
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 38 deletions.
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 {

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
7 changes: 5 additions & 2 deletions backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ spring:

servlet:
multipart:
max-file-size: 30MB
max-request-size: 30MB
max-file-size: 5MB
max-request-size: 35MB

h2:
console:
Expand All @@ -34,6 +34,8 @@ logging:

server:
forward-headers-strategy: FRAMEWORK
tomcat:
max-http-form-post-size: 35MB

springdoc:
swagger-ui:
Expand All @@ -55,3 +57,4 @@ jwt:
token:
secret-key: ${SECRET_KEY}
expiration-time: ${EXPIRATION_TIME}

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 (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)
);
}

}

0 comments on commit 6ff5b38

Please sign in to comment.