OpenIM 源码深度解析系列(十三):好友系统架构与业务设计

266 阅读16分钟

好友系统架构与业务设计

📋 概述

本文档全面解析OpenIM好友系统的完整架构设计,涵盖存储架构、业务逻辑、权限控制和关系管理等核心模块:

🏗️ 系统架构层次

  • 存储架构:客户端SQLite + 服务端MongoDB + Redis缓存的三层存储体系
  • 业务架构:好友管理、申请审核、黑名单控制、关系同步等完整业务流程
  • 权限架构:基于用户关系的分级权限控制体系,支持精细化权限管理
  • 通知架构:实时通知机制,确保好友关系变更的及时同步

通过本文档,您将全面了解OpenIM好友系统从底层存储到上层业务的完整设计架构。


第一部分:客户端存储层(SQLite)

🗄️ 核心表结构概览

OpenIM SDK 使用三个核心表实现完整的好友关系数据存储:

表名作用数据特点查询频率
LocalFriend好友关系信息相对稳定,变更较少高频查询
LocalFriendRequest好友申请记录动态变化,状态流转中频查询
LocalBlack黑名单信息相对稳定,隐私保护中频查询

📋 1. LocalFriend - 本地好友信息表

表名: local_friends

完整字段结构
字段名字段类型索引详细描述
OwnerUserIDstring(64) [主键]PRIMARY KEY好友关系拥有者用户ID
• 标识这条好友关系的拥有者
• 与FriendUserID组成联合主键
• 支持单向好友关系
FriendUserIDstring(64) [主键]PRIMARY KEY好友用户ID
• 被添加为好友的用户ID
• 与OwnerUserID组成联合主键
• 好友关系的目标用户
Remarkstring(255)好友备注名
• 自定义好友显示名称
• 可与真实昵称不同
• 个性化好友管理
CreateTimeint64好友关系创建时间戳
• 毫秒级时间戳
• 好友添加时间记录
• 用于好友列表排序
AddSourceint32添加好友来源枚举
1 = 搜索添加
2 = 扫码添加
3 = 名片添加
4 = 群聊添加
OperatorUserIDstring(64)操作者用户ID
• 执行添加好友操作的用户
• 通常为OwnerUserID
• 管理员操作时可能不同
Nicknamestring(255)好友昵称快照
• 好友的真实昵称
• 添加时的昵称快照
• 好友昵称变更时更新
FaceURLstring(255)好友头像URL快照
• 好友的头像地址
• 添加时的头像快照
• 好友头像变更时更新
Exstring(1024)扩展字段
• 自定义数据存储
• JSON格式
• 业务扩展使用
AttachedInfostring(1024)附加信息
• 额外业务数据
• 可选的元数据信息
• 客户端自定义使用
IsPinnedbool是否置顶好友
• true = 置顶显示
• false = 正常排序
• 好友列表排序依据

👥 2. LocalFriendRequest - 本地好友申请记录表

表名: local_friend_requests

