欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis實(shí)現(xiàn)好友關(guān)注的示例代碼

 更新時(shí)間:2023年01月29日 15:42:52   作者:卒獲有所聞  
本文主要介紹了Redis實(shí)現(xiàn)好友關(guān)注的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、關(guān)注和取關(guān)

加載的時(shí)候會(huì)先發(fā)請(qǐng)求看是否關(guān)注了,來顯示是關(guān)注按鈕還是取關(guān)按鈕

當(dāng)我們點(diǎn)擊關(guān)注或取關(guān)之后再發(fā)請(qǐng)求進(jìn)行操作

數(shù)據(jù)庫表結(jié)構(gòu)

關(guān)注表(主鍵、用戶id、關(guān)注用戶id)

需求

  • 關(guān)注和取關(guān)接口
  • 判斷是否關(guān)注接口
/**
  * 關(guān)注用戶
  * @param id
  * @param isFollow
  * @return
  */
@PutMapping("/{id}/{isFollow}")
public Result follow(@PathVariable("id") Long id, @PathVariable("isFollow") Boolean isFollow){
    return followService.follow(id,isFollow);
}
 
/**
  * 判斷是否關(guān)注指定用戶
  * @param id
  * @return
  */
@GetMapping("/or/not/{id}")
public Result isFollow(@PathVariable("id") Long id){
    return followService.isFollow(id);
}
/**
  * 關(guān)注用戶
  * @param id 
  * @param isFollow
  * @return
  */
@Override
public Result follow(Long id, Boolean isFollow) {
    //獲取當(dāng)前用戶id
    Long userId = UserHolder.getUser().getId();
    //判斷是關(guān)注操作還是取關(guān)操作
    if(BooleanUtil.isTrue(isFollow)){
        //關(guān)注操作
        Follow follow = new Follow();
        follow.setUserId(userId);
        follow.setFollowUserId(id);
        save(follow);
    }else{
        //取關(guān)操作
        remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",id));
    }
    return Result.ok();
}
 
/**
  * 判斷是否關(guān)注指定用戶
  * @param id
  * @return
  */
@Override
public Result isFollow(Long id) {
    //獲取當(dāng)前用戶id
    Long userId = UserHolder.getUser().getId();
    Integer count = query().eq("user_id", userId).eq("follow_user_id", id).count();
    if(count>0){
        return Result.ok(true);
    }
    return Result.ok(false);
}

二、共同關(guān)注                               

需求:利用redis中恰當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)共同關(guān)注功能,在博主個(gè)人頁面展示當(dāng)前用戶和博主的共同好友

可以用redis中set結(jié)構(gòu)的取交集實(shí)現(xiàn)

 先在關(guān)注和取關(guān)增加存入redis

/**
  * 關(guān)注用戶
  * @param id
  * @param isFollow
  * @return
  */
@Override
public Result follow(Long id, Boolean isFollow) {
    //獲取當(dāng)前用戶id
    Long userId = UserHolder.getUser().getId();
    String key = "follow:" + userId;
    //判斷是關(guān)注操作還是取關(guān)操作
    if(BooleanUtil.isTrue(isFollow)){
        //關(guān)注操作
        Follow follow = new Follow();
        follow.setUserId(userId);
        follow.setFollowUserId(id);
        boolean success = save(follow);
        if(success){
            //插入set集合中
            stringRedisTemplate.opsForSet().add(key,id.toString());
        }
    }else{
        //取關(guān)操作
        boolean success = remove(new QueryWrapper<Follow>().eq("user_id", userId).eq("follow_user_id", id));
        //從set集合中移除
        if(success){
            stringRedisTemplate.opsForSet().remove(key,id.toString());
        }
    }
    return Result.ok();
}

然后就可以開始寫查看共同好友接口了

/**
  * 判斷是否關(guān)注指定用戶
  * @param id
  * @return
  */
@GetMapping("common/{id}")
public Result followCommons(@PathVariable("id") Long id){
    return followService.followCommons(id);
}
/**
  * 共同關(guān)注
  * @param id
  * @return
  */
@Override
public Result followCommons(Long id) {
    Long userId = UserHolder.getUser().getId();
    //當(dāng)前用戶的key
    String key1 = "follow:" + userId;
    //指定用戶的key
    String key2 = "follow:" + id;
    //判斷兩個(gè)用戶的交集
    Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);
    if(intersect==null||intersect.isEmpty()){
        //說明沒有共同關(guān)注
        return Result.ok();
    }
    //如果有共同關(guān)注,則獲取這些用戶的信息
    List<Long> userIds = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
    List<UserDTO> userDTOS = userService.listByIds(userIds).stream().map(item -> (BeanUtil.copyProperties(item, UserDTO.class))).collect(Collectors.toList());
    return Result.ok(userDTOS);
}

