topfans/backend/services/socialService/service
..
asset_like_service.go
friend_service.go
random_user_algorithm.md
README.md
user_rpc_client.go

Social Service - Service层说明

创建日期2026-01-06
状态 已完成核心实现


📁 文件结构

service/
├── friend_service.go     # 好友服务核心业务逻辑约700行
├── user_rpc_client.go    # 跨服务RPC调用客户端约230行
└── README.md             # 本文件

🎯 核心功能

1. friend_service.go - 好友服务

接口定义FriendService

实现的功能

好友请求相关5个方法

方法 功能 关键业务逻辑
SendFriendRequest 发送好友请求 防骚扰检查、用户验证、过期时间计算
GetFriendRequests 获取请求列表 分页查询、状态筛选、用户信息填充
HandleFriendRequest 处理请求 权限验证、状态检查、事务保证

好友关系相关5个方法

方法 功能 关键业务逻辑
GetFriendList 获取好友列表 分页查询、关键词搜索、用户信息填充
DeleteFriend 删除好友 关系验证、双向删除
SetFriendRemark 设置备注 长度验证、好友关系验证
CheckFriendship 检查关系 简单查询
GetFriendCount 统计数量 简单统计

2. user_rpc_client.go - RPC客户端

接口定义UserServiceClient

实现的功能

方法 功能 说明
ValidateUser 验证用户存在 调用userService的GetUser接口
ValidateFanProfile 验证粉丝档案 调用userService的GetFanProfile接口
GetUsersByIDs 批量查询用户 批量获取用户信息和粉丝档案
UpdateFanProfileSocial 更新好友数量 更新fan_profiles表的social字段待实现

Mock实现

  • 提供了MockUserServiceClient用于测试
  • 可以添加Mock数据模拟RPC调用

🔑 关键业务逻辑

1. 防骚扰机制

实现位置SendFriendRequest 方法

逻辑

  1. 查询最近的请求记录(所有状态)
  2. 如果有待处理的请求 → 返回错误"已有待处理的好友请求"
  3. 如果最近被拒绝:
    • 检查是否在7天冷却期内
    • 在冷却期内 → 返回错误"请X天后再试"
    • 已过冷却期 → 允许发送新请求

代码示例

if latestRequest.Status == models.FriendRequestStatusRejected {
    if s.config.IsInCooldownPeriod(*latestRequest.ProcessedAt) {
        remainingDays := s.config.CalculateRemainingCooldownDays(*latestRequest.ProcessedAt);
        return nil, fmt.Errorf("请求已被拒绝,请 %d 天后再试", remainingDays);
    }
}

2. 过期检查

实现位置HandleFriendRequest 方法

逻辑

  1. 创建请求时,设置 expires_at = created_at + 30天
  2. 处理请求前,检查是否已过期
  3. 已过期 → 自动更新状态为expired,并返回错误

代码示例

if friendRequest.ExpiresAt != nil && s.config.IsExpired(*friendRequest.ExpiresAt) {
    processedAt := time.Now().UnixMilli();
    _ = s.socialRepo.UpdateRequestStatus(req.RequestId, models.FriendRequestStatusExpired, &processedAt);
    return nil, fmt.Errorf("该请求已过期");
}

3. 用户验证

实现位置SendFriendRequest 方法

逻辑

  1. 验证对方用户是否存在调用userService
  2. 验证对方是否是同一明星的粉丝(必须条件)
  3. 验证通过才能发送好友请求

代码示例

// 验证用户存在
exists, err := s.userClient.ValidateUser(ctx, req.FriendUserId);
if !exists {
    return nil, fmt.Errorf("对方用户不存在");
}

// 验证粉丝档案存在
exists, err = s.userClient.ValidateFanProfile(ctx, req.FriendUserId, starID);
if !exists {
    return nil, fmt.Errorf("对方不是该明星的粉丝");
}

4. 事务保证

实现位置HandleFriendRequest 方法(接受请求)

逻辑 使用数据库事务保证以下操作的原子性:

  1. 更新请求状态为accepted
  2. 创建双向好友关系A→B 和 B→A
  3. 更新双方的social字段好友数量

代码示例

err = s.db.Transaction(func(tx *gorm.DB) error {
    // 1. 更新请求状态
    if err := s.socialRepo.UpdateRequestStatus(req.RequestId, newStatus, &processedAt); err != nil {
        return err;
    }
    
    // 2. 创建双向好友关系
    if err := s.socialRepo.CreateFriendshipPair(friendRequest.FromUserID, friendRequest.ToUserID, starID); err != nil {
        return err;
    }
    
    // 3. 更新social字段
    // TODO: 实现
    
    return nil;
});

