package dto import ( "with_you/internal/model" ) // ==================== Message 转换 ==================== // ConvertMessageToResponse 将Message转换为MessageResponse func ConvertMessageToResponse(message *model.Message) *MessageResponse { return ConvertMessageToResponseWithExpiry(message, nil) } // ConvertMessageToResponseWithExpiry 将Message转换为MessageResponse,并对已过期的 file segment 注入 expired 标记。 // expiredURLSet 为已从 S3 清理的文件 URL 集合(可为 nil,表示不标记)。 // 注意:注入时对 file segment 的 Data 做深拷贝,避免污染消息缓存。 func ConvertMessageToResponseWithExpiry(message *model.Message, expiredURLSet map[string]struct{}) *MessageResponse { if message == nil { return nil } // 直接使用 segments,不需要解析 segments := make(model.MessageSegments, len(message.Segments)) for i, seg := range message.Segments { // 对 file 类型且 URL 已过期的 segment,深拷贝 Data 并注入 expired 标记 if seg.Type == string(model.ContentTypeFile) && len(expiredURLSet) > 0 { url, _ := seg.Data["url"].(string) if _, expired := expiredURLSet[url]; expired { newData := make(map[string]any, len(seg.Data)+1) for k, v := range seg.Data { newData[k] = v } newData["expired"] = true segments[i] = model.MessageSegment{Type: seg.Type, Data: newData} continue } } segments[i] = model.MessageSegment{ Type: seg.Type, Data: seg.Data, } } return &MessageResponse{ ID: message.ID, ConversationID: message.ConversationID, SenderID: message.SenderID, Seq: message.Seq, Segments: segments, ReplyToID: message.ReplyToID, Status: string(message.Status), Category: string(message.Category), CreatedAt: FormatTime(message.CreatedAt), } } // ConvertConversationToResponse 将Conversation转换为ConversationResponse // participants: 会话参与者列表(用户信息) // unreadCount: 当前用户的未读消息数 // lastMessage: 最后一条消息 func ConvertConversationToResponse(conv *model.Conversation, participants []*model.User, unreadCount int, lastMessage *model.Message, isPinned bool, notificationMuted bool) *ConversationResponse { if conv == nil { return nil } var participantResponses []*UserResponse for _, p := range participants { participantResponses = append(participantResponses, ConvertUserToResponse(p)) } // 转换群组信息 var groupResponse *GroupResponse if conv.Group != nil { groupResponse = GroupToResponse(conv.Group) } return &ConversationResponse{ 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, notificationMuted bool) *ConversationDetailResponse { if conv == nil { return nil } var participantResponses []*UserResponse for _, p := range participants { participantResponses = append(participantResponses, ConvertUserToResponse(p)) } return &ConversationDetailResponse{ 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), } } // ConvertMessagesToResponse 将Message列表转换为响应列表 func ConvertMessagesToResponse(messages []*model.Message) []*MessageResponse { result := make([]*MessageResponse, 0, len(messages)) for _, msg := range messages { result = append(result, ConvertMessageToResponse(msg)) } return result } // ConvertMessagesToResponseWithExpiry 将Message列表转换为响应列表,并标记过期文件 func ConvertMessagesToResponseWithExpiry(messages []*model.Message, expiredURLSet map[string]struct{}) []*MessageResponse { result := make([]*MessageResponse, 0, len(messages)) for _, msg := range messages { result = append(result, ConvertMessageToResponseWithExpiry(msg, expiredURLSet)) } return result } // ==================== PushRecord 转换 ==================== // PushRecordToResponse 将PushRecord转换为PushRecordResponse func PushRecordToResponse(record *model.PushRecord) *PushRecordResponse { if record == nil { return nil } resp := &PushRecordResponse{ ID: record.ID, MessageID: record.MessageID, PushChannel: string(record.PushChannel), PushStatus: string(record.PushStatus), RetryCount: record.RetryCount, CreatedAt: record.CreatedAt, } if record.PushedAt != nil { resp.PushedAt = *record.PushedAt } if record.DeliveredAt != nil { resp.DeliveredAt = *record.DeliveredAt } return resp } // PushRecordsToResponse 将PushRecord列表转换为响应列表 func PushRecordsToResponse(records []*model.PushRecord) []*PushRecordResponse { result := make([]*PushRecordResponse, 0, len(records)) for _, record := range records { result = append(result, PushRecordToResponse(record)) } return result } // ==================== DeviceToken 转换 ==================== // DeviceTokenToResponse 将DeviceToken转换为DeviceTokenResponse func DeviceTokenToResponse(token *model.DeviceToken) *DeviceTokenResponse { if token == nil { return nil } resp := &DeviceTokenResponse{ ID: token.ID, DeviceID: token.DeviceID, DeviceType: string(token.DeviceType), IsActive: token.IsActive, DeviceName: token.DeviceName, CreatedAt: token.CreatedAt, } if token.LastUsedAt != nil { resp.LastUsedAt = *token.LastUsedAt } return resp } // DeviceTokensToResponse 将DeviceToken列表转换为响应列表 func DeviceTokensToResponse(tokens []*model.DeviceToken) []*DeviceTokenResponse { result := make([]*DeviceTokenResponse, 0, len(tokens)) for _, token := range tokens { result = append(result, DeviceTokenToResponse(token)) } return result } // DeviceTokenToAdminResponse 将DeviceToken转换为管理端响应(含 registration_id) func DeviceTokenToAdminResponse(token *model.DeviceToken) *AdminDeviceTokenResponse { if token == nil { return nil } resp := &AdminDeviceTokenResponse{ ID: token.ID, UserID: token.UserID, DeviceID: token.DeviceID, DeviceType: string(token.DeviceType), PushToken: token.PushToken, IsActive: token.IsActive, DeviceName: token.DeviceName, CreatedAt: token.CreatedAt, UpdatedAt: token.UpdatedAt, } if token.LastUsedAt != nil { resp.LastUsedAt = *token.LastUsedAt } return resp } // DeviceTokensToAdminResponse 将DeviceToken列表转换为管理端响应列表 func DeviceTokensToAdminResponse(tokens []*model.DeviceToken) []*AdminDeviceTokenResponse { result := make([]*AdminDeviceTokenResponse, 0, len(tokens)) for _, token := range tokens { result = append(result, DeviceTokenToAdminResponse(token)) } return result }