当前位置:网站首页>11. Redis implements follow, unfollow, and follow and follower lists
11. Redis implements follow, unfollow, and follow and follower lists
2022-07-31 02:40:00 【A snicker】
实现关注、取消关注

- value的数据类型时zset,有序集合,Sort by time of interest
followee:userId:entityType -> zset(entityId,now) 某个用户关注的实体(Entities include posts、评论、用户),Stored separately by entity
follower:entityType:entityId -> zset(userId,now) 某个实体拥有的粉丝(Entities include posts、评论、用户)
- This function is requested asynchronously
1、RedisKeyUtil
private static final String PREFIX_FOLLOWEE = "followee";
private static final String PREFIX_FOLLOWER = "follower";
// 某个用户关注的实体(Entities include posts、用户)
// followee:userId:entityType -> zset(entityId,now)
public static String getFolloweeKey(int userId, int entityType) {
return PREFIX_FOLLOWEE + SPLIT + userId + SPLIT + entityType;
}
// 某个实体拥有的粉丝(Entities include posts、用户)
// follower:entityType:entityId -> zset(userId,now)
public static String getFollowerKey(int entityType, int entityId) {
return PREFIX_FOLLOWER + SPLIT + entityType + SPLIT + entityId;
}
2、FollowService
@Service
public class FollowService {
@Autowired
private RedisTemplate redisTemplate;
public void follow(int userId, int entityType, int entityId) {
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
// 某个用户关注的实体
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
// 某个实体拥有的粉丝
String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
operations.multi();
operations.opsForZSet().add(followeeKey, entityId, System.currentTimeMillis());
operations.opsForZSet().add(followerKey, userId, System.currentTimeMillis());
return operations.exec();
}
});
}
public void unfollow(int userId, int entityType, int entityId) {
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
operations.multi();
operations.opsForZSet().remove(followeeKey, entityId);
operations.opsForZSet().remove(followerKey, userId);
return operations.exec();
}
});
}
// 查询关注的实体的数量
public long findFolloweeCount(int userId, int entityType) {
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
return redisTemplate.opsForZSet().zCard(followeeKey);
}
// 查询实体的粉丝的数量
public long findFollowerCount(int entityType, int entityId){
String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
return redisTemplate.opsForZSet().zCard(followerKey);
}
// 查询当前用户是否已关注该实体(the user to such entitiesid有一个清单zset)
public boolean hasFollowed(int userId, int entityType, int entityId) {
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
return redisTemplate.opsForZSet().score(followeeKey, entityId) != null;
}
}
3、profile.js
$(function(){
$(".follow-btn").click(follow);
});
function follow() {
var btn = this;
if($(btn).hasClass("btn-info")) {
// 关注TA
$.post(
CONTEXT_PATH + "/follow",
{
"entityType":3,"entityId":$(btn).prev().val()},
function(data) {
data = $.parseJSON(data);
if(data.code == 0) {
window.location.reload();
} else {
alert(data.msg);
}
}
);
// $(btn).text("已关注").removeClass("btn-info").addClass("btn-secondary");
} else {
// 取消关注
$.post(
CONTEXT_PATH + "/unfollow",
{
"entityType":3,"entityId":$(btn).prev().val()},
function(data) {
data = $.parseJSON(data);
if(data.code == 0) {
window.location.reload();
} else {
alert(data.msg);
}
}
);
//$(btn).text("关注TA").removeClass("btn-secondary").addClass("btn-info");
}
}
关注列表和粉丝列表

