需求:按照点赞时间先后顺序,返回Top5的用户

三种数据结构选择的对比:

Set无序,无法满足需求,虽然List使用RPUSH可以满足有序性,但是不唯一,查找效率低,而SortedSet可以根据score值进行默认升序排序。
ZADD key value score向ZSet中添加元素,并指定score排序值
ZSCORE key value判断ZSet是否有value元素,如果存在,返回该value对应的score值,如果不存在,则返回nil
ZRANGE key start end根据score排序值,查询并返回序号从start到end区间的value值
代码实现:改用ZSet后的存储结构,点赞后,value存储用户id、score存储点赞时的时间戳,并且实现查询点赞前5名的用户列表
java
/**
* 判断当前登录用户是否点赞了这篇博客。
*
* 作用:
* 1.获取当前用户;
* 2.用户未登录,无需查询是否点赞;
* 3.判断当前登录用户是否已点赞;
*
* @param blog当前博客对象
* @return无返回值
*/
private void isBlogLiked(Blog blog) {
//1.获取当前用户
UserDTO user = UserHolder.getUser();
if (user==null){
//用户未登录,无需查询是否点赞
return;
}
Long userId = user.getId();
//2.判断当前登录用户是否已点赞
String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();
Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
blog.setIsLike(score!=null);
}
/**
* 点赞或取消点赞博客。
*
* 作用:
* 1.获取当前用户;
* 2.判断当前登录用户是否已点赞;
* 3.数据库点赞数+1;
* 4.保存用户id到Redis的ZSet集合;
* 5.数据库点赞数-1;
*
* @param id业务id
* @return处理结果
*/
@Override
public Result likeBlog(@PathVariable("id") Long id) {
//1.获取当前用户
Long userId= UserHolder.getUser().getId();
//2.判断当前登录用户是否已点赞
String key= RedisConstants.BLOG_LIKED_KEY + id;
//Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
Double score=stringRedisTemplate.opsForZSet().score(key,userId.toString());
if (score == null) { // 如果时间戳不存在,说明未点赞,可以点赞
//数据库点赞数+1
boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
if (isSuccess) {
//保存用户到Redis的ZSet集合
Boolean added = stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
log.debug("点赞写入Redis,key={},userId={},result={}",key,userId,added);
}
}else { // 如果已经点赞,取消点赞
//数据库点赞数-1
boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
if (isSuccess) {
// 把用户从Redis的ZSet集合移除
stringRedisTemplate.opsForZSet().remove(key, userId.toString());
}
}
return Result.ok();
}
/**
* 查询博客前5个点赞用户。
*
* 作用:
* 1.查询top5点赞用户zrangekey04;
* 2.解析其中的用户id;
* 3.根据用户id查询用户;
* 4.返回;
*
* @param id业务id
* @return处理结果
*/
@Override
public Result queryBlogLikes(Long id) {
String key= RedisConstants.BLOG_LIKED_KEY + id;
//1.查询top5点赞用户zrange key 0 4
Set<String> top5=stringRedisTemplate.opsForZSet().range(key, 0,4);
if(top5==null||top5.isEmpty()){
return Result.ok(Collections.emptyList());
}
//2.解析其中的用户id
List<Long> ids=top5.stream().map(Long::valueOf).collect(Collectors.toList());
String idStr= StrUtil.join(",",ids);
//3.根据用户id查询用户
List<UserDTO> userDTOS=userService.query()
.in("id",ids)
.last("ORDER BY FIELD(id,"+idStr+")")
.list()
.stream()
.map(user-> BeanUtil.copyProperties(user,UserDTO.class))
.collect(Collectors.toList());
//4.返回
return Result.ok(userDTOS);
}注意:如果直接使用MyBatis-Plus的listByIds方法,底层默认使用in进行条件查询,查出来的结果默认按照主键升序排序,返回给前端的点赞排行榜是不对的,这就用到了mysql的field函数,示例'select * from user where id in(3,2,1) order by field(id,3,2,1)',实现了按我们想要的顺序进行排序
- 测试功能
先点赞的在前面

Redis中的点赞顺序按点赞时间排序