完整字段结构
字段名字段类型索引详细描述
FromUserIDstring(64) [联合主键]PRIMARY KEY申请发起者用户ID
• 发送好友申请的用户
• 联合主键组成部分
• 申请方标识
ToUserIDstring(64) [联合主键]PRIMARY KEY申请接收者用户ID
• 接收好友申请的用户
• 联合主键组成部分
• 被申请方标识
FromNicknamestring(255)申请者昵称快照
• 申请时的发起者昵称
• 避免昵称变更后显示异常
• 申请列表展示使用
FromFaceURLstring(255)申请者头像快照
• 申请时的发起者头像
• 保持申请记录的一致性
• 申请列表头像展示
ToNicknamestring(255)接收者昵称快照
• 申请时的接收者昵称
• 申请记录的完整性
• 历史昵称保存
ToFaceURLstring(255)接收者头像快照
• 申请时的接收者头像
• 申请记录的完整性
• 历史头像保存
HandleResultint32处理结果枚举
0 = 待处理 (默认状态)
1 = 同意 (FriendResponseAgree)
-1 = 拒绝 (FriendResponseRefuse)
ReqMsgstring(255)申请消息
• 申请者填写的申请理由
• 帮助接收者决策
• 最大255字符
CreateTimeint64申请创建时间戳
• 申请发起时间
• 毫秒级时间戳
• 申请列表排序依据
HandlerUserIDstring(64)处理者用户ID
• 处理申请的用户ID
• 通常为ToUserID
• 管理员处理时可能不同
HandleMsgstring(255)处理消息
• 处理申请时填写的消息
• 同意/拒绝的具体原因
• 反馈给申请者
HandleTimeint64处理时间戳
• 申请处理时间
• 0表示未处理
• 处理效率统计
Exstring(1024)扩展字段
• 申请相关自定义数据
• JSON格式存储
AttachedInfostring(1024)附加信息
• 申请相关元数据
• 业务扩展支持

🚫 3. LocalBlack - 本地黑名单表

表名: local_blacks

完整字段结构
字段名字段类型索引详细描述
OwnerUserIDstring(64) [联合主键]PRIMARY KEY黑名单拥有者用户ID
• 标识这条黑名单记录的拥有者
• 与BlockUserID组成联合主键
• 黑名单管理者
BlockUserIDstring(64) [联合主键]PRIMARY KEY被屏蔽用户ID
• 被拉入黑名单的用户ID
• 与OwnerUserID组成联合主键
• 黑名单目标用户
Nicknamestring(255)被屏蔽用户昵称快照
• 拉黑时的用户昵称
• 避免昵称变更后显示异常
• 黑名单列表展示使用
FaceURLstring(255)被屏蔽用户头像快照
• 拉黑时的用户头像
• 保持黑名单记录一致性
• 黑名单列表头像展示
CreateTimeint64拉黑时间戳
• 拉黑操作时间
• 毫秒级时间戳
• 黑名单列表排序依据
AddSourceint32拉黑来源枚举
1 = 手动拉黑
2 = 系统拉黑
3 = 批量拉黑
• 拉黑原因统计
OperatorUserIDstring(64)操作者用户ID
• 执行拉黑操作的用户
• 通常为OwnerUserID
• 管理员操作时可能不同
Exstring(1024)扩展字段
• 自定义数据存储
• JSON格式
• 业务扩展使用
AttachedInfostring(1024)附加信息
• 额外业务数据
• 可选的元数据信息
• 客户端自定义使用

第二部分:服务端存储层

🏗️ 架构概览

服务端采用三层存储架构:

  • MongoDB:持久化存储,保证数据安全和事务一致性
  • Redis:高速缓存,提升查询性能,支持大规模好友场景
  • 本地缓存:进程内缓存,进一步优化热点数据访问

🗄️ MongoDB 数据库存储层

1. Friend - 好友关系表

集合名: friend

完整字段结构
字段名字段类型索引详细描述
_idObjectIdPRIMARY KEYMongoDB主键
• 自动生成的唯一标识
OwnerUserIDstringINDEX好友关系拥有者用户ID
• 复合索引:owner_user_id + friend_user_id
• 好友关系的拥有者
• 支持单向好友关系
FriendUserIDstringINDEX好友用户ID
• 复合索引组成部分
• 被添加为好友的用户
• 关系的目标对象
Remarkstring好友备注名
• 自定义好友显示名称
• 个性化好友管理
• 支持表情和特殊字符
CreateTimetime.TimeINDEX好友关系创建时间
• MongoDB时间类型
• 自动索引优化
• 关系建立时间记录
AddSourceint32添加好友来源
• 添加渠道统计
• 社交网络分析
• 推广效果评估
OperatorUserIDstring操作者用户ID
• 最后操作记录
• 管理操作审计
• 关系变更追踪
Exstring扩展字段
• 业务自定义数据
• JSON格式存储
• 灵活扩展支持
IsPinnedbool是否置顶好友
• 好友列表排序
• 个性化展示
• 便捷访问设置
2. FriendRequest - 好友申请表

