From 8b088380efdab55489b268f0a5e41bb9177791a5 Mon Sep 17 00:00:00 2001 From: gazebo Date: Thu, 21 Feb 2019 16:36:19 +0800 Subject: [PATCH] - refactor weixinapi --- platformapi/weixinapi/cgibin.go | 56 +++++++++ platformapi/weixinapi/cgibin_test.go | 32 +++++ platformapi/weixinapi/sns.go | 104 +++++++++++++++++ platformapi/weixinapi/weixinapi.go | 148 +----------------------- platformapi/weixinapi/weixinapi_test.go | 29 ----- 5 files changed, 193 insertions(+), 176 deletions(-) create mode 100644 platformapi/weixinapi/cgibin.go create mode 100644 platformapi/weixinapi/cgibin_test.go create mode 100644 platformapi/weixinapi/sns.go diff --git a/platformapi/weixinapi/cgibin.go b/platformapi/weixinapi/cgibin.go new file mode 100644 index 00000000..b360ef21 --- /dev/null +++ b/platformapi/weixinapi/cgibin.go @@ -0,0 +1,56 @@ +package weixinapi + +import "git.rosy.net.cn/baseapi/utils" + +func (a *API) CBSetToken(newToken string) bool { + curToken := a.CBGetToken() + if curToken != newToken { + a.locker.Lock() + defer a.locker.Unlock() + a.token = newToken + return true + } + return false +} + +func (a *API) CBGetToken() string { + a.locker.RLock() + defer a.locker.RUnlock() + return a.token +} + +func (a *API) CBRetrieveToken() (tokenInfo *TokenInfo, err error) { + result, err := a.AccessAPI("cgi-bin/token", utils.Params2Map("grant_type", "client_credential"), "") + if err != nil { + return nil, err + } + tokenInfo = &TokenInfo{ + AccessToken: utils.Interface2String(result["access_token"]), + ExpiresIn: int(utils.MustInterface2Int64(result["expires_in"])), + } + return tokenInfo, nil +} + +func (a *API) CBRefreshToken() (tokenInfo *TokenInfo, err error) { + if tokenInfo, err = a.CBRetrieveToken(); err == nil { + a.CBSetToken(tokenInfo.AccessToken) + } + return tokenInfo, err +} + +func (a *API) CBMessageTemplateSend(userOpenID, templateID, downloadURL string, miniProgram, data interface{}) (err error) { + bodyJson := map[string]interface{}{ + "touser": userOpenID, + "template_id": templateID, + "url": downloadURL, + "data": data, + } + if downloadURL != "" { + bodyJson["url"] = downloadURL + } + if miniProgram != nil { + bodyJson["miniprogram"] = miniProgram + } + _, err = a.AccessAPI("cgi-bin/message/template/send", nil, string(utils.MustMarshal(bodyJson))) + return err +} diff --git a/platformapi/weixinapi/cgibin_test.go b/platformapi/weixinapi/cgibin_test.go new file mode 100644 index 00000000..33aa8b7a --- /dev/null +++ b/platformapi/weixinapi/cgibin_test.go @@ -0,0 +1,32 @@ +package weixinapi + +import "testing" + +func TestRefreshToken(t *testing.T) { + result, err := weixinapi.CBRefreshToken() + if err != nil || result.ExpiresIn != 7200 { + t.Fatal(err.Error()) + } + sugarLogger.Debug(result) +} + +func TestCBMessageTemplateSend(t *testing.T) { + // "oYN_usk0AeGc_C6VEZfmFQP5VHMQ": 1, // 周小扬 + // "oYN_ust9hXKEvEv0X6Mq6nlAWs_E": 1, // me + // "oYN_usvnObzrPweIgHTad9-uMf78": 1, // 老赵 + weixinapi.CBSetToken("17_HUkrxPrmSWDb-zuV1g9ioYj_MvHST2aGZZ58iX-g5JFiiV4vFJxQS8SvNlhHNh2HtT7aQGC3Lxozw43l-1lojMVu-6nYqqW-h2SKVxwHUvfYn5BJ6vqzQ9uU-da9u4KIazdq-ImOibw-G6pENNCfAFAIIX") + err := weixinapi.CBMessageTemplateSend("oYN_ust9hXKEvEv0X6Mq6nlAWs_E", "_DtNGwmOeR6TkkTVUblxLIlkV2MAPOX57TkvfdqG6nY", "", map[string]interface{}{ + "appid": "wx4b5930c13f8b1170", + "pagepath": "pages/order-manager/main", + }, map[string]interface{}{ + "first": "first", + "Day": "Day", + "orderId": "orderId", + "orderType": "orderType", + "customerName": "customerName", + "customerPhone": "customerPhone", + }) + if err != nil { + t.Fatal(err.Error()) + } +} diff --git a/platformapi/weixinapi/sns.go b/platformapi/weixinapi/sns.go new file mode 100644 index 00000000..7253af08 --- /dev/null +++ b/platformapi/weixinapi/sns.go @@ -0,0 +1,104 @@ +package weixinapi + +import ( + "crypto/aes" + "crypto/cipher" + + "git.rosy.net.cn/baseapi/utils" +) + +func mapData2SNSToken(result map[string]interface{}) *SNSTokenInfo { + return &SNSTokenInfo{ + AccessToken: utils.Interface2String(result["access_token"]), + ExpiresIn: int(utils.MustInterface2Int64(result["expires_in"])), + RefreshToken: utils.Interface2String(result["refresh_token"]), + OpenID: utils.Interface2String(result["openid"]), + Scope: utils.Interface2String(result["scope"]), + } +} + +func (a *API) SNSRetrieveToken(code string) (tokenInfo *SNSTokenInfo, err error) { + result, err := a.AccessAPI("sns/oauth2/access_token", utils.Params2Map("grant_type", "authorization_code", "code", code), "") + if err != nil { + return nil, err + } + return mapData2SNSToken(result), nil +} + +func (a *API) SNSRefreshToken(refreshToken string) (tokenInfo *SNSTokenInfo, err error) { + result, err := a.AccessAPI("sns/oauth2/refresh_token", utils.Params2Map("grant_type", "refresh_token", "refresh_token", refreshToken), "") + if err != nil { + return nil, err + } + return mapData2SNSToken(result), nil +} + +func (a *API) SNSGetUserInfo(accessToken, openid string) (*SNSUserInfo, error) { + result, err := a.AccessAPI("sns/userinfo", map[string]interface{}{ + "access_token": accessToken, + "openid": openid, + }, "") + if err == nil { + retVal := &SNSUserInfo{ + OpenID: utils.Interface2String(result["openid"]), + NickName: utils.Interface2String(result["nickname"]), + Sex: int(utils.MustInterface2Int64(result["sex"])), + Province: utils.Interface2String(result["province"]), + City: utils.Interface2String(result["city"]), + Country: utils.Interface2String(result["country"]), + HeadImgURL: utils.Interface2String(result["headimgurl"]), + Privilege: result["privilege"], + UnionID: utils.Interface2String(result["unionid"]), + } + return retVal, nil + } + return nil, err +} + +func (a *API) SNSIsOpenIDValid(accessToken, openid string) (bool, error) { + _, err := a.AccessAPI("sns/auth", map[string]interface{}{ + "access_token": accessToken, + "openid": openid, + }, "") + if err == nil { + return true, nil + } + return false, err +} + +func (a *API) SNSCode2Session(jsCode string) (sessionInfo *SessionInfo, err error) { + result, err := a.AccessAPI("sns/jscode2session", map[string]interface{}{ + "js_code": jsCode, + "grant_type": "authorization_code", + }, "") + if err == nil { + return &SessionInfo{ + OpenID: utils.Interface2String(result["openid"]), + SessionKey: utils.Interface2String(result["session_key"]), + UnionID: utils.Interface2String(result["unionid"]), + }, nil + } + return nil, err +} + +func (a *API) SNSDecodeMiniProgramData(encryptedData, sessionKey, iv string) (decryptedData []byte, err error) { + decodedDataList, err := utils.Base64DecodeMultiString(encryptedData, sessionKey, iv) + if err != nil { + return nil, err + } + c, err := aes.NewCipher(decodedDataList[1]) + if err != nil { + return nil, err + } + cfbdec := cipher.NewCBCDecrypter(c, decodedDataList[2][:c.BlockSize()]) + decryptedData = make([]byte, len(decodedDataList[0])) + cfbdec.CryptBlocks(decryptedData, decodedDataList[0]) + decryptedData = PKCS7UnPadding(decryptedData) + return decryptedData, nil +} + +func PKCS7UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} diff --git a/platformapi/weixinapi/weixinapi.go b/platformapi/weixinapi/weixinapi.go index bd2c11af..998abffe 100644 --- a/platformapi/weixinapi/weixinapi.go +++ b/platformapi/weixinapi/weixinapi.go @@ -1,8 +1,6 @@ package weixinapi import ( - "crypto/aes" - "crypto/cipher" "net/http" "strings" "sync" @@ -90,23 +88,6 @@ func (a *API) GetSecret() string { return a.secret } -func (a *API) SetToken(newToken string) bool { - curToken := a.GetToken() - if curToken != newToken { - a.locker.Lock() - defer a.locker.Unlock() - a.token = newToken - return true - } - return false -} - -func (a *API) GetToken() string { - a.locker.RLock() - defer a.locker.RUnlock() - return a.token -} - func isSNSAction(action string) bool { return strings.Index(action, "sns/") == 0 } @@ -124,7 +105,7 @@ func (a *API) AccessAPI(action string, params map[string]interface{}, body strin params2["appid"] = a.appID params2["secret"] = a.secret } else if !isSNSAction(action) { - accessToken := a.GetToken() + accessToken := a.CBGetToken() if accessToken == "" { panic("token is empty!") } @@ -171,130 +152,3 @@ func (a *API) AccessAPI(action string, params map[string]interface{}, body strin }) return retVal, err } - -func (a *API) RefreshToken() (tokenInfo *TokenInfo, err error) { - result, err := a.AccessAPI("cgi-bin/token", utils.Params2Map("grant_type", "client_credential"), "") - if err != nil { - return nil, err - } - tokenInfo = &TokenInfo{ - AccessToken: utils.Interface2String(result["access_token"]), - ExpiresIn: int(utils.MustInterface2Int64(result["expires_in"])), - } - // update my token too. - a.SetToken(tokenInfo.AccessToken) - return tokenInfo, nil -} - -func (a *API) MessageTemplateSend(userOpenID, templateID, downloadURL string, miniProgram, data interface{}) (err error) { - bodyJson := map[string]interface{}{ - "touser": userOpenID, - "template_id": templateID, - "url": downloadURL, - "data": data, - } - if downloadURL != "" { - bodyJson["url"] = downloadURL - } - if miniProgram != nil { - bodyJson["miniprogram"] = miniProgram - } - _, err = a.AccessAPI("cgi-bin/message/template/send", nil, string(utils.MustMarshal(bodyJson))) - return err -} - -func mapData2SNSToken(result map[string]interface{}) *SNSTokenInfo { - return &SNSTokenInfo{ - AccessToken: utils.Interface2String(result["access_token"]), - ExpiresIn: int(utils.MustInterface2Int64(result["expires_in"])), - RefreshToken: utils.Interface2String(result["refresh_token"]), - OpenID: utils.Interface2String(result["openid"]), - Scope: utils.Interface2String(result["scope"]), - } -} - -func (a *API) SNSGetToken(code string) (tokenInfo *SNSTokenInfo, err error) { - result, err := a.AccessAPI("sns/oauth2/access_token", utils.Params2Map("grant_type", "authorization_code", "code", code), "") - if err != nil { - return nil, err - } - return mapData2SNSToken(result), nil -} - -func (a *API) SNSRefreshToken(refreshToken string) (tokenInfo *SNSTokenInfo, err error) { - result, err := a.AccessAPI("sns/oauth2/refresh_token", utils.Params2Map("grant_type", "refresh_token", "refresh_token", refreshToken), "") - if err != nil { - return nil, err - } - return mapData2SNSToken(result), nil -} - -func (a *API) SNSGetUserInfo(accessToken, openid string) (*SNSUserInfo, error) { - result, err := a.AccessAPI("sns/userinfo", map[string]interface{}{ - "access_token": accessToken, - "openid": openid, - }, "") - if err == nil { - retVal := &SNSUserInfo{ - OpenID: utils.Interface2String(result["openid"]), - NickName: utils.Interface2String(result["nickname"]), - Sex: int(utils.MustInterface2Int64(result["sex"])), - Province: utils.Interface2String(result["province"]), - City: utils.Interface2String(result["city"]), - Country: utils.Interface2String(result["country"]), - HeadImgURL: utils.Interface2String(result["headimgurl"]), - Privilege: result["privilege"], - UnionID: utils.Interface2String(result["unionid"]), - } - return retVal, nil - } - return nil, err -} - -func (a *API) SNSIsOpenIDValid(accessToken, openid string) (bool, error) { - _, err := a.AccessAPI("sns/auth", map[string]interface{}{ - "access_token": accessToken, - "openid": openid, - }, "") - if err == nil { - return true, nil - } - return false, err -} - -func (a *API) SNSCode2Session(code string) (sessionInfo *SessionInfo, err error) { - result, err := a.AccessAPI("sns/jscode2session", map[string]interface{}{ - "js_code": code, - "grant_type": "authorization_code", - }, "") - if err == nil { - return &SessionInfo{ - OpenID: utils.Interface2String(result["openid"]), - SessionKey: utils.Interface2String(result["session_key"]), - UnionID: utils.Interface2String(result["unionid"]), - }, nil - } - return nil, err -} - -func (a *API) SNSDecodeMiniProgramData(encryptedData, sessionKey, iv string) (decryptedData []byte, err error) { - decodedDataList, err := utils.Base64DecodeMultiString(encryptedData, sessionKey, iv) - if err != nil { - return nil, err - } - c, err := aes.NewCipher(decodedDataList[1]) - if err != nil { - return nil, err - } - cfbdec := cipher.NewCBCDecrypter(c, decodedDataList[2][:c.BlockSize()]) - decryptedData = make([]byte, len(decodedDataList[0])) - cfbdec.CryptBlocks(decryptedData, decodedDataList[0]) - decryptedData = PKCS7UnPadding(decryptedData) - return decryptedData, nil -} - -func PKCS7UnPadding(origData []byte) []byte { - length := len(origData) - unpadding := int(origData[length-1]) - return origData[:(length - unpadding)] -} diff --git a/platformapi/weixinapi/weixinapi_test.go b/platformapi/weixinapi/weixinapi_test.go index 57b8e912..d4a6bdba 100644 --- a/platformapi/weixinapi/weixinapi_test.go +++ b/platformapi/weixinapi/weixinapi_test.go @@ -32,32 +32,3 @@ func handleError(t *testing.T, err error) { func TestTest(t *testing.T) { sugarLogger.Debug(utils.GetCurTimeStr()) } - -func TestRefreshToken(t *testing.T) { - result, err := weixinapi.RefreshToken() - if err != nil || result.ExpiresIn != 7200 { - t.Fatal(err.Error()) - } - sugarLogger.Debug(result) -} - -func TestMessageTemplateSend(t *testing.T) { - // "oYN_usk0AeGc_C6VEZfmFQP5VHMQ": 1, // 周小扬 - // "oYN_ust9hXKEvEv0X6Mq6nlAWs_E": 1, // me - // "oYN_usvnObzrPweIgHTad9-uMf78": 1, // 老赵 - weixinapi.SetToken("17_HUkrxPrmSWDb-zuV1g9ioYj_MvHST2aGZZ58iX-g5JFiiV4vFJxQS8SvNlhHNh2HtT7aQGC3Lxozw43l-1lojMVu-6nYqqW-h2SKVxwHUvfYn5BJ6vqzQ9uU-da9u4KIazdq-ImOibw-G6pENNCfAFAIIX") - err := weixinapi.MessageTemplateSend("oYN_ust9hXKEvEv0X6Mq6nlAWs_E", "_DtNGwmOeR6TkkTVUblxLIlkV2MAPOX57TkvfdqG6nY", "", map[string]interface{}{ - "appid": "wx4b5930c13f8b1170", - "pagepath": "pages/order-manager/main", - }, map[string]interface{}{ - "first": "first", - "Day": "Day", - "orderId": "orderId", - "orderType": "orderType", - "customerName": "customerName", - "customerPhone": "customerPhone", - }) - if err != nil { - t.Fatal(err.Error()) - } -}