Files
nl-im-service/internal/service/search_service.go
2025-12-14 15:19:24 +08:00

353 lines
8.7 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 (
"strings"
"xk-websocket-v2/internal/model"
"gorm.io/gorm"
)
// SearchService 搜索服务结构体
type SearchService struct {
DB *gorm.DB
}
// SearchSvc 全局单例
var SearchSvc *SearchService
/**
* InitSearchService
* 功能:初始化搜索服务
*/
func InitSearchService(db *gorm.DB) {
SearchSvc = &SearchService{DB: db}
}
// SearchResult 搜索结果结构
type SearchResult struct {
Contacts []ContactSearchResult `json:"contacts"`
Groups []GroupSearchResult `json:"groups"`
Messages []MessageSearchResult `json:"messages"`
}
// ContactSearchResult 联系人搜索结果
type ContactSearchResult struct {
User *model.User `json:"user"`
RemarkName string `json:"remark_name"`
RoomID string `json:"room_id"`
}
// GroupSearchResult 群聊搜索结果
type GroupSearchResult struct {
RoomID string `json:"room_id"`
RoomName string `json:"room_name"`
RoomAvatar string `json:"room_avatar"`
OwnerID string `json:"owner_id"`
MemberCount int `json:"member_count"`
}
// MessageSearchResult 消息搜索结果
type MessageSearchResult struct {
ID uint `json:"id"`
RoomID string `json:"room_id"`
RoomName string `json:"room_name"`
Content string `json:"content"`
MessageType int `json:"message_type"`
CreatedAt string `json:"created_at"`
Sender *model.User `json:"sender"`
IsGroupChat bool `json:"is_group_chat"`
MatchContent string `json:"match_content"`
}
/**
* GlobalSearch
* 功能:全局聚合搜索(联系人、群聊、聊天记录)
* @param userID 当前用户ID
* @param keyword 搜索关键词
* @param searchType 搜索类型:all, contacts, groups, messages
* @param limit 每类结果的最大数量
*/
func (s *SearchService) GlobalSearch(userID, keyword, searchType string, limit int) (*SearchResult, error) {
result := &SearchResult{
Contacts: []ContactSearchResult{},
Groups: []GroupSearchResult{},
Messages: []MessageSearchResult{},
}
if keyword == "" {
return result, nil
}
keyword = strings.TrimSpace(keyword)
likeKeyword := "%" + keyword + "%"
// 搜索联系人
if searchType == "all" || searchType == "contacts" {
contacts, err := s.searchContacts(userID, likeKeyword, limit)
if err == nil {
result.Contacts = contacts
}
}
// 搜索群聊
if searchType == "all" || searchType == "groups" {
groups, err := s.searchGroups(userID, likeKeyword, limit)
if err == nil {
result.Groups = groups
}
}
// 搜索聊天记录
if searchType == "all" || searchType == "messages" {
messages, err := s.searchMessages(userID, likeKeyword, limit)
if err == nil {
result.Messages = messages
}
}
return result, nil
}
/**
* searchContacts
* 功能:搜索联系人(好友)
* 搜索字段:用户名、备注名、邮箱、手机号
*/
func (s *SearchService) searchContacts(userID, likeKeyword string, limit int) ([]ContactSearchResult, error) {
var contacts []model.UserContact
var results []ContactSearchResult
// 查询当前用户的好友
err := s.DB.Where("user_id = ?", userID).Find(&contacts).Error
if err != nil {
return results, err
}
// 获取所有好友的用户ID
contactIDs := make([]string, 0, len(contacts))
contactMap := make(map[string]model.UserContact)
for _, c := range contacts {
contactIDs = append(contactIDs, c.ContactID)
contactMap[c.ContactID] = c
}
if len(contactIDs) == 0 {
return results, nil
}
// 查询匹配的用户
var users []model.User
err = s.DB.Where("id IN ?", contactIDs).
Where("name LIKE ? OR email LIKE ? OR phone LIKE ?", likeKeyword, likeKeyword, likeKeyword).
Limit(limit).
Find(&users).Error
if err != nil {
return results, err
}
// 组装结果
for _, user := range users {
contact := contactMap[user.ID]
userCopy := user
results = append(results, ContactSearchResult{
User: &userCopy,
RemarkName: contact.RemarkName,
RoomID: contact.RoomID,
})
}
// 同时搜索备注名匹配的联系人
var remarkContacts []model.UserContact
err = s.DB.Where("user_id = ? AND remark_name LIKE ?", userID, likeKeyword).
Limit(limit).
Find(&remarkContacts).Error
if err == nil {
remarkIDs := make([]string, 0)
existingIDs := make(map[string]bool)
for _, r := range results {
existingIDs[r.User.ID] = true
}
for _, c := range remarkContacts {
if !existingIDs[c.ContactID] {
remarkIDs = append(remarkIDs, c.ContactID)
}
}
if len(remarkIDs) > 0 {
var remarkUsers []model.User
s.DB.Where("id IN ?", remarkIDs).Find(&remarkUsers)
userMap := make(map[string]model.User)
for _, u := range remarkUsers {
userMap[u.ID] = u
}
for _, c := range remarkContacts {
if u, ok := userMap[c.ContactID]; ok && !existingIDs[c.ContactID] {
userCopy := u
results = append(results, ContactSearchResult{
User: &userCopy,
RemarkName: c.RemarkName,
RoomID: c.RoomID,
})
}
}
}
}
return results, nil
}
/**
* searchGroups
* 功能:搜索群聊
* 搜索字段:群名称
*/
func (s *SearchService) searchGroups(userID, likeKeyword string, limit int) ([]GroupSearchResult, error) {
var results []GroupSearchResult
// 获取用户所在的群聊
var members []model.RoomMember
err := s.DB.Where("user_id = ?", userID).Find(&members).Error
if err != nil {
return results, err
}
roomIDs := make([]string, 0, len(members))
for _, m := range members {
if strings.HasPrefix(m.RoomID, "group_") {
roomIDs = append(roomIDs, m.RoomID)
}
}
if len(roomIDs) == 0 {
return results, nil
}
// 查询匹配的群聊
var rooms []model.ChatRoom
err = s.DB.Where("room_id IN ? AND room_type = ? AND room_name LIKE ?", roomIDs, "group", likeKeyword).
Limit(limit).
Find(&rooms).Error
if err != nil {
return results, err
}
// 获取每个群的成员数量
for _, room := range rooms {
var memberCount int64
s.DB.Model(&model.RoomMember{}).Where("room_id = ?", room.RoomID).Count(&memberCount)
results = append(results, GroupSearchResult{
RoomID: room.RoomID,
RoomName: room.RoomName,
RoomAvatar: room.RoomAvatar,
OwnerID: room.OwnerID,
MemberCount: int(memberCount),
})
}
return results, nil
}
/**
* searchMessages
* 功能:搜索聊天记录(仅文本消息)
* 搜索字段:消息内容
*/
func (s *SearchService) searchMessages(userID, likeKeyword string, limit int) ([]MessageSearchResult, error) {
var results []MessageSearchResult
// 获取用户有权限查看的房间
var contacts []model.UserContact
s.DB.Where("user_id = ?", userID).Find(&contacts)
var members []model.RoomMember
s.DB.Where("user_id = ?", userID).Find(&members)
roomIDs := make([]string, 0)
for _, c := range contacts {
if c.RoomID != "" {
roomIDs = append(roomIDs, c.RoomID)
}
}
for _, m := range members {
roomIDs = append(roomIDs, m.RoomID)
}
if len(roomIDs) == 0 {
return results, nil
}
// 查询匹配的消息(仅文本消息)
var messages []model.ChatMessage
err := s.DB.Where("room_id IN ? AND message_type = ? AND content LIKE ?", roomIDs, 0, likeKeyword).
Order("created_at DESC").
Limit(limit).
Find(&messages).Error
if err != nil {
return results, err
}
// 获取发送者信息和房间信息
senderIDs := make([]string, 0)
roomIDSet := make(map[string]bool)
for _, m := range messages {
senderIDs = append(senderIDs, m.SenderUserID)
roomIDSet[m.RoomID] = true
}
var users []model.User
if len(senderIDs) > 0 {
s.DB.Where("id IN ?", senderIDs).Find(&users)
}
userMap := make(map[string]*model.User)
for i := range users {
userMap[users[i].ID] = &users[i]
}
uniqueRoomIDs := make([]string, 0, len(roomIDSet))
for rid := range roomIDSet {
uniqueRoomIDs = append(uniqueRoomIDs, rid)
}
var rooms []model.ChatRoom
if len(uniqueRoomIDs) > 0 {
s.DB.Where("room_id IN ?", uniqueRoomIDs).Find(&rooms)
}
roomMap := make(map[string]*model.ChatRoom)
for i := range rooms {
roomMap[rooms[i].RoomID] = &rooms[i]
}
for _, msg := range messages {
room := roomMap[msg.RoomID]
roomName := ""
isGroupChat := false
if room != nil {
roomName = room.RoomName
isGroupChat = room.RoomType == "group"
}
matchContent := msg.Content
if len(matchContent) > 100 {
matchContent = matchContent[:100] + "..."
}
results = append(results, MessageSearchResult{
ID: msg.ID,
RoomID: msg.RoomID,
RoomName: roomName,
Content: msg.Content,
MessageType: msg.MessageType,
CreatedAt: msg.CreatedAt.Format("2006-01-02 15:04:05"),
Sender: userMap[msg.SenderUserID],
IsGroupChat: isGroupChat,
MatchContent: matchContent,
})
}
return results, nil
}