• redis实战-实现笔记点赞和点赞排行榜


    发布探店笔记

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

    tb_blog:探店笔记表,包含笔记中的标题、文字、图片等

    tb_blog_comments:其他用户对探店笔记的评价

     

     保存笔记service层

    1. @Override
    2. public Result saveBlog(Blog blog) {
    3. // 获取登录用户
    4. UserDTO user = UserHolder.getUser();
    5. blog.setUserId(user.getId());
    6. // 保存探店博文
    7. save(blog);
    8. // 返回id
    9. return Result.ok(blog.getId());
    10. }

    注意修改图片保存路径为自己的目录

     查看探店笔记

     对应的查询接口

    1. @Override
    2. public Result queryBlogById(Long id) {
    3. // 1.查询blog
    4. Blog blog = getById(id);
    5. if (blog == null) {
    6. return Result.fail("笔记不存在!");
    7. }
    8. // 2.查询blog有关的用户
    9. queryBlogUser(blog);
    10. return Result.ok(blog);
    11. }

    实现点赞

    初步实现

    初始代码,即用户一点击点赞按钮,请求这一接口直接访问数据库将这篇笔记的点赞数量+1,没有任何限制,这并不符合我们常规的点赞逻辑,我们应该实现的是一人一赞,当用户点赞之后,点赞按钮会高亮,再次点击按钮之后会取消点赞

    1. @GetMapping("/likes/{id}")
    2. public Result queryBlogLikes(@PathVariable("id") Long id) {
    3. //修改点赞数量
    4. blogService.update().setSql("liked = liked +1 ").eq("id",id).update();
    5. return Result.ok();
    6. }

     完善功能

    需求:

    • 同一个用户只能点赞一次,再次点击则取消点赞

    • 如果当前用户已经点赞,则点赞按钮高亮显示(前端已实现,判断字段Blog类的isLike属性)

    实现步骤:

    • 给Blog类中添加一个isLike字段,标示是否被当前用户点赞

    • 修改点赞功能,利用Redis的set集合判断是否点赞过,未点赞过则点赞数+1,已点赞过则点赞数-1,将这篇笔记点赞过的用户id存储到set集合中,实现去重

    • 修改根据id查询Blog的业务,判断当前登录用户是否点赞过,赋值给isLike字段

    • 修改分页查询Blog业务,判断当前登录用户是否点赞过,赋值给isLike字段

    首先给blog类添加isLike字段,@TableField(exist = false)表示该字段不属于数据库字段

     先从redis中的set集合中判断该用户是否点过赞,如果已经点赞过会从set集合移除该用户,表示取消点赞,否则会先走数据库,并将用户id存入set集合中

    1. @Override
    2. public Result likeBlog(Long id){
    3. // 1.获取登录用户
    4. Long userId = UserHolder.getUser().getId();
    5. // 2.判断当前登录用户是否已经点赞
    6. String key = BLOG_LIKED_KEY + id;
    7. Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
    8. if(BooleanUtil.isFalse(isMember)){
    9. //3.如果未点赞,可以点赞
    10. //3.1 数据库点赞数+1
    11. boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
    12. //3.2 保存用户到Redis的set集合
    13. if(isSuccess){
    14. stringRedisTemplate.opsForSet().add(key,userId.toString());
    15. }
    16. }else{
    17. //4.如果已点赞,取消点赞
    18. //4.1 数据库点赞数-1
    19. boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
    20. //4.2 把用户从Redis的set集合移除
    21. if(isSuccess){
    22. stringRedisTemplate.opsForSet().remove(key,userId.toString());
    23. }
    24. }

    点赞状态查询

     在用户进行点赞之后,我们还需要完善对应的查询笔记接口,以保证用户点赞后的高亮响应

    对判断当前用户是否点赞该笔记,以及笔记对象要封装对应的用户信息这两个方法封装,原因是首页笔记分页查询以及笔记详情都要用到,实现代码复用

    1. //笔记需要封装对应的用户信息
    2. void fillUserToBlog(Blog blog) {
    3. Long userId = blog.getUserId();
    4. User user = userService.getById(userId);
    5. blog.setName(user.getNickName());
    6. blog.setIcon(user.getIcon());
    7. }
    8. //判断当前用户是否已经点赞笔记
    9. void isLikeBlog(Blog blog) {
    10. UserDTO user = UserHolder.getUser();
    11. //这里进行空指针判断,原因是首页并不需要登录就能访问,所以当user为空时直接结束
    12. if (user == null) {
    13. return;
    14. }
    15. Long userId = user.getId();
    16. String key = BLOG_LIKED_KEY + blog.getId();
    17. Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    18. blog.setIsLike(score != null);
    19. }

    首先就是点进笔记,查看笔记详情时,有个点赞状态查询,我们需要找到对应的接口方法进行改造

    1. @Override
    2. public Result queryBlogById(Long id) {
    3. Blog blog = getById(id);
    4. fillUserToBlog(blog);
    5. isLikeBlog(blog);
    6. return Result.ok(blog);
    7. }

    对首页笔记分页查询的方法也需要进行改造,完善点赞状态和用户信息

    1. @Override
    2. public Result queryHotBlog(Integer current) {
    3. // 根据用户查询
    4. Page page = query()
    5. .orderByDesc("liked")
    6. .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
    7. // 获取当前页数据
    8. List records = page.getRecords();
    9. // 查询用户
    10. records.forEach(blog -> {
    11. fillUserToBlog(blog);
    12. isLikeBlog(blog);
    13. });
    14. return Result.ok(records);
    15. }

    点赞排行榜

    在探店笔记的详情页面,会把给该笔记点赞的人显示出来,比如最早点赞的TOP5,形成点赞排行榜,就像朋友圈的点赞的一样,按照时间顺序排序点赞的人,这要求我们采用能排序的数据结构来存储点赞用户的id而不是像set一样是无序的,list虽然能实现按添加顺序排序,但是list元素不能唯一,不能实现一人一赞的功能,故最后sortedSet来实现

    改进点赞功能

    我们现在要将点赞用户的id存储在sortedSet中,需要在原有代码的基础上进行改善,换成存储在sortedSet中,由于sortedSet没有直接判断该value是否在这一集合中的方法,只能获取该value的score值来判断是否存在

    1. @Override
    2. public Result likeBlog(Long id) {
    3. //获取登录用户
    4. Long userId = UserHolder.getUser().getId();
    5. //判断该用户是否已经点过赞
    6. String key = BLOG_LIKED_KEY + id;
    7. Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    8. if (score == null) {
    9. boolean isSuccess = update().setSql("liked=liked+1").eq("id", id).update();
    10. if (isSuccess) {
    11. stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
    12. }
    13. } else {
    14. boolean isSuccess = update().setSql("liked=liked-1").eq("id", id).update();
    15. if (isSuccess) {
    16. stringRedisTemplate.opsForZSet().remove(key, userId.toString());
    17. }
    18. }
    19. return Result.ok();
    20. }
    21. void isLikeBlog(Blog blog) {
    22. UserDTO user = UserHolder.getUser();
    23. if (user == null) {
    24. return;
    25. }
    26. Long userId = user.getId();
    27. String key = BLOG_LIKED_KEY + blog.getId();
    28. Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    29. blog.setIsLike(score != null);

     我们先将点赞用户的前五位从sortset中取出来,需要注意的是,由于我们使用的是stringRedisTemplate,取出的元素是string类型的,我们需要对获取的id集合进行处理,这里使用stream流,将id集合都转化为Long类型,由于sql语句中的in查询出来的用户列表,又重新以id排序了,故我们需要拼接上order by进行排序

    1. @Override
    2. public Result queryBlogLikes(Long id) {
    3. String key = BLOG_LIKED_KEY + id;
    4. Long userId = UserHolder.getUser().getId();
    5. Set range = stringRedisTemplate.opsForZSet().range(key, 0, 4);
    6. List ids = range.stream().map(item -> Long.valueOf(item)).collect(Collectors.toList());
    7. String joinIds = StrUtil.join(",", ids);
    8. List userDTOS = userService.query().in("id", ids)
    9. .last("order by FIELD(id," + joinIds + " )")
    10. .list().stream().map(user ->
    11. BeanUtil.copyProperties(user, UserDTO.class))
    12. .collect(Collectors.toList());
    13. return Result.ok(userDTOS);
    14. }

  • 相关阅读:
    神经网络方法研究及应用,神经网络算法应用案例
    竞赛选题 基于深度学习的人脸性别年龄识别 - 图像识别 opencv
    uniapp使用uv-vi组件创建表单,收集信息
    孙卫琴的《精通JPA与Hibernate》的读书笔记: 用JPQL批量处理数据
    基于LSTM-Adaboost的电力负荷预测(Matlab代码实现)
    一文说明白ECDSA spec256k1 spec256r1 EdDSA ed25519千丝万缕的关系
    String_JavaScript
    MYSQL(索引+SQL优化)
    基于 ActionFilters 的限流库DotNetRateLimiter使用
    ChatGPT背后的经济账
  • 原文地址:https://blog.csdn.net/weixin_64133130/article/details/133047594