当前位置:网站首页>11、Redis实现关注、取消关注以及关注和粉丝列表
11、Redis实现关注、取消关注以及关注和粉丝列表
2022-07-31 02:36:00 【A snicker】
实现关注、取消关注
- value的数据类型时zset,有序集合,按照关注的时间排序
followee:userId:entityType -> zset(entityId,now) 某个用户关注的实体(实体包括帖子、评论、用户),按照实体分别存
follower:entityType:entityId -> zset(userId,now) 某个实体拥有的粉丝(实体包括帖子、评论、用户)
- 这个功能时异步请求
1、RedisKeyUtil
private static final String PREFIX_FOLLOWEE = "followee";
private static final String PREFIX_FOLLOWER = "follower";
// 某个用户关注的实体(实体包括帖子、用户)
// followee:userId:entityType -> zset(entityId,now)
public static String getFolloweeKey(int userId, int entityType) {
return PREFIX_FOLLOWEE + SPLIT + userId + SPLIT + entityType;
}
// 某个实体拥有的粉丝(实体包括帖子、用户)
// 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);
}
// 查询当前用户是否已关注该实体(该用户对这类实体的id有一个清单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);
}
边栏推荐
- 19. Support Vector Machines - Intuitive Understanding of Optimization Objectives and Large Spacing
- FPGA-based vending machine
- 跨专业考研难度大?“上岸”成功率低?这份实用攻略请收下!
- To write good test cases, you must first learn test design
- String为什么不可变?
- cudaMemcpy学习笔记
- AtCoder Beginner Contest 261 部分题解
- Between two orderly array of additive and Topk problem
- 基于opencv实现人脸检测
- Clustering index, and what is the difference between a clustering index
猜你喜欢
The real CTO is a technical person who understands products
公司官网建站笔记(六):域名进行公安备案并将备案号显示在网页底部
LeetCode 1161 最大层内元素和[BFS 二叉树] HERODING的LeetCode之路
What level of software testing does it take to get a 9K job?
How to design the changing system requirements
ShardingJDBC usage summary
Fiddler captures packets to simulate weak network environment testing
Huawei od dice js
Static route analysis (the longest mask matching principle + active and standby routes)
STP选举(步骤+案列)详解
随机推荐
Verify the integer input
Validate XML documents
Linux下redis7的安装,启动与停止
Brute Force/Adjacency List Breadth First Directed Weighted Graph Undirected Weighted Graph
User interaction + formatted output
【shell基础】判断目录是否为空
Classic linked list OJ strong training problem - fast and slow double pointer efficient solution
Huawei od dice js
The comprehensive result of the case statement, do you know it?[Verilog Advanced Tutorial]
First acquaintance with C language -- array
Hanyuan Hi-Tech 8-channel HDMI integrated multi-service high-definition video optical transceiver 8-channel HDMI video + 8-channel two-way audio + 8-channel 485 data + 8-channel E1 + 32-channel teleph
【Bank Series Phase 1】People's Bank of China
Fiddler captures packets to simulate weak network environment testing
MPPT solar charge controller data collection - through the gateway acquisition capacity battery SOC battery voltage, wi-fi
Mathematics to solve the problem - circular linked list
Layer 2 broadcast storm (cause + judgment + solution)
系统需求多变如何设计
Tower of Hanoi problem
关于 mysql8.0数据库中主键位id,使用replace插入id为0时,实际id插入后自增导致数据重复插入 的解决方法
Static routing + PAT + static NAT (explanation + experiment)