点赞功能
业务说明
1、每个用户只能点一次赞,再次点击时取消点赞
2、在Blog属性中增加isLike字段,用于判断当前用户是否点赞
3、isLike的值从Redis中获取,可以用redis自带的持久化机制,也可以在数据库中设计表,定时持久化到数据库
4、点赞功能使用的是redis的set数据结构,用set来判断当前用户是否已经存在Blog的点赞集合中
5、redis中设计的具体数据结构为:key-set 其中key设置为“blog:liked:blogId”,set中放的是已点赞用户的id
代码实现
BlogServiceImpl.java
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
@Resource
private IUserService userService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
@Transactional
public Result getBlog(Long id) {
Blog blog = getById(id);
if(blog == null) return Result.fail("该博客不存在");
//设置博客的作者信息
User user = userService.getById(blog.getUserId());
blog.setIcon(user.getIcon());
blog.setName(user.getNickName());
//对当前登录的用户是否已经对该博客点赞
Double isLike = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY + blog.getId(), UserHolder.getUser().getId().toString());
blog.setIsLike(isLike != null);
return Result.ok(blog);
}
@Override
@Transactional
public Result likeBlog(Long id) {
//1、获取登录用户
UserDTO user = UserHolder.getUser();
Blog blog = getById(id);
//2、判断当前用户是否点赞
Double isLiked = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY + blog.getId(), user.getId().toString());
//3、如果未点赞
if(isLiked == null){
//3.1修改blog的like字段liked = liked + 1
boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
//3.2将用户添加到redis中该blog的点赞集合中
if(isSuccess){
stringRedisTemplate.opsForZSet().add(BLOG_LIKED_KEY + blog.getId(), user.getId().toString(), System.currentTimeMillis());
}
}else{
//4、如果已经点赞了,取消点赞
//4.1将like字段-1
update().setSql("liked = liked - 1").eq("id", id).update();
//4.2将用户从set集合中移除
stringRedisTemplate.opsForZSet().remove(BLOG_LIKED_KEY + blog.getId(), user.getId().toString());
}
return Result.ok();
}
@Override
@Transactional
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 blogUserId = blog.getUserId();
User user = userService.getById(blogUserId);
blog.setName(user.getNickName());
blog.setIcon(user.getIcon());
//当前登录的用户是否对作品进行点赞
UserDTO curUser = UserHolder.getUser();
Double isLike = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY + blog.getId(), curUser.getId().toString());
//System.out.println(userId);
//log.info("是否为set成员{}", isLike);
blog.setIsLike(isLike != null);
});
return Result.ok(records);
}
}
注意:在实现isLike的功能后,每次重新获取blog的数据时都需要从redis中重新确认是否点过赞;
在返回Blog对象时,一定一定注意不要将博客的作者信息,和当前登录用户的信息混淆;
前者是显示作者的头像、昵称,后者是用来判断当前用户是否对这篇博客点过赞
显示部分点赞用户
在点赞的下方会显示点赞该博客的用户的头像,按照点赞的先后顺序显示前五个点赞的用户,是基于时间的排序
重点关注:
1、如何去动态的维护集合的有序性,可以使用redis的有序集合
2、分页问题,与传统的分页不同,这个集合的索引随时都有可能发生变化,因此采用滚动分页
3、使用zset的数据结构,如果要增加排行榜,就需在之前存点赞用户id的基础上加上点赞的时间作为zset的排序分值
业务流程
1、查询最近点赞的五个用户,使用zrange key 0 4
2、解析出用户的id
3、根据用户的id查询用户的信息
4、返回用户的集合
代码实现
BlogServiceImpl.getLikedList()
@Override
public Result getLikedList(Long blogId) {
Blog blog = query().eq("id", blogId).one();
//1、查询最近点赞的五个用户,使用zrange key 0 4
Set<String> userSet = stringRedisTemplate.opsForZSet().range(BLOG_LIKED_KEY + blog.getId(), 0, 4);
if(userSet == null || userSet.isEmpty()){
return Result.ok(Collections.emptyList());
}
//2、解析出用户的id
List<Long> ids = userSet.stream().map(Long::valueOf).collect(Collectors.toList());
//3、根据用户的id查询用户的信息
//注意此处,getByIds()方法用的where xxx in(x, x, x)来查询的,不能保证原有的顺序不变
//List<User> users = userService.listByIds(ids);
String idStr = StrUtil.join(",", ids);
List<UserDTO> likedList = 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(likedList);
}
好友关注、共同关注
业务流程
1、获取用户
2、判断关注还是取关
3、如果是关注,新增一条follow记录,并将关注记录到redis(redis的数据结构选用set类型,方便之后共同关注取交集)
4、如果是取关,删除原有的follow记录
5、无数据返回
共同关注则是利用关注时存在redis中的数据集合,取交集,即可获得二者的共同关注
代码实现
FollowServiceImpl.java
@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private IUserService userService;
@Override
public Result isFollow(Long userId) {
UserDTO user = UserHolder.getUser();
Follow follow = query().eq("user_id", userId).eq("follow_user_id", user.getId()).one();
if(follow == null) return Result.ok(false);
return Result.ok(true);
}
@Override
public Result follow(Long userId, boolean status) {
//关注一个人时先判断是否关注了
//如果没有关注,创建关注的关系
//如果关注了则取消关系
UserDTO user = UserHolder.getUser();
Follow follow;
if(status){
follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(user.getId());
follow.setCreateTime(LocalDateTime.now());
boolean isSuccess = save(follow);
if(isSuccess){
stringRedisTemplate.opsForSet().add("follows:" + user.getId(), userId.toString());
}
return Result.ok(true);
}
LambdaQueryWrapper<Follow> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Follow::getUserId, userId).eq(Follow::getFollowUserId, user.getId());
remove(queryWrapper);
return Result.ok(false);
}
@Override
@Transactional
public Result getCommonFollow(Long userId) {
Set<String> common = stringRedisTemplate.opsForSet().intersect("follows:" + userId.toString(), "follows:" + UserHolder.getUser().getId());
if(common.size() == 0) return Result.ok(Collections.emptyList());
List<Long> commonIds = common.stream().map(Long::valueOf).collect(Collectors.toList());
List<User> users = commonIds.stream().map(commonId -> {
User user = userService.query().eq("id", commonId).one();
return user;
}).collect(Collectors.toList());
return Result.ok(users);
}
}
尚未实现博主和粉丝的消息、动态订阅
标签:Redis,用户,blog,关注,user,点赞,id,Result From: https://www.cnblogs.com/Gw-CodingWorld/p/16810771.html