package service import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "strconv" "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" secret := "abbf27d46c46212c86e60f2ed3c534ee" 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, req *http_model.GetWxQRCodeRequest) (*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 } } var reqPath string var reqData string if req.SelectionId != "" { reqPath = "pageCommerce/taskDetail" reqData = req.SelectionId } if req.SProjectId != 0 || req.ProjectId != "" { reqPath = "pageRecommend/recommendDetail" if req.SProjectId != 0 { reqData = "0" + "-" + strconv.Itoa(req.SProjectId) } else { reqData = req.ProjectId + "-" + "0" } } if req.SLocalId != 0 || req.LocalId != "" { reqPath = "pageLife/lifeDetail" if req.SLocalId != 0 { reqData = "0" + "-" + strconv.Itoa(req.SLocalId) } else { reqData = req.LocalId + "-" + "0" } } fmt.Printf("reqData: %+v\n", reqData) qrRequest := http_model.WxQrCodeRequest{ Scene: reqData, Page: reqPath, 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 }