1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-03-01 00:35:26 +08:00

Compare commits

...

5 Commits

Author SHA1 Message Date
baiyishaoxia
56d963e0ca Merge de92dc0dcd into 4a8371e178 2024-11-30 17:17:16 +00:00
yangyl12345
4a8371e178 Feature/mini program template (#806)
* feat: 小程序发送订阅消息支持返回 msgid

* 小程序发送订阅消息支持返回 msgid

---------

Co-authored-by: w_yangyili <w_yangyili@xiwang.com>
2024-11-27 11:56:14 +08:00
yangyl12345
a571bf3546 Update default_access_token.go (#805)
* Update default_access_token.go

微信获取稳定版token,只有不等于空字符串的情况下才会返回access_token信息,未空的情况,继续调去微信服务

* 微信稳定性获取 access_token 接口,添加防止并发性获取 access_token 和多次微信服务获取代码

---------

Co-authored-by: w_yangyili <w_yangyili@xiwang.com>
2024-11-26 12:13:49 +08:00
CyJaySong
3fbe8634d9 officeaccount 和 miniprogram 添加UseStableAK 配置项 (#788) 2024-11-23 18:25:24 +08:00
baiyishaoxia
de92dc0dcd 增加开放平台授权方小程序登录 2022-12-14 10:36:13 +08:00
8 changed files with 141 additions and 11 deletions

View File

@@ -101,10 +101,11 @@ func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (access
// 不强制更新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
appID string
appSecret string
cacheKeyPrefix string
cache cache.Cache
accessTokenLock *sync.Mutex
}
// NewStableAccessToken new StableAccessToken
@@ -113,10 +114,11 @@ func NewStableAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.C
panic("cache is need")
}
return &StableAccessToken{
appID: appID,
appSecret: appSecret,
cache: cache,
cacheKeyPrefix: cacheKeyPrefix,
appID: appID,
appSecret: appSecret,
cache: cache,
cacheKeyPrefix: cacheKeyPrefix,
accessTokenLock: new(sync.Mutex),
}
}
@@ -130,7 +132,20 @@ func (ak *StableAccessToken) GetAccessTokenContext(ctx context.Context) (accessT
// 先从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
if accessToken = val.(string); accessToken != "" {
return
}
}
// 加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
ak.accessTokenLock.Lock()
defer ak.accessTokenLock.Unlock()
// 双检,防止重复从微信服务器获取
if val := ak.cache.Get(accessTokenCacheKey); val != nil {
if accessToken = val.(string); accessToken != "" {
return
}
}
// cache失效从微信服务器获取

View File

@@ -14,4 +14,5 @@ type Config struct {
Token string `json:"token"` // token
EncodingAESKey string `json:"encoding_aes_key"` // EncodingAESKey
Cache cache.Cache
UseStableAK bool // use the stable access_token
}

View File

@@ -34,7 +34,13 @@ type MiniProgram struct {
// NewMiniProgram 实例化小程序 API
func NewMiniProgram(cfg *config.Config) *MiniProgram {
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyMiniProgramPrefix, cfg.Cache)
var defaultAkHandle credential.AccessTokenContextHandle
const cacheKeyPrefix = credential.CacheKeyMiniProgramPrefix
if cfg.UseStableAK {
defaultAkHandle = credential.NewStableAccessToken(cfg.AppID, cfg.AppSecret, cacheKeyPrefix, cfg.Cache)
} else {
defaultAkHandle = credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, cacheKeyPrefix, cfg.Cache)
}
ctx := &context.Context{
Config: cfg,
AccessTokenHandle: defaultAkHandle,

View File

@@ -1,6 +1,7 @@
package subscribe
import (
"encoding/json"
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
@@ -70,6 +71,13 @@ type TemplateList struct {
Data []TemplateItem `json:"data"`
}
// resTemplateSend 发送获取 msg id
type resTemplateSend struct {
util.CommonError
MsgID int64 `json:"msgid"`
}
// Send 发送订阅消息
func (s *Subscribe) Send(msg *Message) (err error) {
var accessToken string
@@ -85,6 +93,33 @@ func (s *Subscribe) Send(msg *Message) (err error) {
return util.DecodeWithCommonError(response, "Send")
}
// SendGetMsgID 发送订阅消息返回 msgid
func (s *Subscribe) SendGetMsgID(msg *Message) (msgID int64, err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
response, err := util.PostJSON(uri, msg)
if err != nil {
return
}
var result resTemplateSend
if err = json.Unmarshal(response, &result); err != nil {
return
}
if result.ErrCode != 0 {
err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
return
}
msgID = result.MsgID
return
}
// ListTemplates 获取当前帐号下的个人模板列表
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
func (s *Subscribe) ListTemplates() (*TemplateList, error) {

View File

@@ -11,4 +11,5 @@ type Config struct {
Token string `json:"token"` // token
EncodingAESKey string `json:"encoding_aes_key"` // EncodingAESKey
Cache cache.Cache
UseStableAK bool // use the stable access_token
}

View File

@@ -49,7 +49,13 @@ type OfficialAccount struct {
// NewOfficialAccount 实例化公众号API
func NewOfficialAccount(cfg *config.Config) *OfficialAccount {
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyOfficialAccountPrefix, cfg.Cache)
var defaultAkHandle credential.AccessTokenContextHandle
const cacheKeyPrefix = credential.CacheKeyOfficialAccountPrefix
if cfg.UseStableAK {
defaultAkHandle = credential.NewStableAccessToken(cfg.AppID, cfg.AppSecret, cacheKeyPrefix, cfg.Cache)
} else {
defaultAkHandle = credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, cacheKeyPrefix, cfg.Cache)
}
ctx := &context.Context{
Config: cfg,
AccessTokenHandle: defaultAkHandle,

View File

@@ -0,0 +1,60 @@
package auth
import (
stdContext "context"
"encoding/json"
"fmt"
"github.com/silenceper/wechat/v2/openplatform/context"
"github.com/silenceper/wechat/v2/util"
)
const (
code2SessionURL = "https://api.weixin.qq.com/sns/component/jscode2session?appid=%s&js_code=%s&grant_type=authorization_code&component_appid=%s&component_access_token=%s"
)
// Auth 登录/用户信息
type Auth struct {
*context.Context
authorizerAppID string
}
// NewAuth new auth (授权方appID)
func NewAuth(ctx *context.Context, appID string) *Auth {
return &Auth{ctx, appID}
}
// ResCode2Session 登录凭证校验的返回结果
type ResCode2Session struct {
util.CommonError
OpenID string `json:"openid"` // 用户唯一标识
SessionKey string `json:"session_key"` // 会话密钥
UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符在满足UnionID下发条件的情况下会返回
}
// Code2Session 登录凭证校验。
func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error) {
return auth.Code2SessionContext(stdContext.Background(), jsCode)
}
// Code2SessionContext 登录凭证校验。
func (auth *Auth) Code2SessionContext(ctx stdContext.Context, jsCode string) (result ResCode2Session, err error) {
var response []byte
var componentAccessToken string
componentAccessToken, err = auth.GetComponentAccessToken()
if err != nil {
return
}
parse := fmt.Sprintf(code2SessionURL, auth.authorizerAppID, jsCode, auth.Context.AppID, componentAccessToken)
if response, err = util.HTTPGetContext(ctx, parse); err != nil {
return
}
if err = json.Unmarshal(response, &result); err != nil {
return
}
if result.ErrCode != 0 {
err = fmt.Errorf("Code2Session error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
return
}
return
}

View File

@@ -9,6 +9,7 @@ import (
miniContext "github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/miniprogram/urllink"
openContext "github.com/silenceper/wechat/v2/openplatform/context"
"github.com/silenceper/wechat/v2/openplatform/miniprogram/auth"
"github.com/silenceper/wechat/v2/openplatform/miniprogram/basic"
"github.com/silenceper/wechat/v2/openplatform/miniprogram/component"
)
@@ -72,6 +73,11 @@ func (miniProgram *MiniProgram) GetURLLink() *urllink.URLLink {
})
}
// GetAuth 登录/用户信息相关接口
func (miniProgram *MiniProgram) GetAuth() *auth.Auth {
return auth.NewAuth(miniProgram.openContext, miniProgram.AppID)
}
// DefaultAuthrAccessToken 默认获取授权ak的方法
type DefaultAuthrAccessToken struct {
opCtx *openContext.Context