黑马点评-10实现用户点赞和点赞排行榜功能

用户点赞功能

如果用户只要点赞一次就对数据库中blog表中的liked字段的值加1就会导致一个用户无限点赞

PutMapping("/like/{id}")
public Result likeBlog(@PathVariable("id") Long id) {// 修改点赞数量,update tb_blog set liked = liked + 1 where id = ?	blogService.update().setSql("liked = liked + 1").eq("id", id).update();return Result.ok();
}

需求: 同一个用户只能对同一篇笔记点赞一次再次点击则取消点赞,如果当前用户已经点赞则点赞按钮高亮显示

  • 增加isLike字段: 给Blog实体类中添加一个isLike字段,首页查询热门笔记或用户查看笔记详情内容时会根据isLike字段的属性值决定点赞按钮是否高亮显示
  • 点赞修改功能: 利用Redis中的set集合是否包含点赞用户的Id来判断用户是否点赞过,未点赞则点赞数+1,已点赞则点赞数-1
  • 查看笔记详情时根据id查询笔记: 判断当前登录用户是否点赞过,赋值给isLike字段决定点赞图标是否高亮显示
  • 访问首页时分页查询热门笔记: 判断当前登录用户是否点赞过,赋值给isLike字段决定点赞图标是否高亮显示

在这里插入图片描述

一人一赞

第一步: 给Blog实体类增加isLike字段,首页查询热门笔记或用户查看笔记详情内容时会根据isLike字段的属性值决定点赞按钮是否高亮显示

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_blog")
public class Blog implements Serializable {private static final long serialVersionUID = 1L;// isLike属性不属于Blog表中的字段@TableField(exist = false)private Boolean isLike;//..........
}

第二步: 编写控制器方法处理用户点赞的请求

@PutMapping("/like/{id}")
public Result likeBlog(@PathVariable("id") Long id) {return blogService.likeBlog(id);
}

第三步: 编写业务方法,以blog:liked:笔记Id作为Set集合的Key,集合中的元素就是点赞用户的Id

// 在RedisConstants类中声明一个常量作为Set集合的key,集合中包含了所有点赞的用户
public static final String BLOG_LIKED_KEY = "blog:liked:";
// 操作Redis,key和value要求是String类型
@Resource
private StringRedisTemplate stringRedisTemplate;@Override
public Result likeBlog(Long id) {//1. 获取当前登陆用户信息Long userId = UserHolder.getUser().getId();//2. 判断当前用户是否已经点赞//2.1如果用户未点赞则Blog表中like字段值加1,同时将点赞用户的Id加入set集合String key = BLOG_LIKED_KEY + id;Boolean isLiked = stringRedisTemplate.opsForSet().isMember(key, userId.toString());if (BooleanUtil.isFalse(isLiked)) {// update tb_blog set liked = liked + 1 where id = ?boolean success = update().setSql("liked = liked + 1").eq("id", id).update();// 更新成功将点赞用户Id加入set集合if (success) {stringRedisTemplate.opsForSet().add(key, userId.toString());}//2.2如果当前用户已点赞则取消点赞即like字段值减1,同时将点赞用户Id从set集合中移除}else {// update tb_blog set liked = liked - 1 where id = ?boolean success = update().setSql("liked = liked - 1").eq("id", id).update();if (success){// 更新成功则将点赞用户Id从set集合移除stringRedisTemplate.opsForSet().remove(key, userId.toString());}}return Result.ok();
}

修改查询笔记点赞高亮

访问首页时分页查询热门笔记: 判断当前登录用户是否点赞过,根据Blog实体类isLike属性的值决定点赞图标是否高亮显示

@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 -> {// 查询用户昵称和头像封装到Blog实体类当中queryBlogUser(blog);// 判断笔记是否被当前用户点赞isBlogLiked(blog);});return Result.ok(records);
}

查看笔记详情时根据id查询笔记: 判断当前登录用户是否点赞过,根据Blog实体类isLike属性的值决定点赞图标是否高亮显示

@Override
public Result queryBlogById(Integer id) {Blog blog = getById(id);if (blog == null) {return Result.fail("评价不存在或已被删除");}// 查询用户昵称和头像封装到Blog实体类当中queryBlogUser(blog);// 判断笔记是否被当前用户点赞isBlogLiked(blog);return Result.ok(blog);
}

由于查看用户信息判断笔记是否被当前用户点赞的业务逻辑比较通用,所以抽取成独立的方法

