This commit is contained in:
2025-12-04 09:46:18 +08:00
parent 422de6bd72
commit 17621c3435
5 changed files with 267 additions and 28 deletions

View File

@@ -175,6 +175,8 @@ type UserContact struct {
UserID string `gorm:"type:varchar(100);index;comment:用户ID" json:"user_id"`
// 联系人ID - 好友的用户ID
ContactID string `gorm:"type:varchar(100);index;comment:联系人ID" json:"contact_id"`
// 房间ID - 关联的房间ID
RoomID string `gorm:"type:varchar(100);index;comment:房间ID" json:"room_id"`
// 备注名称 - 用户自定义的好友备注
RemarkName string `gorm:"type:varchar(100);comment:备注名称" json:"remark_name"`
// 分组ID - 好友所属分组
@@ -241,8 +243,10 @@ type ChatRoom struct {
RoomName string `gorm:"type:varchar(200);comment:房间名称" json:"room_name"`
// 房间头像 - 群聊时显示
RoomAvatar string `gorm:"type:varchar(500);comment:房间头像" json:"room_avatar"`
// 成员列表 - JSON数组
Members string `gorm:"type:text;comment:成员列表JSON" json:"members"`
// 群主ID - 群聊必填单聊为0
OwnerID string `gorm:"type:varchar(100);index;default:0;comment:群主ID(群聊必填,单聊为0)" json:"owner_id"`
// 管理员ID列表 - 逗号分隔
AdminIDs string `gorm:"type:varchar(500);comment:管理员ID列表(逗号分隔)" json:"admin_ids"`
// 创建者ID
CreatorID string `gorm:"type:varchar(100);index;comment:创建者ID" json:"creator_id"`
// 最后消息时间
@@ -260,6 +264,25 @@ func (ChatRoom) TableName() string {
return "chat_rooms"
}
/**
* RoomMember
* 对应数据库表room_members
* 作用:房间成员表,存储房间与用户的关联关系
*/
type RoomMember struct {
// 房间ID
RoomID string `gorm:"type:varchar(100);primaryKey;comment:房间ID" json:"room_id"`
// 用户ID
UserID string `gorm:"type:varchar(100);primaryKey;comment:用户ID" json:"user_id"`
// 加入时间
JoinedAt time.Time `gorm:"type:datetime;autoCreateTime;comment:加入时间" json:"joined_at"`
}
// TableName 指定表名和注释
func (RoomMember) TableName() string {
return "room_members"
}
/**
* FriendRequest
* 对应数据库表friend_requests
@@ -416,6 +439,8 @@ type ChatConversation struct {
CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"`
// 更新时间
UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"`
// 目标用户信息(关联查询,不存储在数据库中)
TargetUser *User `gorm:"foreignKey:TargetID;references:ID" json:"target_user,omitempty"`
}
// TableName 指定表名和注释

View File

@@ -5,8 +5,12 @@
package service
import (
"encoding/json"
"errors"
"fmt"
"time"
"xk-websocket-v2/internal/model"
"xk-websocket-v2/internal/utils"
"gorm.io/gorm"
)
@@ -69,10 +73,14 @@ func (s *ContactService) AddFriend(fromUserID, toUserID, message string) 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 = ?", userID, "pending").
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
@@ -96,6 +104,11 @@ func (s *ContactService) AcceptFriendRequest(requestID uint, userID string) erro
// 开始事务
tx := s.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 更新申请状态
if err := tx.Model(&request).Update("status", "accepted").Error; err != nil {
@@ -103,14 +116,53 @@ func (s *ContactService) AcceptFriendRequest(requestID uint, userID string) erro
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 {
@@ -122,7 +174,76 @@ func (s *ContactService) AcceptFriendRequest(requestID uint, userID string) erro
return err
}
return tx.Commit().Error
// 为双方创建会话记录
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)
}
}
/**
@@ -288,19 +409,24 @@ func (s *ContactService) GetContactsWithUserInfo(userID string) ([]map[string]in
}
user.Password = ""
// 组合数据
// 组合数据,包含 user 对象
item := map[string]interface{}{
"id": contact.ContactID,
"name": user.Name,
"avatar": user.Avatar,
"desc": user.Desc,
"remark_name": contact.RemarkName,
"group_id": contact.GroupID,
"is_top": contact.IsTop,
"is_muted": contact.IsMuted,
"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,
"unread_count": contact.UnreadCount,
"last_message": contact.LastMessage,
"last_msg": contact.LastMessage,
"unread_count": contact.UnreadCount,
"unread": contact.UnreadCount,
}
result = append(result, item)
}

View File

@@ -32,6 +32,7 @@ func InitConversationService(db *gorm.DB) {
func (s *ConversationService) GetConversations(userID string) ([]model.ChatConversation, error) {
var list []model.ChatConversation
err := s.DB.Where("user_id = ?", userID).
Preload("TargetUser").
Order("is_top DESC, last_time DESC, id DESC").
Find(&list).Error
return list, err

View File

@@ -5,7 +5,6 @@
package service
import (
"encoding/json"
"fmt"
"time"
"xk-websocket-v2/internal/model"
@@ -62,6 +61,8 @@ func GenerateGroupRoomID() string {
*/
func (s *RoomService) CreateRoom(roomType string, members []string, creatorID string) (*model.ChatRoom, error) {
var roomID string
var ownerID string = "0" // 单聊默认为0
if roomType == "p2p" {
if len(members) != 2 {
return nil, fmt.Errorf("点对点房间需要2个成员")
@@ -69,6 +70,8 @@ func (s *RoomService) CreateRoom(roomType string, members []string, creatorID st
roomID = GenerateP2PRoomID(members[0], members[1])
} else {
roomID = GenerateGroupRoomID()
// 群聊时,创建者就是群主
ownerID = creatorID
}
// 检查房间是否已存在
@@ -77,16 +80,19 @@ func (s *RoomService) CreateRoom(roomType string, members []string, creatorID st
return &existingRoom, nil
}
// 序列化成员列表
membersJSON, err := json.Marshal(members)
if err != nil {
return nil, err
}
// 开始事务
tx := s.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 创建房间
room := model.ChatRoom{
RoomID: roomID,
RoomType: roomType,
Members: string(membersJSON),
RoomID: roomID,
RoomType: roomType,
OwnerID: ownerID,
CreatorID: creatorID,
}
@@ -94,7 +100,24 @@ func (s *RoomService) CreateRoom(roomType string, members []string, creatorID st
room.RoomName = "群聊"
}
if err := s.DB.Create(&room).Error; err != nil {
if err := tx.Create(&room).Error; err != nil {
tx.Rollback()
return nil, err
}
// 创建房间成员记录
for _, userID := range members {
member := model.RoomMember{
RoomID: roomID,
UserID: userID,
}
if err := tx.Create(&member).Error; err != nil {
tx.Rollback()
return nil, err
}
}
if err := tx.Commit().Error; err != nil {
return nil, err
}
@@ -146,3 +169,41 @@ func (s *RoomService) UpdateRoomLastMessage(roomID, message string) error {
}).Error
}
/**
* GetRoomMembers
* 功能:获取房间成员列表
*/
func (s *RoomService) GetRoomMembers(roomID string) ([]string, error) {
var members []model.RoomMember
if err := s.DB.Where("room_id = ?", roomID).Find(&members).Error; err != nil {
return nil, err
}
userIDs := make([]string, len(members))
for i, member := range members {
userIDs[i] = member.UserID
}
return userIDs, nil
}
/**
* AddRoomMember
* 功能:添加房间成员
*/
func (s *RoomService) AddRoomMember(roomID, userID string) error {
member := model.RoomMember{
RoomID: roomID,
UserID: userID,
}
return s.DB.Create(&member).Error
}
/**
* RemoveRoomMember
* 功能:移除房间成员
*/
func (s *RoomService) RemoveRoomMember(roomID, userID string) error {
return s.DB.Where("room_id = ? AND user_id = ?", roomID, userID).
Delete(&model.RoomMember{}).Error
}

View File

@@ -107,7 +107,8 @@ CREATE TABLE `chat_rooms` (
`room_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房间类型',
`room_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房间名称',
`room_avatar` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房间头像',
`members` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '成员列表JSON',
`owner_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '群主ID(群聊必填,单聊为0)',
`admin_ids` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '管理员ID列表(逗号分隔)',
`creator_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建者ID',
`last_message_time` datetime NULL DEFAULT NULL COMMENT '最后消息时间',
`last_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '最后消息内容',
@@ -115,6 +116,7 @@ CREATE TABLE `chat_rooms` (
`updated_at` datetime(3) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`room_id`) USING BTREE,
INDEX `idx_chat_rooms_room_type`(`room_type` ASC) USING BTREE,
INDEX `idx_chat_rooms_owner_id`(`owner_id` ASC) USING BTREE,
INDEX `idx_chat_rooms_creator_id`(`creator_id` ASC) USING BTREE,
INDEX `idx_chat_rooms_last_message_time`(`last_message_time` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '聊天房间表,支持点对点和群聊' ROW_FORMAT = Dynamic;
@@ -123,6 +125,26 @@ CREATE TABLE `chat_rooms` (
-- Records of chat_rooms
-- ----------------------------
-- ----------------------------
-- Table structure for room_members
-- ----------------------------
DROP TABLE IF EXISTS `room_members`;
CREATE TABLE `room_members` (
`room_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '房间ID',
`user_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
`joined_at` datetime(3) NULL DEFAULT NULL COMMENT '加入时间',
PRIMARY KEY (`room_id`, `user_id`) USING BTREE,
INDEX `idx_room_members_user_id`(`user_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '房间成员表,存储房间与用户的关联关系' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of room_members
-- ----------------------------
-- ----------------------------
-- Records of chat_rooms
-- ----------------------------
-- ----------------------------
-- Table structure for contact_groups
-- ----------------------------
@@ -195,10 +217,13 @@ CREATE TABLE `user_contacts` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户ID',
`contact_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '联系人ID',
`room_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房间ID',
`remark_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注名称',
`group_id` bigint NULL DEFAULT NULL COMMENT '分组ID',
`is_top` tinyint(1) NULL DEFAULT 0 COMMENT '是否置顶',
`is_muted` tinyint(1) NULL DEFAULT 0 COMMENT '是否免打扰',
`is_special_care` tinyint(1) NULL DEFAULT 0 COMMENT '是否特别关心',
`is_blocked` tinyint(1) NULL DEFAULT 0 COMMENT '是否拉黑',
`last_chat_time` datetime NULL DEFAULT NULL COMMENT '最后聊天时间',
`last_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '最后一条消息',
`unread_count` bigint NULL DEFAULT 0 COMMENT '未读消息数',
@@ -207,6 +232,7 @@ CREATE TABLE `user_contacts` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_contacts_user_id`(`user_id` ASC) USING BTREE,
INDEX `idx_user_contacts_contact_id`(`contact_id` ASC) USING BTREE,
INDEX `idx_user_contacts_room_id`(`room_id` ASC) USING BTREE,
INDEX `idx_user_contacts_group_id`(`group_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户联系人表,存储好友关系、分组、备注等信息' ROW_FORMAT = Dynamic;
@@ -237,7 +263,7 @@ CREATE TABLE `users` (
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('10001', 'user001@example.com', '13800138001', '$2a$10$OsVRHKowu.mVT4GNxzZ3/eo8xEABkWsUGAt4nV5nalzOXW47vtBmC', '张三', '', 'Golang 开发工程师', '北京', '2025-12-03 09:53:49.000', '2025-12-03 09:53:49.000');
INSERT INTO `users` VALUES ('10001', 'workerqi@163.com', '13800138001', '$2a$10$OsVRHKowu.mVT4GNxzZ3/eo8xEABkWsUGAt4nV5nalzOXW47vtBmC', '张三', '', 'Golang 开发工程师', '北京', '2025-12-03 09:53:49.000', '2025-12-03 09:53:49.000');
INSERT INTO `users` VALUES ('10002', 'user002@example.com', '13800138002', '$2a$10$OsVRHKowu.mVT4GNxzZ3/eo8xEABkWsUGAt4nV5nalzOXW47vtBmC', '李四', '', '前端开发工程师', '上海', '2025-12-03 09:53:49.000', '2025-12-03 09:53:49.000');
INSERT INTO `users` VALUES ('10003', 'user003@example.com', '13800138003', '$2a$10$OsVRHKowu.mVT4GNxzZ3/eo8xEABkWsUGAt4nV5nalzOXW47vtBmC', '王五', '', '产品经理', '深圳', '2025-12-03 09:53:49.000', '2025-12-03 09:53:49.000');
INSERT INTO `users` VALUES ('10004', 'user004@example.com', '13800138004', '$2a$10$OsVRHKowu.mVT4GNxzZ3/eo8xEABkWsUGAt4nV5nalzOXW47vtBmC', '赵六', '', 'UI设计师', '广州', '2025-12-03 09:53:49.000', '2025-12-03 09:53:49.000');