服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - 数据库技术 - Redis笔记点赞排行榜的实现示例

Redis笔记点赞排行榜的实现示例

2023-07-13 12:16未知服务器之家 数据库技术

一、发布探店笔记 探店笔记类似点评网站的评价,往往是图文结合。对应的表有两个 探店笔记表(主键、商户id、用户id、标题、文字、图片、探店文字描述、点赞数量、评论数量) 评价表(笔记的评价) 先上传图片请求一次保

一、发布探店笔记

探店笔记类似点评网站的评价,往往是图文结合。对应的表有两个

  • 探店笔记表(主键、商户id、用户id、标题、文字、图片、探店文字描述、点赞数量、评论数量)
  • 评价表(笔记的评价)

 先上传图片请求一次保存图片接口,再点发布请求发布接口。这两个接口已经写好

Redis笔记点赞排行榜的实现示例

二、实现查看笔记接口

Redis笔记点赞排行榜的实现示例

BlogController

@RestController
@RequestMapping("/blog")
public class BlogController {
 
    @Resource
    private IBlogService blogService;
   
    @GetMapping("/hot")
    public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {
 
        return blogService.queryHotBlog(current);
    }
 
    @GetMapping("/{id}")
    public Result queryBlogById(@PathVariable("id") String id){
        return blogService.queryBlogById(id);
    }
}

IBlogService

public interface IBlogService extends IService<Blog> {
 
    Result queryBlogById(String id);
 
    Result queryHotBlog(Integer current);
}

BlogServiceImpl

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
 
    @Autowired
    private IUserService userService;
 
    @Override
    public Result queryHotBlog(Integer current) {
        // 根据用户查询
        Page<Blog> page = query()
                .orderByDesc("liked")
                .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
        // 获取当前页数据
        List<Blog> records = page.getRecords();
        // 查询用户
        records.forEach(this::queryBlogUser);
        return Result.ok(records);
    }
 
    private void queryBlogUser(Blog blog) {
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
    }
 
    @Override
    public Result queryBlogById(String id) {
        Blog blog = getById(id);
        
        if(blog == null){
            return Result.fail("笔记不存在!");
        }
 
        queryBlogUser(blog);
 
        return Result.ok(blog);
    }
}

三、点赞功能

现在已经写好的点赞接口

Redis笔记点赞排行榜的实现示例

问题:这样写接口,可以一直按点赞重复点赞

需求

  • 同一个用户只能点赞一次,再次点赞则取消点赞
  • 如果当前用户已经点赞,则点赞按钮高亮显(前端已经实现,判断blog类的isLike属性)

实现步骤

  • 给Blog类中添加一个isLike字段,表示是否被当前用户点赞
  • 修改点赞功能,利用redis的set集合判断用户是否赞过,未赞则点赞数+1,赞过则-1
  • 修改根据id查询blog的业务,判断当前用户是否点赞过,赋值给isLike字段
  • 修改分页查询blog业务,判断当前用户是否赞过,赋值isLike字段

业务实现

@RestController
@RequestMapping("/blog")
public class BlogController {
 
    @Resource
    private IBlogService blogService;
 
    @PutMapping("/like/{id}")
    public Result likeBlog(@PathVariable("id") Long id) {
        return blogService.likeBlog(id);
    }
 
    @GetMapping("/hot")
    public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {
        return blogService.queryHotBlog(current);
    }
 
    @GetMapping("/{id}")
    public Result queryBlogById(@PathVariable("id") String id){
        return blogService.queryBlogById(id);
    }
}

