From 61bcd6b0e491ca634d2d839034ac12ba9e504b1e Mon Sep 17 00:00:00 2001 From: wind Date: Fri, 27 Sep 2024 16:05:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9EJS-SDK=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=9D=83=E9=99=90=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- credential/work_js_ticket.go | 85 ++++++++++++++++++++++++++++++++ work/js/js.go | 93 ++++++++++++++++++++++++++++++++++++ work/work.go | 6 +++ 3 files changed, 184 insertions(+) create mode 100644 credential/work_js_ticket.go create mode 100644 work/js/js.go diff --git a/credential/work_js_ticket.go b/credential/work_js_ticket.go new file mode 100644 index 0000000..e3906b6 --- /dev/null +++ b/credential/work_js_ticket.go @@ -0,0 +1,85 @@ +package credential + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/silenceper/wechat/v2/cache" + "github.com/silenceper/wechat/v2/util" +) + +// 获取ticket的url https://developer.work.weixin.qq.com/document/path/90506 +const getQyWxTicketURL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s" +const getQyAppTicketURL = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=%s&type=agent_config" + +// WorkJsTicket 默认获取js ticket方法 +type WorkJsTicket struct { + appID string + agentID string + cacheKeyPrefix string + cache cache.Cache + //jsAPITicket 读写锁 同一个AppID一个 + jsAPITicketLock *sync.Mutex +} + +// NewWorkJsTicket new +func NewWorkJsTicket(appID string, agentID string, cacheKeyPrefix string, cache cache.Cache) JsTicketHandle { + return &WorkJsTicket{ + appID: appID, + agentID: agentID, + cache: cache, + cacheKeyPrefix: cacheKeyPrefix, + jsAPITicketLock: new(sync.Mutex), + } +} + +// GetTicket 获取企业微信jsapi_ticket +func (js *WorkJsTicket) GetTicket(accessToken string) (ticketStr string, err error) { + //先从cache中取 + jsAPITicketCacheKey := fmt.Sprintf("%s_jsapi_ticket_%s", js.cacheKeyPrefix, js.appID) + if val := js.cache.Get(jsAPITicketCacheKey); val != nil { + return val.(string), nil + } + + js.jsAPITicketLock.Lock() + defer js.jsAPITicketLock.Unlock() + + // 双检,防止重复从微信服务器获取 + if val := js.cache.Get(jsAPITicketCacheKey); val != nil { + return val.(string), nil + } + + var ticket ResTicket + ticket, err = GetQyWxTicketFromServer(accessToken, js.agentID != "") + if err != nil { + return + } + expires := ticket.ExpiresIn - 1500 + err = js.cache.Set(jsAPITicketCacheKey, ticket.Ticket, time.Duration(expires)*time.Second) + ticketStr = ticket.Ticket + return +} + +// GetQyWxTicketFromServer 从企业微信服务器中获取ticket +func GetQyWxTicketFromServer(accessToken string, isApp bool) (ticket ResTicket, err error) { + var response []byte + url := fmt.Sprintf(getQyWxTicketURL, accessToken) + if isApp { + url = fmt.Sprintf(getQyAppTicketURL, accessToken) + } + response, err = util.HTTPGet(url) + if err != nil { + return + } + err = json.Unmarshal(response, &ticket) + if err != nil { + return + } + if ticket.ErrCode != 0 { + err = fmt.Errorf("getTicket Error : errcode=%d , errmsg=%s", ticket.ErrCode, ticket.ErrMsg) + return + } + return +} diff --git a/work/js/js.go b/work/js/js.go new file mode 100644 index 0000000..4d2a23b --- /dev/null +++ b/work/js/js.go @@ -0,0 +1,93 @@ +package js + +import ( + "fmt" + + "github.com/silenceper/wechat/v2/credential" + "github.com/silenceper/wechat/v2/util" + "github.com/silenceper/wechat/v2/work/context" +) + +// Js struct +type Js struct { + *context.Context + credential.JsTicketHandle +} + +// Config 返回给用户jssdk配置信息 +type Config struct { + CorpID string `json:"corp_id"` + Agentid string `json:"agentid"` + Timestamp int64 `json:"timestamp"` + NonceStr string `json:"nonce_str"` + Signature string `json:"signature"` +} + +// NewJs init +func NewJs(context *context.Context) *Js { + js := new(Js) + js.Context = context + jsTicketHandle := credential.NewWorkJsTicket(context.CorpID, context.AgentID, credential.CacheKeyWorkPrefix, context.Cache) + js.SetJsTicketHandle(jsTicketHandle) + return js +} + +// SetJsTicketHandle 自定义js ticket取值方式 +func (js *Js) SetJsTicketHandle(ticketHandle credential.JsTicketHandle) { + js.JsTicketHandle = ticketHandle +} + +// GetConfig 获取jssdk需要的配置参数 +// uri 为当前网页地址 +func (js *Js) GetConfig(uri string) (config *Config, err error) { + config = new(Config) + var accessToken string + accessToken, err = js.GetAccessToken() + if err != nil { + return + } + var ticketStr string + ticketStr, err = js.GetTicket(accessToken) + if err != nil { + return + } + + nonceStr := util.RandomStr(16) + timestamp := util.GetCurrTS() + str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", ticketStr, nonceStr, timestamp, uri) + sigStr := util.Signature(str) + + config.CorpID = js.CorpID + config.Agentid = js.AgentID + config.NonceStr = nonceStr + config.Timestamp = timestamp + config.Signature = sigStr + return +} + +// GetAgentConfig 获取jssdk需要的配置参数 +// uri 为当前网页地址 +func (js *Js) GetAgentConfig(uri string) (config *Config, err error) { + config = new(Config) + var accessToken string + accessToken, err = js.GetAccessToken() + if err != nil { + return + } + var ticketStr string + ticketStr, err = js.GetTicket(accessToken) + if err != nil { + return + } + + nonceStr := util.RandomStr(16) + timestamp := util.GetCurrTS() + str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", ticketStr, nonceStr, timestamp, uri) + sigStr := util.Signature(str) + + config.CorpID = js.CorpID + config.NonceStr = nonceStr + config.Timestamp = timestamp + config.Signature = sigStr + return +} diff --git a/work/work.go b/work/work.go index 24c5773..60fbf47 100644 --- a/work/work.go +++ b/work/work.go @@ -9,6 +9,7 @@ import ( "github.com/silenceper/wechat/v2/work/context" "github.com/silenceper/wechat/v2/work/externalcontact" "github.com/silenceper/wechat/v2/work/invoice" + "github.com/silenceper/wechat/v2/work/js" "github.com/silenceper/wechat/v2/work/kf" "github.com/silenceper/wechat/v2/work/material" "github.com/silenceper/wechat/v2/work/message" @@ -91,3 +92,8 @@ func (wk *Work) GetInvoice() *invoice.Client { func (wk *Work) GetCheckin() *checkin.Client { return checkin.NewClient(wk.ctx) } + +// GetJs js-sdk配置 +func (wk *Work) GetJs() *js.Js { + return js.NewJs(wk.ctx) +}