集合名: friend_request

完整字段结构
字段名字段类型索引详细描述
_idObjectIdPRIMARY KEYMongoDB主键
FromUserIDstringINDEX申请发起者用户ID
• 复合索引:from_user_id + to_user_id
• 申请者标识
• 申请历史查询
ToUserIDstringINDEX申请接收者用户ID
• 复合索引组成部分
• 被申请用户标识
• 申请管理依据
HandleResultint32INDEX处理结果
0 = 待处理 (默认状态)
1 = 同意 (FriendResponseAgree)
-1 = 拒绝 (FriendResponseRefuse)
• 申请状态过滤索引
ReqMsgstring申请消息
• 申请者添加理由
• 接收者决策参考
• 申请质量评估
CreateTimetime.TimeINDEX申请创建时间
• 申请发起时间
• 申请列表排序
• 处理时效统计
HandlerUserIDstring处理者用户ID
• 处理申请的用户
• 管理操作追踪
• 权限验证使用
HandleMsgstring处理消息
• 处理申请时填写的消息
• 拒绝/同意原因
• 申请者反馈信息
HandleTimetime.Time处理时间
• 申请处理时间
• 处理效率统计
• 时效性分析
Exstring扩展字段
• 申请相关扩展数据
• 自定义申请属性
• 业务数据支持
3. Black - 黑名单表

集合名: black

完整字段结构
字段名字段类型索引详细描述
_idObjectIdPRIMARY KEYMongoDB主键
OwnerUserIDstringINDEX黑名单拥有者用户ID
• 复合索引:owner_user_id + block_user_id
• 黑名单管理者
• 权限控制依据
BlockUserIDstringINDEX被屏蔽用户ID
• 复合索引组成部分
• 被拉黑的用户
• 黑名单目标对象
CreateTimetime.TimeINDEX拉黑时间
• 黑名单创建时间
• 排序和统计使用
• 拉黑历史记录
AddSourceint32拉黑来源
• 拉黑渠道统计
• 用户行为分析
• 安全策略优化
OperatorUserIDstring操作者用户ID
• 最后操作记录
• 管理操作审计
• 拉黑行为追踪
Exstring扩展字段
• 黑名单相关扩展数据
• 自定义拉黑属性
• 业务数据支持

🚀 Redis 缓存层

OpenIM Redis缓存采用分层过期策略和智能预加载,特别针对好友关系场景优化:

1. 好友关系相关缓存
缓存Key格式数据类型TTL时间缓存内容使用场景
FRIEND_IDS:{ownerUserID}String(JSON Array)12小时用户的所有好友ID列表好友列表查询,关系验证
COMMON_FRIENDS_IDS:{ownerUserID}String(JSON Array)12小时双向好友ID列表互相关注的好友查询
FRIEND_INFO:{ownerUserID}-{friendUserID}String(JSON)12小时单个好友详细信息好友信息快速查询
2. 黑名单相关缓存
缓存Key格式数据类型TTL时间缓存内容使用场景
BLACK_IDS:{ownerUserID}String(JSON Array)12小时用户的黑名单ID列表黑名单查询,权限验证
3. 本地缓存相关
缓存Key格式数据类型TTL时间缓存内容使用场景
IS_FRIEND:{userID}-{possibleFriendUserID}Boolean本地缓存好友关系状态关系快速验证
IS_BLACK:{userID}-{possibleBlackUserID}Boolean本地缓存黑名单状态黑名单快速验证
4. 缓存配置参数

本地缓存配置 (基于源码 config/local-cache.yml):