三、關(guān)注推送(feed流)

關(guān)注推送也叫做fedd流,直譯為投喂。為用戶持續(xù)的提供"沉浸式"的體驗(yàn),通過無限下拉刷新獲取新的信息。feed模式,內(nèi)容匹配用戶。

Feed流產(chǎn)品有兩種常見模式:

Timeline:不做內(nèi)容篩選,簡(jiǎn)單的按照內(nèi)容發(fā)布時(shí)間排序,常用于好友或關(guān)注。例如朋友圈

  • 優(yōu)點(diǎn):信息全面,不會(huì)有缺失。并且實(shí)現(xiàn)也相對(duì)簡(jiǎn)單
  • 缺點(diǎn):信息噪音較多,用戶不一定感興趣,內(nèi)容獲取效率低

智能排序:利用智能算法屏蔽掉違規(guī)的、用戶不感興趣的內(nèi)容。推送用戶感興趣信息來吸引用戶

  • 優(yōu)點(diǎn):投喂用戶感興趣信息,用戶粘度很高,容易沉迷
  • 缺點(diǎn):如果算法不精準(zhǔn),可能起到反作用

本例中是基于關(guān)注的好友來做Feed流的,因此采用Timeline的模式。

1、Timeline模式的方案

該模式的實(shí)現(xiàn)方案有

  • 拉模式
  • 推模式
  • 推拉結(jié)合 

 拉模式

優(yōu)點(diǎn):節(jié)省內(nèi)存消息,只用保存一份,保存發(fā)件人的發(fā)件箱,要讀的時(shí)候去拉取就行了

缺點(diǎn):每次讀取都要去拉,耗時(shí)比較久

推模式

優(yōu)點(diǎn):延遲低

缺點(diǎn):太占空間了,一個(gè)消息要保存好多遍

推拉結(jié)合模式

推拉結(jié)合分用戶,比如大v很多粉絲就采用推模式,有自己的發(fā)件箱,讓用戶上線之后去拉取。普通人發(fā)的話就用推模式推給每個(gè)用戶,因?yàn)榉劢z數(shù)也不多直接推給每個(gè)人延遲低。粉絲也分活躍粉絲和普通粉絲,活躍粉絲用推模式有主機(jī)的收件箱,因?yàn)樗焯於伎幢乜?,而普通粉絲用拉模式,主動(dòng)上線再拉取,僵尸粉直接不會(huì)拉取,就節(jié)省空間。

總結(jié)

 由于我們這點(diǎn)評(píng)網(wǎng)站,用戶量比較小,所以我們采用推模式(千萬以下沒問題)。

2、推模式實(shí)現(xiàn)關(guān)注推送

需求

(1)修改新增探店筆記的業(yè)務(wù),在保存blog到數(shù)據(jù)庫的同時(shí),推送到粉絲的收件箱

(2)收件箱滿足可以根據(jù)時(shí)間排序,必須用redis的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)

(3)查詢收件箱數(shù)據(jù)時(shí),可以實(shí)現(xiàn)分頁查詢

要進(jìn)行分頁查詢,那么我們存入redis采用什么數(shù)據(jù)類型呢,是list還是zset呢

feed流分頁問題

假如我們?cè)诜猪摬樵兊臅r(shí)候,這個(gè)時(shí)候加了新的內(nèi)容11, 再查詢下一頁的時(shí)候,6就重復(fù)出現(xiàn)了,為了解決這種問題,我們必須使用滾動(dòng)分頁

feed流的滾動(dòng)分頁

滾動(dòng)分頁就是每次都記住最后一個(gè)id,方便下一次進(jìn)行查詢,用這種lastid的方式來記住,不依賴于角標(biāo),所以我們不會(huì)收到角標(biāo)的影響。所以我們不能用list來存數(shù)據(jù),因?yàn)樗蕾囉诮菢?biāo),zset可以根據(jù)分?jǐn)?shù)值范圍查詢。我們按時(shí)間排序,每次都記住上次最小的,然后從比這小的開始。

實(shí)現(xiàn)推送到粉絲的收件箱

修改新增探店筆記的業(yè)務(wù),在保存blog到數(shù)據(jù)庫的同時(shí),推送到粉絲的收件箱

@Override
public Result saveBlog(Blog blog) {
    // 1.獲取登錄用戶
    UserDTO user = UserHolder.getUser();
    blog.setUserId(user.getId());
    // 2.保存探店筆記
    boolean isSuccess = save(blog);
    if(!isSuccess){
        return Result.fail("新增筆記失敗!");
    }
    // 3.查詢筆記作者的所有粉絲 select * from tb_follow where follow_user_id = ?
    List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();
    // 4.推送筆記id給所有粉絲
    for (Follow follow : follows) {
        // 4.1.獲取粉絲id
        Long userId = follow.getUserId();
        // 4.2.推送
        String key = FEED_KEY + userId;
        stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
    }
    // 5.返回id
    return Result.ok(blog.getId());
}

