go實現QR訂閱的幾種方法

云岛夜川川發表於2024-11-20

1.基於HTTP協議

1.1返回圖片地址

透過qrcode生成二維碼圖片到static目錄下,然後返回二維碼圖片的地址,返回的地址可以使用base64加密也可以直接返回。
controller層

func QrSignHandler(c *gin.Context) {

	qr_url, err := logic.Generate_Qr()
	if err != nil {
		c.JSON(10005, err)
		return
	}

	c.JSON(200, gin.H{
		"qrCodeUrl": "/"+qr_url, //返回絕對路徑
	})
}

//DosignHandler 驗證掃碼的簽到
func DosignHandler(c *gin.Context) {
	// 獲取引數
	token:=c.Param("token")
	// 驗證token是否存在
	if err:=logic.VerifyToken(token);err!=nil {
		c.JSON(502,gin.H{
			"msg": err.Error(),
		})
	}else {
		c.JSON(200,gin.H{"msg":"簽到成功"})
	}

}

logic層

func Generate_Qr() (sign_url string, err error) {
	//1. 生成二維碼token
	token, err := generate_token()
	if err != nil {
		return "", err
	}
	//2.使用redis儲存token

	err = redis.QRsign(token)

	//3. 呼叫第三方 API生成二維碼並返回Url
	sign_url, err = generateQr_url(token)

	return
}
func VerifyToken(token string) error {

	//驗證token

	return redis.VerifyQrToken(token)
}

func generate_token() (string, error) {

	b := make([]byte, 32)
	_, err := rand.Read(b)

	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(b), nil
}

// generateQr_url 生成qr
func generateQr_url(token string) (string, error) {

	//生成內容
	Qr_content := "http://127.0.0.1:8080/api/v1/qr/" + token

	fmt.Println("簽到:",Qr_content)
	//二維碼儲存位置
	qr_root := "static/qrcodes/"
	qr_file_path := qr_root + token + ".png"

	defer delfile(qr_root,qr_file_path)
    // 建立二維碼並儲存
	err := qrcode.WriteFile(Qr_content, qrcode.Medium, 256, qr_file_path)
	if err != nil {
		zap.L().Error("建立二維碼失敗!", zap.Error(err))
		return "", err
	}

	return qr_file_path, nil

}

// delfile 刪除多餘的二維碼
func delfile(dir string,newqr string)  {
	err:= filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
		if  err!=nil {
			return err
		}
		if !info.IsDir(){
			if filepath.Clean(path) != filepath.Clean(newqr) {
				zap.L().Info("我要刪除二維碼了!",zap.Any("遍歷的path:",path),zap.Any("新生成的tokenqr:",newqr))
				return os.Remove(path)
			}
		}
		return nil
	})

	if err!=nil {
		zap.L().Error("刪除二維碼失敗!",zap.Error(err))
		return
	}
}

dao層

package redis

import (
	"context"
	"errors"
	"time"

	"github.com/go-redis/redis/v8"
)

func QRsign(token string) error {
	err := Rdb.Set(context.Background(), "Sign_Token", token, 10*time.Second).Err()
	if err != nil {
		return err
	}
	return nil
}

func VerifyQrToken(token string) error {
	//驗證token是否存在和是否過期
	result, err := Rdb.Get(context.Background(), "Sign_Token").Result()
	if err != nil {
		if err == redis.Nil {
			return errors.New("二維碼已過期")
		}
		return err
	}

	if result != token {
		return errors.New("二維碼不存在")
	}
	return nil

}

1.2 返回圖片的base64內容

由於1.1的方法需要在伺服器生成二維碼圖片,導致會消耗伺服器資源。儘管已經採用了及時刪除圖片策略,但互動還是繁瑣
使用qrcode.Encode把二維碼圖片內容編碼成byte型別的資料,再傳給前端,前端直接使用img標籤就可以解碼,當然需要將byte進行base64編碼再返回。
controller層

// QrsignBybyteHandler 透過二維碼的位元組內容返回

func QrsignBybyteHandler(c *gin.Context) {
	data_byte, err := logic.Generate_Qr_By_byte()

	if err != nil {
		zap.L().Error("Generate_Qr_By_byte err:", zap.Error(err))
		return
	}

	base64data := base64.RawStdEncoding.EncodeToString(data_byte)

	c.JSON(200, gin.H{"data": base64data})
}

logic層

func Generate_Qr_By_byte() ([]byte, error) {
	//1. 生成二維碼token
	token, err := generate_token()
	if err != nil {
		return nil, err
	}
	//2.使用redis儲存token
	err = redis.QRsign(token)
	//3.使用qrcode的encoding方法
	return qrcode.Encode("http://127.0.0.1:8080/api/v1/qr/"+token, qrcode.Medium, 256)

}

2.基於websockts協議

相關文章