• 4-3:点赞功能


    点赞

    点赞

    • 支持对帖子、评论点赞。
    • 第1次点赞,第2次取消点赞。
      首页点赞数量
    • 统计帖子的点赞数量。
      详情页点赞数量
    • 统计点赞数量。
    • 显示点赞状态。

    Redis缓存用于点赞功能,可以提高性能。(面向Key编程)

    1.建立RedisKeyUtil.java
    package com.nowcoder.community.util;
    
    public class RedisKeyUtil {
    
        private static final String SPLIT = ":";
        private static final String PREFIX_ENTITY_LIKE = "like:entity";
    
        // 生成某个实体的赞
        // like:entity:entityType:entityId -> set(userId),用id集合表示赞,不用整数,防止后面需求发生变化,集合中装载UserId,
        public static String getEntityLikeKey(int entityType, int entityId) {
            return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId;//拼的结果
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 开发业务组件LikeService.java
    package com.nowcoder.community.service;
    
    import com.nowcoder.community.util.RedisKeyUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    @Service
    public class LikeService {
    
        @Autowired
        private RedisTemplate redisTemplate;//注入Redis模板
    
        // 点赞
        public void like(int userId, int entityType, int entityId) {
            String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
            boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId);
            if (isMember) {//判断用户是否点过赞,用户id在集合中,就是点过赞。
                redisTemplate.opsForSet().remove(entityLikeKey, userId);
            } else {
                redisTemplate.opsForSet().add(entityLikeKey, userId);
            }
        }
    
        // 查询某实体被点赞的数量
        public long findEntityLikeCount(int entityType, int entityId) {
            String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
            return redisTemplate.opsForSet().size(entityLikeKey);
        }
    
        // 查询某人对某实体的点赞状态
        public int findEntityLikeStatus(int userId, int entityType, int entityId) {
            String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
            return redisTemplate.opsForSet().isMember(entityLikeKey, userId) ? 1 : 0;//返回整数,可以表现更多的状态,如踩等。1:点赞,0:没有点赞
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    3. 表现层

    异步请求,整个页面不刷新,更新点赞数就行

    package com.nowcoder.community.controller;
    import com.nowcoder.community.entity.User;
    import com.nowcoder.community.service.LikeService;
    import com.nowcoder.community.util.CommunityUtil;
    import com.nowcoder.community.util.HostHolder;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Controller
    public class LikeController {
    
        @Autowired
        private LikeService likeService;
    
        @Autowired
        private HostHolder hostHolder;
    
        @RequestMapping(path = "/like", method = RequestMethod.POST)//处理异步请求的方法
        @ResponseBody
        public String like(int entityType, int entityId) {
            User user = hostHolder.getUser();
    
            // 点赞
            likeService.like(user.getId(), entityType, entityId);
    
            // 数量
            long likeCount = likeService.findEntityLikeCount(entityType, entityId);
            // 状态
            int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
            // 返回的结果:统一传递给页面
            Map<String, Object> map = new HashMap<>();
            map.put("likeCount", likeCount);
            map.put("likeStatus", likeStatus);
    
            return CommunityUtil.getJSONString(0, null, map);//返回Json格式的数据:正确返回0,提示,并将数据返回给页面
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    4.discuss-detail

    点赞是在帖子详情页面进行的,也就是说要在该处,进行修改

    <ul class="d-inline float-right">
    								<li class="d-inline ml-2">
    									<a href="javascript:;" th:onclick="|like(this,1,${post.id});|" class="text-primary">
    										<b th:text="${likeStatus==1?'已赞':'赞'}">b> <i th:text="${likeCount}">11i>
    								a>
    								li>
    								<li class="d-inline ml-2">|li>
    								<li class="d-inline ml-2"><a href="#replyform" class="text-primary">回帖 <i th:text="${post.commentCount}">7i>a>li>
    							ul>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    依赖的JS文件

    function like(btn, entityType, entityId) {
        $.post(//POST请求,将点赞key拼接
            CONTEXT_PATH + "/like",
            {"entityType":entityType,"entityId":entityId},
            function(data) {//转存服务器返回的数据
                data = $.parseJSON(data);
                if(data.code == 0) {//判断请求是否成功
                    $(btn).children("i").text(data.likeCount);//获取btn节点的子节点,改变它的内容。
                    $(btn).children("b").text(data.likeStatus==1?'已赞':"赞");//赞成功了显示“已赞”。
                } else {
                    alert(data.msg);//失败传出提示
                }
            }
        );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    5. 重启服务器,判断一下结果

    在这里插入图片描述
    在这里插入图片描述
    注意变量要有“$”符号

    5首页显示的点赞的数量要一致

    在 HomeController.java中,将likeService注入
    方法修改如下:

    public String getIndexPage(Model model, Page page) {
            // 方法调用钱,SpringMVC会自动实例化Model和Page,并将Page注入Model.
            // 所以,在thymeleaf中可以直接访问Page对象中的数据.
            page.setRows(discussPostService.findDiscussPostRows(0));
            page.setPath("/index");
    
            List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
            List<Map<String, Object>> discussPosts = new ArrayList<>();
            if (list != null) {
                for (DiscussPost post : list) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("post", post);
                    User user = userService.findUserById(post.getUserId());
                    map.put("user", user);
    
                    long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());
                    map.put("likeCount", likeCount);
    
                    discussPosts.add(map);
                }
            }
            model.addAttribute("discussPosts", discussPosts);
            return "/index";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在index.html中找到首页显示赞的地方:

    <div class="text-muted font-size-12">
    								<u class="mr-3" th:utext="${map.user.username}">寒江雪u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18b>
    								<ul class="d-inline float-right">
    									<li class="d-inline ml-2"><span th:text="${map.likeCount}">11span>li>
    									<li class="d-inline ml-2">|li>
    									<li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7span>li>
    								ul>
    							div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    5帖子详情页面显示的点赞的数量和状态也要一致

    DiscussPostController.java中将likeService注入,在方法内部进行处理

     public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) {
            // 帖子
            DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
            model.addAttribute("post", post);
            // 作者
            User user = userService.findUserById(post.getUserId());
            model.addAttribute("user", user);
            // 点赞数量
            long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, discussPostId);
            model.addAttribute("likeCount", likeCount);//将状态传到页面
            // 点赞状态
            int likeStatus = hostHolder.getUser() == null ? 0 :
                    likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, discussPostId);//判断用户是否登录,没登录也可以看
            model.addAttribute("likeStatus", likeStatus);
            ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    完整:

    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) {
            // 帖子
            DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
            model.addAttribute("post", post);
            // 作者
            User user = userService.findUserById(post.getUserId());
            model.addAttribute("user", user);
            // 点赞数量
            long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, discussPostId);
            model.addAttribute("likeCount", likeCount);
            // 点赞状态
            int likeStatus = hostHolder.getUser() == null ? 0 :
                    likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, discussPostId);
            model.addAttribute("likeStatus", likeStatus);
    
            // 评论分页信息
            page.setLimit(5);
            page.setPath("/discuss/detail/" + discussPostId);
            page.setRows(post.getCommentCount());
    
            // 评论: 给帖子的评论
            // 回复: 给评论的评论
            // 评论列表
            List<Comment> commentList = commentService.findCommentsByEntity(
                    ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
            // 评论VO列表
            List<Map<String, Object>> commentVoList = new ArrayList<>();
            if (commentList != null) {
                for (Comment comment : commentList) {
                    // 评论VO
                    Map<String, Object> commentVo = new HashMap<>();
                    // 评论
                    commentVo.put("comment", comment);
                    // 作者
                    commentVo.put("user", userService.findUserById(comment.getUserId()));
                    // 点赞数量
                    likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, comment.getId());//评论的点赞状态,数量
                    commentVo.put("likeCount", likeCount);
                    // 点赞状态
                    likeStatus = hostHolder.getUser() == null ? 0 :
                            likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, comment.getId());
                    commentVo.put("likeStatus", likeStatus);//装到VO中
                    // 回复列表
                    List<Comment> replyList = commentService.findCommentsByEntity(
                            ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
                    // 回复VO列表
                    List<Map<String, Object>> replyVoList = new ArrayList<>();
                    if (replyList != null) {
                        for (Comment reply : replyList) {
                            Map<String, Object> replyVo = new HashMap<>();
                            // 回复
                            replyVo.put("reply", reply);
                            // 作者 
                            replyVo.put("user", userService.findUserById(reply.getUserId()));
                            // 回复目标
                            User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
                            replyVo.put("target", target);
                            // 点赞数量
                            likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, reply.getId());
                            replyVo.put("likeCount", likeCount);
                            // 点赞状态
                            likeStatus = hostHolder.getUser() == null ? 0 :
                                    likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId());//回复的处理也是类似的。
                            replyVo.put("likeStatus", likeStatus);
    
                            replyVoList.add(replyVo);
                        }
                    }
                    commentVo.put("replys", replyVoList);
    
                    // 回复数量
                    int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId());
                    commentVo.put("replyCount", replyCount);
    
                    commentVoList.add(commentVo);
                }
            }
    
            model.addAttribute("comments", commentVoList);
    
            return "/site/discuss-detail";
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    6:帖子详情页面也要更改

    在这里插入图片描述
    另外两个位置处理方式相似
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    钟汉良日记:选对平台很重要,乐买买算一个
    A sequence-to-sequence approach for document-level relation extraction
    c++内存对齐
    Android Studio Bumblebee | 2021.1.1 发布,快来看看更新了什么
    首个828 B2B企业节:华为携手生态伙伴,赋能中小企业数字化转型
    华为杯数学建模(准备)<2018 - 2022>
    中小研发团队架构实践,高级架构师的捷径
    [动态规划] (十一) 简单多状态 LeetCode 面试题17.16.按摩师 和 198.打家劫舍
    【计算机网络】 集线器、网桥、交换机、路由器看这一篇就懂了。实验: 路由器的作用,以及没有路由器的情况下,如何用三层交换机实现路由器的功能
    开发 Java 用小而美的框架,Solon v1.9.4 发布
  • 原文地址:https://blog.csdn.net/qq_41026725/article/details/128132285