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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -29,4 +30,13 @@ public ResponseEntity<StatPageResponse> getRankings(

return ResponseEntity.ok().body(response);
}

@LimitPageSize
@GetMapping("/rankings/{nickname}")
public ResponseEntity<StatPageResponse> getRankingsByNickname(
@PathVariable String nickname, @PageableDefault Pageable pageable) {
StatPageResponse response =
statService.getRanksByNickname(nickname, pageable.getPageSize());
return ResponseEntity.ok().body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,49 @@
import io.f1.backend.domain.stat.dao.StatRepository;
import io.f1.backend.domain.stat.dto.StatPageResponse;
import io.f1.backend.domain.stat.dto.StatWithNickname;
import io.f1.backend.global.exception.CustomException;
import io.f1.backend.global.exception.errorcode.RoomErrorCode;

import lombok.RequiredArgsConstructor;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class StatService {

private final StatRepository statRepository;

@Transactional(readOnly = true)
public StatPageResponse getRanks(Pageable pageable) {
Page<StatWithNickname> stats = statRepository.findWithUser(pageable);
Page<StatWithNickname> stats = statRepository.findAllStatsWithUser(pageable);
return toStatListPageResponse(stats);
}

@Transactional(readOnly = true)
public StatPageResponse getRanksByNickname(String nickname, int pageSize) {

Page<StatWithNickname> stats =
statRepository.findAllStatsWithUser(getPageableFromNickname(nickname, pageSize));

return toStatListPageResponse(stats);
}

private Pageable getPageableFromNickname(String nickname, int pageSize) {
long score =
statRepository
.findScoreByNickname(nickname)
.orElseThrow(() -> new CustomException(RoomErrorCode.PLAYER_NOT_FOUND));

long rowNum = statRepository.countByScoreGreaterThan(score);

int pageNumber = rowNum > 0 ? (int) (rowNum / pageSize) : 0;
return PageRequest.of(pageNumber, pageSize, Sort.by(Direction.DESC, "score"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.Optional;

public interface StatRepository extends JpaRepository<Stat, Long> {

@Query(
Expand All @@ -18,5 +20,10 @@ public interface StatRepository extends JpaRepository<Stat, Long> {
FROM
Stat s JOIN s.user u
""")
Page<StatWithNickname> findWithUser(Pageable pageable);
Page<StatWithNickname> findAllStatsWithUser(Pageable pageable);

@Query("SELECT s.score FROM Stat s WHERE s.user.nickname = :nickname")
Optional<Long> findScoreByNickname(String nickname);

long countByScoreGreaterThan(Long score);
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런것도 있군요?,? 하나 배워갑니다 .. 🙂

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.f1.backend.domain.stat;

import static io.f1.backend.global.exception.errorcode.CommonErrorCode.INVALID_PAGINATION;
import static io.f1.backend.global.exception.errorcode.RoomErrorCode.PLAYER_NOT_FOUND;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
Expand Down Expand Up @@ -35,7 +36,7 @@ void totalRankingForSingleUser() throws Exception {

@Test
@DisplayName("100을 넘는 페이지 크기 요청이 오면 예외를 발생시킨다")
void totalRankingForSingleUserWithInvalidPageSize() throws Exception {
void totalRankingWithInvalidPageSize() throws Exception {
// when
ResultActions result = mockMvc.perform(get("/stats/rankings").param("size", "101"));

Expand Down Expand Up @@ -86,4 +87,61 @@ void totalRankingForThreeUserWithPageSize2() throws Exception {
jsonPath("$.totalElements").value(1),
jsonPath("$.ranks.length()").value(1));
}

@Test
@DataSet("datasets/stat/three-user-stat.yml")
@DisplayName("랭킹 페이지에서 존재하지 않는 닉네임을 검색하면 예외를 발생시킨다.")
void totalRankingWithUnregisteredNickname() throws Exception {
// given
String nickname = "UNREGISTERED";

// when
ResultActions result = mockMvc.perform(get("/stats/rankings/" + nickname));

// then
result.andExpectAll(
status().isNotFound(), jsonPath("$.code").value(PLAYER_NOT_FOUND.getCode()));
}

@Test
@DataSet("datasets/stat/three-user-stat.yml")
@DisplayName("총 유저 수가 3명이고 페이지 크기가 2일 때 1위 유저의 닉네임을 검색하면 첫 번째 페이지에 2개의 결과를 반환한다")
void totalRankingForThreeUserWithFirstRankedNickname() throws Exception {
// given
String nickname = "USER3";

// when
ResultActions result =
mockMvc.perform(get("/stats/rankings/" + nickname).param("size", "2"));

// then
result.andExpectAll(
status().isOk(),
jsonPath("$.totalPages").value(2),
jsonPath("$.currentPage").value(1),
jsonPath("$.totalElements").value(2),
jsonPath("$.ranks.length()").value(2),
jsonPath("$.ranks[0].nickname").value(nickname));
}

@Test
@DataSet("datasets/stat/three-user-stat.yml")
@DisplayName("총 유저 수가 3명이고 페이지 크기가 2일 때 3위 유저의 닉네임을 검색하면 두 번째 페이지에 1개의 결과를 반환한다")
void totalRankingForThreeUserWithLastRankedNickname() throws Exception {
// given
String nickname = "USER1";

// when
ResultActions result =
mockMvc.perform(get("/stats/rankings/" + nickname).param("size", "2"));

// then
result.andExpectAll(
status().isOk(),
jsonPath("$.totalPages").value(2),
jsonPath("$.currentPage").value(2),
jsonPath("$.totalElements").value(1),
jsonPath("$.ranks.length()").value(1),
jsonPath("$.ranks[0].nickname").value(nickname));
}
}