feat: Leaderboard to use Riyadh timezone

This commit is contained in:
sBubshait 2025-08-07 09:38:37 +03:00
parent c18f52672a
commit f1a488d70a
2 changed files with 58 additions and 6 deletions

View File

@ -68,10 +68,11 @@ public class PuzzleController {
} }
@GetMapping("/leaderboard") @GetMapping("/leaderboard")
@Operation(summary = "Get daily leaderboard", description = "Returns leaderboard for today's puzzle ordered by submission time") @Operation(summary = "Get daily leaderboard", description = "Returns leaderboard with 9am cutoff logic (before 9am shows yesterday, from 9am shows today)")
public ResponseEntity<ApiResponse<List<LeaderboardEntry>>> getLeaderboard() { public ResponseEntity<ApiResponse<Map<String, Object>>> getLeaderboard() {
try { try {
List<PuzzleAttempt> attempts = puzzleService.getTodaysLeaderboard(); List<PuzzleAttempt> attempts = puzzleService.getTodaysLeaderboard();
String leaderboardInfo = puzzleService.getLeaderboardDateInfo();
List<LeaderboardEntry> leaderboard = attempts.stream() List<LeaderboardEntry> leaderboard = attempts.stream()
.filter(attempt -> attempt.isSolved()) .filter(attempt -> attempt.isSolved())
@ -85,7 +86,12 @@ public class PuzzleController {
)) ))
.collect(Collectors.toList()); .collect(Collectors.toList());
return ResponseEntity.ok(ApiResponse.success(leaderboard)); Map<String, Object> response = Map.of(
"leaderboard", leaderboard,
"dateInfo", leaderboardInfo
);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (RuntimeException e) { } catch (RuntimeException e) {
return ResponseEntity.ok(ApiResponse.error(e.getMessage())); return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
} }

View File

@ -9,6 +9,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -26,6 +30,9 @@ public class PuzzleService {
@Autowired @Autowired
private UserService userService; private UserService userService;
private static final ZoneId RIYADH_TIMEZONE = ZoneId.of("Asia/Riyadh");
private static final LocalTime CUTOFF_TIME = LocalTime.of(9, 0); // 9:00 AM
private static final List<String> WORD_LIST = Arrays.asList( private static final List<String> WORD_LIST = Arrays.asList(
"APPLE", "BEACH", "CHAIN", "DANCE", "EAGLE", "FLAME", "GRAPE", "HOUSE", "IMAGE", "JUDGE", "APPLE", "BEACH", "CHAIN", "DANCE", "EAGLE", "FLAME", "GRAPE", "HOUSE", "IMAGE", "JUDGE",
"KNIFE", "LIGHT", "MUSIC", "NIGHT", "OCEAN", "PEACE", "QUEEN", "RIVER", "STAGE", "TIGER", "KNIFE", "LIGHT", "MUSIC", "NIGHT", "OCEAN", "PEACE", "QUEEN", "RIVER", "STAGE", "TIGER",
@ -88,7 +95,7 @@ public class PuzzleService {
); );
public Puzzle getTodaysPuzzle() { public Puzzle getTodaysPuzzle() {
LocalDate today = LocalDate.now(); LocalDate today = getRiyadhToday();
Optional<Puzzle> existingPuzzle = puzzleRepository.findByDate(today); Optional<Puzzle> existingPuzzle = puzzleRepository.findByDate(today);
if (existingPuzzle.isPresent()) { if (existingPuzzle.isPresent()) {
@ -105,6 +112,26 @@ public class PuzzleService {
return WORD_LIST.get(random.nextInt(WORD_LIST.size())); return WORD_LIST.get(random.nextInt(WORD_LIST.size()));
} }
private LocalDate getRiyadhToday() {
return ZonedDateTime.now(RIYADH_TIMEZONE).toLocalDate();
}
private LocalDateTime getRiyadhNow() {
return ZonedDateTime.now(RIYADH_TIMEZONE).toLocalDateTime();
}
private LocalDate getLeaderboardDate() {
LocalDateTime riyadhNow = getRiyadhNow();
LocalDate today = riyadhNow.toLocalDate();
// If it's before 9 AM, show yesterday's leaderboard
if (riyadhNow.toLocalTime().isBefore(CUTOFF_TIME)) {
return today.minusDays(1);
}
// From 9 AM onwards, show today's leaderboard
return today;
}
public boolean hasUserSolvedToday() { public boolean hasUserSolvedToday() {
User currentUser = userService.getCurrentUser(); User currentUser = userService.getCurrentUser();
Puzzle todaysPuzzle = getTodaysPuzzle(); Puzzle todaysPuzzle = getTodaysPuzzle();
@ -134,7 +161,26 @@ public class PuzzleService {
} }
public List<PuzzleAttempt> getTodaysLeaderboard() { public List<PuzzleAttempt> getTodaysLeaderboard() {
Puzzle todaysPuzzle = getTodaysPuzzle(); LocalDate leaderboardDate = getLeaderboardDate();
return puzzleAttemptRepository.findByPuzzleOrderBySubmittedAtAsc(todaysPuzzle); Optional<Puzzle> puzzleOpt = puzzleRepository.findByDate(leaderboardDate);
if (puzzleOpt.isEmpty()) {
// If no puzzle exists for the leaderboard date, return empty list
return Arrays.asList();
}
return puzzleAttemptRepository.findByPuzzleOrderBySubmittedAtAsc(puzzleOpt.get());
}
public String getLeaderboardDateInfo() {
LocalDateTime riyadhNow = getRiyadhNow();
LocalDate leaderboardDate = getLeaderboardDate();
LocalDate today = riyadhNow.toLocalDate();
if (leaderboardDate.equals(today)) {
return "Today's Leaderboard (from 9:00 AM Riyadh time)";
} else {
return "Yesterday's Leaderboard (before 9:00 AM Riyadh time)";
}
} }
} }