4-3:点赞功能

news2024/11/27 4:15:52

点赞

点赞

  • 支持对帖子、评论点赞。
  • 第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. 开发业务组件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:没有点赞
    }
}

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,提示,并将数据返回给页面
    }

}

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}">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>

在这里插入图片描述
依赖的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);//失败传出提示
            }
        }
    );
}

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";
    }

在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>

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);
        ......

完整:

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";
    }

}

6:帖子详情页面也要更改

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣刷题记录162.1-----127. 单词接龙

目录一、题目二、代码三、运行结果一、题目 二、代码 class Solution { public://广度优先搜索int ladderLength(string beginWord, string endWord, vector<string>& wordList) {int i,j;//将vector转换成unordered_st 提高查询速度 目前理解不深unordered_set&l…

如何判断对象是否是垃圾

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 我们都Java会自动进行内存管理&#xff0c;JVM会进行垃圾回收&#xff0c;哪它是怎么判定哪些是“垃圾”并决定“垃圾”的生死呢&#xf…

【allegro 17.4软件操作保姆级教程七】布线操作基础之二--铜皮操作

目录 1.1全局动态铜皮参数设置 1.2手动绘制铜皮 1.3手动挖铜 1.4 手动修改铜皮边界 1.5删除孤岛铜皮 1.6动/静态铜皮转换 1.7合并铜皮 1.8平面铺铜和铜皮分割 1.9铜皮颜色设置 今天分享布线操作技巧中的铜皮操作。 1.1全局动态铜皮参数设置 单板上的电源部分、铺地都…

【网安神器篇】——wmic_info信息收集工具

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

2万字一网通办远程视频踏勘建设方案67页

目 录 1. 建设背景 2. 建设周期 3. 需求分析 3.1. 需求定义 3.1.1. 需求活动目的 3.1.2. 需求分析流程 3.2. 技术需求 3.3. 用户分析 3.3.1. 办事企业 3.3.2. 踏勘人员 3.3.3. 审批人员 3.3.4. 系统管理人员 3.4. 性能需求 3.4.1. 系统架构 3.4.2. 响应时间 3.…

π110E30 单通道数字隔离器兼容代替Si8610BC-B-IS

π110E30 单通道数字隔离器兼容代替Si8610BC-B-IS 。具有出色的性能特征和可靠性&#xff0c;整体性能优于光耦和基于其他原理的数字隔离器产品。 传输通道间彼此独立&#xff0c;可实现多种传输方向的配置&#xff0c;可实现 1.5kV rms 到 5.0kV rms 隔离耐压等级和 DC 到 600…

MyBatis-Plus中查询操作知识点总结

系列文章目录 Mybatis-Plus知识点[MyBatisMyBatis-Plus的基础运用]_心态还需努力呀的博客-CSDN博客 Mybatis-PlusSpringBoot结合运用_心态还需努力呀的博客-CSDN博客 MyBaits-Plus中TableField和TableId用法_心态还需努力呀的博客-CSDN博客 MyBatis-Plus删除操作知识点总结…

【Android App】物联网中指南针、计步器、感光器、陀螺仪的讲解及实战演示(附源码 超详细必看)

需要源码请点赞关注收藏后评论区留言~~~ 一、指南针-磁场传感器 顾名思义&#xff0c;指南针只要找到朝南的方向就好了。 可是在App中并非使用一个方向传感器这么简单&#xff0c;事实上单独的方向传感器已经弃用&#xff0c;取而代之的是利用加速度传感器和磁场传感器。 获得…

区块链工作原理(区块链治理系统、比特币、以太坊、智能合约)

文章目录Blockchain Governance SystemOn-Chain GovernanceOff-Chain GovernanceBitCoin BlockchainEthereum BlockchainProperties of Blockchain SystemSmart ContractsScalability Issues in Blockchain SystemsBlockchain Governance System 每个国家或者城市都有自己的一…

我的第一个网页之----使用HTML编辑器编写HTML文档

HTML篇_三、使用HTML编辑器编写HTML文档 为了满足使用HTML语言进行学习、开发需求我们还需要选择使用一款开发工具来使用。我们可以选择更专业的开发工具来使用&#xff0c;在这里简单介绍几款本人使用过的开发工具&#xff0c;选择你的趁手武器。 一、简述几款HTML编辑器 1…

Proxmox VE 彻底删除本地存储

作者&#xff1a;田逸&#xff08;formyz&#xff09; 问题描述 从Proxmox VE web管理后台添加本地存储&#xff0c;如LVM、LVM-Thin&#xff08;精简逻辑卷&#xff09;等&#xff0c;有时候可能需要删除这些存储&#xff0c;但其Web管理后台却没有提供菜单或者按钮。要删除这…

手把手教你写Linux线程池

手把手教你写Linux线程池 如果需要线程池源码&#xff0c;关注Linux兵工厂&#xff0c;并由大量Linux资料赠送。 线程池 顾名思义&#xff0c;存储线程的池子。线程池是线程的一种使用模式。在平常业务开发中常规的逻辑是遇到任务然后创建线程去执行。但是线程的频繁创建就类…

NR小区搜索(五)S准则

微信同步更新欢迎关注同名modem协议笔记 UE根据支持的频段进行小区搜索过程&#xff0c;检测PSS/SSS->PBCH&#xff0c;然后就可以读到MIB&#xff0c;根据MIB中的pdcch-ConfigSIB1&#xff0c;可以找到CORESET0 和SearchSpace0的信息&#xff0c;进而可以确定一块时频域资…

软件测试 -- 进阶 6 软件缺陷

上工治未病之病&#xff0c;中工治欲病之病&#xff0c;下工治已病之病。-- 孙思邈 .《千金方药方》 释译&#xff1a;未病之病&#xff1a;未病&#xff0c;未发之病&#xff08;及早干预&#xff0c;防止病发&#xff09;&#xff1b;欲病之病&#xff1a;小病&#xff0…

MIR7创建预制发票BAPI

1、事务代码MIR7 前台输入采购订单等相关字段进行开票 2、代码实现 调用BAPI&#xff1a;BAPI_INCOMINGINVOICE_PARK创建发票 "--------------------斌将军-------------------- DATA:ls_headerdata TYPE bapi_incinv_create_header,lv_invoicedocnumber LIKE ba…

桌面画图工具:Pointofix(fertig)

Pointofix桌面画图工具 Pointofix - der virtuelle Textmarker fr Ihren Bildschirm - Freeware 一、软件下载 官方网址https://www.pointofix.de/ 二、进入下载页面&#xff0c;需要下载安装文件和语言包两个文件 三、网站还提供了一个语言设置小程序&#xff0c;但我没用 …

JavaSE笔记——抽象类和接口

文章目录前言一、抽象类和方法二、接口创建1.默认方法2.多继承3.接口中的静态方法三、抽象类和接口四、完全解耦五、使用继承扩展接口六、接口适配七、接口字段八、接口和工厂方法模式总结前言 接口和抽象类提供了一种将接口与实现分离的更加结构化的方法。 一、抽象类和方法 …

传奇外网架设教程

外网架设前需准备&#xff1a; 准备工具:传奇版本源码&#xff0c;服务器&#xff0c;备案域名&#xff0c;DBC数据库&#xff0c;周年客户端 服务器和备案域名需要自备或者租用&#xff0c;这东西自己造不出来&#xff01;&#xff01;&#xff01; 其他的工具&#xff0c;…

Flink被阿里收购4年,最开心的却是Spark背后的Databricks

最近&#xff0c;Flink Forward Asia&#xff08;FFA&#xff09;峰会成功举行&#xff0c;有关Flink的讨论&#xff0c;又开始在国内热闹起来。 2022 年&#xff0c;Apache Flink 社区保持快速发展&#xff1a;GitHub Star 数突破 2 万&#xff0c;单月下载量突破 1400 万次&…

学习总结 | 下一代人工智能

文章目录 一、前言二、底层逻辑三、六大维度今后发展的方向是第三代人工智能,最主要的措施就是把第一代人工智能知识驱动的方法和第二代人工智能数据驱动的方法结合起来,发展安全、可信、可靠和可扩展的人工智能技术,从而推动人工智能的创新应用。 一、前言 中国科学院院士…