feat:群聊问题修复+相关项目文档更新
This commit is contained in:
@@ -63,6 +63,35 @@ func (ctl *IMController) GetHistoryMessages(c *gin.Context) {
|
||||
utils.ResponseOK(c, result)
|
||||
}
|
||||
|
||||
// SetDoNotDisturb 设置/取消会话消息免打扰
|
||||
// PUT /api/v1/im/conversations/:id/dnd
|
||||
func (ctl *IMController) SetDoNotDisturb(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
userID, ok := middleware.GetCurrentUserID(c)
|
||||
if !ok {
|
||||
utils.ResponseUnauthorized(c, "无法获取当前用户信息")
|
||||
return
|
||||
}
|
||||
|
||||
convID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
utils.ResponseBadRequest(c, "会话 ID 格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.SetDoNotDisturbRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.ResponseBadRequest(c, "请求参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctl.imService.SetDoNotDisturb(ctx, userID, convID, req.IsDoNotDisturb); err != nil {
|
||||
ctl.handleError(c, err, "设置免打扰失败")
|
||||
return
|
||||
}
|
||||
utils.ResponseOK(c, nil)
|
||||
}
|
||||
|
||||
// PinConversation 置顶/取消置顶会话
|
||||
// PUT /api/v1/im/conversations/:id/pin
|
||||
func (ctl *IMController) PinConversation(c *gin.Context) {
|
||||
|
||||
@@ -159,6 +159,29 @@ func (d *ConversationDAO) GetConversationMemberIDs(ctx context.Context, conversa
|
||||
return ids, err
|
||||
}
|
||||
|
||||
// GetMemberNicknameMap 批量获取会话成员的群内昵称
|
||||
// 返回 map[user_id]nickname,未设置昵称的不在 map 中
|
||||
func (d *ConversationDAO) GetMemberNicknameMap(ctx context.Context, conversationID int64) (map[int64]string, error) {
|
||||
type row struct {
|
||||
UserID int64 `gorm:"column:user_id"`
|
||||
Nickname string `gorm:"column:nickname"`
|
||||
}
|
||||
var rows []row
|
||||
err := d.db.WithContext(ctx).
|
||||
Model(&model.ConversationMember{}).
|
||||
Select("user_id, nickname").
|
||||
Where("conversation_id = ? AND nickname != ''", conversationID).
|
||||
Find(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[int64]string, len(rows))
|
||||
for _, r := range rows {
|
||||
result[r.UserID] = r.Nickname
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateMemberPinned 更新会话成员的置顶状态
|
||||
func (d *ConversationDAO) UpdateMemberPinned(ctx context.Context, conversationID, userID int64, isPinned bool) error {
|
||||
funcName := "dao.conversation_dao.UpdateMemberPinned"
|
||||
@@ -257,6 +280,14 @@ func (d *ConversationDAO) GetMemberDNDMap(ctx context.Context, conversationID in
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateMemberDND 设置/取消指定成员的消息免打扰
|
||||
func (d *ConversationDAO) UpdateMemberDND(ctx context.Context, conversationID, userID int64, isDoNotDisturb bool) error {
|
||||
return d.db.WithContext(ctx).
|
||||
Model(&model.ConversationMember{}).
|
||||
Where("conversation_id = ? AND user_id = ?", conversationID, userID).
|
||||
Update("is_do_not_disturb", isDoNotDisturb).Error
|
||||
}
|
||||
|
||||
// ClearUnread 清零指定成员的未读消息数
|
||||
func (d *ConversationDAO) ClearUnread(ctx context.Context, conversationID, userID int64, lastMsgID int64) error {
|
||||
return d.db.WithContext(ctx).
|
||||
|
||||
@@ -14,6 +14,7 @@ func RegisterRoutes(r *gin.Engine, ctrl *controller.IMController, jwtAuth gin.Ha
|
||||
// 会话管理
|
||||
authed.GET("/conversations", ctrl.GetConversations)
|
||||
authed.PUT("/conversations/:id/pin", ctrl.PinConversation)
|
||||
authed.PUT("/conversations/:id/dnd", ctrl.SetDoNotDisturb)
|
||||
authed.DELETE("/conversations/:id", ctrl.DeleteConversation)
|
||||
authed.DELETE("/conversations/:id/messages", ctrl.ClearHistory)
|
||||
|
||||
|
||||
@@ -525,7 +525,7 @@ func (s *IMService) GetHistoryMessages(ctx context.Context, userID int64, req *d
|
||||
return &dto.HistoryMessageResponse{List: list, HasMore: hasMore}, nil
|
||||
}
|
||||
|
||||
// MarkRead 标记会话已读(清零未读 + 更新 Redis 总未读数)
|
||||
// MarkRead 标记会话已读(清零未读 + 更新 Redis 总未读数 + 推送已读 ACK 给对方)
|
||||
func (s *IMService) MarkRead(ctx context.Context, userID int64, conversationID int64) error {
|
||||
funcName := "service.im_service.MarkRead"
|
||||
logs.Info(ctx, funcName, "标记已读",
|
||||
@@ -536,22 +536,35 @@ func (s *IMService) MarkRead(ctx context.Context, userID int64, conversationID i
|
||||
return ErrNotMember
|
||||
}
|
||||
|
||||
if member.UnreadCount == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
latestMsgID, err := s.msgDAO.GetLatestMessageID(ctx, conversationID)
|
||||
if err != nil {
|
||||
logs.Error(ctx, funcName, "获取最新消息 ID 失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.convDAO.ClearUnread(ctx, conversationID, userID, latestMsgID); err != nil {
|
||||
logs.Error(ctx, funcName, "清零未读失败", zap.Error(err))
|
||||
return err
|
||||
if member.UnreadCount > 0 {
|
||||
if err := s.convDAO.ClearUnread(ctx, conversationID, userID, latestMsgID); err != nil {
|
||||
logs.Error(ctx, funcName, "清零未读失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
s.decrementTotalUnread(ctx, userID, member.UnreadCount)
|
||||
}
|
||||
|
||||
memberIDs, err := s.convDAO.GetConversationMemberIDs(ctx, conversationID)
|
||||
if err != nil {
|
||||
logs.Error(ctx, funcName, "获取会话成员失败", zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
for _, peerID := range memberIDs {
|
||||
if peerID != userID {
|
||||
s.pushToUser(ctx, peerID, "im.message.read.ack", map[string]interface{}{
|
||||
"conversation_id": conversationID,
|
||||
"reader_id": userID,
|
||||
"last_read_msg_id": latestMsgID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
s.decrementTotalUnread(ctx, userID, member.UnreadCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -658,6 +671,12 @@ func (s *IMService) GetMessageReadDetail(ctx context.Context, userID int64, mess
|
||||
}
|
||||
}
|
||||
|
||||
nicknameMap, nErr := s.convDAO.GetMemberNicknameMap(ctx, msg.ConversationID)
|
||||
if nErr != nil {
|
||||
logs.Error(ctx, funcName, "获取成员群昵称失败", zap.Error(nErr))
|
||||
nicknameMap = make(map[int64]string)
|
||||
}
|
||||
|
||||
var readList, unreadList []dto.MessageReadDetailDTO
|
||||
for _, uid := range allUserIDs {
|
||||
item := dto.MessageReadDetailDTO{
|
||||
@@ -667,6 +686,9 @@ func (s *IMService) GetMessageReadDetail(ctx context.Context, userID int64, mess
|
||||
item.UserNickname = brief.Nickname
|
||||
item.UserAvatar = brief.Avatar
|
||||
}
|
||||
if gn, ok := nicknameMap[uid]; ok {
|
||||
item.GroupNickname = gn
|
||||
}
|
||||
if readUserIDs[uid] {
|
||||
readList = append(readList, item)
|
||||
} else {
|
||||
@@ -682,6 +704,20 @@ func (s *IMService) GetMessageReadDetail(ctx context.Context, userID int64, mess
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetDoNotDisturb 设置/取消会话消息免打扰
|
||||
func (s *IMService) SetDoNotDisturb(ctx context.Context, userID int64, conversationID int64, isDoNotDisturb bool) error {
|
||||
funcName := "service.im_service.SetDoNotDisturb"
|
||||
logs.Info(ctx, funcName, "更新免打扰状态",
|
||||
zap.Int64("user_id", userID), zap.Int64("conversation_id", conversationID), zap.Bool("is_do_not_disturb", isDoNotDisturb))
|
||||
|
||||
member, err := s.convDAO.GetMember(ctx, conversationID, userID)
|
||||
if err != nil || member == nil {
|
||||
return ErrNotMember
|
||||
}
|
||||
|
||||
return s.convDAO.UpdateMemberDND(ctx, conversationID, userID, isDoNotDisturb)
|
||||
}
|
||||
|
||||
// PinConversation 置顶/取消置顶会话
|
||||
func (s *IMService) PinConversation(ctx context.Context, userID int64, conversationID int64, isPinned bool) error {
|
||||
funcName := "service.im_service.PinConversation"
|
||||
|
||||
Reference in New Issue
Block a user