Просмотр исходного кода

feat: 获取稳定版接口调用凭据 (#669)

* feat: 获取稳定版接口调用凭据

* Update default_access_token.go

* fix: format code

---------

Co-authored-by: ctr <orinfy@foxmail.com>
Co-authored-by: houseme <housemecn@gmail.com>
welllog 3 лет назад
Родитель
Сommit
8821a3856d
1 измененных файлов с 78 добавлено и 6 удалено
  1. 78 6
      credential/default_access_token.go

+ 78 - 6
credential/default_access_token.go

@@ -12,9 +12,11 @@ import (
 )
 
 const (
-	// AccessTokenURL 获取access_token的接口
+	// accessTokenURL 获取access_token的接口
 	accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
-	// AccessTokenURL 企业微信获取access_token的接口
+	// stableAccessTokenURL 获取稳定版access_token的接口
+	stableAccessTokenURL = "https://api.weixin.qq.com/cgi-bin/stable_token"
+	// workAccessTokenURL 企业微信获取access_token的接口
 	workAccessTokenURL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s"
 	// CacheKeyOfficialAccountPrefix 微信公众号cache key前缀
 	CacheKeyOfficialAccountPrefix = "gowechat_officialaccount_"
@@ -79,20 +81,90 @@ func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (access
 
 	// cache失效,从微信服务器获取
 	var resAccessToken ResAccessToken
-	resAccessToken, err = GetTokenFromServerContext(ctx, fmt.Sprintf(accessTokenURL, ak.appID, ak.appSecret))
-	if err != nil {
+	if resAccessToken, err = GetTokenFromServerContext(ctx, fmt.Sprintf(accessTokenURL, ak.appID, ak.appSecret)); err != nil {
 		return
 	}
 
-	expires := resAccessToken.ExpiresIn - 1500
-	err = ak.cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(expires)*time.Second)
+	if err = ak.cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(resAccessToken.ExpiresIn-1500)*time.Second); err != nil {
+		return
+	}
+	accessToken = resAccessToken.AccessToken
+	return
+}
+
+// StableAccessToken 获取稳定版接口调用凭据(与getAccessToken获取的调用凭证完全隔离,互不影响)
+// 不强制更新access_token,可用于不同环境不同服务而不需要分布式锁以及公用缓存,避免access_token争抢
+// https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html
+type StableAccessToken struct {
+	appID          string
+	appSecret      string
+	cacheKeyPrefix string
+	cache          cache.Cache
+}
+
+// NewStableAccessToken new StableAccessToken
+func NewStableAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenContextHandle {
+	if cache == nil {
+		panic("cache is need")
+	}
+	return &StableAccessToken{
+		appID:          appID,
+		appSecret:      appSecret,
+		cache:          cache,
+		cacheKeyPrefix: cacheKeyPrefix,
+	}
+}
+
+// GetAccessToken 获取access_token,先从cache中获取,没有则从服务端获取
+func (ak *StableAccessToken) GetAccessToken() (accessToken string, err error) {
+	return ak.GetAccessTokenContext(context.Background())
+}
+
+// GetAccessTokenContext 获取access_token,先从cache中获取,没有则从服务端获取
+func (ak *StableAccessToken) GetAccessTokenContext(ctx context.Context) (accessToken string, err error) {
+	// 先从cache中取
+	accessTokenCacheKey := fmt.Sprintf("%s_stable_access_token_%s", ak.cacheKeyPrefix, ak.appID)
+	if val := ak.cache.Get(accessTokenCacheKey); val != nil {
+		return val.(string), nil
+	}
+
+	// cache失效,从微信服务器获取
+	var resAccessToken ResAccessToken
+	resAccessToken, err = ak.GetAccessTokenDirectly(ctx, false)
 	if err != nil {
 		return
 	}
+
+	expires := resAccessToken.ExpiresIn - 300
+	_ = ak.cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(expires)*time.Second)
+
 	accessToken = resAccessToken.AccessToken
 	return
 }
 
+// GetAccessTokenDirectly 从微信获取access_token
+func (ak *StableAccessToken) GetAccessTokenDirectly(ctx context.Context, forceRefresh bool) (resAccessToken ResAccessToken, err error) {
+	b, err := util.PostJSONContext(ctx, stableAccessTokenURL, map[string]interface{}{
+		"grant_type":    "client_credential",
+		"appid":         ak.appID,
+		"secret":        ak.appSecret,
+		"force_refresh": forceRefresh,
+	})
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(b, &resAccessToken); err != nil {
+		return
+	}
+
+	if resAccessToken.ErrCode != 0 {
+		err = fmt.Errorf("get stable access_token error : errcode=%v , errormsg=%v", resAccessToken.ErrCode, resAccessToken.ErrMsg)
+		return
+	}
+	return
+}
+
 // WorkAccessToken 企业微信AccessToken 获取
 type WorkAccessToken struct {
 	CorpID          string