介绍
本文将介绍如何通过redis,mysql数据库实现用户关注,以及查看共同关注的功能。、
前置准备
创建一个tb_follow表,用于记录关注信息。

以及一个redis数据库。
实现思路
主要实现3个接口:
- isFollow接口(进入up主页时调用):判断用户是否关注了当前up主,如果已经关注,返回true,否则返回false。
- follow接口(点击关注按钮时调用):如果已经关注了该up,则取关,反之,关注up。
- followCommons接口(点击共同关注按钮时调用):查看你和up都关注了哪些用户。
isFollow
controller
接收请求参数id(up主id),调用service中的业务逻辑。
java
@GetMapping("/or/not/{id}")
public Result isFollow(@PathVariable Long id) {
return followService.isFollow(id);
}
service
java
@Override
public Result isFollow(Long id) {
Long userId = UserHolder.getUser().getId();
long count = query().eq("user_id", userId).eq("follow_user_id", id).count();
return Result.ok(count > 0);
}
业务逻辑 :
从上下文(threadlocal)中获取当前用户id,根据用户id和up主id在tb_follow中查询数据,返回数据count > 0,如果有数据则为true,否则为false。
follow
controller
接收请求参数id(up主id),isFollow(true代表执行关注,false代表执行取关),调用service中的业务逻辑。
java
@PutMapping("{id}/{isFollow}")
public Result follow(@PathVariable Long id, @PathVariable Boolean isFollow) {
return followService.follow(id, isFollow);
}
service
java
@Override
public Result follow(Long id, Boolean isFollow) {
Long userId = UserHolder.getUser().getId();
//判断当前用户是否关注
String key = "follow:" + userId;
if (isFollow) {
//关注,新增数据
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(id);
boolean isSuccess = save(follow);
if (isSuccess) {
//把关注用户的id放入redis的set集合中,key:follow:userId value:followUserId
stringRedisTemplate.opsForSet().add(key, id.toString());
}
} else {
//取关,删除数据
boolean isSuccess = update().eq("user_id", userId).eq("follow_user_id", id).remove();
if (isSuccess) {
//把关注用户的id从redis的set集合中移除
stringRedisTemplate.opsForSet().remove(key, id.toString());
}
}
return Result.ok();
}
业务逻辑:
- 从上下文(threadlocal)中获取当前用户id。
- 执行关注或取关
关注:将信息写入follow实体(属性与tb_follow表字段一致),并写入数据库,如果操作成功,再放入redis的set集合中。
取关:删除数据库信息,如果成功,再移除redis的set集合中的id。
followCommons
controller
接收请求参数id(当前查看up主的id)。
java
@GetMapping("/common/{id}")
public Result followCommons(@PathVariable Long id) {
return followService.followCommons(id);
}
service
java
@Override
public Result followCommons(Long id) {
Long userId = UserHolder.getUser().getId();
String myKey = "follow:" + userId;
String targetKey = "follow:" + id;
//求交集
Set<String> intersect = stringRedisTemplate.opsForSet().intersect(myKey, targetKey);
if (intersect == null || intersect.isEmpty()) {
//没有共同关注
return Result.ok("没有共同关注");
}
//解析id集合
List<Long> commonIds = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
//查询用户
List<User> users = userService.listByIds(commonIds);
//转换为DTO
List<UserDTO> userDTOS = users
.stream()
.map(user -> BeanUtil.copyProperties(user, UserDTO.class))
.collect(Collectors.toList());
return Result.ok(userDTOS);
}
业务逻辑:
- 获取两者在redis中的key。
- 求两者交集。
交集为空->直接返回结果
有交集->解析出id集合 - 根据id集合查询用户,返回数据。
核心要点:
关注时将id放入redis中的set集合,优点:
| 查询效率 | Redis Set 的 SINTER 命令可以一次求出交集,时间复杂度 O(min(n,m)),非常高效 |
|---|---|
| 去重 | Set 自动去重,保证关注列表不重复 |
| 内存优化 | 相比数据库查询,Redis 内存操作快几十倍 |