package fqapi import ( "database/sql" "encoding/json" "fmt" "time" ) // AudioCacheItem 音频缓存项 type AudioCacheItem struct { OriginalURL string `json:"original_url"` // 原始CDN URL DeviceID string `json:"device_id"` // 关联的设备ID Headers map[string]string `json:"headers"` // 请求头信息 CachedAt time.Time `json:"cached_at"` // 缓存时间 } // GenerateAudioProxyURL 生成音频代理URL func GenerateAudioProxyURL(originalURL, deviceID string, headers map[string]string) string { // 生成缓存key key := generateRandomKey(16) // 将headers序列化为JSON字符串 headersJSON, _ := json.Marshal(headers) // 存储到数据库 if cacheDB != nil { cacheMutex.Lock() defer cacheMutex.Unlock() _, err := cacheDB.Exec(` INSERT INTO audio_cache (key, original_url, device_id, headers, created_at) VALUES (?, ?, ?, ?, ?) `, key, originalURL, deviceID, string(headersJSON), time.Now().Unix()) if err != nil { audioLogger.Error("保存音频缓存失败: " + err.Error()) } } // 返回代理URL return fmt.Sprintf("/audio/proxy/%s", key) } // HandleAudioProxy 处理音频代理请求 func HandleAudioProxy(key string) ([]byte, string, error) { if cacheDB == nil { return nil, "", fmt.Errorf("数据库未初始化") } // 从数据库获取音频信息 var originalURL, deviceID, headersJSON string var createdAt int64 cacheMutex.Lock() err := cacheDB.QueryRow(` SELECT original_url, device_id, headers, created_at FROM audio_cache WHERE key = ? `, key).Scan(&originalURL, &deviceID, &headersJSON, &createdAt) cacheMutex.Unlock() if err == sql.ErrNoRows { return nil, "", fmt.Errorf("音频信息不存在,请先调用 /audio 接口") } if err != nil { return nil, "", fmt.Errorf("查询音频缓存失败: %w", err) } // 检查缓存是否过期(2小时,匹配CDN链接有效期) if time.Now().Unix()-createdAt > 7200 { // 2小时 = 7200秒 // 清理过期缓存 cacheMutex.Lock() cacheDB.Exec("DELETE FROM audio_cache WHERE key = ?", key) cacheMutex.Unlock() return nil, "", fmt.Errorf("音频信息已过期,请重新调用 /audio 接口") } // 解析headers var headers map[string]string json.Unmarshal([]byte(headersJSON), &headers) if headers == nil { headers = make(map[string]string) } // 下载音频内容,使用关联的设备信息 data, contentType, err := downloadMediaWithRetry(originalURL, deviceID, headers, 3) if err != nil { return nil, "", fmt.Errorf("下载音频失败: %w", err) } return data, contentType, nil } // CleanExpiredAudioCache 清理过期的音频缓存 func CleanExpiredAudioCache() { if cacheDB == nil { return } cacheMutex.Lock() defer cacheMutex.Unlock() // 删除2小时前的缓存 result, err := cacheDB.Exec("DELETE FROM audio_cache WHERE created_at < ?", time.Now().Unix()-7200) if err != nil { audioLogger.Error("清理过期音频缓存失败: " + err.Error()) return } rowsAffected, _ := result.RowsAffected() if rowsAffected > 0 { audioLogger.Info(fmt.Sprintf("已清理 %d 条过期音频缓存", rowsAffected)) } }