|
|
@@ -2,13 +2,21 @@ package wechat
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
+ "crypto/md5"
|
|
|
"dsbqj-admin/pkg/cache"
|
|
|
"dsbqj-admin/pkg/logger"
|
|
|
"dsbqj-admin/pkg/util"
|
|
|
+ "encoding/hex"
|
|
|
+ "fmt"
|
|
|
"github.com/goccy/go-json"
|
|
|
"github.com/silenceper/wechat"
|
|
|
+ wechatCtx "github.com/silenceper/wechat/context"
|
|
|
"os"
|
|
|
+ "sort"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
"sync/atomic"
|
|
|
+ "time"
|
|
|
|
|
|
wechatCache "dsbqj-admin/pkg/cache/wechat"
|
|
|
)
|
|
|
@@ -28,9 +36,125 @@ type WechatHelper struct {
|
|
|
}
|
|
|
|
|
|
const (
|
|
|
- subscribeUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="
|
|
|
+ subscribeUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="
|
|
|
+ accessTokenURL = "http://fx.wumiao666.com/cp/getaccesstoken"
|
|
|
)
|
|
|
|
|
|
+type SignResult struct {
|
|
|
+ Param string
|
|
|
+ Line string
|
|
|
+ Sign string
|
|
|
+}
|
|
|
+
|
|
|
+func AppSign(params map[string]string, key string) SignResult {
|
|
|
+ var builder strings.Builder
|
|
|
+
|
|
|
+ // 1. 排序 key
|
|
|
+ keys := make([]string, 0, len(params))
|
|
|
+ for k := range params {
|
|
|
+ keys = append(keys, k)
|
|
|
+ }
|
|
|
+ sort.Strings(keys)
|
|
|
+ // 2. 拼接 key=value&
|
|
|
+ for _, k := range keys {
|
|
|
+ builder.WriteString(k)
|
|
|
+ builder.WriteString("=")
|
|
|
+ builder.WriteString(params[k])
|
|
|
+ builder.WriteString("&")
|
|
|
+ }
|
|
|
+
|
|
|
+ strForWX := builder.String()
|
|
|
+ str := strings.TrimSuffix(strForWX, "&")
|
|
|
+ line := str + key
|
|
|
+ sign := md5Hex(line)
|
|
|
+
|
|
|
+ return SignResult{
|
|
|
+ Param: str,
|
|
|
+ Line: line,
|
|
|
+ Sign: strings.ToLower(sign),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func md5Hex(s string) string {
|
|
|
+ sum := md5.Sum([]byte(s))
|
|
|
+ return hex.EncodeToString(sum[:])
|
|
|
+}
|
|
|
+
|
|
|
+type TokenData struct {
|
|
|
+ AccessToken string `json:"accessToken"`
|
|
|
+ ExpiresSecond *int64 `json:"expiresSecond"`
|
|
|
+}
|
|
|
+
|
|
|
+type ResAccessToken struct {
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ Code string `json:"code"`
|
|
|
+ Data *TokenData `json:"data"`
|
|
|
+}
|
|
|
+
|
|
|
+type ResAccessToken2 struct {
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ Code string `json:"code"`
|
|
|
+ Data map[string]interface{} `json:"data"`
|
|
|
+}
|
|
|
+
|
|
|
+func GetAccessTokenFromServer(ctx *wechatCtx.Context) (resAccessToken ResAccessToken, err error) {
|
|
|
+ var params = map[string]string{}
|
|
|
+ params["platformId"] = os.Getenv("SDK_PLATFORMID")
|
|
|
+ params["appId"] = os.Getenv("SDK_APPID")
|
|
|
+ params["wxAppId"] = os.Getenv("WECHAT_APPID")
|
|
|
+ params["clearCache"] = "1"
|
|
|
+ params["requestTime"] = strconv.Itoa(int(util.GetNowSecond()))
|
|
|
+
|
|
|
+ res := AppSign(params, os.Getenv("SDK_APPKEY"))
|
|
|
+ url := fmt.Sprintf("%s?%s&sign=%s", accessTokenURL, res.Param, res.Sign)
|
|
|
+ var body []byte
|
|
|
+ body, err = util.HTTPGet(url)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var temp1 map[string]interface{}
|
|
|
+ err = json.Unmarshal(body, &temp1)
|
|
|
+
|
|
|
+ var temp ResAccessToken2
|
|
|
+ err = json.Unmarshal(body, &temp)
|
|
|
+
|
|
|
+ err = json.Unmarshal(body, &resAccessToken)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if resAccessToken.Code != "0" {
|
|
|
+ err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.Msg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
|
|
|
+ var expires int64 = 600
|
|
|
+ if resAccessToken.Data.ExpiresSecond != nil {
|
|
|
+ expires = *resAccessToken.Data.ExpiresSecond
|
|
|
+ }
|
|
|
+ err = ctx.Cache.Set(accessTokenCacheKey, resAccessToken.Data.AccessToken, time.Duration(expires)*time.Second)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func MyAccessTokenFunc(ctx *wechatCtx.Context) (accessToken string, err error) {
|
|
|
+ accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
|
|
|
+ val := ctx.Cache.Get(accessTokenCacheKey)
|
|
|
+ if val != nil {
|
|
|
+ accessToken = val.(string)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ //从微信服务器获取
|
|
|
+ var resAccessToken ResAccessToken
|
|
|
+ resAccessToken, err = GetAccessTokenFromServer(ctx)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ accessToken = resAccessToken.Data.AccessToken
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
func NewWechatHelper() *WechatHelper {
|
|
|
appID := os.Getenv("WECHAT_APPID")
|
|
|
appSecret := os.Getenv("WECHAT_SECRET")
|
|
|
@@ -40,6 +164,8 @@ func NewWechatHelper() *WechatHelper {
|
|
|
Cache: wechatCache.NewAccessToken(cache.RedisReport),
|
|
|
})
|
|
|
|
|
|
+ wx.Context.SetGetAccessTokenFunc(MyAccessTokenFunc)
|
|
|
+
|
|
|
return &WechatHelper{
|
|
|
ctx: context.Background(),
|
|
|
AppId: appID,
|