首页 > 数据库 >基于Redis实现好友动态推送、附近商铺功能

基于Redis实现好友动态推送、附近商铺功能

时间:2022-10-21 23:11:53浏览次数:54  
标签:Redis 商铺 redis ids 查询 blog key 推送 id

好友动态推送

基于推模式实现探店笔记,一个人发布blog,在将blog保存到数据库的同时将blog发送到每个粉丝的收信箱中;收信箱按时间戳进行排序(类似于朋友圈);收信箱查询数据时按滚动分页进行查询。

滚动分页

在进行分页查询时,如果关注的人有新的动态,则不予查询,接着按照上一页的分页查询(每次查询记录上一页的最小值,第二页就接着最小值查询)

实现思路

用户收信箱的数据结构,采用zset,{"feed:followUserId" : "blogId" : "timeScore"}

使用zrevrangebyscore [key] [max] [min] [withscores] [limit offset count]进行滚动分页查询

key指定集合,min、max指定查询范围、withscores把时间戳作为结果带上(方便下一页的查询)、limit指定从最大值的第几个开始查、查多少个

分页参数

lastId->上一次查询的最小值(初始时为查询时的时间戳) --max

offset->上一次查询中最小值重复情况下的重复次数 --offset

min --0 、count --pageSize 这两个参数固定不变

业务流程

1、获取当前用户

2、在redis中查询当前用户的收信箱

3、解析数据blogId、minTime、offset

​ 3.1获取时间戳和博客id

​ 3.2获取查询中minTime重复的值作为下一轮的offset

4、根据blogId批量查询blog

5、需要将博客的点赞信息进行完善

6、将blog封装并返回

代码实现

BlogServiceImpl.queryBlogOfFollow()

@Override
public Result queryBlogOfFollow(Long max, Integer offset) {
    //1、获取当前用户
    UserDTO user = UserHolder.getUser();

    //2、在redis中查询当前用户的收信箱
    String key = "feed:" + user.getId();
    Set<ZSetOperations.TypedTuple<String>> typedTuples =
        stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 3);

    //3、解析数据blogId、minTime、offset
    long minTime = 0;
    int os = 1;
    List<Long> ids = new ArrayList<>(typedTuples.size());  //查询出的博客的id
    for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
        //3.1获取时间戳和博客id
        long time = typedTuple.getScore().longValue();
        String blogId = typedTuple.getValue();
        ids.add(Long.valueOf(blogId));

        //3.2获取查询中minTime重复的值作为下一轮的offset
        if(time == minTime){
            os++;
        }else{
            minTime = time;
            os = 1;
        }
    }
    if(ids == null || ids.isEmpty()) return Result.ok();
    //4、根据blogId批量查询blog
    String idsStr = StrUtil.join(",", ids);
    List<Blog> blogs = query().in("id", ids).last("order by field(id, " + idsStr + ")").list();

    //5、需要将博客的点赞信息进行完善
    for (Blog blog : blogs) {
        //博客作者的信息
        User blogUser = userService.getById(blog.getId());
        blog.setIcon(blogUser.getIcon());
        blog.setName(blogUser.getNickName());

        //对当前登录的用户是否已经对该博客点赞
        Double isLike = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY + blog.getId(), UserHolder.getUser().getId().toString());
        blog.setIsLike(isLike != null);
    }

    //6、将blog封装并返回
    ScrollResult scrollResult = new ScrollResult();
    scrollResult.setList(blogs);
    scrollResult.setMinTime(minTime);
    scrollResult.setOffset(os);
    return Result.ok(scrollResult);
}

附近商铺

传入参数,有当前商铺id、商铺类型id、用户的个人位置信息

设置具体的redis数据结构为geoset和zset很像:{"shop:geo:typeId":"shopId":"geoscore"}

也就是{key:value:score},score是redis将地理的经纬度转化为一个分值进行存储

注意使用了GEOSearch,redis版本需要在6.2以上

业务流程

一、先将店铺的地理数据导入到redis中

1、查询店铺信息

2、将店铺信息按照typeId进行分组

3、分类型写入redis

​ 3.1、获取类型的id,拼接成key

​ 3.2、获取同类型的店铺集合

​ 3.3、使用geoAdd写入redis中

二、使用redis的GeoSearch进行查询、排序

1、判断是否需要根据坐标查询

2、计算分页参数

3、查询redis,按照举例进行排序、分页,结果以Map<shopId,distance>存储

4、解析出id

​ 4.1、截掉0~from的部分

​ 4.2、获取店铺id

​ 4.3、获取距离

5、查询店铺信息

6、将距离信息放入店铺信息中返回

代码实现

加载地理数据loadShopGeoData()

@Test
public void loadShopGeoData(){
    //1、查询店铺信息
    List<Shop> shopList = shopService.list();
    //2、将店铺信息按照typeId进行分组
    Map<Long, List<Shop>> map = shopList.stream().collect(Collectors.groupingBy(Shop::getTypeId));
    //3、分类型写入redis
    Set<Map.Entry<Long, List<Shop>>> entries = map.entrySet();
    for (Map.Entry<Long, List<Shop>> entry : entries) {
        //3.1、获取类型的id,拼接成key
        String key = SHOP_GEO_KEY + entry.getKey();
        //3.2、获取同类型的店铺集合
        List<Shop> shops = entry.getValue();
        //3.3、使用geoAdd写入redis中
        for (Shop shop : shops) {
            stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(), shop.getY()), shop.getId().toString());
    }

}

