sanket3280/code-execution
0
1const redisClient = require('../config/redis');2 3/**4 * Leaderboard Service5 * Uses Redis sorted sets for efficient leaderboard operations6 */7class LeaderboardService {8 /**9 * Create a new leaderboard10 * @param {string} name - Unique name for the leaderboard11 * @param {number} expireTime - Time in seconds until the leaderboard expires (0 = never)12 */13 static async createLeaderboard(name, expireTime = 0) {14 try {15 // Check if leaderboard already exists16 const exists = await redisClient.exists(`leaderboard:${name}`);17 if (exists) {18 // Clear existing leaderboard19 await redisClient.del(`leaderboard:${name}`);20 }21 22 // Set expiration if specified23 if (expireTime > 0) {24 await redisClient.set(`leaderboard:${name}:info`, JSON.stringify({ 25 created: Date.now(),26 expires: Date.now() + (expireTime * 1000)27 }));28 await redisClient.expire(`leaderboard:${name}:info`, expireTime);29 await redisClient.expire(`leaderboard:${name}`, expireTime);30 }31 32 return true;33 } catch (error) {34 console.error('Error creating leaderboard:', error);35 return false;36 }37 }38 39 /**40 * Add or update a user's score in the leaderboard41 * @param {string} leaderboardName - Name of the leaderboard42 * @param {string} userId - User ID43 * @param {number} score - User's score44 * @param {Object} userData - Additional user data to store45 */46 static async updateScore(leaderboardName, userId, score, userData = {}) {47 try {48 // Update score in the sorted set49 await redisClient.zadd(`leaderboard:${leaderboardName}`, score, userId);50 51 // Store additional user data52 if (Object.keys(userData).length > 0) {53 await redisClient.hset(54 `leaderboard:${leaderboardName}:users`,55 userId,56 JSON.stringify({57 ...userData,58 score,59 lastUpdated: Date.now()60 })61 );62 }63 64 return true;65 } catch (error) {66 console.error('Error updating score:', error);67 return false;68 }69 }70 71 /**72 * Get a user's rank in the leaderboard73 * @param {string} leaderboardName - Name of the leaderboard74 * @param {string} userId - User ID75 * @returns {number} User's rank (0-based) or -1 if not found76 */77 static async getUserRank(leaderboardName, userId) {78 try {79 // Get user's rank (0-based)80 const rank = await redisClient.zrevrank(`leaderboard:${leaderboardName}`, userId);81 return rank !== null ? rank : -1;82 } catch (error) {83 console.error('Error getting user rank:', error);84 return -1;85 }86 }87 88 /**89 * Get a user's score in the leaderboard90 * @param {string} leaderboardName - Name of the leaderboard91 * @param {string} userId - User ID92 * @returns {number} User's score or null if not found93 */94 static async getUserScore(leaderboardName, userId) {95 try {96 const score = await redisClient.zscore(`leaderboard:${leaderboardName}`, userId);97 return score !== null ? parseFloat(score) : null;98 } catch (error) {99 console.error('Error getting user score:', error);100 return null;101 }102 }103 104 /**105 * Get the top users from the leaderboard106 * @param {string} leaderboardName - Name of the leaderboard107 * @param {number} start - Start position (0-based)108 * @param {number} end - End position (inclusive)109 * @returns {Array} Array of [userId, score] pairs110 */111 static async getTopUsers(leaderboardName, start = 0, end = 9) {112 try {113 // Get top users with scores114 const leaderboard = await redisClient.zrevrange(115 `leaderboard:${leaderboardName}`,116 start,117 end,118 'WITHSCORES'119 );120 121 // Format the result as an array of [userId, score] pairs122 const result = [];123 for (let i = 0; i < leaderboard.length; i += 2) {124 const userId = leaderboard[i];125 const score = parseFloat(leaderboard[i + 1]);126 127 // Get additional user data if available128 let userData = {};129 const userDataStr = await redisClient.hget(130 `leaderboard:${leaderboardName}:users`,131 userId132 );133 134 if (userDataStr) {135 userData = JSON.parse(userDataStr);136 }137 138 result.push({139 userId,140 score,141 rank: start + i / 2 + 1, // 1-based rank142 ...userData143 });144 }145 146 return result;147 } catch (error) {148 console.error('Error getting top users:', error);149 return [];150 }151 }152 153 /**154 * Get the total number of users in the leaderboard155 * @param {string} leaderboardName - Name of the leaderboard156 * @returns {number} Total number of users157 */158 static async getTotalUsers(leaderboardName) {159 try {160 return await redisClient.zcard(`leaderboard:${leaderboardName}`);161 } catch (error) {162 console.error('Error getting total users:', error);163 return 0;164 }165 }166 167 /**168 * Get users around a specific user in the leaderboard169 * @param {string} leaderboardName - Name of the leaderboard170 * @param {string} userId - User ID171 * @param {number} range - Number of users to get on each side172 * @returns {Array} Array of users around the specified user173 */174 static async getUsersAroundUser(leaderboardName, userId, range = 5) {175 try {176 // Get user's rank177 const rank = await this.getUserRank(leaderboardName, userId);178 if (rank === -1) {179 return [];180 }181 182 // Calculate start and end positions183 const start = Math.max(0, rank - range);184 const end = rank + range;185 186 // Get users in range187 return await this.getTopUsers(leaderboardName, start, end);188 } catch (error) {189 console.error('Error getting users around user:', error);190 return [];191 }192 }193 194 /**195 * Delete a leaderboard196 * @param {string} leaderboardName - Name of the leaderboard197 * @returns {boolean} Success status198 */199 static async deleteLeaderboard(leaderboardName) {200 try {201 await redisClient.del(`leaderboard:${leaderboardName}`);202 await redisClient.del(`leaderboard:${leaderboardName}:users`);203 await redisClient.del(`leaderboard:${leaderboardName}:info`);204 return true;205 } catch (error) {206 console.error('Error deleting leaderboard:', error);207 return false;208 }209 }210}211 212module.exports = LeaderboardService;