Files
2026-10-08 16:50:13 +08:00

142 lines
3.2 KiB
Go

package reportser
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"sync"
"time"
"91jd-server/app/appg"
"91jd-server/common/log"
"github.com/robfig/cron/v3"
)
const (
sdkConfigPath = "/sdk/getAvailableDomainList"
sdkConfigCacheKey = "report:sdk:domain_config"
sdkConfigCacheTTL = 30 * time.Minute
sdkConfigFetchInterval = "0 */5 * * * *"
)
var (
sdkConfigCron *cron.Cron
sdkConfigOnce sync.Once
sdkConfigMu sync.RWMutex
sdkDomainConfig string
sdkConfigUpdatedAt time.Time
)
type sdkConfigResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data string `json:"data"`
}
// StartSDKConfigCron 定时拉取 SDK 域名配置串,服务端只缓存并原样透传。
func StartSDKConfigCron() {
sdkConfigOnce.Do(func() {
if err := RefreshSDKDomainConfig(); err != nil {
log.Error("RefreshSDKDomainConfig first run fail", log.E(err))
}
sdkConfigCron = cron.New(cron.WithSeconds())
if _, err := sdkConfigCron.AddFunc(sdkConfigFetchInterval, func() {
if err := RefreshSDKDomainConfig(); err != nil {
log.Error("RefreshSDKDomainConfig cron fail", log.E(err))
}
}); err != nil {
log.Error("StartSDKConfigCron AddFunc fail", log.E(err))
return
}
sdkConfigCron.Start()
})
}
func StopSDKConfigCron() {
if sdkConfigCron == nil {
return
}
ctx := sdkConfigCron.Stop()
<-ctx.Done()
}
func RefreshSDKDomainConfig() error {
apiURL := strings.TrimRight(appg.Conf.DataReport.Url, "/") + sdkConfigPath
data, err := fetchSDKDomainConfig(apiURL, &http.Client{Timeout: 5 * time.Second})
if err != nil {
return err
}
setSDKDomainConfig(data)
if appg.Redis == nil {
return nil
}
return appg.Redis.Set(sdkConfigCacheKey, data, sdkConfigCacheTTL)
}
func GetSDKDomainConfig() string {
sdkConfigMu.RLock()
cached := sdkDomainConfig
updatedAt := sdkConfigUpdatedAt
sdkConfigMu.RUnlock()
if cached != "" && time.Since(updatedAt) < time.Minute {
return cached
}
if appg.Redis != nil {
data, err := appg.Redis.Get(sdkConfigCacheKey)
if err != nil {
log.Error("GetSDKDomainConfig redis get fail", log.E(err))
} else if data != nil && *data != "" {
setSDKDomainConfig(*data)
return *data
}
}
return cached
}
func setSDKDomainConfig(data string) {
sdkConfigMu.Lock()
defer sdkConfigMu.Unlock()
sdkDomainConfig = data
sdkConfigUpdatedAt = time.Now()
}
func fetchSDKDomainConfig(apiURL string, client *http.Client) (string, error) {
req, err := http.NewRequest(http.MethodGet, apiURL, nil)
if err != nil {
return "", err
}
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("sdk config http status: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result sdkConfigResponse
if err := json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.Code != 0 {
return "", fmt.Errorf("sdk config code: %d, msg: %s", result.Code, result.Msg)
}
if strings.TrimSpace(result.Data) == "" {
return "", fmt.Errorf("sdk config data is empty")
}
return result.Data, nil
}