1、FollowService
// 查询某用户关注的人
public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) {
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);
if (targetIds == null) {
return null;
}
List<Map<String, Object>> list = new ArrayList<>();
for (Integer targetId : targetIds) {
Map<String, Object> map = new HashMap<>();
User user = userService.findUserById(targetId);
map.put("user", user);
Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
map.put("followTime", new Date(score.longValue()));
list.add(map);
}
return list;
}
// 查询某用户的粉丝
public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit + 1);
if (targetIds == null) {
return null;
}
List<Map<String, Object>> list = new ArrayList<>();
for (Integer targetId : targetIds) {
Map<String, Object> map = new HashMap<>();
User user = userService.findUserById(targetId);
map.put("user", user);
Double score = redisTemplate.opsForZSet().score(followerKey, targetId);
map.put("followTime", new Date(score.longValue()));
list.add(map);
}
return list;
}
2、FollowController
@RequestMapping(path = "/followees/{userId}", method = RequestMethod.GET)
public String getFollowees(@PathVariable("userId") int userId, Page page, Model model) {
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("该用户不存在!");
}
model.addAttribute("user", user);
page.setLimit(5);
page.setPath("/followees/" + userId);
page.setRows((int) followService.findFolloweeCount(userId, ENTITY_TYPE_USER));
List<Map<String, Object>> userList = followService.findFollowees(userId, page.getOffset(), page.getLimit());
if (userList != null) {
for (Map<String, Object> map : userList) {
User u = (User) map.get("user");
map.put("hasFollowed", hasFollowed(u.getId()));
}
}
model.addAttribute("users", userList);// 里面存了user、followTime、hasFollowed
return "/site/followee";
}
@RequestMapping(path = "/followers/{userId}", method = RequestMethod.GET)
public String getFollowers(@PathVariable("userId") int userId, Page page, Model model) {
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("该用户不存在!");
}
model.addAttribute("user", user);
page.setLimit(5);
page.setPath("/followers/" + userId);
page.setRows((int) followService.findFollowerCount(ENTITY_TYPE_USER, userId));
List<Map<String, Object>> userList = followService.findFollowers(userId, page.getOffset(), page.getLimit());
if (userList != null) {
for (Map<String, Object> map : userList) {
User u = (User) map.get("user");
map.put("hasFollowed", hasFollowed(u.getId()));
}
}
model.addAttribute("users", userList);
return "/site/follower";
}
private boolean hasFollowed(int userId) {
if (hostHolder.getUser() == null) {
return false;
}
return followService.hasFollowed(hostHolder.getUser().getId(), ENTITY_TYPE_USER, userId);
}
边栏推荐
- golang GUI for nuxui — HelloWorld
- mysql view
- coldfusion8 background scheduled tasks take shell
- Refuse to work overtime, a productivity tool set developed by programmers
- 直播预告 | KDD2022博士论文奖冠亚军对话
- 【shell基础】判断目录是否为空
- Static route analysis (the longest mask matching principle + active and standby routes)
- The whole process scheduling, MySQL and Sqoop
- execsnoop 工具
- TCP/IP四层模型
猜你喜欢

Coldfusion file read holes (CVE - 2010-2861)

mmdetection trains a model related command

汉源高科8路HDMI综合多业务高清视频光端机8路HDMI视频+8路双向音频+8路485数据+8路E1+32路电话+4路千兆物理隔离网络

Real-time image acquisition based on FPGA

【银行系列第一期】中国人民银行

数学解决——环形链表问题

The application of AI in the whole process of medical imaging equipment

YOLOV5学习笔记(三)——网络模块详解

StringJoiner in detail

Intranet Infiltration - Privilege Escalation
随机推荐
16. Registration Center-consul
Real-time image acquisition based on FPGA
JetPack组件Databinding
Live Preview | KDD2022 Doctoral Dissertation Award Champion and Runner-up Dialogue
String为什么不可变?
The Sad History of Image Processing Technology
Layer 2 broadcast storm (cause + judgment + solution)
英特尔软硬优化,赋能东软加速智慧医疗时代到来
图解lower_bound&upper_bound
php 网站的多语言设置(IP地址区分国内国外)
AI中的数学思想
10、Redis实现点赞(Set)和获取总点赞数
Drools basic introduction, introductory case, basic syntax
多线程下类对象的服务承诺探讨
AtCoder Beginner Contest 261 部分题解
CMOS和TTL的区别?
19.支持向量机-优化目标和大间距直观理解
The effective square of the test (one question of the day 7/29)
JS 函数 this上下文 运行时点语法 圆括号 数组 IIFE 定时器 延时器 self.备份上下文 call apply
CentOS7下mysql5.7.37的安装【完美方案】