【黑马点评】达人探店

【黑马点评】达人探店

1、发布探店笔记

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

具体发布流程

在这里插入图片描述

根据找到对应的上传接口

http://localhost:8080/api/blog

对应数据库表:tb_blog

1)写笔记上传图片

对应的类是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);
        }
    }

这里需要修改文件上传的地址,这里是要上传到前端服务器nginx上

new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName)

找到imgs所在的路径

D:\baidu\Down\nginx-1.18.0\html\hmdp\imgs
IMAGE_UPLOAD_DIR = "D:\\baidu\\Down\\nginx-1.18.0\\html\\hmdp\\imgs\\";

在实际开发中图片一般会放在nginx上或者是云存储上。

图片上传的路径和流程,文件保存路径

在这里插入图片描述

2)发布笔记

BlogController

@Resource
    private IBlogService blogService;

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

这里注意需要修改shop_id默认值为10,在数据库中修改,不然会报错

发布成功后页面会自动跳转到你的个人页面,笔记会出现在你的个人页面中

在这里插入图片描述
在这里插入图片描述

还可以在首页中看到自己发布的笔记

数据库中插入成功

在这里插入图片描述

2、实现查看发布的笔记

在这里插入图片描述

1)Controller层业务转移

BlogController

将controller中的业务转移到service中

@GetMapping("/hot")
    public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {
        return blogService.queryHotBlog(current);
    }
@GetMapping("/{id}")
    public Result queryBlog(@PathVariable("id") long id){
        return blogService.queryBlogById(id);
    }

修改处

在这里插入图片描述

2)service层实现方法

serviceimpl实现类

实现两个方法

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
	@Resource
    private IUserService userService;
    
    @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();
        // 查询blog有关用户
        records.forEach(this::queryBlogUser);
        return Result.ok(records);
    }

    @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);
    }
}

可以看到这里查询blog有关的用户这段代码逻辑在两个方法中都存在,可以将这段代码逻辑进行封装

3)封装查询blog

快捷键==Ctrl+Alt+M

查询blog相关用户统一由一个功能来实现

// 查询blog有关用户逻辑封装
    private void queryBlogUser(Blog blog) {
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
    }

查看笔记内容成功

在这里插入图片描述

3、点赞功能

实际业务中,对哪个笔记点赞,笔记点赞量最多的,就会将这个笔记推在首页的前面

找到点赞对应的接口

http://localhost:8080/api/blog/like/?

在这里插入图片描述

对应的Controller类是BlogController

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

这里的like就是点赞数量

这里可以看到我对这个订单进行了点赞,但是如果同一个人对一个笔记进行重复的点赞,那么服务器承担的压力非常大,所以我们应该限制一人一赞

完善点赞功能

需求:

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

实现步骤:

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

实现步骤

1)实体类Blog加字段
    /**
     * 是否点赞过了
     */
    @TableField(exist = false)
    private Boolean isLike;
2)修改Controller层点赞功能
@PutMapping("/like/{id}")
    public Result likeBlog(@PathVariable("id") Long id) {
        // 修改点赞数量
        return blogService.likeBlog(id);
    }
3)service中创建实现likeBlog方法

把笔记的id作为redis中的KEY,点赞的用户的ID作为Value存在set集合中

//service接口
Result likeBlog(Long id);
//service实现类
@Override
    public Result likeBlog(Long id) {
        //1、获取用户
        Long userId = UserHolder.getUser().getId();
        //2、判断当前用户是否点赞
        String key="blog:liked:"+id;
        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集合中移除
            if (isSuccess) {
                stringRedisTemplate.opsForSet().remove(key,userId.toString());
            }
        }
        return Result.ok();
    }
4)修改前两个方法

在前面两个方法中都需要查询blog是否被点赞

所以我们修改方法

定义这段相同的逻辑

private void isBlogLiked(Blog blog) {
        //1、获取用户
        Long userId = UserHolder.getUser().getId();
        //2、判断当前用户是否点赞
        String key="blog:liked:"+blog.getId();
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        blog.setIsLike(BooleanUtil.isTrue(isMember));
    }

修改方法——加上这段逻辑

queryBlogById方法

在这里插入图片描述

queryHotBlog方法

在这里插入图片描述

5)测试

