关注模块 API
关注用户POST/api/v1/relations/followHeaders:Authorization:Bearer{token}Request:{user_id:target_user_id}Response:{code:0,data:{relation_type:following}}接口语义设计POST /api/v1/relations/follow这是一个**创建关系Follow Relation**的动作有副作用会改变系统状态符合 RESTPOST 创建资源 本质是在创建一条(follower_id, followee_id)的关系记录请求体设计{user_id:target_user_id}user_id表示“被关注的人”followee当前用户是 从Authorization的 token 中解析follower 设计合理点不需要传follower_id避免伪造简化接口参数返回结构设计{relation_type:following}它不是随便返回的而是为了前端 UI 状态统一常见关注状态状态含义none未关注following已关注mutual互相关注 返回relation_type的好处前端无需再查一次关系状态可以直接更新按钮关注 → 已关注后端核心逻辑这个接口背后通常是一个Follow Service社交关系服务1️⃣ 数据表设计核心Follow 表单向关系follows --------- follower_id followee_id created_at 表示A 关注了 B2️⃣ 写入逻辑1. 从 token 获取 follower_id 2. 校验 - 不能关注自己 - 目标用户存在 3. 插入 follow 关系 4. 更新计数粉丝数 / 关注数 5. 返回结果3️⃣ 幂等性设计非常关键用户可能重复点击“关注” 必须保证多次请求 只生效一次常见方案✅ 数据库唯一索引UNIQUE(follower_id,followee_id)已存在 → 忽略 or 返回成功防止重复关注✅ 业务层判断不够强先查再插❌ 有并发问题不推荐单独使用计数更新粉丝数 / 关注数关注成功后通常要更新A.following_count 1 B.follower_count 1更新方式1️⃣ 同步更新简单但风险高直接 update 用户表❌ 高并发下容易成为瓶颈2️⃣ 异步更新推荐发消息RocketMQ / MQ由计数服务更新 优点解耦可扩展和 Feed 系统的联动关注行为不仅是“关系”还会影响 Feed1️⃣ Push 模式普通用户A 关注 B → 把 B 的历史微博 推送到 A 的 Feed 可能触发Fanout历史内容或只影响未来内容2️⃣ Pull 模式大 VA 关注 B → 仅记录关系 → Feed 查询时再拉取 B 内容边界情况处理1️⃣ 不能关注自己if follower_id followee_id → error2️⃣ 用户不存在返回错误码3️⃣ 已关注两种设计✅ 幂等返回成功推荐code:0relation_type:following❌ 返回错误体验差4️⃣ 黑名单 / 拉黑关系如果B 拉黑了 A A 不能关注 B扩展设计1️⃣ 双向关系是否互关可以在返回中扩展{relation_type:mutual}2️⃣ 关注来源{source:search / recommendation}用于推荐系统3️⃣ 通知系统关注后通常会触发给 B 发通知A 关注了你接口设计优点总结这个接口设计是比较标准且合理的✅ REST 语义清晰POST 创建关系✅ 参数简洁只传 target_user_id✅ 使用 token 识别用户安全✅ 返回关系状态方便前端✅ 易于扩展互关 / 推荐 / 通知 这个接口的本质是创建一条单向关注关系 保证幂等 触发计数更新和 Feed 联动取消关注POST/api/v1/relations/unfollowHeaders:Authorization:Bearer{token}Request:{user_id:target_user_id}Response:{code:0,message:取消关注成功}接口语义这里有个小问题POST /api/v1/relations/unfollow从严格 REST 角度来看关注创建关系 →POST✅取消关注删除关系 →应该是DELETE更合理更标准的设计是DELETE /api/v1/relations/follow?user_idxxx或者DELETE /api/v1/users/{user_id}/follow现实中用 POST 的原因前端调用简单不用区分 HTTP 方法某些网关/客户端对 DELETE 支持不好统一风格follow / unfollow 都是 POST 结论当前设计可用但不够 RESTful工程上是常见折中方案请求设计{user_id:target_user_id}和 follow 接口保持一致这点很好✅ 对称性强✅ 易用性高当前用户仍然从 token 中解析。返回结构设计{code:0,message:取消关注成功}和 follow 接口对比接口返回followrelation_typeunfollowmessage 这里其实不够一致更推荐的设计{code:0,data:{relation_type:none}}好处前端可以直接更新状态接口风格统一减少额外请求后端核心逻辑1️⃣ 本质操作删除一条关系记录 (follower_id, followee_id)2️⃣ 执行流程1. 从 token 获取 follower_id 2. 校验 - 不能取消关注自己 3. 删除 follow 关系 4. 更新计数 5. 返回结果3️⃣ SQL 示例DELETEFROMfollowsWHEREfollower_id?ANDfollowee_id?;幂等性设计非常关键取消关注天然是一个幂等操作取消多次 结果一样推荐行为即使用户本来就没关注 也返回成功{code:0}防止前端状态错乱避免多一次“是否关注”的查询简化调用逻辑计数更新和 follow 相反A.following_count -1 B.follower_count -1❗不能减成负数需要保证count 0推荐做法异步更新MQ或使用原子操作Redis / DB和 Feed 系统的联动取消关注不仅是删除关系还会影响 Feed1️⃣ Push 模式普通用户之前B 的内容 → 已 push 到 A 的 Feed取消关注后 有两种策略❌ 方案1删除历史 Feed很重删除 A Feed 中 B 的所有内容成本高 ❌✅ 方案2不删除只影响未来主流已存在的内容保留以后不再推送 更简单、成本更低2️⃣ Pull 模式大 V查询时过滤关注关系取消关注后直接查不到 B 的内容 天然生效 ✅边界情况1️⃣ 未关注却取消关注 应该返回成功幂等2️⃣ 用户不存在返回错误3️⃣ 自己取消关注自己非法操作4️⃣ 黑名单关系如果A 被 B 拉黑 可以自动取消关注关系优化建议总结可以改进的地方1️⃣ HTTP 方法- POST /relations/unfollow DELETE /relations/follow2️⃣ 返回结构统一- message relation_type: none3️⃣ 接口合并高级设计有些系统会这样设计PUT /api/v1/relations/follow{user_id:...,action:follow | unfollow} 优点一个接口搞定更灵活 缺点不够 RESTful 这个接口的本质是删除关注关系 保证幂等 更新计数 影响 Feed 分发策略获取关注列表GET/api/v1/users/{user_id}/following?cursor{cursor}count20Response:{code:0,data:{users:[{user_id:1234567890,nickname:用户昵称,avatar:https://...,bio:个人简介,is_following:true,is_mutual:false}],next_cursor:cursor_string,has_more:true}}接口语义GET /api/v1/users/{user_id}/following非常标准的 REST 设计GET读取资源 ✅/users/{user_id}资源归属明确/following表示“这个用户关注的人” 本质查询「user_id 关注了哪些用户」和“粉丝列表”的区别接口含义/following我关注了谁/followers谁关注了我 底层其实是同一张表两种查询方向分页设计cursor?cursor{cursor}count20继续使用 cursor这是正确的设计。关注关系会动态变化用户不断关注/取关page 分页会导致数据重复数据丢失 cursor 可以保证✅ 顺序稳定✅ 高性能走索引cursor 通常基于follow_id 或 created_atSQL 示例SELECTfollowee_idFROMfollowsWHEREfollower_id?ANDfollow_idcursorORDERBYfollow_idDESCLIMIT20; 关键点必须有顺序字段自增 ID / 时间cursor 就是“上次最后一条的位置”返回结构设计{users:[...],next_cursor:...,has_more:true}1️⃣ users 字段{user_id:...,nickname:...,avatar:...,bio:...,is_following:true,is_mutual:false}✅ 优点去范式化直接返回用户信息避免前端再查/users/{id} 和 Feed 一样减少请求次数个性化字段重点这里有两个关键字段1️⃣is_followingis_following:true这个字段其实是“当前登录用户”是否关注列表中的这个人⚠️ 注意不是{user_id}的关注关系而是viewer当前用户 → 列表中的用户2️⃣is_mutualis_mutual:false表示A 关注 B 且 B 关注 A 即“互关”❗关键点这个接口其实是“带 viewer 视角的”即使你在看B 的 following 列表返回的数据仍然包含你viewer 和这些人的关系 这就是典型的个性化查询后端实现核心1️⃣ 数据表设计Follow 表follows --------- follow_id follower_id // 谁发起关注 followee_id // 关注谁 created_at2️⃣ 查询流程1. 查 follows 表 follower_id user_id 2. 拿到 followee_id 列表 3. 批量查 users 表用户信息 4. 计算 is_following / is_mutual 5. 返回结果如何计算 is_following / is_mutual这是这个接口最“贵”的部分。1️⃣ is_followingviewer → targetviewer 是否关注了这些用户实现方式✅ 批量查询SELECTfollowee_idFROMfollowsWHEREfollower_idviewer_idANDfollowee_idIN(...)2️⃣ is_mutual双向viewer → target target → viewer实现查两次关系或缓存 性能优化关键❌ 错误做法for each user: 查一次数据库 N1 查询直接炸✅ 正确做法批量查询IN或 Redis set例如following_set(viewer_id) followers_set(viewer_id)索引设计非常关键必须有INDEX(follower_id,follow_idDESC)以及INDEX(followee_id) 否则查询关注列表慢查粉丝列表也慢缓存设计这个接口很适合缓存1️⃣ 关注列表缓存following_list:user_id2️⃣ 关系缓存following_set:user_id followers_set:user_id 用于快速判断is_followingis_mutual边界与扩展1️⃣ 是否需要登录查看列表❌ 可以不登录但is_followingis_mutual 需要 viewer2️⃣ 空列表users:[]has_more:false3️⃣ 黑名单过滤如果viewer 被某人拉黑 可以不返回该用户4️⃣ 排序方式默认按关注时间倒序也可以扩展按活跃度按推荐 这个接口的本质是基于 follower_id 的关系分页查询 用户信息聚合 viewer 视角的关系计算获取粉丝列表GET/api/v1/users/{user_id}/followers?cursor{cursor}count20Response:{code:0,data:{users:[...],next_cursor:cursor_string,has_more:true}}接口语义GET /api/v1/users/{user_id}/followers含义非常明确查询“有哪些用户关注了 user_id”和 following 的本质区别虽然结构几乎一样但查询方向完全相反接口查询条件followingfollower_id user_idfollowersfollowee_id user_idfollowing我关注谁主动关系 followers谁关注我被动关系分页设计cursor?cursor{cursor}count20依然是 cursor 分页这是必须的。因为粉丝数量可能极大百万 / 千万级且增长非常频繁大 V如果用 page❌ 深分页性能灾难❌ 数据错乱严重cursor 设计方式通常基于follow_id自增 或 created_atSQL 示例SELECTfollower_idFROMfollowsWHEREfollowee_id?ANDfollow_idcursorORDERBYfollow_idDESCLIMIT20; 本质按“谁关注你的时间”倒序返回结构{users:[...],next_cursor:...,has_more:true}和 following 保持一致这点很好✅ 接口统一✅ 前端复用逻辑users 字段设计通常包含{user_id:...,nickname:...,avatar:...,bio:...,is_following:true,is_mutual:false}个性化字段重点即使是粉丝列表也通常包含1️⃣is_followingviewer 是否关注这个粉丝 用于 UI“回关”按钮“已关注”状态2️⃣is_mutual是否互关双向关注 关系判断A → B B → A后端实现流程1️⃣ 查询 follows 表条件followee_id user_id得到follower_id 列表2️⃣ 查询用户信息users 表批量3️⃣ 计算关系状态is_followingis_mutual基于 viewer4️⃣ 返回结果这个接口的核心难点非常重要粉丝列表比关注列表更难做原因❗1️⃣ 数据量极大大 V 场景例如某大 V1000 万粉丝问题查询压力大分页很深数据冷热不均❗2️⃣ 写入非常频繁每秒成千上万用户关注 follow 表写入压力大❗3️⃣ 读取模式复杂需要排序需要分页需要 join 用户信息索引设计关键必须有这个索引INDEX(followee_id,follow_idDESC) 用于查粉丝列表核心路径同时还需要INDEX(follower_id) 用于判断 is_following查询 following性能优化重点1️⃣ 批量查询避免 N1❌ 错误for each follower: 查用户信息✅ 正确WHERE user_id IN (...)2️⃣ 缓存关系用 Redisfollowers_set:user_id following_set:user_id 用于快速判断is_followingis_mutual3️⃣ 热点用户缓存大 Vfollowers_list:user_id缓存前几页4️⃣ 分库分表核心当数据量巨大常见方案按 user_id hash 分表或按 followee_id 分片 保证单表数据量可控查询可扩展和 Feed 系统的关系粉丝列表其实决定谁会收到你的内容Push 模式Push 模式发微博 → 推送给 followers 所以followers 查询性能直接影响 FanoutPull 模式不依赖 followers 表做推送边界情况1️⃣ 无粉丝users:[]has_more:false2️⃣ 私密账号如果 user_id 是私密账号可能只返回已批准粉丝3️⃣ 黑名单如果某粉丝被拉黑 可以过滤掉和 following 的总结对比维度followingfollowers查询条件follower_idfollowee_id数据规模较小可能极大性能压力中等很高使用场景自己看被别人看 这个接口的本质是基于 followee_id 的高并发关系分页查询是社交系统中读压力最大、最需要优化的接口之一
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2633556.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!