仿牛客网社区项目(二十二)点赞

点赞功能

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

一、点赞#

为了提高Key的可用性,编写一个RedisKeyUtil来拼接RedisKey


public class RedisKeyUtil {
	//key的分隔符
    private static final String SPLIT = ":";
    //点赞的前缀
    private static final String PREFIX_ENTITY_LIKE = "like:entity";
    // 某个实体的赞
    // like:entity:entityType:entityId -> set(userId)
    public static String getEntityLikeKey(int entityType, int entityId) {
        return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId;
    }

}

二、点赞数量#

使用redis 就不需要对持久层操作,直接编写Sevice层即可

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;

    // 点赞
    public void like(int userId, int entityType, int entityId) {
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
        boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId);
        if (isMember) {
            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;
    }

}

继续编写Controller层

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
    //此处未限制未登录点赞,会抛异常 可以采用之前的全局异常注解,后期会对代码重构 增加SpringSecurity框架权限控制
    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);
    }

}

后端业务已编写完毕

对前端模板操作

  • discuss-detail.html 81行
<div class="media-body">
    <div class="mt-0 text-warning" th:utext="${user.username}">寒江雪</div>
    <div class="text-muted mt-3">
        发布于 <b th:text="${#dates.format(post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b>
        <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}">11</i>
                </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}">7</i></a></li>
        </ul>
    </div>
</div>
</div>	
  • 134行

    <div class="mt-4 text-muted font-size-12">
        <span>发布于 <b th:text="${#dates.format(cvo.comment.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b></span>
        <ul class="d-inline float-right">
            <li class="d-inline ml-2">
                <a href="javascript:;" th:onclick="|like(this,2,${cvo.comment.id});|" class="text-primary">
                    <b th:text="${cvo.likeStatus==1?'已赞':'赞'}"></b>(<i th:text="${cvo.likeCount}">1</i>)
                </a>
            </li>
            <li class="d-inline ml-2">|</li>
            <li class="d-inline ml-2"><a href="#" class="text-primary">回复(<i th:text="${cvo.replyCount}">2</i>)</a></li>
        </ul>
    </div>
    
  • 160行

    <div class="mt-3">
        <span th:text="${#dates.format(rvo.reply.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</span>
        <ul class="d-inline float-right">
            <li class="d-inline ml-2">
                <a href="javascript:;" th:onclick="|like(this,2,${rvo.reply.id});|" class="text-primary">
                    <b th:text="${rvo.likeStatus==1?'已赞':'赞'}"></b>(<i th:text="${rvo.likeCount}">1</i>)
                </a>
            </li>
            <li class="d-inline ml-2">|</li>
            <li class="d-inline ml-2"><a th:href="|#huifu-${rvoStat.count}|" data-toggle="collapse" class="text-primary">回复</a></li>
        </ul>
        <div th:id="|huifu-${rvoStat.count}|" class="mt-4 collapse">
    
  • 编写js文件 先在当前html下加入js

<script th:src="@{/js/discuss. js}"></ script> 
  • 编写discuss.js
function like(btn, entityType, entityId) {
    $.post(
        CONTEXT_PATH + "/like",
        {"entityType":entityType,"entityId":entityId},
        function(data) {
            data = $.parseJSON(data);
            if(data.code == 0) {
                $(btn).children("i").text(data.likeCount);
                $(btn).children("b").text(data.likeStatus==1?'已赞':"赞");
            } else {
                alert(data.msg);
            }
        }
    );
}
  • 首页点赞数量处理 在indexController中添加点赞信息

     @RequestMapping(path = "/index", method = RequestMethod.GET)
        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";
        }
    
  • 修改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:18</b>
    <ul class="d-inline float-right">
        <li class="d-inline ml-2"><span th:text="${map.likeCount}">11</span></li>
        <li class="d-inline ml-2">|</li>
        <li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span></li>
    </ul>
</div>
  • 在帖子详情处也要修改点赞数量 DiscussPostController
@Controller
@RequestMapping("/discuss")
public class DiscussPostController implements CommunityConstant {

    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private HostHolder hostHolder;

    @Autowired
    private UserService userService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private LikeService likeService;

    @RequestMapping(path = "/add", method = RequestMethod.POST)
    @ResponseBody
    public String addDiscussPost(String title, String content) {
        User user = hostHolder.getUser();
        if (user == null) {
            return CommunityUtil.getJSONString(403, "你还没有登录哦!");
        }

        DiscussPost post = new DiscussPost();
        post.setUserId(user.getId());
        post.setTitle(title);
        post.setContent(content);
        post.setCreateTime(new Date());
        discussPostService.addDiscussPost(post);

        // 报错的情况,将来统一处理.
        return CommunityUtil.getJSONString(0, "发布成功!");
    }

    @RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
    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);

                // 回复列表
                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";
    }

}

静态页面已做处理,不用单独去调整了

posted @   卷皇  阅读(391)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示
主题色彩