好友关注
1.关注和取关
/**
* 关注或取关
* @param followUserId
* @param isFollow true:关注, false:取关
* @return
*/
@Override
public Result follow(Long followUserId, Boolean isFollow) {
// 1.得到当前用户id
Long userId = UserHolder.getUser().getId();
if(userId == null) {
return Result.fail("未登录!");
}
// 判断关注或取关
if(isFollow){
// 2.关注,新增数据
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(followUserId);
save(follow);
}else{
// 3.取关,删除数据
remove(new QueryWrapper<Follow>()
.eq("user_id", userId).eq("follow_user_id", followUserId));
}
return Result.ok();
}
/**
* 判断是否关注
* @param followUserId
* @return
*/
@Override
public Result isFollow(Long followUserId) {
// 1.获取当前用户id
Long userId = UserHolder.getUser().getId();
// 2.查询是否关注
Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
// 3.判断
boolean isFollow = count != null && count > 0;
return Result.ok(isFollow);
}
2.共同关注

@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private IUserService userService;
/**
* 关注或取关
* @param followUserId
* @param isFollow true:关注, false:取关
* @return
*/
@Override
public Result follow(Long followUserId, Boolean isFollow) {
// 1.得到当前用户id
Long userId = UserHolder.getUser().getId();
if(userId == null) {
return Result.fail("未登录!");
}
// Redis中set的key
String key = "follows:" + userId;
// 判断关注或取关
if(isFollow){
// 2.关注,新增数据
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(followUserId);
boolean isSuccess = save(follow);
if(isSuccess){
// 把关注用户的id,放入Redis的set中 sadd key followedUserId
stringRedisTemplate.opsForSet().add(key, followUserId.toString());
}
}else{
// 3.取关,删除数据
boolean isSuccess = remove(new QueryWrapper<Follow>()
.eq("user_id", userId).eq("follow_user_id", followUserId));
if(isSuccess){
// 把关注用户的id,在Redis的set中移除 srem key followedUserId
stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
}
}
return Result.ok();
}
/**
* 判断是否关注
* @param followUserId
* @return
*/
@Override
public Result isFollow(Long followUserId) {
// 1.获取当前用户id
Long userId = UserHolder.getUser().getId();
// 2.查询是否关注
// 2.1.先查Redis中的set
String key = "follows:" + userId;
Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, followUserId.toString());
if(isMember){
return Result.ok(true);
}
// 2.2.后查DB
Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
// 3.判断
boolean isFollow = count != null && count > 0;
if(isFollow){
stringRedisTemplate.opsForSet().add(key, followUserId.toString());
}
return Result.ok(isFollow);
}
/**
* 查询共同关注
* @param id
* @return
*/
@Override
public Result common(Long id) {
// 1.获取当前用户id
Long userId = UserHolder.getUser().getId();
// 2.获取当前用户Redis中set的key
String key1 = "follows:" + userId;
// 3.获取目标用户Redis中set的key
String key2 = "follows:" + id;
// 4.查询共同集合
Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);
if(intersect == null || intersect.isEmpty()){
// 无交集
return Result.ok(Collections.emptyList());
}
// 5.解析id集合
List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
// 6.查询用户
List<UserDTO> userDTOS = userService.listByIds(ids)
.stream()
.map(user -> BeanUtil.copyProperties(user, UserDTO.class))
.collect(Collectors.toList());
// 7.返回交集
return Result.ok(userDTOS);
}
}
3.关注推送

- 实现方式



- 分页问题


推送到粉丝收件箱
/**
* 发布博客
* @param blog
* @return
*/
@Override
public Result saveBlog(Blog blog) {
// 1.获取登录用户
UserDTO user = UserHolder.getUser();
blog.setUserId(user.getId());
// 2.保存探店博文
boolean isSave = save(blog);
if(!isSave){
return Result.fail("新增博文失败!");
}
// 3.查询博文作者的所有粉丝
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:" + userId;
stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
}
// 5.返回博文id
return Result.ok(blog.getId());
}
滚动分页查询收件箱

@Data
public class ScrollResult {
// 分页结果列表
private List<?> list;
// 下次查询的最大时间戳
private Long minTime;
// 与上次查询最小值一样的个数
private Integer offset;
}
/**
* 查询关注用户的博客
* @param max
* @param offset
* @return
*/
@GetMapping("/of/follow")
public Result queryBlogOfFollow(
@RequestParam("lastId") Long max,
@RequestParam(value = "offset", defaultValue = "0") Integer offset){
return blogService.queryBlogOfFollow(max, offset);
}
/**
* 查询关注用户的博客
* @param max
* @param offset
* @return
*/
@Override
public Result queryBlogOfFollow(Long max, Integer offset) {
// 1.获取当前用户
Long userId = UserHolder.getUser().getId();
// 2.查询当前用户的信箱 ZREVRANGEBYSCORE z1 Max Min LIMIT offset count
String key = FEED_KEY + userId;
Set<ZSetOperations.TypedTuple<String>> typedTuples =
stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);
// 非空判断
if(typedTuples == null || typedTuples.isEmpty()){
return Result.ok();
}
// 3.解析数据:BlogId, minTime, offset
long minTime = 0;
int nextOffset = 1;
List<Long> ids = new ArrayList<>(typedTuples.size());
for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {
// 3.1.获取id,加入集合
ids.add(Long.valueOf(tuple.getValue()));
// 3.2.获取分数,并统计下次查询的offset
long time = tuple.getScore().longValue();
if(time == minTime){
nextOffset ++;
}else{
minTime = time;
nextOffset = 1;
}
}
// 4.根据id查询Blog(不能使用listByIds,'in'问题)
String idStr = StrUtil.join(",", ids);
List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id, " + idStr + ")").list()
.stream().map(blog -> {
queryBlogUser(blog);
isBlogLiked(blog);
return blog;
})
.collect(Collectors.toList());
// 5.封装返回
ScrollResult r = new ScrollResult();
r.setList(blogs);
r.setOffset(nextOffset);
r.setMinTime(minTime);
return Result.ok(r);
}