Redis【实战篇】---- 达人探店

news2024/11/25 7:33:03

Redis【实战篇】---- 达人探店

  • 1. 达人探店 - 发布探店笔记
  • 2. 达人探店 - 查看探店笔记
  • 3. 达人探店 - 点赞功能
  • 4. 达人探店 - 点赞排行榜

1. 达人探店 - 发布探店笔记

发布探店笔记

探店笔记类似点评网站的评价,往往是图文结合。对应的表有两个:
tb_blog:探店笔记表,包含笔记中的标题、文字、图片等
tb_blog_comments:其他用户对探店笔记的评价

具体发布流程
在这里插入图片描述

上传接口

    @PostMapping("blog")
    public Result uploadImage(@RequestParam("file") MultipartFile image) {
        try {
            // 获取原始文件名称
            String originalFilename = image.getOriginalFilename();
            // 生成新文件名
            String fileName = createNewFileName(originalFilename);
            // 保存文件
            image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));
            // 返回结果
            log.debug("文件上传成功,{}", fileName);
            return Result.ok(fileName);
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

注意:同学们在操作时,需要修改SystemConstants.IMAGE_UPLOAD_DIR 自己图片所在的地址,在实际开发中图片一般会放在nginx上或者是云存储上。

BlogController

    @PostMapping
    public Result saveBlog(@RequestBody Blog blog) {
        // 获取登录用户
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        // 保存探店博文
        blogService.save(blog);
        // 返回id
        return Result.ok(blog.getId());
    }

2. 达人探店 - 查看探店笔记

实现查看发布探店笔记的接口

在这里插入图片描述

实现代码:

BlogServiceImpl

    @Override
    public Result queryBlogById(Long id) {
        // 1. 查询blog
        Blog blog = getById(id);
        if (blog == null) {
            return Result.fail("笔记不存在!");
        }
        // 2. 查询blog相关的用户
        queryBlogUser(blog);
        return Result.ok(blog);
    }

3. 达人探店 - 点赞功能

初始代码

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

问题分析:这种方式会导致一个用户无限点赞,明显是不合理的

造成这个问题的原因是,我们现在的逻辑,发起请求只是给数据库+1,所以才会出现这个问题

在这里插入图片描述

完善点赞功能

需求:

  • 同一个用户只能点赞一次,再次点击则取消点赞
  • 如果当前用户已经点赞,则点赞按钮高亮显示(前端已实现,判断字段Blog类的isLike属性)

实现步骤:

  • 给Blog类中添加一个isLike字段,标示是否被当前用户点赞
  • 修改点赞功能,利用Redis的set集合判断是否点赞过,未点赞过则点赞数+1,已点赞过则点赞数-1
  • 修改根据id查询Blog的业务,判断当前登录用户是否点赞过,赋值给isLike字段
  • 修改分页查询Blog业务,判断当前登录用户是否点赞过,赋值给isLike字段

为什么采用set集合:

因为我们的数据是不能重复的,当用户操作过之后,无论他怎么操作,都是

具体步骤:

1、在Blog 添加一个字段

    /**
     * 是否点赞过了
     */
    @TableField(exist = false)
    private Boolean isLike;

2、修改代码

    @Override
    public Result likeBlog(Long id) {
        // 1. 获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 2. 判断当前用户是否已经点赞过了
        String key = BLOG_LIKED_KEY + userId;
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        if (BooleanUtil.isFalse(isMember)) {
            //3. 如果未点赞,可以点赞
            //3.1 数据库点赞数+1
            boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
            //3.2 保存用户到Redis的set集合
            if (isSuccess) {
                stringRedisTemplate.opsForSet().add(key, userId.toString());
            }
        } else {
            //4. 如果已点赞,取消点赞
            //4.1 数据库点赞数-1
            boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
            //4.2 把用户从Redis的set集合移除
            if (isSuccess) {
                stringRedisTemplate.opsForSet().remove(key, userId.toString());
            }
        }
        return Result.ok();
    }

4. 达人探店 - 点赞排行榜

在探店笔记的详情页面,应该把给该笔记点赞的人显示出来,比如最早点赞的TOP5,形成点赞排行榜:

之前的点赞是放到set集合,但是set集合是不能排序的,所以这个时候,咱们可以采用一个可以排序的set集合,就是咱们的sortedSet

在这里插入图片描述

我们接下来来对比一下这些集合的区别是什么

所有点赞的人,需要是唯一的,所以我们应当使用set或者是sortedSet

其次我们需要排序,就可以直接锁定使用sortedSet啦

在这里插入图片描述

修改代码

BlogServiceImpl

