random-api-go/service/data_source_fetcher.go

204 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"encoding/json"
"fmt"
"log"
"random-api-go/database"
"random-api-go/model"
"strconv"
"strings"
"time"
)
// DataSourceFetcher 数据源获取器
type DataSourceFetcher struct {
cacheManager *CacheManager
lankongFetcher *LankongFetcher
apiFetcher *APIFetcher
}
// NewDataSourceFetcher 创建数据源获取器
func NewDataSourceFetcher(cacheManager *CacheManager) *DataSourceFetcher {
// 从配置中获取兰空图床最大重试次数
maxRetries := getIntConfig("lankong_max_retries", 7)
var lankongFetcher *LankongFetcher
if maxRetries > 0 {
// 使用自定义配置
lankongFetcher = NewLankongFetcherWithConfig(maxRetries)
log.Printf("兰空图床获取器配置: 最大重试%d次", maxRetries)
} else {
// 使用默认配置
lankongFetcher = NewLankongFetcher()
log.Printf("兰空图床获取器使用默认配置")
}
return &DataSourceFetcher{
cacheManager: cacheManager,
lankongFetcher: lankongFetcher,
apiFetcher: NewAPIFetcher(),
}
}
// getIntConfig 获取整数配置,如果不存在或无效则返回默认值
func getIntConfig(key string, defaultValue int) int {
configStr := database.GetConfig(key, "")
if configStr == "" {
return defaultValue
}
value, err := strconv.Atoi(configStr)
if err != nil {
log.Printf("配置 %s 的值 '%s' 不是有效整数,使用默认值 %d", key, configStr, defaultValue)
return defaultValue
}
return value
}
// FetchURLs 从数据源获取URL列表
func (dsf *DataSourceFetcher) FetchURLs(dataSource *model.DataSource) ([]string, error) {
// API类型的数据源直接实时请求不使用缓存
if dataSource.Type == "api_get" || dataSource.Type == "api_post" {
log.Printf("实时请求API数据源 (类型: %s, ID: %d)", dataSource.Type, dataSource.ID)
return dsf.fetchAPIURLs(dataSource)
}
// 构建内存缓存的key使用数据源ID
cacheKey := fmt.Sprintf("datasource_%d", dataSource.ID)
// 先检查内存缓存
if cachedURLs, exists := dsf.cacheManager.GetFromMemoryCache(cacheKey); exists && len(cachedURLs) > 0 {
log.Printf("从内存缓存获取到 %d 个URL (数据源ID: %d)", len(cachedURLs), dataSource.ID)
return cachedURLs, nil
}
var urls []string
var err error
log.Printf("开始从数据源获取URL (类型: %s, ID: %d)", dataSource.Type, dataSource.ID)
switch dataSource.Type {
case "lankong":
urls, err = dsf.fetchLankongURLs(dataSource)
case "manual":
urls, err = dsf.fetchManualURLs(dataSource)
case "endpoint":
urls, err = dsf.fetchEndpointURLs(dataSource)
default:
return nil, fmt.Errorf("unsupported data source type: %s", dataSource.Type)
}
if err != nil {
return nil, fmt.Errorf("failed to fetch URLs from %s data source: %w", dataSource.Type, err)
}
if len(urls) == 0 {
log.Printf("警告: 数据源 %d 没有获取到任何URL", dataSource.ID)
return urls, nil
}
// 缓存结果到内存
dsf.cacheManager.SetMemoryCache(cacheKey, urls)
log.Printf("数据源 %d 已缓存 %d 个URL到内存", dataSource.ID, len(urls))
// 更新最后同步时间
now := time.Now()
dataSource.LastSync = &now
if err := dsf.updateDataSourceSyncTime(dataSource); err != nil {
log.Printf("Failed to update sync time for data source %d: %v", dataSource.ID, err)
}
return urls, nil
}
// fetchLankongURLs 获取兰空图床URL
func (dsf *DataSourceFetcher) fetchLankongURLs(dataSource *model.DataSource) ([]string, error) {
var config model.LankongConfig
if err := json.Unmarshal([]byte(dataSource.Config), &config); err != nil {
return nil, fmt.Errorf("invalid lankong config: %w", err)
}
return dsf.lankongFetcher.FetchURLs(&config)
}
// fetchManualURLs 获取手动配置的URL
func (dsf *DataSourceFetcher) fetchManualURLs(dataSource *model.DataSource) ([]string, error) {
// 手动配置可能是JSON格式或者纯文本格式
config := strings.TrimSpace(dataSource.Config)
// 尝试解析为JSON格式
var manualConfig model.ManualConfig
if err := json.Unmarshal([]byte(config), &manualConfig); err == nil {
return manualConfig.URLs, nil
}
// 如果不是JSON按行分割处理
lines := strings.Split(config, "\n")
var urls []string
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "#") { // 忽略空行和注释
urls = append(urls, line)
}
}
return urls, nil
}
// fetchAPIURLs 获取API接口URL (实时请求,不缓存)
func (dsf *DataSourceFetcher) fetchAPIURLs(dataSource *model.DataSource) ([]string, error) {
var config model.APIConfig
if err := json.Unmarshal([]byte(dataSource.Config), &config); err != nil {
return nil, fmt.Errorf("invalid API config: %w", err)
}
// 对于API类型的数据源直接进行实时请求不使用预存储的数据
return dsf.apiFetcher.FetchSingleURL(&config)
}
// fetchEndpointURLs 获取端点URL (直接返回端点URL列表)
func (dsf *DataSourceFetcher) fetchEndpointURLs(dataSource *model.DataSource) ([]string, error) {
var config model.EndpointConfig
if err := json.Unmarshal([]byte(dataSource.Config), &config); err != nil {
return nil, fmt.Errorf("invalid endpoint config: %w", err)
}
if len(config.EndpointIDs) == 0 {
return nil, fmt.Errorf("no endpoints configured")
}
// 这里我们需要导入database包来查询端点信息
// 为了避免循环依赖我们返回一个特殊的URL格式让服务层处理
var urls []string
for _, endpointID := range config.EndpointIDs {
// 使用特殊格式标记这是一个端点引用
urls = append(urls, fmt.Sprintf("endpoint://%d", endpointID))
}
return urls, nil
}
// updateDataSourceSyncTime 更新数据源的同步时间
func (dsf *DataSourceFetcher) updateDataSourceSyncTime(dataSource *model.DataSource) error {
// 这里需要导入database包来更新数据库
// 为了避免循环依赖,我们通过回调或者接口来处理
// 暂时先记录日志,具体实现在主服务中处理
log.Printf("需要更新数据源 %d 的同步时间", dataSource.ID)
return nil
}
// PreloadDataSource 预加载数据源(在保存时调用)
func (dsf *DataSourceFetcher) PreloadDataSource(dataSource *model.DataSource) error {
log.Printf("开始预加载数据源 (类型: %s, ID: %d)", dataSource.Type, dataSource.ID)
_, err := dsf.FetchURLs(dataSource)
if err != nil {
return fmt.Errorf("failed to preload data source %d: %w", dataSource.ID, err)
}
log.Printf("数据源 %d 预加载完成", dataSource.ID)
return nil
}