redis_点评(21.好友关注——关注、取关功能实现;共同关注功能实现)

一、关注、取关功能代码详解

这是一套完整的用户关注/取关业务实现,包含「关注/取消关注」和「查询关注状态」两个核心接口,采用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);
    }
}

关键说明

  1. @PutMapping("/{id}/{isFollow}")

    1. 接收 PUT 请求,路径中携带 followUserId(被关注用户ID)和 isFollow(true=关注,false=取关)

    2. 直接调用 followService.follow() 处理业务

  2. @GetMapping("/or/not/{id}")

    1. 接收 GET 请求,路径中携带 followUserId

    2. 调用 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_idfollow_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,前端可根据布尔值渲染"已关注/未关注"状态


五、改造亮点与细节

  1. Controller 职责单一:仅接收请求、转发调用,不写业务逻辑。

  2. 关注/取关合并接口 :通过 isFollow 参数区分操作,减少接口数量。

  3. 状态查询高效 :直接通过数据库 count() 判断,无需额外缓存。

  4. 参数安全 :使用 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);
}
关键步骤拆解
  1. Redis 交集计算intersect(key, key2) 直接获取两个用户关注列表的交集,性能极高,避免了数据库的复杂查询。

  2. 空值 处理:无共同关注时直接返回空列表,避免空指针。

  3. ID 类型转换:将 Redis 中存储的字符串ID转为Long类型,用于后续查询。

  4. 批量查询与 DTO 转换 :通过 listByIds 批量查询用户信息,再转为 UserDTO,只返回前端需要的字段。


四、改造亮点总结

  1. 性能优化 :利用 Redis Set 的 intersect 命令实现交集计算,比数据库 INNER JOIN 高效得多。

  2. 数据一致性保障:关注/取关操作先操作数据库,再同步Redis,避免数据不一致。

  3. 接口能力扩展:新增共同关注查询接口,满足社交场景的需求。

  4. 分层清晰:Controller 负责请求转发,Service 负责业务逻辑,代码易维护。


五、总结

通过将用户关注列表同步到 Redis Set,利用其高效的交集计算能力,实现了高性能的共同关注查询功能,同时兼容原有的关注/取关业务。

相关推荐
Rick19931 小时前
索引的排序和分组
数据库·mysql
爱莉希雅&&&1 小时前
zabbix快速搭建和使用
android·linux·数据库·zabbix·监控
JohnYan1 小时前
工作笔记 - PG分组极值
数据库·后端·postgresql
清溪5491 小时前
DataEase H2 JDBC-RCE(CVE-2025-32966)复现
数据库·安全
ServBay2 小时前
不要再盲选了,PostgreSQL、MySQL与SQLite真实性能对比
数据库·mysql·sqlite
Trouvaille ~2 小时前
【Redis篇】Set 与 Zset:集合运算与排行榜的终极武器
数据库·redis·缓存·set·跳表·后端开发·zset
無限進步D2 小时前
MySQL 创建和管理表
数据库·mysql
六月雨滴2 小时前
归档模式配置与切换
数据库·oracle·dba
卡次卡次12 小时前
vibecoding起步注意点:插件、Skills、MCP、Hooks
服务器·数据库·python·oracle