群聊优化
This commit is contained in:
@@ -244,18 +244,18 @@ func ListUserGroupsHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
result = append(result, map[string]interface{}{
|
||||
"room_id": group.RoomID,
|
||||
"room_type": group.RoomType,
|
||||
"room_name": group.RoomName,
|
||||
"room_avatar": group.RoomAvatar,
|
||||
"owner_id": group.OwnerID,
|
||||
"creator_id": group.CreatorID,
|
||||
"category": category,
|
||||
"role": int(member.Role),
|
||||
"room_id": group.RoomID,
|
||||
"room_type": group.RoomType,
|
||||
"room_name": group.RoomName,
|
||||
"room_avatar": group.RoomAvatar,
|
||||
"owner_id": group.OwnerID,
|
||||
"creator_id": group.CreatorID,
|
||||
"category": category,
|
||||
"role": int(member.Role),
|
||||
"last_message_time": lastMessageTime,
|
||||
"last_message": group.LastMessage,
|
||||
"created_at": group.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
"updated_at": group.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
"last_message": group.LastMessage,
|
||||
"created_at": group.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
"updated_at": group.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -379,12 +379,12 @@ func AddGroupMembersHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
notifExtra := map[string]interface{}{
|
||||
"type": "member_invite",
|
||||
"room_id": roomID,
|
||||
"operator": userID.(string),
|
||||
"type": "member_invite",
|
||||
"room_id": roomID,
|
||||
"operator": userID.(string),
|
||||
"operator_name": operator.Name,
|
||||
"targets": req.MemberIDs,
|
||||
"target_names": invitedNames,
|
||||
"targets": req.MemberIDs,
|
||||
"target_names": invitedNames,
|
||||
}
|
||||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||||
|
||||
@@ -420,9 +420,9 @@ func AddGroupMembersHandler(c *gin.Context) {
|
||||
for _, memberID := range req.MemberIDs {
|
||||
systemContent := fmt.Sprintf("你被 %s 邀请加入群聊", operator.Name)
|
||||
systemExtra := map[string]interface{}{
|
||||
"type": "member_invited",
|
||||
"room_id": roomID,
|
||||
"operator": userID.(string),
|
||||
"type": "member_invited",
|
||||
"room_id": roomID,
|
||||
"operator": userID.(string),
|
||||
"operator_name": operator.Name,
|
||||
}
|
||||
systemExtraJSON, _ := json.Marshal(systemExtra)
|
||||
@@ -490,7 +490,7 @@ func RemoveGroupMemberHandler(c *gin.Context) {
|
||||
// 获取操作者和被移除成员信息
|
||||
operator, opErr := service.UserSvc.GetUserByID(operatorID.(string))
|
||||
member, memErr := service.UserSvc.GetUserByID(memberID)
|
||||
|
||||
|
||||
if opErr == nil && memErr == nil {
|
||||
// 获取群成员列表
|
||||
memberIDs, mErr := service.RoomSvc.GetRoomMembers(roomID)
|
||||
@@ -498,30 +498,30 @@ func RemoveGroupMemberHandler(c *gin.Context) {
|
||||
// 构建群通知消息
|
||||
notifContent := fmt.Sprintf("%s 将 %s 移出了群聊", operator.Name, member.Name)
|
||||
notifExtra := map[string]interface{}{
|
||||
"type": "member_remove",
|
||||
"room_id": roomID,
|
||||
"operator": operatorID.(string),
|
||||
"target": memberID,
|
||||
"type": "member_remove",
|
||||
"room_id": roomID,
|
||||
"operator": operatorID.(string),
|
||||
"target": memberID,
|
||||
"operator_name": operator.Name,
|
||||
"target_name": member.Name,
|
||||
}
|
||||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||||
|
||||
|
||||
// 构建系统消息
|
||||
systemContent := fmt.Sprintf("你被 %s 移出了群聊", operator.Name)
|
||||
systemExtra := map[string]interface{}{
|
||||
"type": "member_removed",
|
||||
"room_id": roomID,
|
||||
"type": "member_removed",
|
||||
"room_id": roomID,
|
||||
"operator": operatorID.(string),
|
||||
}
|
||||
systemExtraJSON, _ := json.Marshal(systemExtra)
|
||||
|
||||
|
||||
// 为所有成员发送群通知
|
||||
for _, uid := range memberIDs {
|
||||
if uid == memberID {
|
||||
continue // 被移除的成员不接收群通知
|
||||
}
|
||||
|
||||
|
||||
// 群通知消息
|
||||
notifMsg := model.ChatMessage{
|
||||
RoomID: roomID,
|
||||
@@ -531,13 +531,13 @@ func RemoveGroupMemberHandler(c *gin.Context) {
|
||||
Content: notifContent,
|
||||
Extra: string(notifExtraJSON),
|
||||
}
|
||||
|
||||
|
||||
if err := service.ChatSvc.DB.Create(¬ifMsg).Error; err == nil {
|
||||
// 更新会话
|
||||
if service.ConversationSvc != nil {
|
||||
_ = service.ConversationSvc.UpsertConversationOnMessage(uid, roomID, roomID, notifMsg, false)
|
||||
}
|
||||
|
||||
|
||||
// 推送消息
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
@@ -547,7 +547,7 @@ func RemoveGroupMemberHandler(c *gin.Context) {
|
||||
service.ChatSvc.DispatchMessage(uid, msgBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 为被移除的成员发送系统消息
|
||||
systemMsg := model.ChatMessage{
|
||||
RoomID: roomID,
|
||||
@@ -557,13 +557,13 @@ func RemoveGroupMemberHandler(c *gin.Context) {
|
||||
Content: systemContent,
|
||||
Extra: string(systemExtraJSON),
|
||||
}
|
||||
|
||||
|
||||
if err := service.ChatSvc.DB.Create(&systemMsg).Error; err == nil {
|
||||
// 更新会话
|
||||
if service.ConversationSvc != nil {
|
||||
_ = service.ConversationSvc.UpsertConversationOnMessage(memberID, roomID, roomID, systemMsg, false)
|
||||
}
|
||||
|
||||
|
||||
// 推送消息
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
@@ -697,7 +697,7 @@ func QuitGroupHandler(c *gin.Context) {
|
||||
if service.ChatSvc != nil && service.UserSvc != nil {
|
||||
// 获取退出成员信息
|
||||
member, memErr := service.UserSvc.GetUserByID(userID.(string))
|
||||
|
||||
|
||||
if memErr == nil {
|
||||
// 获取群成员列表(退出后剩余的成员)
|
||||
memberIDs, mErr := service.RoomSvc.GetRoomMembers(roomID)
|
||||
@@ -711,7 +711,7 @@ func QuitGroupHandler(c *gin.Context) {
|
||||
"target_name": member.Name,
|
||||
}
|
||||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||||
|
||||
|
||||
// 为所有剩余成员发送群通知
|
||||
for _, uid := range memberIDs {
|
||||
notifMsg := model.ChatMessage{
|
||||
@@ -722,13 +722,13 @@ func QuitGroupHandler(c *gin.Context) {
|
||||
Content: notifContent,
|
||||
Extra: string(notifExtraJSON),
|
||||
}
|
||||
|
||||
|
||||
if err := service.ChatSvc.DB.Create(¬ifMsg).Error; err == nil {
|
||||
// 更新会话
|
||||
if service.ConversationSvc != nil {
|
||||
_ = service.ConversationSvc.UpsertConversationOnMessage(uid, roomID, roomID, notifMsg, false)
|
||||
}
|
||||
|
||||
|
||||
// 推送消息
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
@@ -815,9 +815,9 @@ func GetGroupNotificationsHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
utils.SuccessWithData(c, gin.H{
|
||||
"data": messages,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"data": messages,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}, "获取成功")
|
||||
}
|
||||
@@ -870,3 +870,61 @@ func UpdateGroupAnnouncementHandler(c *gin.Context) {
|
||||
|
||||
utils.Success(c, "更新成功")
|
||||
}
|
||||
|
||||
/**
|
||||
* MuteGroupMemberHandler
|
||||
* 功能:禁言群成员
|
||||
* 路径:POST /api/groups/:room_id/members/:user_id/mute
|
||||
*/
|
||||
func MuteGroupMemberHandler(c *gin.Context) {
|
||||
operatorID, _ := c.Get("user_id")
|
||||
roomID := c.Param("room_id")
|
||||
memberID := c.Param("user_id")
|
||||
|
||||
var req struct {
|
||||
Duration int64 `json:"duration"` // 禁言时长(秒),0=永久,-1=解除禁言
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.BadRequest(c, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.RoomSvc.MuteGroupMember(roomID, operatorID.(string), memberID, req.Duration); err != nil {
|
||||
utils.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if req.Duration == -1 {
|
||||
utils.Success(c, "已解除禁言")
|
||||
} else if req.Duration == 0 {
|
||||
utils.Success(c, "已永久禁言")
|
||||
} else {
|
||||
utils.Success(c, "禁言成功")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GetMemberMuteStatusHandler
|
||||
* 功能:获取成员禁言状态
|
||||
* 路径:GET /api/groups/:room_id/members/:user_id/mute
|
||||
*/
|
||||
func GetMemberMuteStatusHandler(c *gin.Context) {
|
||||
roomID := c.Param("room_id")
|
||||
memberID := c.Param("user_id")
|
||||
|
||||
isMuted, mutedUntil, err := service.RoomSvc.IsGroupMemberMuted(roomID, memberID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
utils.NotFound(c, "成员不存在")
|
||||
return
|
||||
}
|
||||
utils.InternalError(c, "查询失败")
|
||||
return
|
||||
}
|
||||
|
||||
utils.SuccessWithData(c, gin.H{
|
||||
"is_muted": isMuted,
|
||||
"muted_until": mutedUntil,
|
||||
}, "获取成功")
|
||||
}
|
||||
|
||||
@@ -280,12 +280,22 @@ type RoomMember struct {
|
||||
Role int8 `gorm:"type:tinyint(1);default:0;comment:成员角色(0=成员,1=管理员,2=群主)" json:"role"`
|
||||
// 加入时间
|
||||
JoinedAt time.Time `gorm:"type:datetime;autoCreateTime;comment:加入时间" json:"joined_at"`
|
||||
// 禁言到期时间(null或过期时间之前表示未禁言)
|
||||
MutedUntil *time.Time `gorm:"type:datetime;comment:禁言到期时间" json:"muted_until,omitempty"`
|
||||
// 用户信息(关联查询,不存储在数据库中)
|
||||
User *User `gorm:"foreignKey:UserID;references:ID" json:"user,omitempty"`
|
||||
// 群名片(可选,用于群聊中显示的自定义昵称)
|
||||
Nickname string `gorm:"-" json:"nickname,omitempty"`
|
||||
}
|
||||
|
||||
// IsMuted 判断成员是否被禁言
|
||||
func (m *RoomMember) IsMuted() bool {
|
||||
if m.MutedUntil == nil {
|
||||
return false
|
||||
}
|
||||
return m.MutedUntil.After(time.Now())
|
||||
}
|
||||
|
||||
// TableName 指定表名和注释
|
||||
func (RoomMember) TableName() string {
|
||||
return "room_members"
|
||||
@@ -700,8 +710,8 @@ type MomentNotification struct {
|
||||
// 创建时间
|
||||
CreatedAt time.Time `gorm:"autoCreateTime;index;comment:创建时间" json:"created_at"`
|
||||
// 关联字段(不存储在数据库中)
|
||||
FromUser *User `gorm:"-" json:"from_user,omitempty"`
|
||||
Moment *Moment `gorm:"-" json:"moment,omitempty"`
|
||||
FromUser *User `gorm:"-" json:"from_user,omitempty"`
|
||||
Moment *Moment `gorm:"-" json:"moment,omitempty"`
|
||||
Comment *MomentComment `gorm:"-" json:"comment,omitempty"`
|
||||
}
|
||||
|
||||
@@ -799,10 +809,10 @@ const (
|
||||
ChanClusterBroadcast = "ws:cluster:broadcast"
|
||||
|
||||
// 文件大小限制(字节)
|
||||
MaxImageSize = 10 * 1024 * 1024 // 10MB
|
||||
MaxVideoSize = 500 * 1024 * 1024 // 500MB
|
||||
MaxAudioSize = 50 * 1024 * 1024 // 50MB
|
||||
MaxFileSize = 100 * 1024 * 1024 // 100MB
|
||||
MaxImageSize = 10 * 1024 * 1024 // 10MB
|
||||
MaxVideoSize = 500 * 1024 * 1024 // 500MB
|
||||
MaxAudioSize = 50 * 1024 * 1024 // 50MB
|
||||
MaxFileSize = 100 * 1024 * 1024 // 100MB
|
||||
|
||||
// 消息类型常量
|
||||
MessageTypeText = 0 // 文本
|
||||
|
||||
@@ -579,3 +579,77 @@ func (s *RoomService) UpdateGroupAnnouncement(roomID, userID, announcement strin
|
||||
Where("room_id = ? AND room_type = ?", roomID, "group").
|
||||
Update("announcement", announcement).Error
|
||||
}
|
||||
|
||||
/**
|
||||
* MuteGroupMember
|
||||
* 功能:禁言群成员(仅群主或管理员可操作)
|
||||
* @param roomID 房间ID
|
||||
* @param operatorID 操作者ID
|
||||
* @param memberID 被禁言的成员ID
|
||||
* @param duration 禁言时长(秒),0表示永久禁言,-1表示解除禁言
|
||||
*/
|
||||
func (s *RoomService) MuteGroupMember(roomID, operatorID, memberID string, duration int64) error {
|
||||
// 权限校验:必须是群主或管理员
|
||||
isAdmin, err := s.isGroupAdminOrOwner(roomID, operatorID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isAdmin {
|
||||
return fmt.Errorf("无权限禁言成员")
|
||||
}
|
||||
|
||||
// 检查被禁言的成员是否存在
|
||||
var member model.RoomMember
|
||||
err = s.DB.Where("room_id = ? AND user_id = ?", roomID, memberID).First(&member).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return fmt.Errorf("成员不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 不能禁言群主
|
||||
if member.Role == 2 {
|
||||
return fmt.Errorf("不能禁言群主")
|
||||
}
|
||||
|
||||
// 管理员不能禁言其他管理员
|
||||
var operator model.RoomMember
|
||||
s.DB.Where("room_id = ? AND user_id = ?", roomID, operatorID).First(&operator)
|
||||
if operator.Role == 1 && member.Role == 1 {
|
||||
return fmt.Errorf("管理员不能禁言其他管理员")
|
||||
}
|
||||
|
||||
// 计算禁言到期时间
|
||||
var mutedUntil *time.Time
|
||||
if duration == -1 {
|
||||
// 解除禁言
|
||||
mutedUntil = nil
|
||||
} else if duration == 0 {
|
||||
// 永久禁言(设置为100年后)
|
||||
t := time.Now().AddDate(100, 0, 0)
|
||||
mutedUntil = &t
|
||||
} else {
|
||||
// 指定时长禁言
|
||||
t := time.Now().Add(time.Duration(duration) * time.Second)
|
||||
mutedUntil = &t
|
||||
}
|
||||
|
||||
// 更新禁言状态
|
||||
return s.DB.Model(&model.RoomMember{}).
|
||||
Where("room_id = ? AND user_id = ?", roomID, memberID).
|
||||
Update("muted_until", mutedUntil).Error
|
||||
}
|
||||
|
||||
/**
|
||||
* IsGroupMemberMuted
|
||||
* 功能:检查成员是否被禁言
|
||||
*/
|
||||
func (s *RoomService) IsGroupMemberMuted(roomID, userID string) (bool, *time.Time, error) {
|
||||
var member model.RoomMember
|
||||
err := s.DB.Where("room_id = ? AND user_id = ?", roomID, userID).First(&member).Error
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
return member.IsMuted(), member.MutedUntil, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user