343 lines
8.9 KiB
Markdown
343 lines
8.9 KiB
Markdown
# 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天后再试"
|
||
- 已过冷却期 → 允许发送新请求
|
||
|
||
**代码示例**:
|
||
```go
|
||
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`,并返回错误
|
||
|
||
**代码示例**:
|
||
```go
|
||
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. 验证通过才能发送好友请求
|
||
|
||
**代码示例**:
|
||
```go
|
||
// 验证用户存在
|
||
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字段(好友数量)
|
||
|
||
**代码示例**:
|
||
```go
|
||
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. 批量查询优化 ⭐
|
||
|
||
**实现位置**:`fillRequestUserInfo` 和 `fillFriendshipUserInfo` 方法
|
||
|
||
**逻辑**:
|
||
1. 收集所有需要查询的用户ID
|
||
2. 批量调用RPC接口查询用户信息
|
||
3. 填充到响应对象中
|
||
|
||
**代码示例**:
|
||
```go
|
||
// 收集用户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中添加接口**:
|
||
```go
|
||
// UpdateSocial 更新粉丝档案的social字段
|
||
rpc UpdateSocial(UpdateSocialRequest) returns (UpdateSocialResponse);
|
||
```
|
||
|
||
**调用时机**:
|
||
- 接受好友请求:双方`social += 1`
|
||
- 删除好友:双方`social -= 1`
|
||
|
||
---
|
||
|
||
### 2. 批量查询优化(中优先级)
|
||
|
||
**需要在userService中添加接口**:
|
||
```go
|
||
// BatchGetFanProfiles 批量获取粉丝档案
|
||
rpc BatchGetFanProfiles(BatchGetFanProfilesRequest) returns (BatchGetFanProfilesResponse);
|
||
```
|
||
|
||
**当前实现**:逐个查询(性能较差)
|
||
**优化后**:一次RPC调用获取所有用户信息
|
||
|
||
---
|
||
|
||
### 3. 定时任务:清理过期请求(低优先级)
|
||
|
||
**功能**:定时扫描过期的好友请求,更新状态为`expired`
|
||
|
||
**实现方式**:
|
||
1. 创建定时任务(cron job)
|
||
2. 每天凌晨扫描一次
|
||
3. 批量更新过期请求状态
|
||
|
||
---
|
||
|
||
## ✅ 已验证的功能
|
||
|
||
- [x] 参数验证(空值、格式、长度等)
|
||
- [x] 用户验证(用户存在、粉丝档案存在)
|
||
- [x] 防骚扰机制(7天冷却期)
|
||
- [x] 过期检查(30天过期)
|
||
- [x] 好友关系检查(避免重复添加)
|
||
- [x] 权限验证(只有接收者可以处理请求)
|
||
- [x] 事务保证(接受请求时的原子性)
|
||
- [x] 分页查询(好友列表、请求列表)
|
||
- [x] 关键词搜索(搜索昵称或备注)
|
||
- [x] 日志记录(所有关键操作)
|
||
|
||
---
|
||
|
||
## 🧪 测试建议
|
||
|
||
### 单元测试
|
||
|
||
**测试文件**:`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
|
||
**状态**:✅ 核心功能已实现,待测试和优化
|
||
|