You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/**
* 更新用户步数
* @param user
* @param date
* @param step
*/
public void updateUserStep(User user, Date date, int step) {
String key = getRankingKey(date);
//计算score,为了让步数大的排在前面
double score = MAX_STEP - step;
stringRedisTemplate.opsForZSet().add(key, serializeUser(user), score);
}
2.获取用户在排行榜中的排名
/**
* 获取用户在排行榜中的排名
* @param user
* @param date
*/
public long getUserRanking(User user, Date date) {
String key = getRankingKey(date);
return stringRedisTemplate.opsForZSet().rank(key, serializeUser(user)).longValue() + 1;
}
3.获取排行榜列表
/**
* 获取排行榜列表
* @param date
* @param num
* @return
*/
public List<RankingItem> getTopN(Date date, int num) {
String key = getRankingKey(date);
Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().rangeWithScores(key, 0, num);
if (CollectionUtils.isEmpty(tuples)) {
return Collections.emptyList();
}
List<RankingItem> rankingList = new ArrayList<>(tuples.size());
for (ZSetOperations.TypedTuple<String> tuple: tuples) {
RankingItem item = new RankingItem();
User user = deserializeUser(tuple.getValue());
item.setUserId(user.getId());
item.setNickname(user.getNickname());
//计算用户步数
Double score = tuple.getScore();
item.setStep(MAX_STEP - score.intValue());
rankingList.add(item);
}
return rankingList;
}
4. 单元测试
/**
* @author Ricky Fung
*/
public class WechatStepRankingServiceTest extends BaseSpringJUnitTest {
@Resource(name = "wechatStepRankingService")
private WechatStepRankingService wechatStepRankingService;
@Test
public void testUpdateRanking() {
Date now = new Date();
int step = 2000;
for (int i=0; i<100; i++) {
User user = new User();
user.setId(Long.valueOf(i));
user.setNickname("ws"+i);
wechatStepRankingService.updateUserStep(user, now, step+i);
}
}
@Test
public void testRankingList() {
Date now = new Date();
List<RankingItem> list = wechatStepRankingService.getTopN(now, 20);
System.out.println(JsonUtils.toJson(list));
}
@Test
public void testUserRanking() {
Date now = new Date();
User user = new User();
Long userId = 98L;
user.setId(userId);
user.setNickname("ws"+userId);
long rank = wechatStepRankingService.getUserRanking(user, now);
System.out.println(rank);
}
}
5.完整代码
WechatStepRankingService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
/**
* 微信运动-步数排行榜
* @author Ricky Fung
*/
@Service
public class WechatStepRankingService {
/**
* 用户每天步数上限:100万步
*/
private static final int MAX_STEP = 1000000;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 更新用户步数
* @param user
* @param date
* @param step
*/
public void updateUserStep(User user, Date date, int step) {
String key = getRankingKey(date);
//计算score,为了让步数大的排在前面
double score = MAX_STEP - step;
stringRedisTemplate.opsForZSet().add(key, serializeUser(user), score);
}
/**
* 获取排行榜列表
* @param date
* @param num
* @return
*/
public List<RankingItem> getTopN(Date date, int num) {
String key = getRankingKey(date);
Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().rangeWithScores(key, 0, num);
if (CollectionUtils.isEmpty(tuples)) {
return Collections.emptyList();
}
List<RankingItem> rankingList = new ArrayList<>(tuples.size());
for (ZSetOperations.TypedTuple<String> tuple: tuples) {
RankingItem item = new RankingItem();
User user = deserializeUser(tuple.getValue());
item.setUserId(user.getId());
item.setNickname(user.getNickname());
//计算用户步数
Double score = tuple.getScore();
item.setStep(MAX_STEP - score.intValue());
rankingList.add(item);
}
return rankingList;
}
/**
* 获取用户排行榜排名
* @param user
* @param date
*/
public long getUserRanking(User user, Date date) {
String key = getRankingKey(date);
return stringRedisTemplate.opsForZSet().rank(key, serializeUser(user)).longValue() + 1;
}
private String getRankingKey(Date date) {
return String.format("%s:%s", "wechat:rank", DateUtils.formatDate(date));
}
//----------
private String serializeUser(User user) {
return String.format("%s#%s", user.getId(), user.getNickname());
}
private User deserializeUser(String str) {
String[] arr = str.split("#");
User user = new User();
user.setId(Long.parseLong(arr[0]));
user.setNickname(arr[1]);
return user;
}
}
User.java
public class User {
private Long id;
private String nickname;
//省略 getter/setter
}
RankingItem.java
public class RankingItem {
private Long userId;
private String nickname;
private int step;
//省略 getter/setter
}
The text was updated successfully, but these errors were encountered:
场景
在互金领域中公司为了拉动用户投资,推出投资擂台赛活动(活动期间用户的总投资金额PK),在游戏领域中会有玩家等级排行榜,记步软件如 微信运动/支付宝-运动 中行走步数排行榜。
设计思路
说到排行榜就不得不说Redis 提供的 有序集合SortedSet数据结构。
Redis 有序集合和集合一样也是string类型元素的集合且不允许重复的成员,不同的是每个元素都会关联一个double类型的分数。redis通过分数来为集合中的成员进行从小到大的排序。
简而言之,一共三步:
ZADD key score member
命令添加用户步数到SortedSet;ZRANK key member
命令获取某个用户在排行榜中到名次;ZRANGE key start stop [WITHSCORES]
命令获取Top N用户列表。代码实现
本篇以 微信运动中的步数排行榜为例 进行讲解。
maven依赖
1.更新用户步数
2.获取用户在排行榜中的排名
3.获取排行榜列表
4. 单元测试
5.完整代码
WechatStepRankingService.java
User.java
RankingItem.java
The text was updated successfully, but these errors were encountered: