重点:
- 基于Redis中List的点赞列表
- 基于Redis中SortedSet的点赞排行榜
1、发布探店笔记
使用springboot框架整合mybatisplus实现
2、查看探店笔记
请求接口:
一种实现方式是返回一个包含blog和user信息的Dto对象,但是过程繁琐。
因此简化为直接在Blog实体类,添加用户头像和用户名称两个属性,并加上mp提供的注解@TableField(exist = false) //当前属性不属于表中字段
//BlogController类中调用queryBlogById方法
@GetMapping("/{id}")
public Result queryBlogById(@PathVariable("id") Long id){
return blogService.queryBlogById(id);
}
//IBlogService接口中定义queryBlogById方法
public interface IBlogService extends IService<Blog> {
Result queryHotBlog(Integer current);
Result queryBlogById(Long id);
}
//BlogServiceImpl类中实现queryBlogById方法
@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);
}
private void queryBlogUser(Blog blog) {
Long userId = blog.getUserId();
User user = userService.getById(userId);
blog.setName(user.getNickName());
blog.setIcon(user.getIcon());
}
3、点赞
请求接口:
代码实现:
1、给Blog对象增加isLike属性
/**
* 是否点赞过了
*/
@TableField(exist = false)
private Boolean isLike;
2、调用likeBlog方法
//BlogController调用likeBlog方法
@PutMapping("/like/{id}")
public Result likeBlog(@PathVariable("id") Long id) {
// 修改点赞数量
return blogService.likeBlog(id);
}
//IBlogService接口声明likeBlog方法
Result likeBlog(Long id);
//BlogServiceImpl类实现likeBlog方法
@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 isSucess = update().setSql("liked = liked + 1").eq("id", id).update();
//3.2 保存用户到Redis的set集合
if(isSucess){
stringRedisTemplate.opsForSet().add(key, userId.toString());
}
}else{
//4 如果已点赞
//4.1 数据库点赞数-1
boolean isSucess = update().setSql("liked = liked - 1").eq("id", id).update();
//4.2 把用户从Redis的set集合移除
stringRedisTemplate.opsForSet().remove(key, userId.toString());
}
return Result.ok();
}
3、修改根据id查询博客的点赞业务
public Result queryBlogById(Long id) {
//1 查询blog
Blog blog = getById(id);
if (blog == null) {
return Result.fail("笔记不存在!");
}
//2 查询blog有关用户
queryBlogUser(blog);
//3 查询blog是否被点赞
isBlogLiked(blog);
return Result.ok(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));
}
4、修改分页查询下的点赞业务
@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 -> {
this.queryBlogUser(blog);
this.isBlogLiked(blog);
});
return Result.ok(records);
}
4、点赞排行榜
因为set集合是无序的,但排行榜需要显示前5个点赞的用户,要想有序且不重复,只能用sortSet了。
这里用redis的sortSet来代替set,把之前的点赞功能,用户id存入set集合改为存入ZSet,使用sorce(key,value)方法来获取该键值的sorce,若没有则返回null,用来代替之前set的ismembet方法
代码实现:
1、修改点赞功能
@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());
Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
// if(BooleanUtil.isFalse(isMember)){
if(score==null){
//3 如果未点赞,可以点赞
//3.1 数据库点赞数+1
boolean isSucess = update().setSql("liked = liked + 1").eq("id", id).update();
//3.2 保存用户到Redis的sorted_set集合 zadd key value score
if(isSucess){
// stringRedisTemplate.opsForSet().add(key, userId.toString());
stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
}
}else{
//4 如果已点赞
//4.1 数据库点赞数-1
boolean isSucess = update().setSql("liked = liked - 1").eq("id", id).update();
//4.2 把用户从Redis的set集合移除
// stringRedisTemplate.opsForSet().remove(key, userId.toString());
stringRedisTemplate.opsForZSet().remove(key, userId.toString());
}
return Result.ok();
}
2、修改查询(根据id查,查询详情)
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();
// Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
// blog.setIsLike(BooleanUtil.isTrue(isMember));
Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
blog.setIsLike(score!=null);
}
3、点赞列表查询
//BlogController类调用queryBlogLikes方法
@GetMapping("/likes/{id}")
public Result queryBlogLikes(@PathVariable("id") Long id){
return blogService.queryBlogLikes(id);
}
//IBlogService接口声明queryBlogLikes方法
Result queryBlogLikes(Long id);
//BlogServiceImpl类实现queryBlogLikes方法
@Override
public Result queryBlogLikes(Long id) {
//1 查询top5的点赞用户 zrange key 0 4
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());
// List<UserDTO> userDTOS = userService.listByIds(ids).stream()
// .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
// .collect(Collectors.toList());
//4 返回
return Result.ok(userDTOS);
}
注意: queryBlogLikes方法中sql语句的 list.in(…, …)查询出来的是后点赞的在前,先点赞的在后,需要我们自定义sql查询,用last最后一条sql语句,手写order by的sql。