friend:
  topic: DELETE_CACHE_FRIEND    # 缓存失效通知主题
  slotNum: 100                  # 缓存槽数量,提高并发性能
  slotSize: 2000                # 每个槽的大小
  successExpire: 300            # 成功缓存过期时间(秒) - 5分钟
  failedExpire: 5               # 失败缓存过期时间(秒) - 5秒

Redis缓存TTL设置 (基于源码):

const (
    // friendExpireTime 好友缓存过期时间:12小时
    friendExpireTime = time.Second * 60 * 60 * 12
    
    // blackExpireTime 黑名单缓存过期时间:12小时  
    blackExpireTime = time.Second * 60 * 60 * 12
)

第三部分:好友管理操作权限矩阵

🔐 好友系统操作权限控制表

OpenIM好友系统采用基于用户关系的权限控制机制,不同于群组的角色权限,好友系统主要基于关系状态和操作主体进行权限判断:

操作名称功能描述系统管理员关系拥有者其他用户特殊说明
ApplyToAddFriend申请添加好友所有用户都可以发起好友申请
RespondFriendApply回应好友申请只有申请接收者可以处理申请
DeleteFriend删除好友关系只能删除自己的好友关系
SetFriendRemark设置好友备注只能设置自己好友的备注
AddBlack添加黑名单只能将用户加入自己的黑名单
RemoveBlack移除黑名单只能从自己的黑名单移除用户
ImportFriends批量导入好友批量建立好友关系,需要权限验证
GetFriendInfo获取好友信息只能查看自己的好友信息
GetPaginationFriends分页获取好友列表只能查看自己的好友列表
GetDesignatedFriends获取指定好友信息查看指定好友的详细信息
IsFriend检查好友关系检查双向好友关系状态
GetFriendIDs获取好友ID列表获取用户的所有好友ID
GetPaginationFriendsApplyTo获取发送的申请列表查看自己发送的好友申请
GetPaginationFriendsApplyFrom获取接收的申请列表查看收到的好友申请
GetPaginationBlacks分页获取黑名单只能查看自己的黑名单
IsBlack检查黑名单状态检查用户是否在黑名单中
GetSpecifiedBlacks获取指定黑名单信息查看指定用户的黑名单状态
UpdateFriends批量更新好友信息批量更新好友属性信息

🏷️ 权限标识说明

标识含义说明
完全权限可以执行该操作,无特殊限制
无权限不能执行该操作

🔒 详细权限控制规则

1. 系统管理员权限特性
  • 最高权限:系统管理员通过authverify.IsAppManagerUid()验证,拥有所有好友操作的完全控制权
  • 跨用户操作:可以操作任何用户的好友关系,不受用户身份限制
  • 权限覆盖:可以绕过大部分业务权限验证
2. 关系拥有者权限特性
  • 自我管理:只能管理自己的好友关系和黑名单
  • 隐私保护:不能查看或操作其他用户的好友数据
  • 关系控制:可以主动发起、接受、拒绝、删除好友关系
3. 其他用户权限特性
  • 受限访问:只能进行部分公开的查询操作
  • 关系验证:可以检查与其他用户的好友关系状态
  • 申请权限:可以向任何用户发起好友申请
4. 权限验证代码模式
// 模式1:系统管理员检查
if authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
    // 系统管理员直接执行操作
    return s.processOperation(ctx, req)
}

// 模式2:操作权限检查
if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil {
    return nil, err
}