滾動(dòng)分頁接收思路

第一次查詢是分?jǐn)?shù)(時(shí)間)從1000(很大的數(shù))開始到0(最?。┻@個(gè)范圍,然后限制查3個(gè)(一頁數(shù)量),偏移量是0,然后記錄結(jié)尾(上一次的最小值)

以后每次都是從上一次的最小值到0,限定查3個(gè),偏移量是1(因?yàn)橛涗浀哪莻€(gè)值不算),再記錄結(jié)尾的值。

但是有一種情況,如果有相同的時(shí)間,分?jǐn)?shù)一樣的話,比如兩個(gè)6分,而且上一頁都顯示完,我們下一頁是按照第一個(gè)6分當(dāng)結(jié)尾的,第二個(gè)6分可能會(huì)出現(xiàn)的,所以我們這個(gè)偏移量不能固定是1,要看有幾個(gè)和結(jié)尾相同的數(shù),如果是兩個(gè)就得是2,3個(gè)就是3。

滾動(dòng)分頁查詢參數(shù):

  • 最大值:當(dāng)前時(shí)間戳 | 上一次查詢的最小時(shí)間戳
  • 最小值:0
  • 偏移量:0 | 最后一個(gè)值的重復(fù)數(shù)
  • 限制數(shù):一頁顯示的數(shù)

實(shí)現(xiàn)滾動(dòng)分頁查詢

前端需要傳來兩條數(shù)據(jù),分別是lastId和offset,如果是第一次查詢,那么這兩個(gè)值是固定的,會(huì)由前端來指定,lastId是發(fā)起查詢時(shí)的時(shí)間戳,而offset就是零,當(dāng)后端查詢完分頁信息后需要返回三條數(shù)據(jù),第一條自然就是分頁信息,第二條是此次分頁查詢數(shù)據(jù)中最后一條數(shù)據(jù)的時(shí)間戳,第三條信息是偏移量,我們需要在分頁查詢后計(jì)算有多少條信息的時(shí)間戳與最后一條是相同的,作為偏移量來返回。而前端拿到這后兩個(gè)參數(shù)之后就會(huì)分別保存在前端的lastId和offset中,下一次分頁查詢時(shí)就會(huì)將這兩條數(shù)據(jù)作為請(qǐng)求參數(shù)來訪問,然后不斷循環(huán)上述過程,這樣也就實(shí)現(xiàn)了分頁查詢。

定義返回值實(shí)體類

@Data
public class ScrollResult {
    private List<?> list;
    private Long minTime;
    private Integer offset;
}

Controller

@GetMapping("/of/follow")
public Result queryBlogOfFollow(
    @RequestParam("lastId") Long max, @RequestParam(value = "offset", defaultValue = "0") Integer offset){
    return blogService.queryBlogOfFollow(max, offset);
}

BlogServiceImpl

@Override
public Result queryBlogOfFollow(Long max, Integer offset) {
    //獲取當(dāng)前用戶
    Long userId = UserHolder.getUser().getId();
    //組裝key
    String key = RedisConstants.FEED_KEY + userId;
    //分頁查詢收件箱,一次查詢兩條 ZREVRANGEBYSCORE key Max Min LIMIT offset count
    Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);
    //若收件箱為空則直接返回
    if (typedTuples == null || typedTuples.isEmpty()) {
        return Result.ok();
    }
    //通過上述數(shù)據(jù)獲取筆記id,偏移量和最小時(shí)間
    ArrayList<Long> ids = new ArrayList<>();
    long minTime = 0;
    //因?yàn)檫@里的偏移量是下一次要傳給前端的偏移量,所以初始值定為1
    int os = 1;
    for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
        //添加博客id
        ids.add(Long.valueOf(typedTuple.getValue()));
        //獲取時(shí)間戳
        long score = typedTuple.getScore().longValue();
        //由于數(shù)據(jù)是按時(shí)間戳倒序排列的,因此最后被賦值的就是最小時(shí)間
        if (minTime == score) {
            //如果有兩個(gè)數(shù)據(jù)時(shí)間戳相等,那么偏移量開始計(jì)數(shù)
            os++;
        } else {
            //如果當(dāng)前數(shù)據(jù)的時(shí)間戳與已經(jīng)記錄的最小時(shí)間戳不相等,則說明當(dāng)前時(shí)間小于已記錄的最小時(shí)間戳,將其賦給minTime
            minTime = score;
            //偏移量重置
            os = 1;
        }
    }
    //需要考慮到時(shí)間戳相等的消息數(shù)量大于2的情況,這時(shí)候偏移量就需要加上上一頁查詢時(shí)的偏移量
    os = minTime == max ? os : os + offset;
 
    //根據(jù)id查詢blog
    String idStr = StrUtil.join(",", ids);
    //查詢時(shí)需要手動(dòng)指定順序
    List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();
    //這里還需要查詢博客作者的相關(guān)信息,這里對(duì)比視頻中,用一次查詢代替了多次查詢,提高效率
    List<Long> blogUserIds = blogs.stream().map(blog -> blog.getUserId()).collect(Collectors.toList());
    String blogUserIdStr = StrUtil.join(",", blogUserIds);
    HashMap<Long, User> userHashMap = new HashMap<>();
    userService.query().in("id", blogUserIds).last("ORDER BY FIELD(id," + blogUserIdStr + ")").list().
        stream().forEach(user -> {
        userHashMap.put(user.getId(), user);
    });
    //為blog封裝數(shù)據(jù)
    Iterator<Blog> blogIterator = blogs.iterator();
    while (blogIterator.hasNext()) {
        Blog blog = blogIterator.next();
        User user = userHashMap.get(blog.getUserId());
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
        blog.setIsLike(isLikeBlog(blog.getId()));
    }
    //返回封裝數(shù)據(jù)
    ScrollResult scrollResult = new ScrollResult();
    scrollResult.setList(blogs);
    scrollResult.setMinTime(minTime);
    scrollResult.setOffset(os);
    return Result.ok(scrollResult);
}