// 查看用户信息
private void queryBlogUser(Blog blog) {Long userId = blog.getUserId();User user = userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());
}// 判断用户是否已经点赞
private void isBlogLiked(Blog blog) {//1. 获取当前用户信息Long userId = UserHolder.getUser().getId();//2. 判断当前用户是否点赞String key = BLOG_LIKED_KEY + blog.getId();Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());//3. 如果点赞了则将Blog类的isLike属性设置为trueblog.setIsLike(BooleanUtil.isTrue(isMember));
}

点赞排行榜

需求: 当我们点击探店笔记详情页面时,应该按点赞顺序展示点赞过的用户,比如显示最早点赞的TOP5形成点赞排行榜

在这里插入图片描述

因为Set集合不能对点赞的用户进行排序,所以我们需要使用SortedSet(Zset)集合存储点赞的用户Id,score属性的值是当前时间戳(默认按照从小到大排序)

在这里插入图片描述

第一步: ZSet没有判断元素是否存在的方法,是通过获取集合中元素的score属性的值来判断集合中是否有该元素

  • ZSCORE key e1: 获取集合元素的score属性值,若元素存在则返回对应score值,若不存在则返回null
@Override
public Result likeBlog(Long id) {//1. 获取当前登陆用户信息Long userId = UserHolder.getUser().getId();//2. 判断当前用户是否已经点赞//2.1如果用户未点赞则Blog表中like字段值加1,同时将点赞用户的Id加入set集合String key = BLOG_LIKED_KEY + id;// 尝试获取当前用户的score属性值Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());// 如果score为null则表示集合中没有该用户if (score == null) {// update tb_blog set liked = liked + 1 where id = ?boolean success = update().setSql("liked = liked + 1").eq("id", id).update();//更新成功则将点赞用户Id加入SortedSet集合,score属性的值就是当前的时间戳(默认按照从小到大排序)if (success) {stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}//2.2如果当前用户已点赞则取消点赞即like字段值减1,同时将点赞用户Id从SortedSet集合中移除} else {// update tb_blog set liked = liked - 1 where id = ?boolean success = update().setSql("liked = liked - 1").eq("id", id).update();if (success) {//更新成功将点赞用户Id从SortedSet集合移除stringRedisTemplate.opsForZSet().remove(key, userId.toString());}}return Result.ok();
}

第二步: 判断用户是否点赞时如果用户没有登录就不需要判断用户是否点过赞了,因为用户没有登陆就获取不到userId此时会报空指针异常

private void isBlogLiked(Blog blog) {//1. 获取当前用户信息UserDTO userDTO = UserHolder.getUser();//2. 当用户未登录时就不判断用户是否点赞,直接return结束逻辑if (userDTO == null) {return;}//3. 判断当前用户是否点赞String key = BLOG_LIKED_KEY + blog.getId();Double score = stringRedisTemplate.opsForZSet().score(key, userDTO.getId().toString());// score不等于null返回true表示用户已经点过赞同时给Blog类的isLike属性赋值trueblog.setIsLike(score != null);
}

第三步: 显示点赞排行列表当浏览器发起GET请求blog/likes/4时服务器返回一个List集合包含top5点赞的用户信息

  • ZRANGE key start end: 获取指定范围的元素,SortedSet内的元素会自动排序(默认升序)
@GetMapping("/likes/{id}")
public Result queryBlogLikes(@PathVariable Integer id){return blogService.queryBlogLikes(id);
}
@Override
public Result queryBlogLikes(Integer id) {String key = BLOG_LIKED_KEY + id;// 查询SortedSet集合的前5个元素即用户的id(不包含元素的分数)Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);// 如果返回的set集合是空的即没人点赞,直接返回一个空的List集合if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 将Set集合中String类型的用户id转变为Long类型的id然后收集到List集合中List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());//select * from tb_user where id in (ids[0],...) order by field(id, ids[0],...)String idsStr = StrUtil.join(",", ids);// 把ids集合拼成一个以","分隔的字符串List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by field(id," + idsStr + ")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class))// 将查询出来的用户隐私信息隐藏.collect(Collectors.toList());return Result.ok(userDTOS);
}

由于MySQL默认会对查询出来的所有结果按照id从小到大的方式排序,它并不会按照我们查询到的用户Id顺序去排序

  • order by field(排序字段,排序顺序) : 可以指定查询结果按照某个字段的排序方式

select * from tb_user where id in (ids[0], ids[1] ...): 这种方式并不会按照Set集合中Id的顺序对查询结果进行排序

List<UserDTO> userDTOS = userService.listByIds(ids).steram().map(user -> BeanUtil.copyProperties(user, UserDTO.class))// 将查询出来的用户隐私信息隐藏.collect(Collectors.toList());

