From 1f80c26a1510e86b00cdd20156c08d6f40f6be14 Mon Sep 17 00:00:00 2001 From: hb Date: Wed, 26 Jan 2022 11:34:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E5=86=85?= =?UTF-8?q?=E9=83=A8=E5=BC=80=E5=8F=91API=EF=BC=9A=E6=96=B0=E5=A2=9Ejssdk?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=92=8COauth=E7=94=A8=E6=88=B7=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- credential/work_js_ticket.go | 85 ++++++++++++++++++++++++++++++++ work/externalcontact/user.go | 6 +-- work/js/js.go | 93 ++++++++++++++++++++++++++++++++++++ work/user/user.go | 4 +- work/work.go | 6 +++ 5 files changed, 189 insertions(+), 5 deletions(-) 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..1ce8c11 --- /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/externalcontact/user.go b/work/externalcontact/user.go index c4fd2c1..34282ed 100644 --- a/work/externalcontact/user.go +++ b/work/externalcontact/user.go @@ -20,7 +20,7 @@ type ReqGetByUser struct { type OneUser struct { util.CommonError ExternalContact ExternalContact `json:"external_contact"` - FollowUser []FollowInfo `json:"follow_user"` //注意,仅获取单个客户详情的时候这里返回的是跟进记录列表 + FollowUser []FollowInfo `json:"follow_user"` //注意,仅获取单个客户详情的时候这里返回的是跟进人列表 NextCursor string `json:"next_cursor"` } type resUserList struct { @@ -35,7 +35,7 @@ type resUserids struct { type UserInfo struct { ExternalContact ExternalContact `json:"external_contact"` - FollowInfo FollowInfo `json:"follow_info"` //企业成员客户跟进信息,可以参考获取客户详情,但标签信息只会返回企业标签和规则组标签的tag_id,个人标签将不再返回 + FollowInfo FollowInfo `json:"follow_info"` //企业成员客户跟进人信息,可以参考获取客户详情,但标签信息只会返回企业标签和规则组标签的tag_id,个人标签将不再返回 } type ExternalContact struct { @@ -134,7 +134,7 @@ func (tpl *Client) GetQyUserInfoList(qyUserid []string) ([]UserInfo, error) { return userInfoList, nil } -//GetUserInfoAndAllFollow 获取客户详情以及全部跟进记录 +//GetUserInfoAndAllFollow 获取客户详情以及全部跟进人 func (tpl *Client) GetUserInfoAndAllFollow(userid string) (OneUser, error) { var result, res OneUser var err error diff --git a/work/js/js.go b/work/js/js.go new file mode 100644 index 0000000..dc4a8f1 --- /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/user/user.go b/work/user/user.go index 506e8d8..66eda7c 100644 --- a/work/user/user.go +++ b/work/user/user.go @@ -13,7 +13,7 @@ const ( userInfoURL = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s" updateURL = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=%s&department_id=%s&fetch_child=1" userListURL = "https://qyapi.weixin.qq.com/cgi-bin/user/get" - launchCode = "https://qyapi.weixin.qq.com/cgi-bin/get_launch_code" + launchCode = "https://qyapi.weixin.qq.com/cgi-bin/get_launch_code?access_token=%s" ) //User 用户管理 @@ -207,7 +207,7 @@ func (user *User) GetLaunchCode(userID, other string) (userInfo *RespLaunchCode, return } - uri := fmt.Sprintf(launchCode, accessToken, userID) + uri := fmt.Sprintf(launchCode, accessToken) var response []byte response, err = util.PostJSON(uri, map[string]interface{}{"operator_userid": userID, "single_chat": map[string]string{"userid": other}}) if err != nil { diff --git a/work/work.go b/work/work.go index 782d219..72f51de 100644 --- a/work/work.go +++ b/work/work.go @@ -5,6 +5,7 @@ import ( "github.com/silenceper/wechat/v2/work/config" "github.com/silenceper/wechat/v2/work/context" "github.com/silenceper/wechat/v2/work/externalcontact" + "github.com/silenceper/wechat/v2/work/js" "github.com/silenceper/wechat/v2/work/kf" "github.com/silenceper/wechat/v2/work/msgaudit" "github.com/silenceper/wechat/v2/work/oauth" @@ -47,6 +48,11 @@ func (wk *Work) GetOauth() *oauth.Oauth { return oauth.NewOauth(wk.ctx) } +// GetJs js-sdk配置 +func (wk *Work) GetJs() *js.Js { + return js.NewJs(wk.ctx) +} + // GetMsgAudit get msgAudit func (wk *Work) GetMsgAudit() (*msgaudit.Client, error) { return msgaudit.NewClient(wk.ctx.Config)