到此這篇關(guān)于Redis實(shí)現(xiàn)好友關(guān)注的示例代碼的文章就介紹到這了,更多相關(guān)Redis 好友關(guān)注內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Redission的分布式鎖實(shí)戰(zhàn)

    基于Redission的分布式鎖實(shí)戰(zhàn)

    本文主要介紹了基于Redission的分布式鎖實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • window下創(chuàng)建redis出現(xiàn)問題小結(jié)

    window下創(chuàng)建redis出現(xiàn)問題小結(jié)

    這篇文章主要介紹了window下創(chuàng)建redis出現(xiàn)問題總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 基于redis實(shí)現(xiàn)世界杯排行榜功能項(xiàng)目實(shí)戰(zhàn)

    基于redis實(shí)現(xiàn)世界杯排行榜功能項(xiàng)目實(shí)戰(zhàn)

    前段時(shí)間,做了一個(gè)世界杯競(jìng)猜積分排行榜。對(duì)世界杯64場(chǎng)球賽勝負(fù)平進(jìn)行猜測(cè),猜對(duì)+1分,錯(cuò)誤+0分,一人一場(chǎng)只能猜一次。下面通過本文給大家分享基于redis實(shí)現(xiàn)世界杯排行榜功能項(xiàng)目實(shí)戰(zhàn),感興趣的朋友一起看看吧
    2018-10-10
  • redis中如何使用lua腳本讓你的靈活性提高5個(gè)逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個(gè)逼格詳解

    這篇文章主要給大家介紹了關(guān)于redis中如何使用lua腳本讓你的靈活性提高5個(gè)逼格的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • 為啥懶 Redis 是更好的 Redis

    為啥懶 Redis 是更好的 Redis

    本文是由zicode, 李中凱, 無若翻譯的英文文章Lazy Redis is better Redis,小編認(rèn)為非常不錯(cuò),這里推薦給大家
    2018-07-07
  • redis哨兵模式說明與搭建詳解

    redis哨兵模式說明與搭建詳解

    這篇文章主要介紹了redis哨兵模式說明與搭建詳解,需要的朋友可以參考下
    2023-01-01
  • NestJS+Redis實(shí)現(xiàn)手寫一個(gè)限流器

    NestJS+Redis實(shí)現(xiàn)手寫一個(gè)限流器

    限流是大型系統(tǒng)必備的保護(hù)措施,本文將結(jié)合redis , lua 腳本 以及 Nestjs Guard 來實(shí)現(xiàn) 限流的效果,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11
  • redis實(shí)現(xiàn)分布式全局唯一id的示例代碼

    redis實(shí)現(xiàn)分布式全局唯一id的示例代碼

    在某些場(chǎng)景中,我們需要生成全局的唯一ID,本文主要介紹了redis實(shí)現(xiàn)分布式全局唯一id的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-04-04
  • Redis分布式鎖的實(shí)現(xiàn)方式(redis面試題)

    Redis分布式鎖的實(shí)現(xiàn)方式(redis面試題)

    這篇文章主要介紹了Redis分布式鎖的實(shí)現(xiàn)方式(面試常見),需要的朋友可以參考下
    2020-01-01
  • 完美解決linux上啟動(dòng)redis后配置文件未生效的問題

    完美解決linux上啟動(dòng)redis后配置文件未生效的問題

    今天小編就為大家分享一篇完美解決linux上啟動(dòng)redis后配置文件未生效的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05

最新評(píng)論