532 lines
13 KiB
Go
532 lines
13 KiB
Go
/**
|
||
* package service
|
||
* 作用:联系人管理服务
|
||
*/
|
||
package service
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"time"
|
||
"xk-websocket-v2/internal/model"
|
||
"xk-websocket-v2/internal/utils"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// ContactService 联系人服务结构体
|
||
type ContactService struct {
|
||
DB *gorm.DB
|
||
}
|
||
|
||
// ContactSvc 全局单例
|
||
var ContactSvc *ContactService
|
||
|
||
/**
|
||
* InitContactService
|
||
* 功能:初始化联系人服务
|
||
*/
|
||
func InitContactService(db *gorm.DB) {
|
||
ContactSvc = &ContactService{DB: db}
|
||
}
|
||
|
||
/**
|
||
* SearchUsers
|
||
* 功能:搜索用户(按用户名、ID、邮箱、手机号)
|
||
*/
|
||
func (s *ContactService) SearchUsers(keyword string, limit int) ([]model.User, error) {
|
||
return UserSvc.SearchUsers(keyword, limit)
|
||
}
|
||
|
||
/**
|
||
* AddFriend
|
||
* 功能:发送好友申请
|
||
*/
|
||
func (s *ContactService) AddFriend(fromUserID, toUserID, message string) error {
|
||
// 检查是否已经是好友
|
||
var existingContact model.UserContact
|
||
result := s.DB.Where("user_id = ? AND contact_id = ?", fromUserID, toUserID).First(&existingContact)
|
||
if result.Error == nil {
|
||
return errors.New("已经是好友关系")
|
||
}
|
||
|
||
// 检查是否已有待处理的申请
|
||
var existingRequest model.FriendRequest
|
||
result = s.DB.Where("from_user_id = ? AND to_user_id = ? AND status = ?",
|
||
fromUserID, toUserID, "pending").First(&existingRequest)
|
||
if result.Error == nil {
|
||
return errors.New("已发送过好友申请")
|
||
}
|
||
|
||
// 创建好友申请
|
||
request := model.FriendRequest{
|
||
FromUserID: fromUserID,
|
||
ToUserID: toUserID,
|
||
Message: message,
|
||
Status: "pending",
|
||
}
|
||
|
||
return s.DB.Create(&request).Error
|
||
}
|
||
|
||
/**
|
||
* GetFriendRequests
|
||
* 功能:获取好友申请列表
|
||
* 包括:
|
||
* 1. 发送给当前用户的待处理申请 (to_user_id = userID AND status = 'pending')
|
||
* 2. 当前用户发送的被拒绝的申请 (from_user_id = userID AND status = 'rejected')
|
||
*/
|
||
func (s *ContactService) GetFriendRequests(userID string) ([]model.FriendRequest, error) {
|
||
var requests []model.FriendRequest
|
||
result := s.DB.Where("(to_user_id = ? AND status = ?) OR (from_user_id = ? AND status = ?)",
|
||
userID, "pending", userID, "rejected").
|
||
Order("created_at DESC").
|
||
Find(&requests)
|
||
return requests, result.Error
|
||
}
|
||
|
||
/**
|
||
* AcceptFriendRequest
|
||
* 功能:接受好友申请
|
||
*/
|
||
func (s *ContactService) AcceptFriendRequest(requestID uint, userID string) error {
|
||
// 查找申请
|
||
var request model.FriendRequest
|
||
if err := s.DB.First(&request, requestID).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// 验证是否为接收者
|
||
if request.ToUserID != userID {
|
||
return errors.New("无权操作此申请")
|
||
}
|
||
|
||
// 开始事务
|
||
tx := s.DB.Begin()
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
tx.Rollback()
|
||
}
|
||
}()
|
||
|
||
// 更新申请状态
|
||
if err := tx.Model(&request).Update("status", "accepted").Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
// 使用雪花ID生成唯一的房间ID
|
||
roomID, err := utils.NextIDString()
|
||
if err != nil {
|
||
tx.Rollback()
|
||
return fmt.Errorf("生成房间ID失败: %v", err)
|
||
}
|
||
|
||
// 创建房间(p2p类型)
|
||
room := model.ChatRoom{
|
||
RoomID: roomID,
|
||
RoomType: "p2p",
|
||
OwnerID: "0", // 单聊群主为0
|
||
CreatorID: request.ToUserID, // 接受者为创建者
|
||
}
|
||
if err := tx.Create(&room).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
// 创建房间成员记录(两个用户)
|
||
member1 := model.RoomMember{
|
||
RoomID: roomID,
|
||
UserID: request.FromUserID,
|
||
}
|
||
member2 := model.RoomMember{
|
||
RoomID: roomID,
|
||
UserID: request.ToUserID,
|
||
}
|
||
if err := tx.Create(&member1).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
if err := tx.Create(&member2).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
// 创建双向好友关系,并绑定房间ID
|
||
contact1 := model.UserContact{
|
||
UserID: request.FromUserID,
|
||
ContactID: request.ToUserID,
|
||
RoomID: roomID,
|
||
}
|
||
contact2 := model.UserContact{
|
||
UserID: request.ToUserID,
|
||
ContactID: request.FromUserID,
|
||
RoomID: roomID,
|
||
}
|
||
|
||
if err := tx.Create(&contact1).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
if err := tx.Create(&contact2).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
// 为双方创建会话记录
|
||
now := time.Now()
|
||
conv1 := model.ChatConversation{
|
||
UserID: request.FromUserID,
|
||
TargetID: request.ToUserID,
|
||
RoomID: roomID,
|
||
Type: 1, // 私聊
|
||
LastTime: now,
|
||
}
|
||
conv2 := model.ChatConversation{
|
||
UserID: request.ToUserID,
|
||
TargetID: request.FromUserID,
|
||
RoomID: roomID,
|
||
Type: 1, // 私聊
|
||
LastTime: now,
|
||
}
|
||
|
||
if err := tx.Create(&conv1).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
if err := tx.Create(&conv2).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
// 提交事务
|
||
if err := tx.Commit().Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// 发送欢迎消息(在事务外执行,避免影响主流程)
|
||
go s.sendWelcomeMessage(request.ToUserID, request.FromUserID, roomID)
|
||
|
||
return nil
|
||
}
|
||
|
||
/**
|
||
* sendWelcomeMessage
|
||
* 功能:发送欢迎消息
|
||
*/
|
||
func (s *ContactService) sendWelcomeMessage(senderID, receiverID, roomID string) {
|
||
// 创建消息记录
|
||
msg := model.ChatMessage{
|
||
RoomID: roomID,
|
||
SenderUserID: senderID,
|
||
ReceiverUserID: receiverID,
|
||
MessageType: 0, // 文本消息
|
||
Content: "我已经通过了你的好友申请,开始和我聊天吧~",
|
||
}
|
||
|
||
if err := s.DB.Create(&msg).Error; err != nil {
|
||
return
|
||
}
|
||
|
||
// 更新会话列表
|
||
if ConversationSvc != nil {
|
||
_ = ConversationSvc.UpsertConversationOnMessage(senderID, receiverID, roomID, msg, true)
|
||
_ = ConversationSvc.UpsertConversationOnMessage(receiverID, senderID, roomID, msg, false)
|
||
}
|
||
|
||
// 通过 WebSocket 推送消息
|
||
if ChatSvc != nil {
|
||
pushMsg := model.WsPayload{
|
||
RequestType: "receive_message",
|
||
Data: msg,
|
||
}
|
||
msgBytes, _ := json.Marshal(pushMsg)
|
||
ChatSvc.DispatchMessage(receiverID, msgBytes)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* RejectFriendRequest
|
||
* 功能:拒绝好友申请
|
||
*/
|
||
func (s *ContactService) RejectFriendRequest(requestID uint, userID string) error {
|
||
var request model.FriendRequest
|
||
if err := s.DB.First(&request, requestID).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
if request.ToUserID != userID {
|
||
return errors.New("无权操作此申请")
|
||
}
|
||
|
||
return s.DB.Model(&request).Update("status", "rejected").Error
|
||
}
|
||
|
||
/**
|
||
* GetContacts
|
||
* 功能:获取好友列表
|
||
*/
|
||
func (s *ContactService) GetContacts(userID string) ([]model.UserContact, error) {
|
||
var contacts []model.UserContact
|
||
result := s.DB.Where("user_id = ?", userID).
|
||
Order("is_top DESC, last_chat_time DESC, created_at DESC").
|
||
Find(&contacts)
|
||
return contacts, result.Error
|
||
}
|
||
|
||
/**
|
||
* GetContactDetail
|
||
* 功能:获取好友详情
|
||
*/
|
||
func (s *ContactService) GetContactDetail(userID, contactID string) (*model.UserContact, error) {
|
||
var contact model.UserContact
|
||
result := s.DB.Where("user_id = ? AND contact_id = ?", userID, contactID).First(&contact)
|
||
if result.Error != nil {
|
||
return nil, result.Error
|
||
}
|
||
return &contact, nil
|
||
}
|
||
|
||
/**
|
||
* UpdateContact
|
||
* 功能:更新好友信息(备注、分组等)
|
||
*/
|
||
func (s *ContactService) UpdateContact(userID, contactID string, updates map[string]interface{}) error {
|
||
return s.DB.Model(&model.UserContact{}).
|
||
Where("user_id = ? AND contact_id = ?", userID, contactID).
|
||
Updates(updates).Error
|
||
}
|
||
|
||
/**
|
||
* DeleteContact
|
||
* 功能:删除好友
|
||
*/
|
||
func (s *ContactService) DeleteContact(userID, contactID string) error {
|
||
// 删除双向好友关系
|
||
tx := s.DB.Begin()
|
||
|
||
if err := tx.Where("user_id = ? AND contact_id = ?", userID, contactID).Delete(&model.UserContact{}).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
if err := tx.Where("user_id = ? AND contact_id = ?", contactID, userID).Delete(&model.UserContact{}).Error; err != nil {
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
return tx.Commit().Error
|
||
}
|
||
|
||
/**
|
||
* GetGroups
|
||
* 功能:获取分组列表
|
||
*/
|
||
func (s *ContactService) GetGroups(userID string) ([]model.ContactGroup, error) {
|
||
var groups []model.ContactGroup
|
||
result := s.DB.Where("user_id = ?", userID).
|
||
Order("sort_order ASC, created_at ASC").
|
||
Find(&groups)
|
||
return groups, result.Error
|
||
}
|
||
|
||
/**
|
||
* CreateGroup
|
||
* 功能:创建分组
|
||
*/
|
||
func (s *ContactService) CreateGroup(userID, groupName string) (*model.ContactGroup, error) {
|
||
// 获取当前最大排序值
|
||
var maxOrder int
|
||
s.DB.Model(&model.ContactGroup{}).
|
||
Where("user_id = ?", userID).
|
||
Select("COALESCE(MAX(sort_order), 0)").
|
||
Scan(&maxOrder)
|
||
|
||
group := model.ContactGroup{
|
||
UserID: userID,
|
||
GroupName: groupName,
|
||
SortOrder: maxOrder + 1,
|
||
}
|
||
|
||
if err := s.DB.Create(&group).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &group, nil
|
||
}
|
||
|
||
/**
|
||
* UpdateGroup
|
||
* 功能:更新分组
|
||
*/
|
||
func (s *ContactService) UpdateGroup(groupID uint, userID string, updates map[string]interface{}) error {
|
||
return s.DB.Model(&model.ContactGroup{}).
|
||
Where("id = ? AND user_id = ?", groupID, userID).
|
||
Updates(updates).Error
|
||
}
|
||
|
||
/**
|
||
* DeleteGroup
|
||
* 功能:删除分组
|
||
*/
|
||
func (s *ContactService) DeleteGroup(groupID uint, userID string) error {
|
||
// 检查分组是否存在且属于该用户
|
||
var group model.ContactGroup
|
||
if err := s.DB.Where("id = ? AND user_id = ?", groupID, userID).First(&group).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// 将该分组下的联系人移到默认分组(group_id = 0)
|
||
if err := s.DB.Model(&model.UserContact{}).
|
||
Where("user_id = ? AND group_id = ?", userID, groupID).
|
||
Update("group_id", 0).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
// 删除分组
|
||
return s.DB.Delete(&group).Error
|
||
}
|
||
|
||
/**
|
||
* GetContactsWithUserInfo
|
||
* 功能:获取好友列表(包含用户信息)
|
||
*/
|
||
func (s *ContactService) GetContactsWithUserInfo(userID string) ([]map[string]interface{}, error) {
|
||
var contacts []model.UserContact
|
||
if err := s.DB.Where("user_id = ?", userID).
|
||
Order("is_top DESC, last_chat_time DESC").
|
||
Find(&contacts).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var result []map[string]interface{}
|
||
for _, contact := range contacts {
|
||
// 获取联系人用户信息
|
||
var user model.User
|
||
if err := s.DB.Where("id = ?", contact.ContactID).First(&user).Error; err != nil {
|
||
continue
|
||
}
|
||
user.Password = ""
|
||
|
||
// 组合数据,包含 user 对象
|
||
item := map[string]interface{}{
|
||
"id": contact.ContactID,
|
||
"user_id": contact.ContactID,
|
||
"contact_user_id": contact.ContactID,
|
||
"user": user, // 包含完整的用户信息对象
|
||
"remark_name": contact.RemarkName,
|
||
"room_id": contact.RoomID,
|
||
"group_id": contact.GroupID,
|
||
"is_top": contact.IsTop,
|
||
"is_muted": contact.IsMuted,
|
||
"is_special_care": contact.IsSpecialCare,
|
||
"is_blocked": contact.IsBlocked,
|
||
"last_chat_time": contact.LastChatTime,
|
||
"last_message": contact.LastMessage,
|
||
"last_msg": contact.LastMessage,
|
||
"unread_count": contact.UnreadCount,
|
||
"unread": contact.UnreadCount,
|
||
}
|
||
result = append(result, item)
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
/**
|
||
* GetFriendUserIDs
|
||
* 功能:获取用户的所有好友ID列表
|
||
*/
|
||
func (s *ContactService) GetFriendUserIDs(userID string) ([]string, error) {
|
||
var contacts []model.UserContact
|
||
if err := s.DB.Where("user_id = ?", userID).Find(&contacts).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
friendIDs := make([]string, 0, len(contacts))
|
||
for _, contact := range contacts {
|
||
friendIDs = append(friendIDs, contact.ContactID)
|
||
}
|
||
|
||
return friendIDs, nil
|
||
}
|
||
|
||
/**
|
||
* SearchUsersWithFriendStatus
|
||
* 功能:搜索用户并返回带有 is_friend 标识的结果
|
||
* 参数:
|
||
* - currentUserID: 当前登录用户ID(用于判断好友关系,可为空)
|
||
* - keyword: 搜索关键词
|
||
* - limit: 返回数量限制
|
||
*/
|
||
func (s *ContactService) SearchUsersWithFriendStatus(currentUserID, keyword string, limit int) ([]map[string]interface{}, error) {
|
||
// 搜索用户
|
||
users, err := UserSvc.SearchUsers(keyword, limit)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 获取当前用户的好友ID集合
|
||
friendIDSet := make(map[string]bool)
|
||
if currentUserID != "" {
|
||
friendIDs, err := s.GetFriendUserIDs(currentUserID)
|
||
if err == nil {
|
||
for _, id := range friendIDs {
|
||
friendIDSet[id] = true
|
||
}
|
||
}
|
||
}
|
||
|
||
// 构建带有 is_friend 标识的结果
|
||
result := make([]map[string]interface{}, 0, len(users))
|
||
for _, user := range users {
|
||
item := map[string]interface{}{
|
||
"id": user.ID,
|
||
"email": user.Email,
|
||
"phone": user.Phone,
|
||
"name": user.Name,
|
||
"avatar": user.Avatar,
|
||
"desc": user.Desc,
|
||
"region": user.Region,
|
||
"created_at": user.CreatedAt,
|
||
"updated_at": user.UpdatedAt,
|
||
"is_friend": friendIDSet[user.ID],
|
||
}
|
||
result = append(result, item)
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
/**
|
||
* GetUserDetailWithFriendStatus
|
||
* 功能:获取用户详情(包含好友关系)
|
||
* 参数:
|
||
* - currentUserID: 当前登录用户ID
|
||
* - targetUserID: 目标用户ID
|
||
* 返回:
|
||
* - map 包含 is_friend、contact、user 字段
|
||
*/
|
||
func (s *ContactService) GetUserDetailWithFriendStatus(currentUserID, targetUserID string) (map[string]interface{}, error) {
|
||
// 获取目标用户信息
|
||
user, err := UserSvc.GetUserByID(targetUserID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 查询联系人关系,判断是否是好友
|
||
var isFriend bool
|
||
var contact *model.UserContact
|
||
contact, err = s.GetContactDetail(currentUserID, targetUserID)
|
||
if err == nil && contact != nil {
|
||
isFriend = true
|
||
}
|
||
|
||
return map[string]interface{}{
|
||
"is_friend": isFriend,
|
||
"contact": contact,
|
||
"user": user,
|
||
}, nil
|
||
}
|