// 模式3:关系验证检查
func (s *friendServer) checkUserRelation(ctx context.Context, ownerUserID, targetUserID string) error {
    if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
        opUserID := mcontext.GetOpUserID(ctx)
        if opUserID != ownerUserID {
            return errs.ErrNoPermission.WrapMsg("no permission to access other user's data")
        }
    }
    return nil
}
5. 特殊权限验证场景
场景权限规则代码实现
好友申请处理只有申请接收者可以处理RespondFriendApply中的用户ID验证
双向关系验证检查互相的好友关系状态IsFriend中的双向关系查询
黑名单权限基于拥有者身份的访问控制IsBlack中的关系验证
批量操作权限确保所有目标用户的权限一致ImportFriends中的批量权限验证
数据隔离用户只能访问自己的关系数据各查询方法中的用户ID过滤
6. 权限验证函数映射表
验证函数适用操作权限要求
authverify.CheckAccessV3用户相关操作本人操作 或 系统管理员
authverify.IsAppManagerUid所有操作系统管理员验证
checkUserRelation关系查询操作关系拥有者 或 系统管理员
validateFriendOperation好友操作好友关系验证 + 权限检查

第四部分:好友管理操作详细流程分析

🔄 操作关系图谱

在深入分析每个操作的详细流程之前,我们先通过关系图谱了解各个操作之间的依赖和关联关系:

graph TD
    subgraph "核心好友操作"
        A[ApplyToAddFriend] --> B[RespondFriendApply]
        B --> C[FriendApplicationAgreedNotification]
        B --> D[FriendApplicationRefusedNotification]
        C --> E[BecomeFriends]
    end
    
    subgraph "好友管理操作"
        F[SetFriendRemark] --> G[FriendRemarkSetNotification]
        H[DeleteFriend] --> I[FriendDeletedNotification]
        J[ImportFriends] --> E
        E --> K[FriendAddedNotification]
    end
    
    subgraph "黑名单操作"
        L[AddBlack] --> M[BlackAddedNotification]
        N[RemoveBlack] --> O[BlackDeletedNotification]
    end
    
    subgraph "查询操作"
        P[GetPaginationFriends] --> Q[IsFriend]
        R[GetPaginationBlacks] --> S[IsBlack]
        T[GetDesignatedFriends] --> U[GetFriendInfo]
    end
    
    subgraph "同步操作"
        V[GetIncrementalFriends] --> W[NotificationUserInfoUpdate]
        X[GetFullFriendUserIDs] --> Y[UserInfoSync]
    end

🎯 操作分类与数据流转模式

操作类型典型操作数据流转特点缓存影响范围通知触发
申请类ApplyToAddFriend, RespondFriendApply状态流转,双向影响双方用户影响双方通知
修改类SetFriendRemark, UpdateFriends单向修改,关系保持局部影响单方通知
删除类DeleteFriend, RemoveBlack关系解除,状态清理双方影响双方通知
查询类GetPaginationFriends, IsFriend读取操作,缓存优先无影响无通知
黑名单类AddBlack, IsBlack单向控制,权限隔离局部影响单方通知

📊 1. ApplyToAddFriend - 申请添加好友详细流程

sequenceDiagram
    participant Client as 客户端
    participant API as Friend RPC
    participant Redis as Redis缓存
    participant MongoDB as MongoDB
    participant Notify as 通知服务
    participant User as 用户服务
    participant Webhook as Webhook服务
    
    Client->>API: ApplyToAddFriendReq
    
    Note over API: 1. 权限验证阶段
    API->>API: CheckAccessV3(申请者权限)
    
    Note over API: 2. 用户验证阶段
    API->>User: GetUsersInfo(验证目标用户存在)
    User-->>API: 返回用户信息
    
    Note over API: 3. 关系状态检查
    API->>MongoDB: 检查是否已经是好友
    API->>MongoDB: 检查是否在黑名单中
    API->>MongoDB: 检查是否已有申请记录
    
    Note over API: 4. Webhook前置回调
    API->>Webhook: BeforeAddFriend
    Webhook-->>API: 回调结果验证
    
    Note over API: 5. 创建好友申请
    API->>MongoDB: CreateFriendRequest
    Note over MongoDB: 插入friend_requests集合<br/>- from_user_id: 申请者<br/>- to_user_id: 接收者<br/>- handle_result: 0(待处理)<br/>- req_msg: 申请消息<br/>- create_time: 当前时间
    
    Note over API: 6. 发送申请通知
    API->>Notify: FriendApplicationAddNotification
    Note over Notify: 通知类型: FriendApplication<br/>通知对象: 申请接收者<br/>消息内容: 好友申请信息
    
    Note over API: 7. Webhook后置回调
    API->>Webhook: AfterAddFriend
    
    API-->>Client: ApplyToAddFriendResp(成功)