点赞逻辑代码

    private void isBlogLiked(Blog blog) {
        // 1. 获取当前登录用户
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            return;
        }
        Long userId = user.getId();
        // 2. 判断当前用户是否已经点赞过了
        String key = "blog:liked:" + blog.getId();
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        blog.setIsLike(score != null);
    }

    @Override
    public Result likeBlog(Long id) {
        // 1. 获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 2. 判断当前用户是否已经点赞过了
        String key = BLOG_LIKED_KEY + userId;
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        if (BooleanUtil.isFalse(isMember)) {
            //3. 如果未点赞,可以点赞
            //3.1 数据库点赞数+1
            boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
            // 3.2.保存用户到Redis的set集合  zadd key value score
            if (isSuccess) {
                stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
            }
        } else {
            //4. 如果已点赞,取消点赞
            //4.1 数据库点赞数-1
            boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
            //4.2 把用户从Redis的set集合移除
            if (isSuccess) {
                stringRedisTemplate.opsForZSet().remove(key, userId.toString());
            }
        }
        return Result.ok();
    }

点赞列表查询列表

BlogController

    @GetMapping("/likes/{id}")
    public Result queryBlogLikes(@PathVariable("id") Long id) {
        return blogService.queryBlogLikes(id);
    }

BlogService

    @Override
    public Result queryBlogLikes(Long id) {
        String key = BLOG_LIKED_KEY + id;
        Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if (top5 == null || top5.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        // 2.解析出其中的用户id
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        String idStr = StrUtil.join(",", ids);
        // 3.根据用户id查询用户 WHERE id IN ( 5 , 1 ) ORDER BY FIELD(id, 5, 1)
        List<UserDTO> userDTOS = userService.query()
                .in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        // 4.返回
        return Result.ok(userDTOS);
    }

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

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

相关文章

集成算法概述

集成算法的基本思想&#xff1a;训练时用多种分类器一起完成同一份任务。 测试时对待测试样本分别通过不同的分类器&#xff0c;汇总最后的结果。投票方式&#xff0c;可分为软投票和硬投票。 集成算法一般有三种&#xff0c;分别是Bagging模型、Boosting模型和Stacking模型。 …

微前端(拆分和细化,整合历史系统)

接入微前端 项目背景说明 假如我们完成了基于Vue2的项目&#xff0c;假设另外一个团队的小伙伴来实现前台可视化部分&#xff0c;他们使用的是最新的Vue3技术栈&#xff0c;现在有一个需求&#xff0c;他们的前台项目想直接使用我们的登录功能&#xff0c;获得token&#xff…

使用Thread和队列Queue实现线程池复用线程

线程池实现 线程池是一个线程管理技术&#xff0c;创建一个或者多个线程进行管理&#xff0c;避免线程的创建和销毁带来的开销线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。这…

【BUG历险记】vivado报错:binding vhdl entity ‘ xxxxx ‘does not have port ‘ xxxxx ‘解决方案

&#x1f3d4;【BUG历险记】vivado报错&#xff1a;binding vhdl entity xxxxx does not have port xxxxx 解决方案 问题描述 我在编写雷达的脉冲压缩仿真时&#xff0c;先例化了FFT和复数乘法器&#xff0c;并仿真验证通过了&#xff1b;再例化IFFT&#xff0c;结果仿真时…

可信区块链运行监测服务平台(TBM)正式发布

6月29日&#xff0c;“Web3.0信任科技大会”于长沙开幕&#xff0c;大会由长沙市人民政府、中国信息通信研究院、中国通信标准化协会联合主办&#xff0c;长沙市委网络安全和信息化委员会办公室、长沙市工业和信息化局、长沙经济技术开发区管理委员会、可信区块链推进计划和中国…

leetcode周赛352

leetcode周赛352 1. 最长奇偶子数组 思路分析 这是一道变形的双指针题目我们可以使用相关算法模板基础上来书写左边界&#xff1a;偶数&#xff0c;且小于值threshold;所以我们需要寻找符合要求的左边界判断是否奇偶相间&#xff1a;只有 奇数偶数奇数 class Solution {static…

国产MCU-CW32F030开发学习--移植rtthread-nano

国产MCU-CW32F030开发学习–移植rtthread-nano 硬件平台 CW32_48F大学计划板CW32_IOT_EVA物联网开发评估套件 RT-Thread Nano RT-Thread Nano 是一个极简版的硬实时内核&#xff0c;它是由 C 语言开发&#xff0c;采用面向对象的编程思维&#xff0c;具有良好的代码风格&…

git clone 丢东西

1. git clone 丢东西 使用git clone https://gitee.com/123456789/123456789.git 克隆gitee 远程库上的代码发现少了一部分&#xff0c;只克隆了一部分下来&#xff0c;反复删除重新克隆都是一样的结果 2. 克隆下来后的文件&#xff0c;该目录下只有5 个文件&#xff0c;少了…

一文读懂北斗三号短报文!通信频度、电文长度、北斗民用智能卡申请、典型终端全面介绍

一、北斗短报文概述 北斗卫星导航系统&#xff08;Beidou Navigation Satellite System&#xff09;是中国自主研发和建设的全球卫星导航系统。该系统由一系列卫星、地面监控站和用户终端组成&#xff0c;旨在提供高精度、高可靠性的定位、导航和授时服务。北斗卫星导航系统的发…

QT编写的数字键盘

https://download.csdn.net/download/qq_20189555/88001237

Heroku 实践:如何成功将 Vue.js 的前端内容部署到 Heroku

文章目录 Vue.js 脚手架安装Vue CLI&#xff08;如果你还没有安装&#xff09;&#xff1a;创建新项目 Heroku 部署 Vue2项目目录server.jspackage.json创建 Procfile按照 heroku 的引导初始化 git 并提交代码 可能问题 Vue.js 脚手架 首先要使用 Vue 的脚手架来创建一个 vue …

nodeJs学习笔记

创建服务器 在根目录下创建server.js的文件&#xff0c;里面写如入以下代码&#xff1a; // 请求nodejs自带的http模块 var http require(http);http.createServer(function (request, response) {// 发送 HTTP 头部 // HTTP 状态值: 200 : OK// 内容类型: text/plainrespon…

Update the WSL kernel by running “wsl --update“ or……【老电脑装Docker】

给老电脑装Docker遇到了这个问题记录一下 Update the WSL kernel by running “wsl --update” or follow instructions at https://docs.microsoft.com/windows/wsl/wsl2-kernel. 打开Docker一直报这个界面&#xff0c;然后就退出 根据上面的意思&#xff0c;试着更新了下 …

汇编学习教程:走进 bp

引言 此前我们学习了 bp 寄存器&#xff0c;我们知道 bp 的作用是为访问栈空间数据提供方便&#xff0c;其默认绑定的段寄存器就是 SS 段寄存器。在此前的博文中博主提及到&#xff0c;bp 的作用其实不止方便访问栈空间数据这一条&#xff0c;对于栈如此重要的空间&#xff0c…

区块链技术:解锁未来的去中心化革命

在数字时代的浪潮中&#xff0c;区块链技术如一颗璀璨的明星崛起&#xff0c;被誉为解锁未来的去中心化革命。作为一种分布式账本技术&#xff0c;区块链不仅改变着传统商业模式&#xff0c;还催生了全新的经济形态和社会关系。本文将从技术原理、应用场景和前景展望三个方面&a…

spring security权限路由匹配restful格式的详情id设计

解决方案&#xff1a; 先直接说下解决方案&#xff0c;权限点设计成如下&#xff1a; /api/books/{id:\d*}问题描述&#xff1a; 获取书本详情的标准restful路由&#xff0c;一般是这样的/api/books/12&#xff0c; 12即该book的id&#xff0c;如果需要拥有访问该路由的权限…

【C语言初阶】带你轻松掌握指针基础知识(1)——指针的定义,类型,大小

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello&#xff0c;这里是君兮_&#xff0c;最近刚回家有点懒&#xff0c;从今天开始恢复更新并开始更新新的刷题系列&#xff0c;我们先继续更新0基础入门C语言的内容&#xff0c;今天给大家带来的是指针方面的内容&…

【MYSQL基础】MYSQL用户管理

MYSQL用户管理 数据库的root用户拥有操作数据库的所有权限&#xff0c;如果要团队协作开发&#xff0c;为了避免有成员误操作&#xff0c;可以给成员创建一个权限较低的用户账号 创建用户 create user 用户名 identified by ‘密码’; mysql> create user dam identified …

数据劫持大揭秘:Vue的隐形力量和无限可能

文章目录 1. 初始化阶段2. 响应式侦测器3. 数据劫持4. 模板编译5.总结 Vue.js 通过数据劫持实现了数据的双向绑定。它使用了一个名为 “响应式系统” 的机制来追踪和响应数据的变化&#xff0c;从而自动更新相关的视图。 Vue 的数据劫持原理主要分为以下几个步骤&#xff1a; …

main函数和其他函数

##什么是main函数函数就很敏感&#xff0c;在我认为的函数函数就是功能 有系统给的也有自己写的 函数就是一个封装好的功能 function 函数&#xff0c;功能。main函数 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9a92f39cf6a842f5a56dbc1689012ceb.png 函数的参数&am…