diff --git a/src/main/java/com/example/green/domain/dashboard/rankingmodule/entity/WeeklyRanking.java b/src/main/java/com/example/green/domain/dashboard/rankingmodule/entity/WeeklyRanking.java index eaae93f7..179239f3 100644 --- a/src/main/java/com/example/green/domain/dashboard/rankingmodule/entity/WeeklyRanking.java +++ b/src/main/java/com/example/green/domain/dashboard/rankingmodule/entity/WeeklyRanking.java @@ -58,10 +58,13 @@ public class WeeklyRanking extends TimeBaseEntity { @Column(name = "week_end", nullable = false) private LocalDate weekEnd; - public void setRank(int rank) { - if (this.rank != rank) { - this.rank = rank; - } + public void updatePointAndCertification(BigDecimal totalPoint, int certificationCount) { + this.totalPoint = totalPoint; + this.certificationCount = certificationCount; + } + + public void updateRank(int rank) { + this.rank = rank; } } diff --git a/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepository.java b/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepository.java index e95fdbc6..2431d1cc 100644 --- a/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepository.java +++ b/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepository.java @@ -6,10 +6,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import com.example.green.domain.dashboard.rankingmodule.entity.WeeklyRanking; -import com.querydsl.core.Tuple; public interface WeeklyRankingRepository extends JpaRepository, WeeklyRankingRepositoryCustom { - List findByWeekStart(LocalDate weekStart); - - void deleteByWeekStart(LocalDate weekStart); + List findByWeekStart(LocalDate weekStart); } diff --git a/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryCustom.java b/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryCustom.java index 2e1283c7..4412992f 100644 --- a/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryCustom.java +++ b/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryCustom.java @@ -15,5 +15,7 @@ public interface WeeklyRankingRepositoryCustom { List findTopNByWeekStart(LocalDate weekStart, int topN); List findAllRankings(); + + List findTop8ByWeekStart(LocalDate weekStart); } diff --git a/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryImpl.java b/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryImpl.java index c7e88907..a9ab7fd6 100644 --- a/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryImpl.java +++ b/src/main/java/com/example/green/domain/dashboard/rankingmodule/repository/WeeklyRankingRepositoryImpl.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Optional; +import org.springframework.data.repository.query.Param; + import com.example.green.domain.challenge.entity.challenge.QParticipation; import com.example.green.domain.dashboard.rankingmodule.entity.QWeeklyRanking; import com.example.green.domain.dashboard.rankingmodule.entity.WeeklyRanking; @@ -97,4 +99,17 @@ public List findAllRankings() { .fetch(); } + @Override + public List findTop8ByWeekStart(@Param("weekStart") LocalDate weekStart) { + + QWeeklyRanking weeklyRanking = QWeeklyRanking.weeklyRanking; + + return queryFactory + .selectFrom(weeklyRanking) + .where(weeklyRanking.weekStart.eq(weekStart)) + .orderBy(weeklyRanking.rank.asc()) + .limit(8) // TOP 8 + .fetch(); + } + } diff --git a/src/main/java/com/example/green/domain/dashboard/rankingmodule/service/WeeklyRankingService.java b/src/main/java/com/example/green/domain/dashboard/rankingmodule/service/WeeklyRankingService.java index d6dc48b2..dd2012db 100644 --- a/src/main/java/com/example/green/domain/dashboard/rankingmodule/service/WeeklyRankingService.java +++ b/src/main/java/com/example/green/domain/dashboard/rankingmodule/service/WeeklyRankingService.java @@ -43,9 +43,6 @@ public class WeeklyRankingService { @Transactional public void updateWeeklyRanks(LocalDate weekStart, LocalDate weekEnd) { - // 기존 주차 랭킹 삭제 - weeklyRankingRepository.deleteByWeekStart(weekStart); - // 모든 회원 조회 List allMembers = memberRepository.findAll(); List memberIds = allMembers.stream() @@ -65,60 +62,62 @@ public void updateWeeklyRanks(LocalDate weekStart, LocalDate weekEnd) { MemberCertifiedCountProjection::getCertifiedCount )); - List weeklyRankings = allMembers.stream() - .map(member -> { - BigDecimal point = memberPoints.getOrDefault(member.getId(), BigDecimal.ZERO); - int certificationCount = certifiedCounts.getOrDefault(member.getId(), 0); - String profileImageUrl = (member.getProfile() != null) ? - member.getProfile().getProfileImageUrl() : null; - - return WeeklyRanking.builder() - .memberId(member.getId()) - .memberName(member.getName()) - .profileImageUrl(profileImageUrl) - .totalPoint(point) // DB 저장/정렬용 - .certificationCount(certificationCount) - .weekStart(weekStart) - .weekEnd(weekEnd) - .rank(0) - .build(); - }) - .toList(); + Map existingMap = weeklyRankingRepository + .findByWeekStart(weekStart) + .stream() + .collect(Collectors.toMap( + WeeklyRanking::getMemberId, + w -> w + )); + + List updatedRanks = new ArrayList<>(); + + for (Member member : allMembers) { + + BigDecimal point = memberPoints.getOrDefault(member.getId(), BigDecimal.ZERO); + + int certificationCount = certifiedCounts.getOrDefault(member.getId(), 0); + + String profileImageUrl = (member.getProfile() != null) ? + member.getProfile().getProfileImageUrl() : null; + + WeeklyRanking existing = existingMap.get(member.getId()); + + if (existing != null) { + existing.updatePointAndCertification(point, certificationCount); + updatedRanks.add(existing); + } + } // 정렬 (포인트 내림차순, 동점이면 인증 수 내림차순) - weeklyRankings.sort( + updatedRanks.sort( Comparator.comparing(WeeklyRanking::getTotalPoint, Comparator.reverseOrder()) .thenComparing(WeeklyRanking::getCertificationCount, Comparator.reverseOrder()) ); - int rankCounter = 1; - for (int i = 0; i < weeklyRankings.size(); i++) { + int ranking = 1; + for (int i = 0; i < updatedRanks.size(); i++) { + if (i > 0 && - weeklyRankings.get(i).getTotalPoint().compareTo(weeklyRankings.get(i - 1).getTotalPoint()) == 0 && - weeklyRankings.get(i).getCertificationCount() == - weeklyRankings.get(i - 1).getCertificationCount()) { + updatedRanks.get(i).getTotalPoint().compareTo(updatedRanks.get(i - 1).getTotalPoint()) == 0 && + updatedRanks.get(i).getCertificationCount() == + updatedRanks.get(i - 1).getCertificationCount()) { - // 완전 동점 → 이전 rank와 동일 - weeklyRankings.get(i).setRank(weeklyRankings.get(i - 1).getRank()); + updatedRanks.get(i).updateRank(updatedRanks.get(i - 1).getRank()); } else { - weeklyRankings.get(i).setRank(rankCounter); + updatedRanks.get(i).updateRank(ranking); } - rankCounter++; + + ranking++; } - // DB 저장 - weeklyRankingRepository.saveAll(weeklyRankings); + weeklyRankingRepository.saveAll(updatedRanks); } public List getAllRankData(LocalDate weekStart, int topN) { // 상위 N명 랭킹 엔티티 조회 - List topMembersFromDb = weeklyRankingRepository.findTopNByWeekStart(weekStart, - topN); - - if (topMembersFromDb.isEmpty()) { - return new ArrayList<>(); - } + List topMembersFromDb = weeklyRankingRepository.findTop8ByWeekStart(weekStart); // 엔티티 → DTO 변환 return topMembersFromDb.stream() diff --git a/src/test/java/com/example/green/domain/dashboard/ranking/service/WeeklyRankingServiceTest.java b/src/test/java/com/example/green/domain/dashboard/ranking/service/WeeklyRankingServiceTest.java index 31ee0ba5..4bec787f 100644 --- a/src/test/java/com/example/green/domain/dashboard/ranking/service/WeeklyRankingServiceTest.java +++ b/src/test/java/com/example/green/domain/dashboard/ranking/service/WeeklyRankingServiceTest.java @@ -5,18 +5,15 @@ import java.math.BigDecimal; import java.time.LocalDate; -import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import com.example.green.domain.dashboard.rankingmodule.dto.response.MemberPointResponse; -import com.example.green.domain.dashboard.rankingmodule.dto.response.TopMemberPointResponse; import com.example.green.domain.dashboard.rankingmodule.entity.WeeklyRanking; import com.example.green.domain.dashboard.rankingmodule.repository.WeeklyRankingRepository; import com.example.green.domain.dashboard.rankingmodule.service.WeeklyRankingService; @@ -66,24 +63,6 @@ void setUp() { .build(); } - @Test - @DisplayName("상위 랭킹 조회 성공") - void 상위_랭킹_조회_성공() { - // given - when(weeklyRankingRepository.findTopNByWeekStart(any(), anyInt())) - .thenReturn(List.of(rank1, rank2)); - - // when - List response = weeklyRankingService.getAllRankData(weekStart, 2); - - // then - assertThat(response).hasSize(2); - assertThat(response.get(0).nickname()).isEqualTo("홍길동"); - assertThat(response.get(1).nickname()).isEqualTo("김철수"); - - verify(weeklyRankingRepository, times(1)).findTopNByWeekStart(any(), anyInt()); - } - @Test void 내_주간_기록_조회_성공() { // given