• 黑马点评-发布探店笔记


    探店笔记

    探店笔记类似点评网站的评价,往往是图文结合。

    对应的表有两个:

    tb_blog:探店笔记表,包含笔记中的标题、文字、图片等

    tb_blog_comments:其他用户对探店笔记的评价

    流程如下:

    上传接口:

    1. @Slf4j
    2. @RestController
    3. @RequestMapping("upload")
    4. public class UploadController {
    5. @PostMapping("blog")
    6. public Result uploadImage(@RequestParam("file") MultipartFile image) {
    7. try {
    8. // 获取原始文件名称
    9. String originalFilename = image.getOriginalFilename();
    10. // 生成新文件名
    11. String fileName = createNewFileName(originalFilename);
    12. // 保存文件
    13. image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));
    14. // 返回结果
    15. log.debug("文件上传成功,{}", fileName);
    16. return Result.ok(fileName);
    17. } catch (IOException e) {
    18. throw new RuntimeException("文件上传失败", e);
    19. }
    20. }
    21. }

    博客接口:

    1. @RestController
    2. @RequestMapping("/blog")
    3. public class BlogController {
    4. @Resource
    5. private IBlogService blogService;
    6. @PostMapping
    7. public Result saveBlog(@RequestBody Blog blog) {
    8. //获取登录用户
    9. UserDTO user = UserHolder.getUser();
    10. blog.setUpdateTime(user.getId());
    11. //保存探店博文
    12. blogService.saveBlog(blog);
    13. //返回id
    14. return Result.ok(blog.getId());
    15. }
    16. }

    查看探店笔记代码:

    1. @Override
    2. public Result queryBlogById(Long id) {
    3. // 1.查询blog
    4. Blog blog = getById(id);
    5. if (blog == null) {
    6. return Result.fail("笔记不存在!");
    7. }
    8. // 2.查询blog有关的用户
    9. queryBlogUser(blog);
    10. return Result.ok(blog);
    11. }

    点赞功能

    接口如下:

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

    但是这种方式会导致一个用户无限点赞,明显是不合理的。造成这个问题的原因是,我们现在的逻辑,发起请求只是给数据库+1,所以才会出现这个问题。

    我们需要实现以下功能:

    • 同一个用户只能点赞一次,再次点击则取消点赞

    • 如果当前用户已经点赞,则点赞按钮高亮显示(前端判断字段Blog类的isLike属性)

    实现步骤:

    • 给Blog类中添加一个isLike字段,标示是否被当前用户点赞

    • 修改点赞功能,利用Redis的set集合判断是否点赞过,未点赞过则点赞数+1,已点赞过则点赞数-1

    • 修改根据id查询Blog的业务,判断当前登录用户是否点赞过,赋值给isLike字段

    • 修改分页查询Blog业务,判断当前登录用户是否点赞过,赋值给isLike字段

    实现代码:

    1. @Override
    2. public Result likeBlog(Long id){
    3. // 1.获取登录用户
    4. Long userId = UserHolder.getUser().getId();
    5. // 2.判断当前登录用户是否已经点赞
    6. String key = BLOG_LIKED_KEY + id;
    7. Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
    8. if(BooleanUtil.isFalse(isMember)){
    9. //3.如果未点赞,可以点赞
    10. //3.1 数据库点赞数+1
    11. boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
    12. //3.2 保存用户到Redis的set集合
    13. if(isSuccess){
    14. stringRedisTemplate.opsForSet().add(key,userId.toString());
    15. }
    16. }else{
    17. //4.如果已点赞,取消点赞
    18. //4.1 数据库点赞数-1
    19. boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
    20. //4.2 把用户从Redis的set集合移除
    21. if(isSuccess){
    22. stringRedisTemplate.opsForSet().remove(key,userId.toString());
    23. }
    24. }

    然后把给该笔记点赞的人显示出来,比如最早点赞的TOP5,形成点赞排行榜,Redis使用sortedSet(代码中为ZSet):

    点赞逻辑代码:

    1. @Override
    2. public Result likeBlog(Long id) {
    3. // 1.获取登录用户
    4. Long userId = UserHolder.getUser().getId();
    5. // 2.判断当前登录用户是否已经点赞
    6. String key = BLOG_LIKED_KEY + id;
    7. Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    8. if (score == null) {
    9. // 3.如果未点赞,可以点赞
    10. // 3.1.数据库点赞数 + 1
    11. boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
    12. // 3.2.保存用户到Redis的set集合 zadd key value score
    13. if (isSuccess) {
    14. stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
    15. }
    16. } else {
    17. // 4.如果已点赞,取消点赞
    18. // 4.1.数据库点赞数 -1
    19. boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
    20. // 4.2.把用户从Redis的set集合移除
    21. if (isSuccess) {
    22. stringRedisTemplate.opsForZSet().remove(key, userId.toString());
    23. }
    24. }
    25. return Result.ok();
    26. }
    27. private void isBlogLiked(Blog blog) {
    28. // 1.获取登录用户
    29. UserDTO user = UserHolder.getUser();
    30. if (user == null) {
    31. // 用户未登录,无需查询是否点赞
    32. return;
    33. }
    34. Long userId = user.getId();
    35. // 2.判断当前登录用户是否已经点赞
    36. String key = "blog:liked:" + blog.getId();
    37. Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    38. blog.setIsLike(score != null);
    39. }

    点赞列表查询:

    接口:

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

    逻辑代码:

    1. @Override
    2. public Result queryBlogLikes(Long id) {
    3. String key = BLOG_LIKED_KEY + id;
    4. // 1.查询top5的点赞用户 zrange key 0 4
    5. Set top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
    6. if (top5 == null || top5.isEmpty()) {
    7. return Result.ok(Collections.emptyList());
    8. }
    9. // 2.解析出其中的用户id
    10. List ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
    11. String idStr = StrUtil.join(",", ids);
    12. // 3.根据用户id查询用户 WHERE id IN ( 5 , 1 ) ORDER BY FIELD(id, 5, 1)
    13. List userDTOS = userService.query()
    14. .in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list()
    15. .stream()
    16. .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
    17. .collect(Collectors.toList());
    18. // 4.返回
    19. return Result.ok(userDTOS);
    20. }
  • 相关阅读:
    CountDownLatch使用错误+未最终断开连接导致线程池资源耗尽
    HXAPIGate系列——HXAPIGate快速入门
    我用pandas解决了美女同事的难题,美女直呼很Nice
    React---路由
    Linux系统编程
    【深度学习实验】线性模型(一):使用NumPy实现简单线性模型:搭建、构造损失函数、计算损失值
    ASP.NET Core 6框架揭秘实例演示[20]:“数据保护”框架基于文件的密钥存储
    RedisKey的基本命令
    操作系统(一)概述&基本组成&运行机制
    LeetCode 每日一题 2022/8/1-2022/8/7
  • 原文地址:https://blog.csdn.net/cangshanjiang/article/details/136570490