select * from tb_user where id in (ids[0],...) order by field(id, ids[0],...): 指定查询结果按照我们指定的字段顺序排序

// 把ids集合拼成一个以","分隔的字符串
String idsStr = StrUtil.join(",", ids);
List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by field(id," + idsStr + ")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class))// 将查询出来的用户隐私信息隐藏.collect(Collectors.toList());

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVMj之console Java监视与管理控制台

jconsole Java监视与管理控制台 1、jconsole介绍 jconsole (java monitoring and management console)是一款基于JMX (Java Management Extensions) 的可视化监视和管理工具。 2、启动jconsole 1、在linux和windwos下通过jconsole启动即可。 2、然后会自动搜索本机运行的…

Zero-Shot Restoration of Back-lit Images Using Deep Internal Learning

ABSTRACT 如何恢复背光图像仍然是一项具有挑战性的任务。该领域最先进的方法基于监督学习&#xff0c;因此通常仅限于特定的训练数据。在本文中&#xff0c;我们提出了一种用于背光图像恢复的“零样本”方案&#xff0c;该方案利用深度学习的力量&#xff0c;但不依赖于任何先…

好工具|datamap,一个好用的地图可视化Excel插件,在Excel中实现地理编码、拾取坐标

在做VRP相关研究的时候&#xff0c;需要对地图数据做很多处理&#xff0c;比如地理编码&#xff0c;根据“重庆市沙坪坝区沙正街174号”这样的一个文本地址知道他的经纬度&#xff1b;再比如绘制一些散点图&#xff0c;根据某个位置的经纬度在地图上把它标注出来。还有有的时候…

标签打印机打印标签时出现,数据处理过程中错误 无法设置项目 图片1的内容无法打印

环境&#xff1a; Win10专业版 NiceLabel Designer 10.1 问题描述&#xff1a; 标签打印机打印标签时出现&#xff0c;数据处理过程中错误 无法设置项目 图片1的内容无法打印 解决方案&#xff1a; 1.删除标签部分文字打印测试 还是一样&#xff08;未解决&#xff09; …

数字逻辑电路基础-时序逻辑电路之触发器

文章目录 一、D触发器二、verilog源码三、综合及仿真结果一、D触发器 本文介绍数字逻辑电路中常用的基础时序逻辑电路触发器。它有记忆和存储信息功能,触发器是边沿触发电路。 下图是触发器常用表示方式(时钟上升沿有效): 触发器由两个锁存器组成,前级是主锁存器,后级…

python爬取天气数据并做可视化分析

数据采集逻辑 数据schema 历史天气数据schema { ‘当日信息’&#xff1a;2023-01-01 星期日, 最高气温: 8℃, 最低气温: 5℃, ‘天气’: 多云, 风向信息:北风 3级 } 数据爬取 1.导入库 import numpy as np import pandas as pd import requests from bs4 import BeautifulSoup…

什么是指针碰撞

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

postgresql数据库中update使用的坑

简介 在数据库中进行增删改查比较常见&#xff0c;经常会用到update的使用。但是在近期发现update在oracle和postgresql使用却有一些隐形区别&#xff0c;oracle 在执行update语句的时候set 后面必须跟着1对1的数据关联而postgresql数据库却可以一对多&#xff0c;这就导致数据…

LabVIEW中如何达到NI SMU最大采样率

LabVIEW中如何达到NI SMU最大采样率 NISMU的数字化仪功能对于捕获SMU详细的瞬态响应特性或表征待测设备&#xff08;DUT&#xff09;响应&#xff08;例如线性调整率和负载调整率&#xff09;至关重要。没有此功能&#xff0c;将需要一个外部示波器。 例如&#xff0c;假设在…

vite项目配置vite.config.ts在打包过程中去除日志

在生产环境上&#xff0c;务必要将日志清除干净&#xff0c;其因有二&#xff0c;在webgis系统中&#xff0c;有很多几何数据&#xff0c;体积大、数量多&#xff0c;很容易引起系统卡顿&#xff1b;清除log后&#xff0c;系统看着舒服&#xff0c;协同开发有很多无聊的日志&am…

C++基础入门(超详细)

话不多说&#xff0c;序言搞起来&#xff1a; 自从开始学老师布置的任务后&#xff0c;目前还是OpenCV&#xff0c;哈~哈。我就莫名问老师&#xff1a;“以后编程是用C还是python&#xff1f;”&#xff0c;果然还是太年轻&#xff0c;老师说&#xff1a;“两们都要精通”。唉&…