搜索好友接口优化
This commit is contained in:
@@ -224,6 +224,7 @@ func main() {
|
||||
service.InitAttachmentService(db) // 附件服务(文件上传、管理)
|
||||
service.InitLoginLogService(db) // 登录日志服务(记录登录历史)
|
||||
service.InitMomentService(db) // 朋友圈服务(动态、点赞、评论)
|
||||
service.InitSearchService(db) // 搜索服务(聚合搜索)
|
||||
|
||||
// 步骤5: 启动TURN服务器(用于WebRTC音视频通话)
|
||||
go turnserver.Start()
|
||||
@@ -329,6 +330,9 @@ func main() {
|
||||
authGroup := apiGroup.Group("")
|
||||
authGroup.Use(middleware.JWTAuthMiddleware()) // 使用JWT认证中间件
|
||||
{
|
||||
// 聚合搜索
|
||||
authGroup.GET("/search", api.GlobalSearchHandler)
|
||||
|
||||
// 用户管理
|
||||
authGroup.GET("/user/my-info", api.GetMyInfoHandler)
|
||||
authGroup.GET("/user/list", api.GetUserListHandler)
|
||||
|
||||
67
internal/api/search_handler.go
Normal file
67
internal/api/search_handler.go
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* package api
|
||||
* 作用:全局搜索API处理器
|
||||
*/
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"xk-websocket-v2/internal/service"
|
||||
"xk-websocket-v2/internal/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
/**
|
||||
* GlobalSearchHandler
|
||||
* 功能:聚合搜索(联系人、群聊、聊天记录)
|
||||
* 路径:GET /api/search?keyword=xxx&type=all|contacts|groups|messages&limit=20
|
||||
*
|
||||
* 参数说明:
|
||||
* - keyword: 搜索关键词(必填)
|
||||
* - type: 搜索类型,可选值:all(默认)、contacts、groups、messages
|
||||
* - limit: 每类结果的最大数量,默认20
|
||||
*
|
||||
* 返回结构:
|
||||
* {
|
||||
* "contacts": [{ "user": {...}, "remark_name": "xxx", "room_id": "xxx" }],
|
||||
* "groups": [{ "room_id": "xxx", "room_name": "xxx", ... }],
|
||||
* "messages": [{ "id": 1, "room_id": "xxx", "content": "xxx", "sender": {...} }]
|
||||
* }
|
||||
*/
|
||||
func GlobalSearchHandler(c *gin.Context) {
|
||||
// 获取当前用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
utils.Unauthorized(c, "未登录")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取搜索参数
|
||||
keyword := c.Query("keyword")
|
||||
if keyword == "" {
|
||||
utils.BadRequest(c, "搜索关键词不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
searchType := c.DefaultQuery("type", "all")
|
||||
if searchType != "all" && searchType != "contacts" && searchType != "groups" && searchType != "messages" {
|
||||
searchType = "all"
|
||||
}
|
||||
|
||||
limitStr := c.DefaultQuery("limit", "20")
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil || limit <= 0 || limit > 100 {
|
||||
limit = 20
|
||||
}
|
||||
|
||||
// 执行搜索
|
||||
result, err := service.SearchSvc.GlobalSearch(userID.(string), keyword, searchType, limit)
|
||||
if err != nil {
|
||||
utils.InternalError(c, "搜索失败")
|
||||
return
|
||||
}
|
||||
|
||||
utils.SuccessWithData(c, result, "搜索成功")
|
||||
}
|
||||
|
||||
352
internal/service/search_service.go
Normal file
352
internal/service/search_service.go
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user