2026-04-22 16:01:59 +08:00
|
|
|
|
package dto
|
2026-03-13 09:38:18 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2026-04-22 16:01:59 +08:00
|
|
|
|
"with_you/internal/model"
|
2026-03-13 09:38:18 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== Message 转换 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertMessageToResponse 将Message转换为MessageResponse
|
|
|
|
|
|
func ConvertMessageToResponse(message *model.Message) *MessageResponse {
|
2026-06-17 20:41:55 +08:00
|
|
|
|
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 {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
if message == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接使用 segments,不需要解析
|
|
|
|
|
|
segments := make(model.MessageSegments, len(message.Segments))
|
|
|
|
|
|
for i, seg := range message.Segments {
|
2026-06-17 20:41:55 +08:00
|
|
|
|
// 对 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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-13 09:38:18 +08:00
|
|
|
|
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: 最后一条消息
|
2026-04-25 21:22:52 +08:00
|
|
|
|
func ConvertConversationToResponse(conv *model.Conversation, participants []*model.User, unreadCount int, lastMessage *model.Message, isPinned bool, notificationMuted bool) *ConversationResponse {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
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{
|
2026-04-25 21:22:52 +08:00
|
|
|
|
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),
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ConvertConversationToDetailResponse 将Conversation转换为ConversationDetailResponse
|
2026-04-25 21:22:52 +08:00
|
|
|
|
func ConvertConversationToDetailResponse(conv *model.Conversation, participants []*model.User, unreadCount int64, lastMessage *model.Message, myLastReadSeq int64, otherLastReadSeq int64, isPinned bool, notificationMuted bool) *ConversationDetailResponse {
|
2026-03-13 09:38:18 +08:00
|
|
|
|
if conv == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var participantResponses []*UserResponse
|
|
|
|
|
|
for _, p := range participants {
|
|
|
|
|
|
participantResponses = append(participantResponses, ConvertUserToResponse(p))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &ConversationDetailResponse{
|
2026-04-25 21:22:52 +08:00
|
|
|
|
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),
|
2026-03-13 09:38:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-17 20:41:55 +08:00
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 09:38:18 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 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
|
|
|
|
|
|
}
|