编写完点赞操作的接口后还要修改之前的查询接口,增加查询是否已经点赞 

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
 
    @Autowired
    private IUserService userService;
 
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
 
    @Override
    public Result queryHotBlog(Integer current) {
        // 根据用户查询
        Page<Blog> page = query()
                .orderByDesc("liked")
                .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
        // 获取当前页数据
        List<Blog> records = page.getRecords();
        // 查询用户
        records.forEach(blog -> {
            this.queryBlogUser(blog);
            this.isBlogLiked(blog);
        });
        return Result.ok(records);
    }
 
    @Override
    public Result likeBlog(Long id) {
        // 1、获取登录用户
        UserDTO user = UserHolder.getUser();
        // 2、判断当前登录用户是否已经点赞
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString());
        if(BooleanUtil.isFalse(isMember)) {
            // 3、如果未点赞,可以点赞
            // 3.1、数据库点赞数 +1
            boolean isSuccess = update().setSql("liked = liked+1").eq("id", id).update();
            // 3.2、保存用户到 Redis 的 set 集合
            if(isSuccess){
                stringRedisTemplate.opsForSet().add(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString());
            }
        } else {
            // 4、如果已点赞,取消点赞
            // 4.1、数据库点赞数 -1
            boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
            // 4.2、把用户从 Redis 的 set 集合移除
            if(isSuccess){
                stringRedisTemplate.opsForSet().remove(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString());
            }
        }
        return Result.ok();
    }
 
 
    private void queryBlogUser(Blog blog) {
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
    }
 
    @Override
    public Result queryBlogById(String id) {
        Blog blog = getById(id);
        
        if(blog == null){
            return Result.fail("笔记不存在!");
        }
 
        queryBlogUser(blog);
        // 查询 Blog 是否被点赞
        isBlogLiked(blog);
 
        return Result.ok(blog);
    }
 
    private void isBlogLiked(Blog blog) {
        Long userId = blog.getUserId();
        String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        blog.setIsLike(BooleanUtil.isTrue(isMember));
    }
}

四、点赞排行榜 

Redis笔记点赞排行榜的实现示例

查出给这个笔记点赞的人,类似微信朋友圈的点赞,可以展示谁点赞了,而且我们要进行排序

所以我们得用SortedSet这种数据类型

1、修改点赞逻辑

把原本存入set改为存入zset多加个分数,分数就是时间戳

@Override
    public Result likeBlog(Long id) {
        // 1、获取登录用户
        UserDTO user = UserHolder.getUser();
        // 2、判断当前登录用户是否已经点赞
        Double score = stringRedisTemplate.opsForZSet().score(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString());
        if(score == null) {
            // 3、如果未点赞,可以点赞
            // 3.1、数据库点赞数 +1
            boolean isSuccess = update().setSql("liked = liked+1").eq("id", id).update();
            // 3.2、保存用户到 Redis 的 set 集合
            if(isSuccess){
                // 时间作为 key 的 score
                stringRedisTemplate.opsForZSet().add(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString(), System.currentTimeMillis());
            }
        } else {
            // 4、如果已点赞,取消点赞
            // 4.1、数据库点赞数 -1
            boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
            // 4.2、把用户从 Redis 的 set 集合移除
            if(isSuccess){
                stringRedisTemplate.opsForZSet().remove(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString());
            }
        }
        return Result.ok();
    }

然后是否被点赞的方法也要修改,根据key取出分数,分数不为null就是点赞过了

 private void isBlogLiked(Blog blog) {
        UserDTO user = UserHolder.getUser();
        if(user == null){
            return;
        }
        Long userId = user.getId();
        String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        blog.setIsLike(score != null);
    }

2、点赞排行榜功能

需求:实现前五个点赞的用户返回

我们先用动态id去redis中查询出前五个点赞用户的id

然后根据id去数据库中查询信息封装到dto再返回

    @GetMapping("/likes/{id}")
    public Result queryBlogLikes(@PathVariable("id") String id) {
        return blogService.queryBlogLikes(id);
    }
    @Override
    public Result queryBlogLikes(String id) {
        String key = RedisConstants.BLOG_LIKED_KEY + id;
        // 查询 top5 的点赞用户
        Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if(top5 == null){
            return Result.ok(Collections.emptyList());
        }
        // 解析出其中的用户id
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        String join = StrUtil.join(",", ids);
        // 根据用户id查询用户
        List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by filed(id, "+join+")").list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
 
        return Result.ok(userDTOS);
    }

注意:如果我们mp直接用in来查询根本不能保证点赞的顺序,因为in查询出来的是按照id顺序返回的,没有排序,我们要按照查询id的顺序来查,order by field(id,5,1)这样

延伸 · 阅读

精彩推荐