一、关注、取关功能代码详解
这是一套完整的用户关注/取关业务实现,包含「关注/取消关注」和「查询关注状态」两个核心接口,采用MVC 分层结构,逻辑清晰且易维护。
一、整体业务架构
|----------------|---------------------|-------------------------------|
| 层级 | 核心内容 | 作用 |
| Controller | FollowController | 接收前端请求,转发给Service层 |
| Service 接口 | IFollowService | 定义 follow()、isFollow() 方法 |
| Service 实现 | FollowServiceImpl | 实现关注/取关、状态查询的核心逻辑 |
二、Controller 层详解
java
@RestController
@RequestMapping("/follow")
public class FollowController {
@Resource
private IFollowService followService;
// 关注/取关接口
@PutMapping("/{id}/{isFollow}")
public Result follow(
@PathVariable("id") Long followUserId,
@PathVariable("isFollow") Boolean isFollow
){
return followService.follow(followUserId, isFollow);
}
// 查询关注状态接口
@GetMapping("/or/not/{id}")
public Result isFollow(@PathVariable("id") Long followUserId){
return followService.isFollow(followUserId);
}
}
关键说明
-
@PutMapping("/{id}/{isFollow}"):-
接收
PUT请求,路径中携带followUserId(被关注用户ID)和isFollow(true=关注,false=取关) -
直接调用
followService.follow()处理业务
-
-
@GetMapping("/or/not/{id}"):-
接收
GET请求,路径中携带followUserId -
调用
followService.isFollow()查询当前用户是否关注该用户
-
三、Service 接口定义
java
public interface IFollowService extends IService<Follow> {
// 关注/取关操作
Result follow(Long followUserId, Boolean isFollow);
// 查询关注状态
Result isFollow(Long followUserId);
}
定义了两个核心方法,实现与接口分离,符合MVC规范。
四、Service 实现层详解
1. 关注/取关核心方法 follow()
java
@Override
public Result follow(Long followUserId, Boolean isFollow) {
// 1. 获取当前登录用户ID
Long userId = UserHolder.getUser().getId();
// 2. 判断是关注还是取关
if(isFollow) {
// isFollow=true:关注操作,新增关注记录
Follow follow = new Follow();
follow.setUserId(userId); // 当前用户ID
follow.setFollowUserId(followUserId); // 被关注用户ID
save(follow); // 插入数据库
} else {
// isFollow=false:取关操作,删除关注记录
remove(new QueryWrapper<Follow>()
.eq("user_id", userId)
.eq("follow_user_id", followUserId)
);
}
return Result.ok();
}
1. new QueryWrapper<Follow>()
-
QueryWrapper 是 MyBatis‑Plus 提供的 "条件构造器",专门用来拼 SQL 的 WHERE 条件
-
针对
Follow实体类(对应follow表) -
作用:拼接 WHERE 删除条件
-
等价 SQL:
sql
DELETE FROM follow
WHERE user_id = ? AND follow_user_id = ?
//精准删除一条关注记录 → 实现取关
逻辑拆解
- 关注操作(
isFollow=true):
新建 Follow 对象,设置当前用户和被关注用户ID,调用 save() 插入数据库。
- 取关操作(
isFollow=false):
用 QueryWrapper 拼接条件,同时匹配 user_id 和 follow_user_id,调用 remove() 删除记录。
2. 查询关注状态方法 isFollow()
java
@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. 返回结果:count>0=已关注,否则=未关注
return Result.ok(count > 0);
}
逻辑拆解
-
通过
count()方法统计同时匹配当前用户和被关注用户的记录数 -
直接返回
count > 0,前端可根据布尔值渲染"已关注/未关注"状态
五、改造亮点与细节
-
Controller 职责单一:仅接收请求、转发调用,不写业务逻辑。
-
关注/取关合并接口 :通过
isFollow参数区分操作,减少接口数量。 -
状态查询高效 :直接通过数据库
count()判断,无需额外缓存。 -
参数安全 :使用
UserHolder获取当前用户ID,避免前端传入篡改。
六、总结
这是一套标准的用户关注/取关业务实现,通过Controller接收请求,Service层处理数据库的增删操作,同时提供关注状态查询接口,逻辑清晰、分层规范,适配前后端交互需求。
二、共同关注功能代码详解
这是「关注/取关」功能的升级版,新增了"共同关注"查询能力,并将用户关注列表同步到 Redis Set,实现高效的交集计算,避免数据库复杂查询。
一、整体业务架构
|----------------|------------------------------------------------------------|-----------------|
| 层级 | 核心内容 | 作用 |
| Controller | 新增 followCommons 接口 | 接收前端请求,查询共同关注列表 |
| Service 接口 | 新增 followCommons(Long id) 方法 | 定义共同关注查询能力 |
| Service 实现 | 1. 关注/取关时同步更新 Redis Set<br> 2. 利用 Redis Set 求交集,获取共同关注用户 | 实现核心业务逻辑 |
二、Controller 层新增接口
java
@GetMapping("/common/{id}")
public Result followCommons(@PathVariable("id") Long id){
return followService.followCommons(id);
}
- 提供
GET /follow/common/{id}接口,接收目标用户ID,返回双方的共同关注列表。
三、Service 层关键改造
1. 关注/取关时同步 Redis Set
java
@Override
public Result follow(Long followUserId, Boolean isFollow) {
//1.获取登录用户
Long userId = UserHolder.getUser().getId();
String key="follows:"+userId;
//1.判断到底是关注还是取关
if(isFollow){
// 如果 isFollow = true → 关注
//2.关注,新增数据
Follow follow = new Follow();
follow.setUserId(userId); // 我(当前用户)
follow.setFollowUserId(followUserId); // 我要关注的人
//save(follow);
boolean isSuccess=save(follow); // 插入数据库:新增一条关注记录
if (isSuccess) {
//把关注用户的id,放入redis的set集合 sadd userId followUserId
stringRedisTemplate.opsForSet().add(key,followUserId.toString());
}
}else {
//remove(new QueryWrapper<Follow>()
boolean isSuccess=remove(new QueryWrapper<Follow>()
.eq("user_id",userId).eq("follow_user_id",followUserId));
//把关注的用户id从Redis集合中移除
if (isSuccess) {
stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
}
}
return Result.ok();
}
-
核心逻辑:关注/取关操作时,先操作数据库,再同步更新 Redis Set,保证数据一致性。
-
Redis 中存储
follows:用户ID集合,集合内是该用户关注的所有用户ID。
2. 共同关注查询核心方法 followCommons(Long id)
java
@Override
public Result followCommons(Long id) {
// 1. 获取当前用户和目标用户ID,拼接Redis key
Long userId = UserHolder.getUser().getId();
String key = "follows:" + userId;
String key2 = "follows:" + id;
// 2. 用Redis Set求交集,直接得到共同关注的用户ID集合
Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);
if (intersect == null || intersect.isEmpty()) {
return Result.ok(Collections.emptyList());
}
// 3. 将字符串ID转为Long类型列表
List<Long> ids = intersect.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 4. 批量查询用户信息,转为UserDTO返回
List<UserDTO> users = userService.listByIds(ids)
.stream()
.map(user -> BeanUtil.copyProperties(user, UserDTO.class))
.collect(Collectors.toList());
return Result.ok(users);
}
关键步骤拆解
-
Redis 交集计算 :
intersect(key, key2)直接获取两个用户关注列表的交集,性能极高,避免了数据库的复杂查询。 -
空值 处理:无共同关注时直接返回空列表,避免空指针。
-
ID 类型转换:将 Redis 中存储的字符串ID转为Long类型,用于后续查询。
-
批量查询与 DTO 转换 :通过
listByIds批量查询用户信息,再转为UserDTO,只返回前端需要的字段。
四、改造亮点总结
-
性能优化 :利用 Redis Set 的
intersect命令实现交集计算,比数据库INNER JOIN高效得多。 -
数据一致性保障:关注/取关操作先操作数据库,再同步Redis,避免数据不一致。
-
接口能力扩展:新增共同关注查询接口,满足社交场景的需求。
-
分层清晰:Controller 负责请求转发,Service 负责业务逻辑,代码易维护。
五、总结
通过将用户关注列表同步到 Redis Set,利用其高效的交集计算能力,实现了高性能的共同关注查询功能,同时兼容原有的关注/取关业务。