关键业务逻辑检查:

  1. 重复申请检查:防止重复发送申请
  2. 好友关系检查:已经是好友则不能再申请
  3. 黑名单检查:被拉黑则无法发送申请
  4. 自己申请自己检查:防止用户申请自己为好友

MongoDB操作清单:

  • friend_requests 集合 - INSERT ONE (新申请记录)

👥 2. RespondFriendApply - 处理好友申请详细流程

sequenceDiagram
    participant Client as 客户端
    participant API as Friend RPC
    participant Redis as Redis缓存
    participant MongoDB as MongoDB
    participant Notify as 通知服务
    participant Webhook as Webhook服务
    
    Client->>API: RespondFriendApplyReq
    
    Note over API: 1. 权限验证阶段
    API->>API: CheckAccessV3(处理者权限)
    API->>API: 验证处理者是否为申请接收者
    
    Note over API: 2. 申请记录验证
    API->>MongoDB: FindFriendRequest(获取申请详情)
    MongoDB-->>API: 申请记录信息
    API->>API: 检查申请状态是否为待处理
    
    Note over API: 3. 处理结果分支
    alt 同意申请(handleResult=1)
        Note over API: 3.1 Webhook前置回调
        API->>Webhook: BeforeAddFriendAgree
        Webhook-->>API: 回调结果验证
        
        Note over API: 3.2 建立好友关系
        API->>MongoDB: 开启事务
        
        Note over MongoDB: 创建双向好友关系
        API->>MongoDB: friends.insertMany([<br/>  {owner_user_id: fromUserID, friend_user_id: toUserID},<br/>  {owner_user_id: toUserID, friend_user_id: fromUserID}<br/>])
        
        Note over MongoDB: 更新申请状态
        API->>MongoDB: friend_requests.updateOne(<br/>  {from_user_id, to_user_id},<br/>  {handle_result: 1, handler_user_id, handle_time}<br/>)
        
        API->>MongoDB: 提交事务
        
        Note over API: 3.3 缓存清理
        API->>Redis: DelFriendIDs(fromUserID, toUserID)
        API->>Redis: DelTwoWayFriendIDs(fromUserID, toUserID)
        
        Note over API: 3.4 发送同意通知
        API->>Notify: FriendApplicationAgreedNotification
        
        Note over API: 3.5 Webhook后置回调
        API->>Webhook: AfterAddFriendAgree
        
    else 拒绝申请(handleResult=-1)
        Note over API: 3.6 更新申请状态
        API->>MongoDB: friend_requests.updateOne(<br/>  {from_user_id, to_user_id},<br/>  {handle_result: -1, handler_user_id, handle_time}<br/>)
        
        Note over API: 3.7 发送拒绝通知
        API->>Notify: FriendApplicationRefusedNotification
    end
    
    API-->>Client: RespondFriendApplyResp(成功)

关键缓存操作:

  • FRIEND_IDS:{fromUserID} - DELETE
  • FRIEND_IDS:{toUserID} - DELETE
  • COMMON_FRIENDS_IDS:{fromUserID} - DELETE
  • COMMON_FRIENDS_IDS:{toUserID} - DELETE

MongoDB事务操作:

