首页 > 数据库 >基于Redis实现点赞、点赞用户按时间排序、好友关注和共同关注等业务

基于Redis实现点赞、点赞用户按时间排序、好友关注和共同关注等业务

时间:2022-10-20 18:15:45浏览次数:47  
标签:Redis 用户 blog 关注 user 点赞 id Result

点赞功能

业务说明

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

相关文章

  • Docker | redis集群部署实战
    前面已经简单熟悉过redis的下载安装使用,今天接着部署redis集群(cluster),简单体会一下redis集群的高可用特性。环境准备Redis是C语言开发,安装Redis需要先将Redis的源码进行......
  • 一种关注于重要样本的目标检测方法!
    作者:宋志龙,浙江工业大学,Datawhale成员在目标检测中训练模型时,样本间往往有差异性,不能被简单地同等对待。这次介绍的论文提出了一种重要样本的关注机制,在训练过程中帮助模型......
  • 用redis-shake从阿里去redis迁移到aws redis
    基于redis-shake迁移第一步:安装golang环境yuminstall-ygolang第二步:下载redis-shakehttps://github.com/alibaba/RedisShake?spm=a2c4g.11186623.0.0.5bdb735610oRPT......
  • Ubuntu18.04 安装redis5.0.5集群
    目标:使用3台服务器A、B、C,每台服务器部署一套主从(1主1从),三个主服务来平分redis槽10.108.1.88(6379、6380)10.108.1.89(6379、6380)10.108.1.90(6379、6380)安装Redis......
  • RedisGraph多活设计方案功能测试
    该文档主要是针对RedisGraph多活设计方案的功能测试,来说明方案是可实施是可行的。该方案设计文档参见上一篇文章 ​​RedisGraph图形数据库多活设计方案​​功能测试准备条......
  • springboot +redis+token
    1、pom.xml<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId......
  • PHP REDIS GEO 经纬度
    本文是使用redis代替数据库金纬度查询,由于数据库金纬度度让数据库去做运算影响性能所以下面就介绍了用redis去使用redis中提供了geo类,使用就行了 $redis=newre......
  • Redis 设置密码
    描述本人图省事本机用的rediswindows版本的,其余配置内容一样。配置文件区别修改配置文件使用分两种情况当前redis不是服务启动的用:redis.windows.conf文件当前redis是以......
  • 数据库选型,技术团队与运维首先关注的内容
    数据库关注点:首先1、部署、维护的难度,文档是否齐全2、数据库对mysql语法的兼容性 3、相同配置下数据库查询速度(会执行业务相关的SQL执行性能) 4、数据库运行、查询稳定性......
  • .netcore里使用StackExchange.Redis TimeOut 情况解决方法
    在用StackExchange.Redis这个组件时候,时不时会出现异常TimeOut解决方法如下,解决方法: 在Program的Main入口方法里添加一句话:200,200); 比如以下代码:publicclassProg......