根据当前位置坐标查询方圆5000m方位内的店铺
ShopServiceImpl.queryShopByType()

@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
    //1、判断是否需要根据坐标查询
    if(x == null || y == null){
        //不需要坐标进行查询,直接分页
        Page<Shop> page = query()
            .eq("type_id", typeId)
            .page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));
        // 返回数据
        return Result.ok(page.getRecords());
    }
    //2、计算分页参数
    int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
    int end = current * SystemConstants.DEFAULT_PAGE_SIZE;

    String key = SHOP_GEO_KEY + typeId;
    //3、查询redis,按照举例进行排序、分页,结果以Map<shopId,distance>存储
    GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo()  //用geoSearch进行查找指定中心点、查询半径、排序、记录条数等信息
        .search(key,
                GeoReference.fromCoordinate(x, y),
                new Distance(5000),
                //加上redis的一些命令参数,比如查找结果带有距离、分页等等
                RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end)
               );
    //4、解析出id
    if(results == null){
        return Result.ok(Collections.emptyList());
    }
    List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
    //4.1、截掉0~from的部分
    List<Long> ids = new ArrayList<>(list.size());
    Map<String, Distance> distanceMap = new HashMap<>();
    list.stream().skip(from).forEach(result -> {
        //4.2、获取店铺id
        String shopId = result.getContent().getName();
        ids.add(Long.valueOf(shopId));
        //4.3、获取距离
        Distance distance = result.getDistance();

        //将id和distance匹配存放在map中
        distanceMap.put(shopId, distance);
    });
    //5、查询店铺信息
    //根据ids查询店铺数据
    String strIds = StrUtil.join(",", ids);
    List<Shop> shopList = query().in("id", ids).last("order by field(id, " + strIds + ")").list();
    //6、将距离信息放入店铺信息中返回
    for (Shop shop : shopList) {
        shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
    }

    return Result.ok(shopList);
}

标签:Redis,商铺,redis,ids,查询,blog,key,推送,id
From: https://www.cnblogs.com/Gw-CodingWorld/p/16815049.html

相关文章

  • AI安全帽识别/人脸识别智能分析网关何在EasyCVR配置告警信息推送
    智能分析网关是TSINGSEE青犀视频研发的智能硬件设备,部署了全新嵌入式多算法框架软件,可支持AI视频智能分析功能,包括人脸识别、车辆检测及识别、烟火识别、物体识别、行为识......
  • 微信实时线报推送福利
    一时撸线报一时爽,一直撸线报一直爽。通常我们在各大线报报站薅羊毛撸现金,每次点进去之后基本上已经黄了或者改变规则,而你们获得有效的线报少之又少,大水的线报都是别人撸完......
  • Redis处理秒杀并发
     一、现公司解决秒杀方案:1. 利用Redis原子性自增接口incr2. Redis缓存+异步同步数据到数据库优点:解决超卖问题,库存读写都在内存中,故同时解决性能问题。缺点:由于......
  • Fabric8 Docker Maven Plugin 如何让部署的时候执行 Docker 打包推送
    配置方案在下面的这几句话。<executions><execution><id>default</id><phase......
  • 19. redis之scan
    在巨大的数据量的情况下,做类似查找符合某种规则的key的信息主要有两种方法:第一种方法使用keys 命令,然后后面带上正则匹配,比如匹配keys redis* -->匹配以redis开......
  • redis7.0主从+sentinel
    #redis7需要python3环境wgethttps://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xztar-xv Python-3.7.2.tar.xzcdPython-3.7.2./configure--prefix=/usr/l......
  • 通过 Github Action 实现定时推送天气预报
    偶然间,看到GitHubActions教程:定时发送天气邮件-阮一峰的网络日志这篇文章,没错,这个正好能打发自己的折腾之心,也能通过代码给生活引入一些变化。还是在这里简单记录一......
  • .NET Core C#系列之XiaoFeng.Redis组件库础操作篇教程
    XiaoFeng.Redis组件库拥有丰富的API和巅峰之作的性能,下面我就先以基础的操作做如下讲解,循序渐进,慢慢提升。欢迎关注xiaofeng组件库,开源不易,多多支持。开源地址:https://git......
  • 【收藏看】关于面试常见问题:Redis有哪些数据结构?
    面试场景  Redis作为时下最火的缓存中间件之一,面试的时候面试官肯定会问Redis的相关内容,而往往问的第一个问题就是:你知道Redis有哪几种数据结构吗?你们项目中使用......
  • Redisson 大bug处理: Command xxx succesfully sent, but channel xxx has been close
    棘手的bug,无从下手使用redisson的过程中, 我们的应用程序突然报错: org.redisson.client.RedisConnectionClosedException:Command(EXISTS),params:[paypal_fee_i......