|
@@ -0,0 +1,123 @@
|
|
|
+package service
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "context"
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "io/ioutil"
|
|
|
+ "net/http"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+ "youngee_m_api/consts"
|
|
|
+ "youngee_m_api/model/http_model"
|
|
|
+ "youngee_m_api/model/system_model"
|
|
|
+ "youngee_m_api/redis"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ accessTokenUrlFormat = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
|
|
|
+ qrCodeUrlFormat = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"
|
|
|
+ wxAccesssTokenKey = "wx_access_token"
|
|
|
+)
|
|
|
+
|
|
|
+var QrCode *qrcode
|
|
|
+
|
|
|
+type qrcode struct {
|
|
|
+ sessionTTL time.Duration
|
|
|
+}
|
|
|
+
|
|
|
+func QrCodeInit(config *system_model.Session) {
|
|
|
+ qrCode := new(qrcode)
|
|
|
+ qrCode.sessionTTL = time.Duration(config.TTL) * time.Minute
|
|
|
+ QrCode = qrCode
|
|
|
+}
|
|
|
+
|
|
|
+// getAndCacheWxAccessToken 获取并缓存微信的access token
|
|
|
+func getAndCacheWxAccessToken(ctx context.Context) (string, error) {
|
|
|
+ appId := "wxac396a3be7a16844"
|
|
|
+ secret := "c82ae9e75b4ed7d8022db5bda5371892"
|
|
|
+ url := fmt.Sprintf(accessTokenUrlFormat, appId, secret)
|
|
|
+
|
|
|
+ resp, err := http.Get(url)
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.New("request access token failed")
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ // 解析微信服务端返回的信息
|
|
|
+ var accessTokenRes http_model.WxAccessTokenResponse
|
|
|
+ decoder := json.NewDecoder(resp.Body)
|
|
|
+ if err = decoder.Decode(&accessTokenRes); err != nil {
|
|
|
+ return "", errors.New("decode wx response failed")
|
|
|
+ }
|
|
|
+
|
|
|
+ if accessTokenRes.Errcode != 0 {
|
|
|
+ return "", errors.New("request access token failed")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 缓存获取的access token,比微信返回的有效时间短5分钟失效
|
|
|
+ err = redis.Set(ctx, wxAccesssTokenKey, accessTokenRes.AccessToken, QrCode.sessionTTL)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ return accessTokenRes.AccessToken, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (q *qrcode) GetWxQrCode(ctx context.Context, Scene string, Page string) (*http_model.GetWxQRCodeData, error) {
|
|
|
+ // 获取access_token
|
|
|
+ accessToken, err := redis.Get(ctx, wxAccesssTokenKey)
|
|
|
+ if err != nil && err != consts.RedisNil {
|
|
|
+ // fmt.Printf("err: %+v\n", err)
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if accessToken == "" {
|
|
|
+ // 如果没有缓存的access token 则获取并缓存
|
|
|
+ accessToken, err = getAndCacheWxAccessToken(ctx)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ qrRequest := http_model.WxQrCodeRequest{
|
|
|
+ Scene: Scene,
|
|
|
+ Page: Page,
|
|
|
+ Width: 430,
|
|
|
+ CheckPath: false,
|
|
|
+ EnvVersion: "release",
|
|
|
+ }
|
|
|
+ jsonBody, err := json.Marshal(qrRequest)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ qrCodeUrl := fmt.Sprintf(qrCodeUrlFormat, accessToken)
|
|
|
+ resp, err := http.Post(qrCodeUrl, "application/json; charset=utf8", bytes.NewReader(jsonBody))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ var qrcodeBytes []byte
|
|
|
+
|
|
|
+ switch header := resp.Header.Get("content-Type"); {
|
|
|
+ case strings.HasPrefix(header, "application/json"):
|
|
|
+ // 如果返回的是json结构,说明发生错误
|
|
|
+ var qrResponse *http_model.WxQrCodeResponse
|
|
|
+ decoder := json.NewDecoder(resp.Body)
|
|
|
+ if err = decoder.Decode(&qrResponse); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return nil, nil
|
|
|
+ case strings.HasPrefix(header, "image"):
|
|
|
+ qrcodeBytes, err = ioutil.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ data := http_model.GetWxQRCodeData{
|
|
|
+ QrCodeBytes: qrcodeBytes,
|
|
|
+ }
|
|
|
+ return &data, nil
|
|
|
+}
|