黑马 Redis -- 达人探店

1. 达人探店
1.1. 发布探店笔记
  • 对应的实体类,数据表中并没有用户头像和用户昵称,但是对应的实体类里却有,这是因为使用了@TableField(exist = false) 用来解决实体类中有的属性但是数据表中没有的字段
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_blog")
public class Blog implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 商户id
     */
    private Long shopId;
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 用户图标
     */
    @TableField(exist = false)
    private String icon;
    /**
     * 用户姓名
     */
    @TableField(exist = false)
    private String name;
    /**
     * 是否点赞过了
     */
    @TableField(exist = false)
    private Boolean isLike;

    /**
     * 标题
     */
    private String title;

    /**
     * 探店的照片,最多9张,多张以","隔开
     */
    private String images;

    /**
     * 探店的文字描述
     */
    private String content;

    /**
     * 点赞数量
     */
    private Integer liked;

    /**
     * 评论数量
     */
    private Integer comments;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}
  • 随便点击一张图片,查看发送的请求
请求网址: http://localhost:8080/api/blog/6
请求方法: GET
  • Cotroller层
/**
     * 查看贴子
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Result queryById(@PathVariable("id") Long id){
        return blogService.queryById(id);
    }
  • ServiceImpl层
@Override
    public Result queryById(Long id) {
        Blog blog = getById(id);
        if(blog == null){
            return Result.fail("评价不存在或已被删除");
        }
        queryBlogUser(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());
    }

1.2. 点赞功能
  • 点击点赞按钮,查看发送的请求
请求网址: http://localhost:8083/api/blog/like/4
请求方法: PUT
  • 实现方法
@PutMapping("/like/{id}")
public Result likeBlog(@PathVariable("id") Long id) {
    // 修改点赞数量
    blogService.update().setSql("liked = liked + 1").eq("id", id).update();
    return Result.ok();
}
  • 问题分析:这种方式会导致一个用户无限点赞,明显是不合理的
  • 造成这个问题的原因是,我们现在的逻辑,发起请求只是给数据库+1,所以才会出现这个问题
  • 需求
    1. 同一个用户只能对同一篇笔记点赞一次,再次点击则取消点赞
    2. 如果当前用户已经点赞,则点赞按钮高亮显示(前端已实现,判断字段Blog类的isLike属性)
  • 实现步骤
    1. 修改点赞功能,利用Redis中的set集合来判断是否点赞过,未点赞则点赞数+1,已点赞则点赞数-1
    2. 修改根据id查询的业务,判断当前登录用户是否点赞过,赋值给isLike字段
    3. 修改分页查询Blog业务,判断当前登录用户是否点赞过,赋值给isLike字段
  • 具体实现
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 用户点赞,取消点赞
     * @param id 贴在 id
     * @return
     */
    @Override
    public Result likeBlog(Long id) {
        //1. 获取当前用户的信息
        Long userId = UserHolder.getUser().getId();
        //2. 如果用户未点赞,则点赞数加 1,同时将用户加入 set 集合
        String key = RedisConstants.BLOG_LIKED_KEY + id;
        Boolean isLiked = redisTemplate.opsForSet().isMember(key, userId.toString());
        if(BooleanUtil.isFalse(isLiked)){
            //点赞数 +1
            boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
            //将用户加入集合 set
            if(success){
                redisTemplate.opsForSet().add(key, userId.toString());
            }
        }else{
            //点赞数-1
            boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
            if(success){
                redisTemplate.opsForSet().remove(key, userId.toString());
            }
        }
        return Result.ok();
    }
}
  • 修改完毕之后,页面上还不能立即显示点赞完毕的后果,我们还需要修改查询Blog业务,判断Blog是否被当前用户点赞过
@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 -> {
        queryBlogUser(blog);
        //追加判断blog是否被当前用户点赞,逻辑封装到isBlogLiked方法中
        isBlogLiked(blog);
    });
    return Result.ok(records);
}

@Override
public Result queryById(Integer id) {
    Blog blog = getById(id);
    if (blog == null) {
        return Result.fail("评价不存在或已被删除");
    }
    queryBlogUser(blog);
    //追加判断blog是否被当前用户点赞,逻辑封装到isBlogLiked方法中
    isBlogLiked(blog);
    return Result.ok(blog);
}

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. 如果点赞了,则将isLike设置为true
    blog.setIsLike(BooleanUtil.isTrue(isMember));
}
1.3. 点赞排行榜


 

List

Set

SortedSet

排序方式

按添加顺序排序

无法排序

根据score值排序

唯一性

不唯一

唯一

唯一

查找方式

按索引查找或首尾查找

根据元素查找

根据元素查找

  • 修改BlogServiceImpl
    由于ZSet没有isMember方法,所以这里只能通过查询score来判断集合中是否有该元素,如果有该元素,则返回值是对应的score,如果没有该元素,则返回值为null
/**
     * 用户点赞,取消点赞
     * @param id 贴在 id
     * @return
     */
@Override
public Result likeBlog(Long id) {
//1. 获取当前用户的信息
Long userId = UserHolder.getUser().getId();
//2. 如果用户未点赞,则点赞数加 1,同时将用户加入 set 集合
String key = RedisConstants.BLOG_LIKED_KEY + id;
Double score = redisTemplate.opsForZSet().score(key, userId.toString());
if(score == null){
    //点赞数 +1
    boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
    //将用户加入集合 set
    if(success){
        redisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
    }
}else{
    //点赞数-1
    boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
    if(success){
        redisTemplate.opsForZSet().remove(key, userId.toString());
    }
}
return Result.ok();
}

  • 继续来完善显示点赞列表功能,查看浏览器请求,这个请求目前应该是404的,因为我们还没有写,他需要一个list返回值,显示top5点赞的用户
请求网址: http://localhost:8083/api/blog/likes/4
请求方法: GET
  • 在Controller层中编写对应的方法,点赞查询列表,具体逻辑写到BlogServiceImpl中
@GetMapping("/likes/{id}")
public Result queryBlogLikes(@PathVariable Integer id){
    return blogService.queryBlogLikes(id);
}
  • 具体逻辑如下
@Override
    public Result queryBlogLikes(Long id) {
        String key = RedisConstants.BLOG_LIKED_KEY + id;
        // 查询 zset 中的前 5 个元素
        Set<String> top5 = redisTemplate.opsForZSet().range(key, 0, 4);
        //如果为空的,直接返回一个空集合
        if(top5 == null || top5.isEmpty()){
            return Result.ok(Collections.emptyList());
        }
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        //将ids使用`,`拼接,SQL语句查询出来的结果并不是按照我们期望的方式进行排
        //所以我们需要用order by field来指定排序方式,期望的排序方式就是按照查询出来的id进行排序
        String idStr = StrUtil.join(",", ids);
        //select * from tb_user where id in (ids[0], ids[1] ...) order by field(id, ids[0], ids[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);
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值