package fadada import ( "fmt" "strconv" "sync" "time" ) // tokenCache accessToken 内存缓存(官方有效期约 2 小时) type tokenCache struct { mu sync.Mutex token string expiresAt time.Time } func (c *tokenCache) get() (string, bool) { c.mu.Lock() defer c.mu.Unlock() if c.token == "" || time.Now().After(c.expiresAt) { return "", false } return c.token, true } func (c *tokenCache) set(token string, ttl time.Duration) { c.mu.Lock() defer c.mu.Unlock() c.token = token c.expiresAt = time.Now().Add(ttl) } // ensureAccessToken 获取可用 accessToken,过期前自动刷新 func (c *Client) ensureAccessToken() (string, error) { if token, ok := c.tokenCache.get(); ok { return token, nil } c.tokenMu.Lock() defer c.tokenMu.Unlock() if token, ok := c.tokenCache.get(); ok { return token, nil } res, err := c.http.PostToken(pathGetAccessToken) if err != nil { return "", err } if res.Code != "" && res.Code != successCode { return "", fmt.Errorf("获取法大大 accessToken 失败: code=%s msg=%s requestId=%s", res.Code, res.Msg, res.RequestID) } var data accessTokenData if err := decodeData(res.Data, &data); err != nil { return "", fmt.Errorf("解析 accessToken 失败: %w", err) } if data.AccessToken == "" { return "", fmt.Errorf("获取法大大 accessToken 失败: 响应为空 code=%s msg=%s", res.Code, res.Msg) } ttl := 2*time.Hour - 5*time.Minute if expiresIn, err := strconv.Atoi(data.ExpiresIn); err == nil && expiresIn > 0 { ttl = time.Duration(expiresIn)*time.Second - 5*time.Minute if ttl < time.Minute { ttl = time.Minute } } c.tokenCache.set(data.AccessToken, ttl) return data.AccessToken, nil }