Files
nl-im-service/internal/service/contact_service.go
2025-12-10 15:19:26 +08:00

532 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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
}