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

@@ -19,6 +19,7 @@ type Config struct {
Log LogConfig `mapstructure:"log"`
RateLimit RateLimitConfig `mapstructure:"rate_limit"`
Upload UploadConfig `mapstructure:"upload"`
FileCleanup FileCleanupConfig `mapstructure:"file_cleanup"`
OpenAI OpenAIConfig `mapstructure:"openai"`
TencentTMS TencentTMSConfig `mapstructure:"tencent_tms"`
Email EmailConfig `mapstructure:"email"`
@@ -95,6 +96,11 @@ func Load(configPath string) (*Config, error) {
viper.SetDefault("rate_limit.requests_per_minute", 60)
viper.SetDefault("upload.max_file_size", 10485760)
viper.SetDefault("upload.allowed_types", []string{"image/jpeg", "image/png", "image/gif", "image/webp"})
// 文件过期清理默认值
viper.SetDefault("file_cleanup.enabled", true)
viper.SetDefault("file_cleanup.retention_days", 7)
viper.SetDefault("file_cleanup.interval_minutes", 360)
viper.SetDefault("file_cleanup.batch_size", 100)
viper.SetDefault("s3.endpoint", "")
viper.SetDefault("s3.access_key", "")
viper.SetDefault("s3.secret_key", "")
@@ -174,6 +180,59 @@ func Load(configPath string) (*Config, error) {
viper.SetDefault("jpush.app_key", "")
viper.SetDefault("jpush.master_secret", "")
viper.SetDefault("jpush.production", false)
// JPush 厂商通道 channel_id 默认值
// 各厂商 channel_id 通过环境变量配置(见下方 BindEnv
viper.SetDefault("jpush.channel.system", "")
viper.SetDefault("jpush.channel.chat", "")
viper.SetDefault("jpush.channel.vendor.xiaomi.system", "")
viper.SetDefault("jpush.channel.vendor.xiaomi.chat", "")
viper.SetDefault("jpush.channel.vendor.huawei.system", "")
viper.SetDefault("jpush.channel.vendor.huawei.chat", "")
viper.SetDefault("jpush.channel.vendor.oppo.system", "")
viper.SetDefault("jpush.channel.vendor.oppo.chat", "")
viper.SetDefault("jpush.channel.vendor.vivo.system", "")
viper.SetDefault("jpush.channel.vendor.vivo.chat", "")
viper.SetDefault("jpush.channel.vendor.meizu.system", "")
viper.SetDefault("jpush.channel.vendor.meizu.chat", "")
viper.SetDefault("jpush.channel.vendor.honor.system", "")
viper.SetDefault("jpush.channel.vendor.honor.chat", "")
viper.SetDefault("jpush.channel.vendor.fcm.system", "")
viper.SetDefault("jpush.channel.vendor.fcm.chat", "")
// 小米/OPPO 模板 id 默认值
viper.SetDefault("jpush.channel.vendor.xiaomi.mi_system_template_id", "")
viper.SetDefault("jpush.channel.vendor.xiaomi.mi_chat_template_id", "")
viper.SetDefault("jpush.channel.vendor.oppo.oppo_system_private_template_id", "")
viper.SetDefault("jpush.channel.vendor.oppo.oppo_chat_private_template_id", "")
// iOS 通知分组 thread-id 默认值
viper.SetDefault("jpush.channel.ios.chat_thread_id", "")
viper.SetDefault("jpush.channel.ios.system_thread_id", "")
// JPush 厂商 channel_id 环境变量显式绑定
// AutomaticEnv 对深层嵌套 key 不可靠,故逐个 BindEnv
// 命名规则APP_JPUSH_CHANNEL_{VENDOR}_{SYSTEM|CHAT}
viper.BindEnv("jpush.channel.system", "APP_JPUSH_CHANNEL_SYSTEM")
viper.BindEnv("jpush.channel.chat", "APP_JPUSH_CHANNEL_CHAT")
viper.BindEnv("jpush.channel.vendor.xiaomi.system", "APP_JPUSH_CHANNEL_XIAOMI_SYSTEM")
viper.BindEnv("jpush.channel.vendor.xiaomi.chat", "APP_JPUSH_CHANNEL_XIAOMI_CHAT")
viper.BindEnv("jpush.channel.vendor.huawei.system", "APP_JPUSH_CHANNEL_HUAWEI_SYSTEM")
viper.BindEnv("jpush.channel.vendor.huawei.chat", "APP_JPUSH_CHANNEL_HUAWEI_CHAT")
viper.BindEnv("jpush.channel.vendor.oppo.system", "APP_JPUSH_CHANNEL_OPPO_SYSTEM")
viper.BindEnv("jpush.channel.vendor.oppo.chat", "APP_JPUSH_CHANNEL_OPPO_CHAT")
viper.BindEnv("jpush.channel.vendor.vivo.system", "APP_JPUSH_CHANNEL_VIVO_SYSTEM")
viper.BindEnv("jpush.channel.vendor.vivo.chat", "APP_JPUSH_CHANNEL_VIVO_CHAT")
viper.BindEnv("jpush.channel.vendor.meizu.system", "APP_JPUSH_CHANNEL_MEIZU_SYSTEM")
viper.BindEnv("jpush.channel.vendor.meizu.chat", "APP_JPUSH_CHANNEL_MEIZU_CHAT")
viper.BindEnv("jpush.channel.vendor.honor.system", "APP_JPUSH_CHANNEL_HONOR_SYSTEM")
viper.BindEnv("jpush.channel.vendor.honor.chat", "APP_JPUSH_CHANNEL_HONOR_CHAT")
viper.BindEnv("jpush.channel.vendor.fcm.system", "APP_JPUSH_CHANNEL_FCM_SYSTEM")
viper.BindEnv("jpush.channel.vendor.fcm.chat", "APP_JPUSH_CHANNEL_FCM_CHAT")
// 小米/OPPO 模板 id 环境变量绑定
viper.BindEnv("jpush.channel.vendor.xiaomi.mi_system_template_id", "APP_JPUSH_CHANNEL_XIAOMI_MI_SYSTEM_TEMPLATE_ID")
viper.BindEnv("jpush.channel.vendor.xiaomi.mi_chat_template_id", "APP_JPUSH_CHANNEL_XIAOMI_MI_CHAT_TEMPLATE_ID")
viper.BindEnv("jpush.channel.vendor.oppo.oppo_system_private_template_id", "APP_JPUSH_CHANNEL_OPPO_SYSTEM_PRIVATE_TEMPLATE_ID")
viper.BindEnv("jpush.channel.vendor.oppo.oppo_chat_private_template_id", "APP_JPUSH_CHANNEL_OPPO_CHAT_PRIVATE_TEMPLATE_ID")
// iOS thread-id 环境变量绑定
viper.BindEnv("jpush.channel.ios.chat_thread_id", "APP_JPUSH_CHANNEL_IOS_CHAT_THREAD_ID")
viper.BindEnv("jpush.channel.ios.system_thread_id", "APP_JPUSH_CHANNEL_IOS_SYSTEM_THREAD_ID")
viper.SetDefault("setup_secret", "")
// WebSocket 默认值
viper.SetDefault("websocket.mode", "standalone")