// 同意申请的事务操作
session.withTransaction(() => {
    // 1. 创建双向好友关系
    db.friends.insertMany([
        {
            "owner_user_id": fromUserID,
            "friend_user_id": toUserID,
            "create_time": new Date(),
            "add_source": 1  // 通过申请添加
        },
        {
            "owner_user_id": toUserID, 
            "friend_user_id": fromUserID,
            "create_time": new Date(),
            "add_source": 1
        }
    ]);
    
    // 2. 更新申请状态
    db.friend_requests.updateOne(
        { "from_user_id": fromUserID, "to_user_id": toUserID },
        {
            $set: {
                "handle_result": 1,
                "handler_user_id": handlerUserID,
                "handle_time": new Date()
            }
        }
    );
});

🗑️ 3. DeleteFriend - 删除好友关系详细流程

sequenceDiagram
    participant Client as 客户端  
    participant API as Friend RPC
    participant Redis as Redis缓存
    participant MongoDB as MongoDB
    participant Notify as 通知服务
    
    Client->>API: DeleteFriendReq
    
    Note over API: 1. 权限验证阶段
    API->>API: CheckAccessV3(操作者权限)
    
    Note over API: 2. 好友关系验证
    API->>MongoDB: 检查好友关系是否存在
    MongoDB-->>API: 好友关系确认
    
    Note over API: 3. 数据库删除操作  
    API->>MongoDB: 删除单向好友关系
    Note over MongoDB: DELETE FROM friends<br/>WHERE owner_user_id=operatorUserID<br/>AND friend_user_id IN (friendUserIDs)
    
    Note over API: 4. 缓存清理
    API->>Redis: DelFriendIDs(operatorUserID)
    API->>Redis: DelTwoWayFriendIDs(operatorUserID)
    loop 每个被删除的好友
        API->>Redis: DelFriend(operatorUserID, friendUserID)
        API->>Redis: DelTwoWayFriendIDs(friendUserID)
    end
    
    Note over API: 5. 版本控制更新
    API->>API: OwnerIncrVersion(friends, operatorUserID, friendUserIDs, DELETE)
    
    Note over API: 6. 发送删除通知
    API->>Notify: FriendDeletedNotification
    Note over Notify: 通知对象: 被删除的好友<br/>通知内容: 好友关系已被删除
    
    API-->>Client: DeleteFriendResp(成功)

删除特点分析:

  • 单向删除:只删除操作者的好友关系,不影响对方
  • 批量支持:支持同时删除多个好友
  • 缓存一致性:删除相关的所有缓存数据

✏️ 4. SetFriendRemark - 设置好友备注详细流程

flowchart TD
    A[SetFriendRemarkReq] --> B[权限验证]
    B --> C[好友关系验证]
    C --> D[Webhook前置回调]
    D --> E[MongoDB更新操作]
    E --> F[缓存清理]
    F --> G[通知发送]
    G --> H[Webhook后置回调]
    H --> I[返回成功响应]
    
    E --> J["UPDATE friends SET remark=? WHERE owner_user_id=? AND friend_user_id=?"]
    F --> K["DEL FRIEND_INFO:{ownerUserID}-{friendUserID}"]

MongoDB更新操作:

db.friends.updateOne(
    { 
        "owner_user_id": ownerUserID, 
        "friend_user_id": friendUserID 
    },
    {
        $set: {
            "remark": newRemark,
            "operator_user_id": ownerUserID
        }
    }
)

缓存操作:

  • FRIEND_INFO:{ownerUserID}-{friendUserID} - DELETE

🚫 5. AddBlack - 添加黑名单详细流程