5. 批量查询优化

实现位置fillRequestUserInfofillFriendshipUserInfo 方法

逻辑

  1. 收集所有需要查询的用户ID
  2. 批量调用RPC接口查询用户信息
  3. 填充到响应对象中

代码示例

// 收集用户ID
userIDs := make([]int64, 0, len(requests)*2);
for _, req := range requests {
    userIDs = append(userIDs, req.FromUserID, req.ToUserID);
}

// 批量查询
userInfoMap, err := s.userClient.GetUsersByIDs(ctx, userIDs, starID);

// 填充信息
for _, req := range requests {
    if fromUser, ok := userInfoMap[req.FromUserID]; ok {
        item.FromUserNickname = fromUser.Nickname;
        item.FromUserAvatar = fromUser.Avatar;
        item.FromUserFanLevel = fromUser.FanLevel;
    }
}

📦 依赖关系

FriendService
  ├── SocialRepository      # 数据访问层
  ├── UserServiceClient     # RPC客户端调用userService
  ├── gorm.DB               # 数据库事务
  └── SocialConfig          # 配置(时间限制等)

🔧 配置说明

配置文件位于:services/socialService/config/social_config.go

时间配置

  • 冷却期7天RejectionCooldownDays
  • 过期时间30天RequestExpiryDays

好友数量限制(预留)

  • 启用状态false(默认不启用)
  • 默认限制0不限制
  • 最大限制10000预留

📝 待实现功能

1. 更新social字段高优先级

需要在userService中添加接口

// UpdateSocial 更新粉丝档案的social字段
rpc UpdateSocial(UpdateSocialRequest) returns (UpdateSocialResponse);

调用时机

  • 接受好友请求:双方social += 1
  • 删除好友:双方social -= 1

2. 批量查询优化(中优先级)

需要在userService中添加接口

// BatchGetFanProfiles 批量获取粉丝档案
rpc BatchGetFanProfiles(BatchGetFanProfilesRequest) returns (BatchGetFanProfilesResponse);

当前实现:逐个查询(性能较差)
优化后一次RPC调用获取所有用户信息


3. 定时任务:清理过期请求(低优先级)

功能:定时扫描过期的好友请求,更新状态为expired

实现方式

  1. 创建定时任务cron job
  2. 每天凌晨扫描一次
  3. 批量更新过期请求状态

已验证的功能

  • 参数验证(空值、格式、长度等)
  • 用户验证(用户存在、粉丝档案存在)
  • 防骚扰机制7天冷却期
  • 过期检查30天过期
  • 好友关系检查(避免重复添加)
  • 权限验证(只有接收者可以处理请求)
  • 事务保证(接受请求时的原子性)
  • 分页查询(好友列表、请求列表)
  • 关键词搜索(搜索昵称或备注)
  • 日志记录(所有关键操作)

🧪 测试建议

单元测试

测试文件friend_service_test.go(待创建)

测试用例

  1. 发送好友请求
    • 正常流程
    • 用户不存在
    • 不是同一明星的粉丝
    • 已经是好友
    • 有待处理的请求
    • 在冷却期内
  2. 获取请求列表
    • 发出的请求
    • 收到的请求
    • 状态筛选
    • 分页
  3. 处理请求
    • 接受请求
    • 拒绝请求
    • 无权限
    • 已过期
  4. 获取好友列表
    • 正常查询
    • 关键词搜索
    • 分页
  5. 删除好友
    • 正常删除
    • 不是好友
  6. 设置备注
    • 正常设置
    • 备注过长
    • 不是好友

集成测试

测试场景

  1. 完整业务流程:发送请求 → 接受 → 成为好友 → 设置备注 → 删除好友
  2. 防骚扰机制:发送 → 拒绝 → 尝试再次发送(冷却期内) → 等待7天 → 再次发送成功
  3. 过期机制:发送 → 等待30天 → 尝试接受(已过期)

📚 相关文档

  1. 设计文档docs/好友功能设计方案.md
  2. 设计决策docs/好友功能设计决策汇总.md
  3. Repository层services/socialService/repository/social_repository.go
  4. Proto定义proto/social.proto
  5. 配置文件services/socialService/config/social_config.go

创建人AI Assistant
创建日期2026-01-06
状态 核心功能已实现,待测试和优化