feat(server): implement chat file TTL cleanup and enhance JPush vendor channel support
All checks were successful
Build Backend / build (push) Successful in 3m1s
Build Backend / build-docker (push) Successful in 2m10s

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:
lafay
2026-06-17 20:41:55 +08:00
parent d9aa4b46c3
commit a0e210feab
24 changed files with 1573 additions and 127 deletions

View File

@@ -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 转换 ====================