数据模型
数据库中的tb_follow
记录博主与粉丝的关系
tb_follow
表对应的实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_follow")
public class Follow implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户id
*/
private Long userId;
/**
* 关联的用户id
*/
private Long followUserId;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
是否关注/关注/取关
需求: 在探店图文的详情页面中,可以查看用户是否关注了笔记博主,用户也可以手动关注/取消关注
发布笔记的作者
第一步: 在FollowController
层中编写判断是否关注
和关注/取关
的两个方法
@RestController
@RequestMapping("/follow")
public class FollowController {
@Resource
private IFollowService followService;
// 判断当前用户是否关注了笔记博主,参数是发布笔记的博主Id
@GetMapping("/or/not/{id}")
public Result isFollow(@PathVariable("id") Long followUserId) {
return followService.isFollow(followUserId);
}
// 实现取关/关注,参数是发布笔记的博主Id以及是否关注(true表示关注,false表示取关)
@PutMapping("/{id}/{isFollow}")
public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFellow) {
return followService.follow(followUserId,isFellow);
}
}
第二步: 在FellowServiceImp
中来编写具体的业务逻辑
判断当前用户是否关注了笔记博主
: 将请求参数携带的发布笔记的博主Id和当前登陆的用户Id
作为条件去数据库中查询是否有对应的记录关注和取消关注
: 请求参数中的true(关注)/fasle(取关)
,关注是将用户和博主的关联信息保存到数据库,取关即将他们的关联信息从数据库移除,避免堆积数据@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
@Override
public Result isFollow(Long followUserId) {
// 获取当前登录的用户Id
Long userId = UserHolder.getUser().getId();
LambdaQueryWrapper<Follow> queryWrapper = new LambdaQueryWrapper<>();
// 查询tb_follow表判断当前用户是否关注了该笔记的博主
queryWrapper.eq(Follow::getUserId, userId).eq(Follow::getFollowUserId, followUserId);
// 没必要查询出具体数据,只需要判断数据存不存在即可
//select count(*) from tb_follow where user_id = ? and follow_user_id = ?
int count = this.count(queryWrapper);
return Result.ok(count > 0);
}
@Override
public Result follow(Long followUserId, Boolean isFellow) {
// 获取当前登录的用户Id
Long userId = UserHolder.getUser().getId();
// 判断用户是要关注还是取关,true表示关注,false表示取关
if (isFellow) {
// 关注则将用户和笔记博主的关联信息保存到数据库
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(followUserId);
save(follow);
} else {
// 取关则将用户和博主的关联信息从数据库中移除,避免数据库中堆积大量数据
//delete from tb_follow where user_id = ? and follow_user_id = ?
LambdaQueryWrapper<Follow> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Follow::getUserId, userId).eq(Follow::getFollowUserId, followUserId);
remove(queryWrapper);
}
return Result.ok();
}
}
共同关注
需求:当我们点击博主用户头像时进入到详情页,可以查看到博主发布的笔记
以及用户和博主的好友共同关注列表
第一步: 在UserController
中编写查询博主信息
的方法
@GetMapping("/{id}")
public Result queryUserById(@PathVariable("id") Long userId) {
// 查询详情
User user = userService.getById(userId);
if (user == null) {
// 查不到返回空
return Result.ok();
}
// 查到则转为userDTO对象
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
// 返回查询到的数据
return Result.ok(userDTO);
}
第二步: 在BlogController
中编写分页查询博主发布的所有笔记
的方法
// 根据博主id查询博主的探店笔记
@GetMapping("/of/user")
public Result queryBlogByUserId(
@RequestParam(value = "current", defaultValue = "1") Integer current,
@RequestParam("id") Long id) {
LambdaQueryWrapper<Blog> queryWrapper = new LambdaQueryWrapper<>();
// 根据博主id查询用户信息
queryWrapper.eq(Blog::getUserId, id);
Page<Blog> pageInfo = new Page<>(current, SystemConstants.MAX_PAGE_SIZE);
blogService.page(pageInfo, queryWrapper);
// 获取查询到的所有用户信息
List<Blog> records = pageInfo.getRecords();
return Result.ok(records);
}
// 根据博主id查询博主的探店笔记
@GetMapping("/of/user")
public Result queryBlogByUserId(
@RequestParam(value = "current", defaultValue = "1") Integer current,
@RequestParam("id") Long id) {
// 根据博主id查询用户信息
Page<Blog> page = blogService.query()
.eq("user_id", id).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
// 获取查询到的所有用户信息
List<Blog> records = page.getRecords();
return Result.ok(records);
}
第三步: 修改关注/取关
的逻辑,以follows:userId
作为Set集合的Key,存放当前用户关注的所有博主Id
关注/取关
: 将关注的博主Id放到当前登陆用户关注的Set集合中,取关就是将关注的博主Id从当前登陆用户的Set集合中移除共同关注
:通过SINTER key1 key2
查询登陆用户的Set集合和其关注博主的Set集合中元素的交集@Resource
private StringRedisTemplate stringRedisTemplate;
@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
@Override
public Result follow(Long followUserId, Boolean isFellow) {
// 获取当前登录的用户Id
Long userId = UserHolder.getUser().getId();
String key = "follows:" + userId;
// 判断用户是要关注还是取关,true表示关注,false表示取关
if (isFellow) {
// 关注则将用户和笔记博主的关联信息保存到数据库
Follow follow = new Follow();
follow.setUserId(userId);
follow.setFollowUserId(followUserId);
boolean iSsuccess = save(follow);
// 如果更新成功则将关联信息也写入Redis,key是当前的用户id,value就是关注的博主id
if (iSsuccess) {
stringRedisTemplate.opsForSet().add(key, followUserId.toString());
}
} else {
// 取关则将用户和博主的关联信息从数据库中移除,避免数据库中堆积大量数据
//delete from tb_follow where user_id = ? and follow_user_id = ?
LambdaQueryWrapper<Follow> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Follow::getUserId, userId).eq(Follow::getFollowUserId, followUserId);
boolean iSsuccess = remove(queryWrapper);
// 如果取关成功则将关联的博主Id从当前登陆用户的set集合中移除
if (iSsuccess){
stringRedisTemplate.opsForSet().remove(key,followUserId.toString());
}
}
return Result.ok();
}
}
第四步: 编写控制器方法,查看登陆用户和其关注博主的好友共同关注列表
@GetMapping("/common/{id}")
public Result followCommons(@PathVariable Long id){
return followService.followCommons(id);
}
@Resource
private IUserService userService;
@Override
public Result followCommons(Long id) {
// 获取当前登陆用户的id
Long userId = UserHolder.getUser().getId();
// // 获取当前登陆对应的set集合的key
String key1 = "follows:" + userId;
// 获取当前登陆用户关注博主所对应的set集合的key
String key2 = "follows:" + id;
// 对当前登陆用户和其关注博主的Set集合取交集
Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);
// 无交集就返回个空的List集合
if (intersect == null || intersect.isEmpty()) {
return Result.ok(Collections.emptyList());
}
// 将String类型的用户id转化为Long类型的用户id然后使用List集合收集
List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
// 根据ids集合中的用户id去数据库中查询登陆用户和博主共同关注的用户信息并封装成UserDto对象,最后存入List集合中返回
List<UserDTO> userDTOS = userService.listByIds(ids).stream().map(user ->
BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());
return Result.ok(userDTOS);
}