【Redis】Redis实战:黑马点评之达人探店

Redis实战:黑马点评之达人探店

1 发布探店笔记

探店笔记类似点评网站的评价,往往是图文结合。对应的表有两个:

  • tb_blog:探店笔记表,包含笔记中的标题、文字、图片等
  • tb_blog_comments:其他用户对探店笔记的评价

具体发布流程如下:

在这里插入图片描述

UploadController(已完成)

@Slf4j
@RestController
@RequestMapping("upload")
public class UploadController {

    @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中imgs目录的绝对路径

在这里插入图片描述

BlogController(已完成)

@RestController
@RequestMapping("/blog")
public class BlogController {

    @Resource
    private IBlogService blogService;

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

2 查看探店笔记

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

在这里插入图片描述

代码具体实现如下:

在BlogController中(queryHotBlog的功能已经完成,但是我们需要将其具体业务逻辑移至service层)

@GetMapping("/hot")
public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {
    return blogService.queryHotBlog(current);

}

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

在BlogServiceImpl中

@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 ->{
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
    });
    return Result.ok(records);
}

@Override
public Result queryById(Long id) {
    Blog blog = getById(id);
    if(blog==null){
        return Result.fail("博客不存在");
    }
    User user = userService.getById(blog.getUserId());
    blog.setName(user.getNickName());
    blog.setIcon(user.getIcon());
    return Result.ok(blog);
}

3 点赞功能

点赞功能在原有项目中提供了一份初始代码:

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

上述代码的问题在于,一个用户是可以无限点赞的,而且用户点赞了之后也不能取消点赞。

不仅如此,当前代码还有一些其他功能尚未实现,比如当前用户为某篇笔记点赞之后,这篇笔记下面的点赞按钮应该是高亮显示的,而当前项目,即使点赞了,点赞按钮仍然显示灰色。

在这里插入图片描述

接下来我们需要完善点赞功能,主要是针对两点:

  • 同一个用户只能点赞一次,再次点击则取消点赞
  • 如果当前用户已经点赞,则点赞按钮高亮显示(前端根据Blog类的isLike是否为true判断点赞按钮是否需要高亮显示)

问题来了,怎样快速判断当前用户是否点赞了呢?我们在这里可以使用redis中的set集合,我们可以以博客id为key,已经点赞的用户id为set集合中的数据,由set集合中的数据是不能重复的,因此同一个用户无论点赞多少次,集合中都只会有一个元素

具体实现步骤如下:

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

代码编写如下:

1 在Blog 添加一个字段

@TableField(exist = false)
private Boolean isLike;

2 修改UserController中的likeBlog方法

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

3 修改BlogServiceImpl

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Autowired
    private IUserService userService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @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 ->{
            Long userId = blog.getUserId();
            User user = userService.getById(userId);
            //查看当前用户是否点赞该博客
            blog.setIsLike(isLikeBlog(blog.getId()));
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
        });
        return Result.ok(records);
    }

    @Override
    public Result queryById(Long id) {
        Blog blog = getById(id);
        if(blog==null){
            return Result.fail("博客不存在");
        }
        User user = userService.getById(blog.getId());
        //判断当前用户是否点赞该文章
        blog.setIsLike(isLikeBlog(id));
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
        return Result.ok(blog);
    }

    /**
     * 博客点赞
     * @param id
     * @return
     */
    @Override
    public Result likeBlog(Long id) {
        Long userId = UserHolder.getUser().getId();
        String blogKey = RedisConstants.BLOG_LIKED_KEY+id;
        //判断文章是否已经点赞
        boolean member = isLikeBlog(id);
        if(!member){
            //当前用户未点赞
            boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
            //保存用户id到set集合
            if(success){
                stringRedisTemplate.opsForSet().add(blogKey,userId.toString());
            }
        }else{
            //当前用户已点赞
            boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
            if(success){
                stringRedisTemplate.opsForSet().remove(blogKey,userId.toString());
            }
        }
        return Result.ok();
    }

    /**
     * 判断当前用户是否点赞指定文章
     * @param id
     * @return
     */
    public boolean isLikeBlog(Long id){
        Long userId = UserHolder.getUser().getId();
        //当前用户未登录,直接返回false
        if(userId == null){
            return false;
        }
        String blogKey = RedisConstants.BLOG_LIKED_KEY+id;
        //查询set集合中是否有用户当前用户id
        Boolean member = stringRedisTemplate.opsForSet().isMember(blogKey, userId.toString());
        return BooleanUtil.isTrue(member);
    }

}

4 点赞排行榜

在探店笔记的详情页面,应该把最早给该笔记点赞的五位用户显示出来,形成点赞排行榜:

在这里插入图片描述

之前的点赞用户id是放到set集合,但是set集合是不能排序的,所以这个时候我们可以使用sortedSet

在这里插入图片描述

代码编写如下:

修改BlogServiceImpl

/**
  * 博客点赞
  * @param id
  * @return
  */
@Override
public Result likeBlog(Long id) {
    Long userId = UserHolder.getUser().getId();
    String blogKey = RedisConstants.BLOG_LIKED_KEY+id;
    //判断文章是否已经点赞
    boolean member = isLikeBlog(id);
    if(!member){
        //当前用户未点赞
        boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
        if(success){
            //保存用户id到sortedSet集合,按照时间进行排序
            stringRedisTemplate.opsForZSet().add(blogKey,userId.toString(),System.currentTimeMillis());
        }
    }else{
        //当前用户已点赞
        boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
        if(success){
            //将用户从sortedSet中移除
            stringRedisTemplate.opsForZSet().remove(blogKey,userId.toString());
        }
    }
    return Result.ok();
}

/**
  * 判断当前用户是否点赞指定文章
  * @param id
  * @return
  */
public boolean isLikeBlog(Long id){
    Long userId = UserHolder.getUser().getId();
    //当前用户未登录,直接返回false
    if(userId == null){
        return false;
    }
    String blogKey = RedisConstants.BLOG_LIKED_KEY+id;
    //查询sortedSet集合中是否有用户当前用户id
    //score()为查询指定用户的分数,如果分数为null说明该用户不存在
    Double score = stringRedisTemplate.opsForZSet().score(blogKey, userId.toString());
    if(score==null){
        return false;
    }
    return true;
}

在BlogController中编写likes方法

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

在BlogService中编写likes方法

/**
  * 点赞排行榜
  * @param id
  * @return
  */
@Override
public Result likes(Long id) {
    String blogKey = RedisConstants.BLOG_LIKED_KEY+id;
    //获取前五位点赞的用户
    Set<String> userSet = stringRedisTemplate.opsForZSet().range(blogKey, 0, 5);
    if(userSet==null||userSet.isEmpty()){
        //说明当前没有用户点赞
        return Result.ok();
    }
    //将set集合解析成用户id集合
    List<Long> ids = userSet.stream().map(Long::valueOf).collect(Collectors.toList());
    String idStr= StrUtil.join(",",ids);
    //根据用户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());
    return Result.ok(userDTOS);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值