package service import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "strings" "time" "youngee_b_api/consts" "youngee_b_api/model/http_model" "youngee_b_api/model/system_model" "youngee_b_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 = 5 * 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 { fmt.Printf("err: %+v\n", err) 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 { fmt.Printf("err: %+v\n", err) return nil, err } qrCodeUrl := fmt.Sprintf(qrCodeUrlFormat, accessToken) resp, err := http.Post(qrCodeUrl, "application/json; charset=utf8", bytes.NewReader(jsonBody)) if err != nil { fmt.Printf("err: %+v\n", err) 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 { fmt.Printf("decoder: %+v\nerr: %+v", decoder, err) return nil, err } fmt.Printf("qrResponse: %+v\ndecoder: %+v\n", qrResponse, decoder) return nil, nil case strings.HasPrefix(header, "image"): qrcodeBytes, err = ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("qrcodeBytes: %+v\nerr: %+v", qrcodeBytes, err) return nil, err } } data := http_model.GetWxQRCodeData{ QrCodeBytes: qrcodeBytes, } //fmt.Printf("data: %+v\n", data) return &data, nil }