feat(server): implement chat file TTL cleanup and enhance JPush vendor channel support
Add FileCleanupWorker for automatic expiration of chat files with configurable retention period and batch processing. Files uploaded via `/api/v1/uploads/files` are tracked in `uploaded_files` table with expiration timestamps, then deleted from S3 and database upon expiry. Expired file URLs are injected as `"expired": true` in message responses. Extend JPush push notification configuration with vendor-specific channel_id support (xiaomi, huawei, oppo, vivo, meizu, honor, fcm) for differentiating system vs chat notifications. Add iOS APNs thread-id grouping configuration for notification categorization.
This commit is contained in:
@@ -8,6 +8,13 @@ import (
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -15,6 +22,19 @@ func ConvertMessageToResponse(message *model.Message) *MessageResponse {
|
||||
// 直接使用 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,
|
||||
@@ -107,6 +127,15 @@ func ConvertMessagesToResponse(messages []*model.Message) []*MessageResponse {
|
||||
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 转换 ====================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user