mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-12 08:42:28 +08:00
Compare commits
2 Commits
e6d960c22f
...
v2.1.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
010e49c35c | ||
|
|
9c87d1cb34 |
@@ -229,6 +229,7 @@ func (ak *WorkAccessToken) GetAccessTokenContext(ctx context.Context) (accessTok
|
|||||||
|
|
||||||
// 构建缓存key
|
// 构建缓存key
|
||||||
var accessTokenCacheKey string
|
var accessTokenCacheKey string
|
||||||
|
|
||||||
if ak.AgentID != "" {
|
if ak.AgentID != "" {
|
||||||
// 如果设置了AgentID,使用新的key格式
|
// 如果设置了AgentID,使用新的key格式
|
||||||
accessTokenCacheKey = fmt.Sprintf("%s_access_token_%s_%s", ak.cacheKeyPrefix, ak.CorpID, ak.AgentID)
|
accessTokenCacheKey = fmt.Sprintf("%s_access_token_%s_%s", ak.cacheKeyPrefix, ak.CorpID, ak.AgentID)
|
||||||
|
|||||||
118
credential/work_js_ticket.go
Normal file
118
credential/work_js_ticket.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package credential
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/cache"
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TicketType ticket类型
|
||||||
|
type TicketType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TicketTypeCorpJs 企业jsapi ticket
|
||||||
|
TicketTypeCorpJs TicketType = iota
|
||||||
|
// TicketTypeAgentJs 应用jsapi ticket
|
||||||
|
TicketTypeAgentJs
|
||||||
|
)
|
||||||
|
|
||||||
|
// 企业微信相关的 ticket URL
|
||||||
|
const (
|
||||||
|
// 企业微信 jsapi ticket
|
||||||
|
getWorkJsTicketURL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s"
|
||||||
|
// 企业微信应用 jsapi ticket
|
||||||
|
getWorkAgentJsTicketURL = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=%s&type=agent_config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WorkJsTicket 企业微信js ticket获取
|
||||||
|
type WorkJsTicket struct {
|
||||||
|
corpID string
|
||||||
|
agentID string
|
||||||
|
cacheKeyPrefix string
|
||||||
|
cache cache.Cache
|
||||||
|
jsAPITicketLock *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWorkJsTicket new WorkJsTicket
|
||||||
|
func NewWorkJsTicket(corpID, agentID, cacheKeyPrefix string, cache cache.Cache) *WorkJsTicket {
|
||||||
|
return &WorkJsTicket{
|
||||||
|
corpID: corpID,
|
||||||
|
agentID: agentID,
|
||||||
|
cache: cache,
|
||||||
|
cacheKeyPrefix: cacheKeyPrefix,
|
||||||
|
jsAPITicketLock: new(sync.Mutex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTicket 根据类型获取相应的jsapi_ticket
|
||||||
|
func (js *WorkJsTicket) GetTicket(accessToken string, ticketType TicketType) (ticketStr string, err error) {
|
||||||
|
var cacheKey string
|
||||||
|
switch ticketType {
|
||||||
|
case TicketTypeCorpJs:
|
||||||
|
cacheKey = fmt.Sprintf("%s_corp_jsapi_ticket_%s", js.cacheKeyPrefix, js.corpID)
|
||||||
|
case TicketTypeAgentJs:
|
||||||
|
if js.agentID == "" {
|
||||||
|
err = fmt.Errorf("agentID is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cacheKey = fmt.Sprintf("%s_agent_jsapi_ticket_%s_%s", js.cacheKeyPrefix, js.corpID, js.agentID)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported ticket type: %v", ticketType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := js.cache.Get(cacheKey); val != nil {
|
||||||
|
return val.(string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
js.jsAPITicketLock.Lock()
|
||||||
|
defer js.jsAPITicketLock.Unlock()
|
||||||
|
|
||||||
|
// 双检,防止重复从微信服务器获取
|
||||||
|
if val := js.cache.Get(cacheKey); val != nil {
|
||||||
|
return val.(string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticket ResTicket
|
||||||
|
ticket, err = js.getTicketFromServer(accessToken, ticketType)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expires := ticket.ExpiresIn - 1500
|
||||||
|
err = js.cache.Set(cacheKey, ticket.Ticket, time.Duration(expires)*time.Second)
|
||||||
|
ticketStr = ticket.Ticket
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTicketFromServer 从服务器中获取ticket
|
||||||
|
func (js *WorkJsTicket) getTicketFromServer(accessToken string, ticketType TicketType) (ticket ResTicket, err error) {
|
||||||
|
var url string
|
||||||
|
switch ticketType {
|
||||||
|
case TicketTypeCorpJs:
|
||||||
|
url = fmt.Sprintf(getWorkJsTicketURL, accessToken)
|
||||||
|
case TicketTypeAgentJs:
|
||||||
|
url = fmt.Sprintf(getWorkAgentJsTicketURL, accessToken)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported ticket type: %v", ticketType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var response []byte
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -39,7 +39,6 @@ func (js *Js) SetJsTicketHandle(ticketHandle credential.JsTicketHandle) {
|
|||||||
// GetConfig 获取jssdk需要的配置参数
|
// GetConfig 获取jssdk需要的配置参数
|
||||||
// uri 为当前网页地址
|
// uri 为当前网页地址
|
||||||
func (js *Js) GetConfig(uri string) (config *Config, err error) {
|
func (js *Js) GetConfig(uri string) (config *Config, err error) {
|
||||||
config = new(Config)
|
|
||||||
var accessToken string
|
var accessToken string
|
||||||
accessToken, err = js.GetAccessToken()
|
accessToken, err = js.GetAccessToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -50,12 +49,11 @@ func (js *Js) GetConfig(uri string) (config *Config, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nonceStr := util.RandomStr(16)
|
nonceStr := util.RandomStr(16)
|
||||||
timestamp := util.GetCurrTS()
|
timestamp := util.GetCurrTS()
|
||||||
str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", ticketStr, nonceStr, timestamp, uri)
|
str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", ticketStr, nonceStr, timestamp, uri)
|
||||||
sigStr := util.Signature(str)
|
sigStr := util.Signature(str)
|
||||||
|
config = new(Config)
|
||||||
config.AppID = js.AppID
|
config.AppID = js.AppID
|
||||||
config.NonceStr = nonceStr
|
config.NonceStr = nonceStr
|
||||||
config.Timestamp = timestamp
|
config.Timestamp = timestamp
|
||||||
|
|||||||
75
work/jsapi/jsapi.go
Normal file
75
work/jsapi/jsapi.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package jsapi
|
||||||
|
|
||||||
|
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
|
||||||
|
jsTicket *credential.WorkJsTicket
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJs init
|
||||||
|
func NewJs(context *context.Context) *Js {
|
||||||
|
js := new(Js)
|
||||||
|
js.Context = context
|
||||||
|
js.jsTicket = credential.NewWorkJsTicket(
|
||||||
|
context.Config.CorpID,
|
||||||
|
context.Config.AgentID,
|
||||||
|
credential.CacheKeyWorkPrefix,
|
||||||
|
context.Cache,
|
||||||
|
)
|
||||||
|
return js
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config 返回给用户使用的配置
|
||||||
|
type Config struct {
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
NonceStr string `json:"nonce_str"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig 获取企业微信JS配置 https://developer.work.weixin.qq.com/document/path/90514
|
||||||
|
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.jsTicket.GetTicket(accessToken, credential.TicketTypeCorpJs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.NonceStr = util.RandomStr(16)
|
||||||
|
config.Timestamp = util.GetCurrTS()
|
||||||
|
str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", ticketStr, config.NonceStr, config.Timestamp, uri)
|
||||||
|
config.Signature = util.Signature(str)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAgentConfig 获取企业微信应用JS配置 https://developer.work.weixin.qq.com/document/path/94313
|
||||||
|
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.jsTicket.GetTicket(accessToken, credential.TicketTypeAgentJs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.NonceStr = util.RandomStr(16)
|
||||||
|
config.Timestamp = util.GetCurrTS()
|
||||||
|
str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", ticketStr, config.NonceStr, config.Timestamp, uri)
|
||||||
|
config.Signature = util.Signature(str)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/silenceper/wechat/v2/work/context"
|
"github.com/silenceper/wechat/v2/work/context"
|
||||||
"github.com/silenceper/wechat/v2/work/externalcontact"
|
"github.com/silenceper/wechat/v2/work/externalcontact"
|
||||||
"github.com/silenceper/wechat/v2/work/invoice"
|
"github.com/silenceper/wechat/v2/work/invoice"
|
||||||
|
"github.com/silenceper/wechat/v2/work/jsapi"
|
||||||
"github.com/silenceper/wechat/v2/work/kf"
|
"github.com/silenceper/wechat/v2/work/kf"
|
||||||
"github.com/silenceper/wechat/v2/work/material"
|
"github.com/silenceper/wechat/v2/work/material"
|
||||||
"github.com/silenceper/wechat/v2/work/message"
|
"github.com/silenceper/wechat/v2/work/message"
|
||||||
@@ -52,6 +53,11 @@ func (wk *Work) GetKF() (*kf.Client, error) {
|
|||||||
return kf.NewClient(wk.ctx.Config)
|
return kf.NewClient(wk.ctx.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JsSdk get JsSdk
|
||||||
|
func (wk *Work) JsSdk() *jsapi.Js {
|
||||||
|
return jsapi.NewJs(wk.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// GetExternalContact get external_contact
|
// GetExternalContact get external_contact
|
||||||
func (wk *Work) GetExternalContact() *externalcontact.Client {
|
func (wk *Work) GetExternalContact() *externalcontact.Client {
|
||||||
return externalcontact.NewClient(wk.ctx)
|
return externalcontact.NewClient(wk.ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user