1.友達がフォローする
ストアの写真とテキストの詳細ページでは、メモを公開した著者をフォローできます。
Tandian のノート詳細ページに入ると、ユーザーをフォローしたかどうかを判断するリクエストと、ユーザーのリクエストをフォローしようとするリクエストの 2 つが発行されます。
Follow はユーザー間の関係、ブロガーとファンの関係です。データベースには次のことを示すtb_followテーブルがあります。
userId は現在ログインしているユーザー ID、follow_user_id はフォローされているユーザー ID です。
対応するインターフェイスを実装します。
1. 以下の点に注意を払う必要があるかどうかを決定します。
コントローラー層:
@GetMapping("/or/not/{id}")
public Result isFollow(@PathVariable("id") Long followUserId) {
return followService.isFollow(followUserId);
}
サービス層:
@Override
public Result isFollow(Long followUserId) {
// 1.获取登录用户
Long userId = UserHolder.getUser().getId();
// 2.查询是否关注 select count(*) from tb_follow where user_id = ? and follow_user_id = ?
Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
// 3.判断
return Result.ok(count > 0);
}
2. ビジネス インターフェイスをフォローします。すでにフォローしている場合は、userid (現在ログインしているユーザーの ID) やフォローされているユーザー ID (follow_user_id) などのデータをデータベースに追加します。
気にならない場合はデータを削除してください。
コントローラー層:
//关注
@PutMapping("/{id}/{isFollow}")
public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow) {
return followService.follow(followUserId, isFollow);
}
サービス層:
关注service
@Override
public Result follow(Long followUserId, Boolean isFollow) {
// 1.获取登录用户
Long userId = UserHolder.getUser().getId();
// 1.判断到底是关注还是取关
if (isFollow) {
// 2.关注,新增数据
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(followUserId);
boolean isSuccess = save(follow);
} else {
// 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?
remove(new QueryWrapper<Follow>()
.eq("user_id", userId).eq("follow_user_id", followUserId));
}
return Result.ok();
}
2. 友人の注意を共有する
要件: 共通アテンション機能を実現するには、Redis で適切なデータ構造を使用します。ブロガーの個人ページには、現在のユーザーとブロガーの共通の懸念事項が表示されます。
Redis のセット コレクションを使用して、各ユーザーがフォローするユーザー ID、つまり follow_user_id をセット コレクションに保存できます。各ユーザーはブロガーをフォローするセット コレクションに対応し、それらの間の共通部分を取得します。従うこと。
したがって、最初にユーザーをフォローするビジネス ロジックを変換し、ユーザーが注目するたびにフォローするユーザーを Set コレクションに保存する必要があります。
ビジネス変革に焦点を当てる:
@Override
public Result follow(Long followUserId, Boolean isFollow) {
// 1.获取登录用户
Long userId = UserHolder.getUser().getId();
String key = "follows:" + userId;
// 1.判断到底是关注还是取关
if (isFollow) {
// 2.关注,新增数据
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(followUserId);
boolean isSuccess = save(follow);
if (isSuccess) {
// 把关注用户的id,放入redis的set集合 sadd userId followerUserId
stringRedisTemplate.opsForSet().add(key, followUserId.toString());
}
} else {
// 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?
boolean isSuccess = remove(new QueryWrapper<Follow>()
.eq("user_id", userId).eq("follow_user_id", followUserId));
if (isSuccess) {
// 把关注用户的id从Redis集合中移除
stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
}
}
return Result.ok();
}
次に、共通の関心事インターフェイスを実装できます。
コントローラー層:
@GetMapping("/common/{id}")
public Result followCommons(@PathVariable("id") Long followUserId){
return followService.followCommons(followUserId);
}
サービス層:
1. Redis で現在のユーザー ID とターゲット ユーザー ID のキーを取得します。
2. セット内の交差メソッドを使用して結果を取得します
3. 結果を解析し、ID の組み合わせを取得します。
4. ID の組み合わせを通じてユーザーの組み合わせを取得します。
5. ユーザーの組み合わせをフロントエンドに戻します。
@Override
public Result followCommons(Long id) {
// 1.获取当前用户
Long userId = UserHolder.getUser().getId();
String key = "follows:" + userId;
// 2.求交集
String key2 = "follows:" + id;
Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);
if (intersect == null || intersect.isEmpty()) {
// 无交集
return Result.ok(Collections.emptyList());
}
// 3.解析id集合
List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
// 4.查询用户
List<UserDTO> users = userService.listByIds(ids)
.stream()
.map(user -> BeanUtil.copyProperties(user, UserDTO.class))
.collect(Collectors.toList());
return Result.ok(users);
}
共通の焦点は関数の実装です。
3. 押し込み(送りの流れ)に注意
フォロープッシュはフィードフローとも呼ばれ、直訳するとフィードとなります。ユーザーに「没入型」体験を継続的に提供し、無限のプルダウン更新を通じて新しい情報を取得します。
従来のプッシュモードはユーザーが自分でコンテンツを検索するものですが、フィードがコンテンツ情報を基に該当するユーザーを取得して見つけてプッシュします。
フィードストリーム製品には 2 つの一般的なパターンがあります。
タイムライン: コンテンツの選別はなく、単にコンテンツのリリース時間によって並べ替えられており、友人やフォロワーに対してよく使用されます。たとえば友達の輪
-
利点: 包括的な情報があり、見逃すことはありません。そして実装は比較的簡単です
-
デメリット:情報ノイズが多く、ユーザーの興味が薄い可能性があり、コンテンツ取得効率が低い
インテリジェントな並べ替え: インテリジェントなアルゴリズムを使用して、規制に違反し、ユーザーの興味のないコンテンツをブロックします。ユーザーが興味のある情報をプッシュしてユーザーを惹きつける
-
利点: ユーザーが興味のある情報をフィードする、ユーザーの粘度が非常に高い、中毒になりやすい
-
短所: アルゴリズムが正確でない場合、逆効果になる可能性があります。
ここではプッシュに焦点を当て、最初のタイプの タイムラインを使用します。
このモードを実装するには、次の 3 つの方法もあります。
1.プルモード
読み取り拡散とも呼ばれます。各ブロガーがブログ情報を送信した後、送信ボックスを持っていることを意味し、ファンが自分の情報を読みたいとき、この送信ボックスから自分の受信ボックスに情報をプルして読むことになるため、プルモードと呼ばれます。
デメリット:遅延が発生する可能性がある
2. プッシュモード
書き込み拡散とも呼ばれます。ブロガーがブログ情報を公開すると、そのブログがファンの受信箱に直接プッシュされます。この方法では、プル モードの遅延の問題は解決されますが、メモリの問題も発生します。
3. プッシュプルの組み合わせ
読み取り/書き込み混合とも呼ばれるこのモードには、プッシュ モードとプル モードの両方の利点があります。
現在、大物 V ブロガーを同時にフォローしているファンが 3 人、一般ブロガーの張三さんをフォローしているファンが 2 人います。
このとき、Zhang San はブログ情報を送信しましたが、彼はファン層が少ない一般のブロガーであるため、メモリの問題を心配する必要がないため、プッシュ モードを使用して直接ファンに送信します。
ビッグ V ブロガーの場合、ファンはアクティブ ファンと一般ファンに分かれており、アクティブ ファンはプッシュ モードを使用し、一般ファンはプル モードを使用して受信トレイにメッセージを送ります。
これら 3 つの実装モードを比較してみましょう。
ここでは、プッシュモードを使用してフレンドプッシュ機能を実装します。
ページネーションの質問は次のとおりです。
フィードストリーム内のデータは継続的に更新されるため、データの字幕も変更されるため、従来のページネーション モードは使用できません。
従来のページング モード:
したがって、フィードストリームのスクロール ページネーションを採用する必要があります。
1. まず、ショップ探索メモを追加するビジネスを見直し、ブログをデータベースに保存しながらファンの受信箱にプッシュします。
@Override
public Result saveBlog(Blog blog) {
// 获取登录用户
UserDTO user = UserHolder.getUser();
blog.setUserId(user.getId());
// 保存探店博文
boolean isSuccess = save(blog);
if (!isSuccess){
//没有保存成功,返回错误信息
return Result.fail("新增失败!");
}
//成功,将博客信息放到粉丝收件箱
//1.先获取粉丝群体 select * from tb_follow where follow_user_id = ?
QueryChainWrapper<Follow> follows = followService.query().eq("follow_user_id", user.getId());
//2.将消息推送至粉丝收件箱
String key = "feeds:"+user.getId();
stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());
// 返回id
return Result.ok(blog.getId());
}