LeaderboardServiceImpl.java
package no.ntnu.idi.stud.savingsapp.service.impl;
import no.ntnu.idi.stud.savingsapp.service.LeaderboardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import no.ntnu.idi.stud.savingsapp.model.leaderboard.Leaderboard;
import no.ntnu.idi.stud.savingsapp.model.leaderboard.LeaderboardEntry;
import no.ntnu.idi.stud.savingsapp.model.leaderboard.LeaderboardFilter;
import no.ntnu.idi.stud.savingsapp.model.leaderboard.LeaderboardType;
import no.ntnu.idi.stud.savingsapp.model.user.Friend;
import no.ntnu.idi.stud.savingsapp.model.user.User;
import no.ntnu.idi.stud.savingsapp.repository.FriendRepository;
import no.ntnu.idi.stud.savingsapp.repository.UserRepository;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* Implementation of the LeaderBoard interface for leaderboard-related operations.
*/
@Service
public class LeaderboardServiceImpl implements LeaderboardService {
@Autowired
private UserRepository userRepository;
@Autowired
private FriendRepository friendRepository;
/**
* Retrieves a leaderboard containing the top users based on the specified type and
* filter.
* @param type The type of leaderboard to retrieve (e.g., TOTAL_POINTS,
* CURRENT_STREAK, TOP_STREAK).
* @param filter The filter for who the leaderboard should contain (e.g., GLOBAL or
* FRIENDS).
* @param entryCount The amount of entries you want the leaderboard to contain.
* @param userId The ID of the user if you wanna find a leaderboard filtered on
* FRIENDS.
* @return A Leaderboard object containing the top entries for the specified type and
* filter.
*/
@Override
public Leaderboard getTopUsers(LeaderboardType type, LeaderboardFilter filter, int entryCount, Long userId) {
Leaderboard leaderboard = new Leaderboard();
leaderboard.setType(type);
leaderboard.setFilter(filter);
List<LeaderboardEntry> entries = new ArrayList<>();
List<User> users = new ArrayList<>();
switch (filter) {
case GLOBAL:
switch (type) {
case TOTAL_POINTS:
users = userRepository.findTopUsersByTotalEarnedPoints(entryCount);
break;
case CURRENT_STREAK:
users = userRepository.findTopUsersByHighestCurrentStreak(entryCount);
break;
case TOP_STREAK:
users = userRepository.findTopUsersByHighestEverStreak(entryCount);
break;
}
for (User user : users) {
int score = 0;
long rank = 0;
switch (type) {
case TOTAL_POINTS:
score = user.getPoint().getTotalEarnedPoints();
rank = userRepository.findUserRankByTotalEarnedPoints(user.getId());
break;
case CURRENT_STREAK:
score = user.getStreak().getCurrentStreak();
rank = userRepository.findUserRankByCurrentStreak(user.getId());
break;
case TOP_STREAK:
score = user.getStreak().getHighestStreak();
rank = userRepository.findUserRankByHighestEverStreak(user.getId());
break;
}
entries.add(new LeaderboardEntry(user, score, rank));
}
break;
case FRIENDS:
switch (type) {
case TOTAL_POINTS:
users = userRepository.findTopFriendsByTotalEarnedPoints(userId, entryCount);
break;
case CURRENT_STREAK:
users = userRepository.findTopFriendsByHighestCurrentStreak(userId, entryCount);
break;
case TOP_STREAK:
users = userRepository.findTopFriendsByHighestEverStreak(userId, entryCount);
break;
}
for (User user : users) {
int score = 0;
long rank = 0;
switch (type) {
case TOTAL_POINTS:
score = user.getPoint().getTotalEarnedPoints();
rank = userRepository.findUserRankByTotalEarnedPoints(user.getId());
break;
case CURRENT_STREAK:
score = user.getStreak().getCurrentStreak();
rank = userRepository.findUserRankByCurrentStreak(user.getId());
break;
case TOP_STREAK:
score = user.getStreak().getHighestStreak();
rank = userRepository.findUserRankByHighestEverStreak(user.getId());
break;
}
entries.add(new LeaderboardEntry(user, score, rank));
}
break;
}
leaderboard.setEntries(entries);
return leaderboard;
}
/**
* Retrieves a leaderboard containing the users surrounding a user on the specified
* type and filter. User with ID userID will be in the middle.
* @param type The type of leaderboard to retrieve (e.g., TOTAL_POINTS,
* CURRENT_STREAK, TOP_STREAK).
* @param filter The filter for who the leaderboard should contain (e.g., GLOBAL or
* FRIENDS).
* @param entryCount The amount of entries you want the leaderboard to contain.
* @param userId The ID of the user you want to be in the middle of the leaderboard.
* @return A Leaderboard object containing the specified user entry in the middle and
* entries surrounding it for the specified type and filter.
*/
@Override
public Leaderboard getSurrounding(LeaderboardType type, LeaderboardFilter filter, int entryCount, Long userId) {
Leaderboard leaderboard = new Leaderboard();
leaderboard.setType(type);
leaderboard.setFilter(filter);
List<LeaderboardEntry> entries = new ArrayList<>();
List<User> users = new ArrayList<>();
switch (filter) {
case GLOBAL:
switch (type) {
case TOTAL_POINTS:
users = userRepository.findSurroundingUsersByTotalEarnedPoints(userId, entryCount);
break;
case CURRENT_STREAK:
users = userRepository.findSurroundingUsersByHighestCurrentStreak(userId, entryCount);
break;
case TOP_STREAK:
users = userRepository.findSurroundingUsersByHighestEverStreak(userId, entryCount);
break;
}
for (User user : users) {
int score = 0;
long rank = 0;
switch (type) {
case TOTAL_POINTS:
score = user.getPoint().getTotalEarnedPoints();
rank = userRepository.findUserRankByTotalEarnedPoints(user.getId());
break;
case CURRENT_STREAK:
score = user.getStreak().getCurrentStreak();
rank = userRepository.findUserRankByCurrentStreak(user.getId());
break;
case TOP_STREAK:
score = user.getStreak().getHighestStreak();
rank = userRepository.findUserRankByHighestEverStreak(user.getId());
break;
}
entries.add(new LeaderboardEntry(user, score, rank));
}
break;
case FRIENDS:
List<Friend> friends = friendRepository.findAllById_UserOrId_FriendAndPendingFalse(userId);
// Add friends to users and remove duplicates
users = friends.stream()
.map(friend -> friend.getId().getUser().getId().equals(userId) ? friend.getId().getFriend()
: friend.getId().getUser())
.distinct()
.collect(Collectors.toList());
// Add user to users
User yourself = userRepository.findById(userId).orElse(null);
if (yourself != null && !users.contains(yourself)) {
users.add(yourself);
}
// Sort users based on type
users = users.stream().sorted(Comparator.comparing(user -> {
switch (type) {
case TOTAL_POINTS:
return user.getPoint().getTotalEarnedPoints();
case CURRENT_STREAK:
return user.getStreak().getCurrentStreak();
case TOP_STREAK:
return user.getStreak().getHighestStreak();
default:
return 0;
}
}, Comparator.reverseOrder())).collect(Collectors.toList());
// Find the index of user
int index = 0;
for (User user : users) {
if (user.getId().equals(userId)) {
break;
}
index++;
}
// Calculate the start and end index of the surrounding friends
int startIndex = Math.max(index - entryCount, 0);
int endIndex = Math.min(index + entryCount + 1, users.size());
// Extract the sublist
users = users.subList(startIndex, endIndex);
// Add the users as entries
for (User user : users) {
int score = 0;
long rank = 0;
switch (type) {
case TOTAL_POINTS:
score = user.getPoint().getTotalEarnedPoints();
rank = userRepository.findUserRankByTotalEarnedPoints(user.getId());
break;
case CURRENT_STREAK:
score = user.getStreak().getCurrentStreak();
rank = userRepository.findUserRankByCurrentStreak(user.getId());
break;
case TOP_STREAK:
score = user.getStreak().getHighestStreak();
rank = userRepository.findUserRankByHighestEverStreak(user.getId());
break;
}
entries.add(new LeaderboardEntry(user, score, rank));
}
break;
}
leaderboard.setEntries(entries);
return leaderboard;
}
/**
* Get the total sum of the total points of all users.
* @return Long
*/
@Override
public long getSumTotalEarnedPoints() {
return userRepository.getSumTotalEarnedPoints();
}
}