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.
92 lines
2.9 KiB
Go
92 lines
2.9 KiB
Go
package repository
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
"with_you/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// UploadedFileRepository 文件上传记录仓储接口
|
||
type UploadedFileRepository interface {
|
||
// Create 写入一条上传记录
|
||
Create(ctx context.Context, file *model.UploadedFile) error
|
||
// GetByURL 按 URL 查询记录(DTO 失效标记用)
|
||
GetByURL(ctx context.Context, url string) (*model.UploadedFile, error)
|
||
// ListPendingCleanup 取 created_at < before 且 expired=false 的记录,分批
|
||
ListPendingCleanup(ctx context.Context, before time.Time, limit int) ([]model.UploadedFile, error)
|
||
// MarkExpired 标记某条记录已清理
|
||
MarkExpired(ctx context.Context, id uint, at time.Time) error
|
||
// GetExpiredURLSet 批量查询已标记过期的 URL 集合(DTO 注入用)
|
||
// 注意:这是全量扫描 expired=true 的记录,适用于上传量不大的场景;
|
||
// 若 uploaded_files 表增长过大,可改为按时间窗口分页。
|
||
GetExpiredURLSet(ctx context.Context) (map[string]struct{}, error)
|
||
}
|
||
|
||
// uploadedFileRepository 文件上传记录仓储实现
|
||
type uploadedFileRepository struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
// NewUploadedFileRepository 创建文件上传记录仓储
|
||
func NewUploadedFileRepository(db *gorm.DB) UploadedFileRepository {
|
||
return &uploadedFileRepository{db: db}
|
||
}
|
||
|
||
// Create 写入一条上传记录
|
||
func (r *uploadedFileRepository) Create(ctx context.Context, file *model.UploadedFile) error {
|
||
return r.db.WithContext(ctx).Create(file).Error
|
||
}
|
||
|
||
// GetByURL 按 URL 查询记录
|
||
func (r *uploadedFileRepository) GetByURL(ctx context.Context, url string) (*model.UploadedFile, error) {
|
||
var file model.UploadedFile
|
||
if err := r.db.WithContext(ctx).Where("url = ?", url).First(&file).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
return &file, nil
|
||
}
|
||
|
||
// ListPendingCleanup 取待清理记录
|
||
func (r *uploadedFileRepository) ListPendingCleanup(ctx context.Context, before time.Time, limit int) ([]model.UploadedFile, error) {
|
||
if limit <= 0 {
|
||
limit = 100
|
||
}
|
||
var files []model.UploadedFile
|
||
err := r.db.WithContext(ctx).
|
||
Where("created_at < ? AND expired = ?", before, false).
|
||
Order("created_at ASC").
|
||
Limit(limit).
|
||
Find(&files).Error
|
||
return files, err
|
||
}
|
||
|
||
// MarkExpired 标记已清理
|
||
func (r *uploadedFileRepository) MarkExpired(ctx context.Context, id uint, at time.Time) error {
|
||
return r.db.WithContext(ctx).Model(&model.UploadedFile{}).
|
||
Where("id = ?", id).
|
||
Updates(map[string]any{
|
||
"expired": true,
|
||
"expired_at": at,
|
||
}).Error
|
||
}
|
||
|
||
// GetExpiredURLSet 批量查询已过期 URL 集合
|
||
func (r *uploadedFileRepository) GetExpiredURLSet(ctx context.Context) (map[string]struct{}, error) {
|
||
var urls []string
|
||
err := r.db.WithContext(ctx).
|
||
Model(&model.UploadedFile{}).
|
||
Where("expired = ?", true).
|
||
Pluck("url", &urls).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
set := make(map[string]struct{}, len(urls))
|
||
for _, u := range urls {
|
||
set[u] = struct{}{}
|
||
}
|
||
return set, nil
|
||
}
|