使用Redis 中的set类型实现关注功能呢
关注取关
@Autowired
StringRedisTemplate stringRedisTemplate;
private static final String FOLLOW_KEY_PREFIX = "follow:user:";
/**
* 关注和取关
*
* @param followUserId 关注和取关的userId
* @param isFollow 关注 ,取关 true是关注,false是取关
* @param userId 自己的userId
*/
public void follow(Long followUserId, Boolean isFollow, Long userId) {
String key = FOLLOW_KEY_PREFIX + userId;
if (isFollow) {
stringRedisTemplate.opsForSet().add(key, followUserId.toString());
} else {
stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
}
}
是否关注
/**
* 是否已经关注
*
* @param followUserId 关注和取关的userId
* @param userId 自己的userId
* @return true已关注,false未关注
*/
public boolean isFollow(Long followUserId, Long userId) {
String key = FOLLOW_KEY_PREFIX + userId;
Boolean member = stringRedisTemplate.opsForSet().isMember(key, followUserId);
return BooleanUtil.isTrue(member);
}
共同关注的好友
/**
* 获取共同关注的好友
* @param friendId 好友的id
* @param userId 我的Id
* @return 所有的共同好友
*/
public Set<User> commonFollow(Long friendId, Long userId) {
String myKey = FOLLOW_KEY_PREFIX + userId;
String friendKey = FOLLOW_KEY_PREFIX + friendId;
Set<String> intersect = stringRedisTemplate.opsForSet().intersect(myKey, friendKey);
if (CollectionUtil.isEmpty(intersect)){
return null;
}
List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
//模拟数据库根绝userId 集合查询User对象
Set<User> userSet = new HashSet<>();
return userSet;
}
关注推送
当被关注的人发朋友圈时,推送给关注的人内容
方便起见使用推模式
@Component
public class BlogService {
@Autowired
StringRedisTemplate stringRedisTemplate;
/**
* 保存自己发送的朋友圈, 推送到粉丝的收件箱中
*
* @param blog
*/
public void saveBlog(Blog blog) {
//1.模拟保存数据库
//saveBolg(blog);
//2.保存完了将id发送给粉丝
//从数据库查询关系表中followId为 userId的所有userId
//select * from follow where follow_id = ?
List<Long> users = new ArrayList<>();
for (Long userId : users) {
String key = "feed:" + userId;
stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
}
}
/**
* 查看自己收件箱的朋友圈信息,不是一次全部查出来,根绝传入的参数,获取时间戳小于max,跳过offset个后的3条数据
* 这样查询的目的,如果按照角标查询,如果在查询的过程中,又有新的数据插入,则角标变化,导致查询到重复数据,则使用以下方式
* 当第一次查询时max = System.currentTimeMillis(), offset =0的三条记录
* 当第二次时,max = 第一次返回的maxTime, offset 为第一次返回的os记录.
* 这样就可以避免查询到重复的数据
* 在查询过程中,如果想获取最新的第一条数据,则,max = System.currentTimeMillis(), offset =0 就可以查询到最新的朋友圈
* @param max 查询时间戳小于max的记录
* @param offset 在时间戳小于max后跳过几条记录
* @param userId 查询谁的收件箱
* @return ScrollResult
* data为朋友圈详情
* maxTime 查询时间戳的最大值限制
* offset 跳过的个数
*/
public ScrollResult getFriendsBlog(Long max, Integer offset, Long userId) {
//查询收件箱
String key = "feed:" + userId;
Set<ZSetOperations.TypedTuple<String>> typedTuples =
stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 3);
if (CollectionUtil.isEmpty(typedTuples)) {
return null;
}
//解析数据: blogId , minTime(时间戳), offset--跟最小值相同的个数
List<Long> ids = new ArrayList<>(typedTuples.size());
long minTime = 0;
int os = 1;
for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {
//获取id
ids.add(Long.valueOf(tuple.getValue()));
//获取分数(时间戳)
long time = tuple.getScore().longValue();
if (minTime == time) {
os++;
} else {
minTime = time;
os = 1;
}
}
//存在一个问题,ids的顺序虽然符合需求的,但是数据库查询时in(...)时,就会乱,则
//sql 改为: select * from Blog where id (5,1) order by FIELD(id , 5,1)
List<Blog> list = new ArrayList<>();
ScrollResult scrollResult = new ScrollResult(list, minTime, os);
return scrollResult;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class ScrollResult {
List<?> data;
long maxTime;
int offset;
}