sequenceDiagram
    participant Client as 客户端
    participant API as Friend RPC
    participant Redis as Redis缓存  
    participant MongoDB as MongoDB
    participant Notify as 通知服务
    participant Webhook as Webhook服务
    
    Client->>API: AddBlackReq
    
    Note over API: 1. 权限验证
    API->>API: CheckAccessV3(操作者权限)
    
    Note over API: 2. 用户验证
    API->>API: 验证被拉黑用户存在
    API->>API: 检查是否已在黑名单中
    
    Note over API: 3. Webhook前置回调
    API->>Webhook: BeforeAddBlack
    Webhook-->>API: 回调结果验证
    
    Note over API: 4. 删除现有好友关系
    API->>MongoDB: 检查并删除好友关系
    Note over MongoDB: 如果存在好友关系,先删除<br/>DELETE FROM friends WHERE<br/>owner_user_id=operatorUserID AND<br/>friend_user_id=blockedUserID
    
    Note over API: 5. 创建黑名单记录
    API->>MongoDB: CreateBlack
    Note over MongoDB: 插入blacks集合<br/>- owner_user_id: 操作者<br/>- block_user_id: 被拉黑用户<br/>- create_time: 当前时间<br/>- add_source: 添加来源
    
    Note over API: 6. 缓存清理
    API->>Redis: DelBlackIDs(operatorUserID)
    API->>Redis: DelFriendIDs(operatorUserID)
    
    Note over API: 7. 发送拉黑通知
    API->>Notify: BlackAddedNotification
    
    Note over API: 8. Webhook后置回调
    API->>Webhook: AfterAddBlack
    
    API-->>Client: AddBlackResp(成功)

拉黑操作的特殊逻辑:

  1. 自动解除好友关系:拉黑时自动删除好友关系
  2. 单向黑名单:拉黑是单向操作,不影响对方的好友列表
  3. 权限隔离:被拉黑后无法发送消息和申请

📋 6. GetPaginationFriends - 分页获取好友列表详细流程

sequenceDiagram
    participant Client as 客户端
    participant API as Friend RPC
    participant LocalCache as 本地缓存
    participant Redis as Redis缓存
    participant MongoDB as MongoDB
    
    Client->>API: GetPaginationFriendsReq
    
    Note over API: 1. 权限验证
    API->>API: 验证查询者权限
    
    Note over API: 2. 三级缓存查询
    API->>LocalCache: 查询本地缓存
    
    alt 本地缓存命中
        LocalCache-->>API: 返回好友列表
    else 本地缓存未命中
        API->>Redis: LRANGE FRIEND_IDS:{ownerUserID}
        
        alt Redis缓存命中
            Redis-->>API: 返回好友ID列表
            loop 每个好友ID
                API->>Redis: HGETALL FRIEND_INFO:{ownerUserID}-{friendUserID}
            end
            API->>LocalCache: 更新本地缓存
        else Redis缓存未命中
            API->>MongoDB: db.friends.find({owner_user_id}).skip().limit()
            MongoDB-->>API: 完整好友信息
            
            API->>Redis: 缓存预热
            API->>LocalCache: 更新本地缓存
        end
    end
    
    API-->>Client: GetPaginationFriendsResp{好友列表}

分页查询优化:

// MongoDB聚合查询,支持高效分页
db.friends.aggregate([
    { $match: { "owner_user_id": ownerUserID } },
    { $sort: { 
        "is_pinned": -1,    // 置顶好友优先
        "create_time": -1   // 按添加时间倒序
    }},
    { $skip: offset },
    { $limit: pageSize }
]);

📊 7. 操作性能优化策略

缓存策略优化
操作类型缓存预热缓存失效批量优化
好友申请申请者好友列表无需失效批量申请处理
建立关系双方好友列表双方相关缓存批量好友导入
删除好友无需预热删除者缓存批量删除处理
查询操作主动预加载无需失效分页批量查询
数据库优化策略
  1. 索引优化:复合索引提升查询性能
  2. 分页优化:游标分页避免深度分页性能问题
  3. 批量操作:减少数据库往返次数
  4. 事务控制:确保数据一致性的同时提升性能

以上详细解析了OpenIM好友系统的核心功能模块和完整的权限控制体系。每个功能都包含完整的权限验证、数据存储、缓存管理和通知机制,确保好友关系管理的安全性、一致性和用户体验。完善的权限矩阵为系统提供了精细化的访问控制,满足不同场景下的隐私和安全需求。