feat(conversations): add notification mute functionality for conversations
Some checks failed
Build Backend / build-docker (push) Has been cancelled
Build Backend / build (push) Has been cancelled

- Rename Muted field to NotificationMuted across model, DTOs and converters
- Add UpdateNotificationMuted repository method with upsert support
- Add SetConversationNotificationMuted service method
- Add PUT /:id/notification_muted route for setting mute status
- Improve S3 storage resilience by skipping bucket check on access denied
This commit is contained in:
lafay
2026-04-25 21:22:52 +08:00
parent 52f62ef230
commit d8b0825ac0
9 changed files with 178 additions and 75 deletions

View File

@@ -350,26 +350,27 @@ type MessageResponse struct {
// ConversationResponse 会话响应
type ConversationResponse struct {
ID string `json:"id"`
Type string `json:"type"`
IsPinned bool `json:"is_pinned"`
Group *GroupResponse `json:"group,omitempty"`
LastSeq int64 `json:"last_seq"`
LastMessage *MessageResponse `json:"last_message"`
LastMessageAt string `json:"last_message_at"`
UnreadCount int `json:"unread_count"`
Participants []*UserResponse `json:"participants,omitempty"` // 私聊时使用
MemberCount int `json:"member_count,omitempty"` // 聊时使用
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ID string `json:"id"`
Type string `json:"type"`
IsPinned bool `json:"is_pinned"`
NotificationMuted bool `json:"notification_muted"`
Group *GroupResponse `json:"group,omitempty"`
LastSeq int64 `json:"last_seq"`
LastMessage *MessageResponse `json:"last_message"`
LastMessageAt string `json:"last_message_at"`
UnreadCount int `json:"unread_count"`
Participants []*UserResponse `json:"participants,omitempty"` // 聊时使用
MemberCount int `json:"member_count,omitempty"` // 群聊时使用
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// ConversationParticipantResponse 会话参与者响应
type ConversationParticipantResponse struct {
UserID string `json:"user_id"`
LastReadSeq int64 `json:"last_read_seq"`
Muted bool `json:"muted"`
IsPinned bool `json:"is_pinned"`
UserID string `json:"user_id"`
LastReadSeq int64 `json:"last_read_seq"`
NotificationMuted bool `json:"notification_muted"`
IsPinned bool `json:"is_pinned"`
}
// ==================== Auth DTOs ====================
@@ -453,18 +454,19 @@ type ConversationListResponse struct {
// ConversationDetailResponse 会话详情响应
type ConversationDetailResponse struct {
ID string `json:"id"`
Type string `json:"type"`
IsPinned bool `json:"is_pinned"`
LastSeq int64 `json:"last_seq"`
LastMessage *MessageResponse `json:"last_message"`
LastMessageAt string `json:"last_message_at"`
UnreadCount int64 `json:"unread_count"`
Participants []*UserResponse `json:"participants"`
MyLastReadSeq int64 `json:"my_last_read_seq"` // 当前用户的已读位置
OtherLastReadSeq int64 `json:"other_last_read_seq"` // 对方用户的已读位置
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ID string `json:"id"`
Type string `json:"type"`
IsPinned bool `json:"is_pinned"`
NotificationMuted bool `json:"notification_muted"`
LastSeq int64 `json:"last_seq"`
LastMessage *MessageResponse `json:"last_message"`
LastMessageAt string `json:"last_message_at"`
UnreadCount int64 `json:"unread_count"`
Participants []*UserResponse `json:"participants"`
MyLastReadSeq int64 `json:"my_last_read_seq"` // 当前用户的已读位置
OtherLastReadSeq int64 `json:"other_last_read_seq"` // 对方用户的已读位置
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// UnreadCountResponse 未读数响应

View File

@@ -38,7 +38,7 @@ func ConvertMessageToResponse(message *model.Message) *MessageResponse {
// participants: 会话参与者列表(用户信息)
// unreadCount: 当前用户的未读消息数
// lastMessage: 最后一条消息
func ConvertConversationToResponse(conv *model.Conversation, participants []*model.User, unreadCount int, lastMessage *model.Message, isPinned bool) *ConversationResponse {
func ConvertConversationToResponse(conv *model.Conversation, participants []*model.User, unreadCount int, lastMessage *model.Message, isPinned bool, notificationMuted bool) *ConversationResponse {
if conv == nil {
return nil
}
@@ -55,22 +55,23 @@ func ConvertConversationToResponse(conv *model.Conversation, participants []*mod
}
return &ConversationResponse{
ID: conv.ID,
Type: string(conv.Type),
IsPinned: isPinned,
Group: groupResponse,
LastSeq: conv.LastSeq,
LastMessage: ConvertMessageToResponse(lastMessage),
LastMessageAt: FormatTimePointer(conv.LastMsgTime),
UnreadCount: unreadCount,
Participants: participantResponses,
CreatedAt: FormatTime(conv.CreatedAt),
UpdatedAt: FormatTime(conv.UpdatedAt),
ID: conv.ID,
Type: string(conv.Type),
IsPinned: isPinned,
NotificationMuted: notificationMuted,
Group: groupResponse,
LastSeq: conv.LastSeq,
LastMessage: ConvertMessageToResponse(lastMessage),
LastMessageAt: FormatTimePointer(conv.LastMsgTime),
UnreadCount: unreadCount,
Participants: participantResponses,
CreatedAt: FormatTime(conv.CreatedAt),
UpdatedAt: FormatTime(conv.UpdatedAt),
}
}
// ConvertConversationToDetailResponse 将Conversation转换为ConversationDetailResponse
func ConvertConversationToDetailResponse(conv *model.Conversation, participants []*model.User, unreadCount int64, lastMessage *model.Message, myLastReadSeq int64, otherLastReadSeq int64, isPinned bool) *ConversationDetailResponse {
func ConvertConversationToDetailResponse(conv *model.Conversation, participants []*model.User, unreadCount int64, lastMessage *model.Message, myLastReadSeq int64, otherLastReadSeq int64, isPinned bool, notificationMuted bool) *ConversationDetailResponse {
if conv == nil {
return nil
}
@@ -81,18 +82,19 @@ func ConvertConversationToDetailResponse(conv *model.Conversation, participants
}
return &ConversationDetailResponse{
ID: conv.ID,
Type: string(conv.Type),
IsPinned: isPinned,
LastSeq: conv.LastSeq,
LastMessage: ConvertMessageToResponse(lastMessage),
LastMessageAt: FormatTimePointer(conv.LastMsgTime),
UnreadCount: unreadCount,
Participants: participantResponses,
MyLastReadSeq: myLastReadSeq,
OtherLastReadSeq: otherLastReadSeq,
CreatedAt: FormatTime(conv.CreatedAt),
UpdatedAt: FormatTime(conv.UpdatedAt),
ID: conv.ID,
Type: string(conv.Type),
IsPinned: isPinned,
NotificationMuted: notificationMuted,
LastSeq: conv.LastSeq,
LastMessage: ConvertMessageToResponse(lastMessage),
LastMessageAt: FormatTimePointer(conv.LastMsgTime),
UnreadCount: unreadCount,
Participants: participantResponses,
MyLastReadSeq: myLastReadSeq,
OtherLastReadSeq: otherLastReadSeq,
CreatedAt: FormatTime(conv.CreatedAt),
UpdatedAt: FormatTime(conv.UpdatedAt),
}
}
@@ -109,7 +111,7 @@ func ConvertMessagesToResponse(messages []*model.Message) []*MessageResponse {
func ConvertConversationsToResponse(convs []*model.Conversation) []*ConversationResponse {
result := make([]*ConversationResponse, 0, len(convs))
for _, conv := range convs {
result = append(result, ConvertConversationToResponse(conv, nil, 0, nil, false))
result = append(result, ConvertConversationToResponse(conv, nil, 0, nil, false, false))
}
return result
}