实战篇 05. 好友关注 - 关注和取关学习文档

6 阅读3分钟

📚 实战篇 05. 好友关注 - 关注和取关学习文档

一、 业务场景与需求分析

在达人探店的 App 中,当我们点击进入某个用户的个人主页时,会看到一个**“关注”**按钮。

核心需求:

  1. 关注 / 取消关注: 点击按钮,如果当前未关注,则执行关注操作;如果已经关注,则执行取消关注操作。
  2. 状态回显: 当用户进入别人的主页时,前端需要知道当前登录用户是否已经关注了该博主,从而决定按钮显示“关注”还是“已关注”。

二、 数据库表设计 (tb_follow)

关注关系的本质是多对多的关系(一个用户可以关注多个人,也可以被多个人关注)。因此,在 MySQL 中,我们需要一张中间表来记录这种映射关系。

tb_follow 表核心字段:

  • id: 主键
  • user_id: 关注者(粉丝)的 ID,也就是当前登录操作的用户。
  • follow_user_id: 被关注者(博主)的 ID。
  • create_time: 关注时间。

(💡 设计规范提示:通常在这种关系表中,我们会对 user_idfollow_user_id 建立联合唯一索引 (Unique Key) ,从数据库物理层面彻底防止因为高并发导致的重复关注脏数据。)


三、 核心代码落地 1:查询是否关注 (isFollow)

当用户进入博主主页时,前端会立刻调用这个接口,判断按钮的高亮状态。

这步的逻辑非常简单,就是去数据库里查一查有没有对应的记录。

Java

@Override
public Result isFollow(Long followUserId) {
    // 1. 获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    
    // 2. 去 tb_follow 表查询是否关注 (SELECT count(*) FROM tb_follow WHERE user_id = ? AND follow_user_id = ?)
    Integer count = query()
            .eq("user_id", userId)
            .eq("follow_user_id", followUserId)
            .count();
            
    // 3. 返回判断结果 (count > 0 说明查到了记录,即已关注)
    return Result.ok(count > 0);
}

四、 核心代码落地 2:关注与取关操作 (follow)

前端会传来两个参数:被关注者的 ID (followUserId),以及当前的操作是关注还是取关 (isFollow 布尔值)。

⚠️ 进阶伏笔预警: 如果只实现关注功能,单纯操作 MySQL 就够了。但是!在真实的社交软件中,我们往往还需要实现**“共同关注”的功能。为了方便后续的高性能集合运算(求交集),我们必须在操作数据库的同时,将关注关系同步保存到 Redis 的 Set 集合中!**

Java

@Override
public Result follow(Long followUserId, boolean isFollow) {
    // 1. 获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    String key = "follows:" + userId; // Redis Set 的 Key 设计:follows:当前用户ID

    // 2. 判断到底是关注还是取关
    if (isFollow) {
        // ------------------ 执行关注逻辑 ------------------
        Follow follow = new Follow();
        follow.setUserId(userId);
        follow.setFollowUserId(followUserId);
        
        // 2.1 将关注记录保存到 MySQL 数据库 (INSERT)
        boolean isSuccess = save(follow);
        if (isSuccess) {
            // 2.2 【Redis 同步】:将博主的 ID 塞入当前用户的关注 Set 集合中 (SADD)
            stringRedisTemplate.opsForSet().add(key, followUserId.toString());
        }
    } else {
        // ------------------ 执行取关逻辑 ------------------
        // 3.1 从 MySQL 数据库中物理删除记录 (DELETE FROM tb_follow WHERE user_id = ? AND follow_user_id = ?)
        boolean isSuccess = remove(new QueryWrapper<Follow>()
                .eq("user_id", userId).eq("follow_user_id", followUserId));
                
        if (isSuccess) {
            // 3.2 【Redis 同步】:将博主的 ID 从当前用户的关注 Set 集合中剔除 (SREM)
            stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
        }
    }
    
    return Result.ok();
}

五、 学习总结与架构反思

在这个功能中,我们采用了 “MySQL 落盘主数据 + Redis 冗余辅助数据” 的双写策略。

  • MySQL 的作用: 保证数据的绝对持久化和强关系约束,是关注列表的“真理之源”。
  • Redis 的作用: 虽然这节课我们还没用到 Redis 里的这些 Set 集合,但这其实是在**“蓄力”**。