点赞功能

业务说明

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

尚未实现博主和粉丝的消息、动态订阅

原文地址:http://www.cnblogs.com/Gw-CodingWorld/p/16810771.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性