Parcourir la source

新增微信二维码生成

Ohio-HYF il y a 2 ans
Parent
commit
58f6ef8ca4
4 fichiers modifiés avec 225 ajouts et 1 suppressions
  1. 3 1
      config/init.go
  2. 58 0
      handler/qrcode.go
  3. 41 0
      model/http_model/qrcode.go
  4. 123 0
      service/qrcode.go

+ 3 - 1
config/init.go

@@ -2,13 +2,14 @@ package config
 
 import (
 	"fmt"
-	"gopkg.in/yaml.v2"
 	"io/ioutil"
 	"os"
 	"youngee_m_api/db"
 	"youngee_m_api/model/system_model"
 	"youngee_m_api/redis"
 	"youngee_m_api/service"
+
+	"gopkg.in/yaml.v2"
 )
 
 func Init() *system_model.Server {
@@ -34,6 +35,7 @@ func loadExternelConfig(config *system_model.Config) {
 	redis.Init(config.Redis)
 	service.LoginAuthInit(config.Server.Session)
 	//service.SendCodeInit(config.Server.Session)
+	service.QrCodeInit(config.Server.Session)
 }
 
 func getEnv() string {

+ 58 - 0
handler/qrcode.go

@@ -0,0 +1,58 @@
+package handler
+
+import (
+	"youngee_m_api/consts"
+	"youngee_m_api/model/http_model"
+	"youngee_m_api/service"
+	"youngee_m_api/util"
+
+	"github.com/gin-gonic/gin"
+	"github.com/sirupsen/logrus"
+)
+
+func WrapGetWxQRCodeHandler(ctx *gin.Context) {
+	handler := newGetWxQRCodeHandler(ctx)
+	BaseRun(handler)
+}
+
+type GetWxQRCodeHandler struct {
+	ctx  *gin.Context
+	req  *http_model.GetWxQRCodeRequest
+	resp *http_model.CommonResponse
+}
+
+func (q GetWxQRCodeHandler) getContext() *gin.Context {
+	return q.ctx
+}
+
+func (q GetWxQRCodeHandler) getResponse() interface{} {
+	return q.resp
+}
+
+func (q GetWxQRCodeHandler) getRequest() interface{} {
+	return q.req
+}
+
+func (q GetWxQRCodeHandler) run() {
+	req := http_model.GetWxQRCodeRequest{}
+	req = *q.req
+	data, err := service.QrCode.GetWxQrCode(q.ctx, req.Scene, req.Page)
+	if err != nil {
+		logrus.WithContext(q.ctx).Errorf("[GetWxQRCodeHandler] error GetWxQrCode, err:%+v", err)
+		util.HandlerPackErrorResp(q.resp, consts.ErrorInternal, consts.DefaultToast)
+		return
+	}
+	q.resp.Data = data
+}
+
+func (q GetWxQRCodeHandler) checkParam() error {
+	return nil
+}
+
+func newGetWxQRCodeHandler(ctx *gin.Context) *GetWxQRCodeHandler {
+	return &GetWxQRCodeHandler{
+		ctx:  ctx,
+		req:  http_model.NewGetWxQRCodeRequest(),
+		resp: http_model.NewGetWxQRCodeResponse(),
+	}
+}

+ 41 - 0
model/http_model/qrcode.go

@@ -0,0 +1,41 @@
+package http_model
+
+type GetWxQRCodeRequest struct {
+	Scene string `json:"scene"` // 需要携带的参数,目前为任务id
+	Page  string `json:"page"`  // 页面path
+}
+
+type WxAccessTokenResponse struct {
+	AccessToken string `json:"access_token"` // 获取到的凭证
+	ExpiresIn   int    `json:"expires_in"`   // 凭证有效时间,单位:秒。目前是7200秒之内的值。
+	Errcode     int    `json:"errcode"`      // 错误码
+	Errmsg      string `json:"errmsg"`       // 错误信息
+}
+
+type WxQrCodeRequest struct {
+	Scene      string `json:"scene"`
+	Page       string `json:"page"`
+	Width      int    `json:"width,omitempty"`
+	CheckPath  bool   `json:"check_path,omitempty"`
+	EnvVersion string `json:"env_version,omitempty"`
+}
+
+type WxQrCodeResponse struct {
+	Errcode     int    `json:"errcode"`
+	Errmsg      string `json:"errmsg"`
+	ContentType string `json:"contentType"`
+	Buffer      []byte `json:"buffer"`
+}
+type GetWxQRCodeData struct {
+	QrCodeBytes []byte `json:"qr_code_bytes"`
+}
+
+func NewGetWxQRCodeRequest() *GetWxQRCodeRequest {
+	return new(GetWxQRCodeRequest)
+}
+
+func NewGetWxQRCodeResponse() *CommonResponse {
+	resp := new(CommonResponse)
+	resp.Data = new(GetWxQRCodeData)
+	return resp
+}

+ 123 - 0
service/qrcode.go

@@ -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
+}