这里要注意,要先登录,不然直接报空指针异常

在这里插入图片描述

点赞后,图标显示高亮,再次点击数字减一,高亮消失,并且缓存中点赞用户id消失

在这里插入图片描述

数据库中数据正常

4、点赞排行榜

一、分析

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

在这里插入图片描述

对应实现接口为

http://localhost:8080/api/blog/like/?

我们选定的集合要满足要存多个元素、唯一、排序

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

在这里插入图片描述

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

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

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

二、选用集合

语法:

ZSOCORE key member

实现:

--创建集合z1,并添加集合元素3个
> ZADD z1 1 m1 2 m2 3 m3
3
--查询集合元素
> ZSCORE z1 m1
1
> ZSCORE z1 m2
2
> ZSCORE z1 m3
3

排行榜范围查询

ZRANGE key min max

实现

--查集合z1前5名
> ZRANGE z1 0 4
m1
m2
m3

三、代码实现

修改点赞功能——likeBlog方法

BlogServiceimpl实现类

1)修改集合类型

将set集合改为Zset集合

likeBlog方法

代码逻辑

  1. 通过UserHolder.getUser().getId()获取当前用户的id。
  2. 判断当前用户是否已经点赞该博客,通过Redis中ZSet的score方法判断该用户id是否存在。
  3. 如果该用户未点赞,需要执行以下步骤:
    • 数据库点赞数+1,使用EasyMybatis中的update对象进行操作。
    • 将当前用户的id和当前时间戳加入到Redis的ZSet集合中,使得可以记录该用户的点赞时间和支持排序。
  4. 如果该用户已经点赞,需要执行以下步骤:
    • 数据库点赞数-1,使用EasyMybatis中的update对象进行操作。
    • 从Redis的ZSet集合中将当前用户id移除。

而返回值是一个Result对象,并且返回码为200,即请求成功。

在这里插入图片描述

isBlogLiked方法

在这里插入图片描述

2)实现点赞排行榜
@Override
    public Result queryBlogLikes(long id) {
        String key=BLOG_LIKED_KEY+id;
        //1、查询top5(前5名)的点赞用户  zrange key 0 4
        Set<String> tops5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if (tops5==null||tops5.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        //2、解析出其中的用户id
        List<Long> ids = tops5.stream().map(Long::valueOf).collect(Collectors.toList());
        //3、根据用户id查询用户
        List<UserDTO> userDTOS = userService.listByIds(ids)
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        //4、返回
        return Result.ok(userDTOS);
    }
3)Controller层实现接口

BlogController类

@GetMapping("likes/{id}")
    public Result queryBlogLikes(@PathVariable("id") long id){
        return blogService.queryBlogLikes(id);
    }
4)登录问题

首页没登录的时候看不到页面内容,原因是我们这里设置了用户点赞,当前用户没登录就无法查看订单的点赞

我们需要用户不用登录也能查看到内容

BlogServiceImpl类

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

可以看到点赞图标出现了点赞用户的头像

在这里插入图片描述

查看缓存

在这里插入图片描述

我使用另一个账号登陆后点赞后

在这里插入图片描述

这里可以发现一个问题:

点赞第一用户的位置被排在了后面

四、问题解决

1)原因

这是数据库的sql语句原因

select ... from user where id in(10,7);

这里用IN的时候查询的结果不会按照你提供的id顺序

这里我先使用的是id=10的用户点赞,而这个用户却出现在后面

2)问题解决

手动指定id的顺序

select ... from user where id in(10,7) order by(id,10,7);

修改queryBlogLikes方法

@Override
    public Result queryBlogLikes(long id) {
        String key=BLOG_LIKED_KEY+id;
        //1、查询top5(前5名)的点赞用户  zrange key 0 4
        Set<String> tops5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if (tops5==null||tops5.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        //2、解析出其中的用户id
        List<Long> ids = tops5.stream().map(Long::valueOf).collect(Collectors.toList());
        String idStr = StrUtil.join(",", ids);
        //3、根据用户id查询用户  select ... from user where id in(10,7) order by(id,10,7);
        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);
    }

修改部分

在这里插入图片描述

结果看出排行榜功能实现

在这里插入图片描述

达人探店功能完结!!!😁😁😁😁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值