diff --git a/go.mod b/go.mod index 002880ba..b96f3462 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,10 @@ go 1.20 require ( github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/zeromicro/go-zero v1.5.2 + golang.org/x/image v0.0.0-20190802002840-cff245a6509b ) require ( diff --git a/go.sum b/go.sum index 5bbedf7f..990452fa 100644 --- a/go.sum +++ b/go.sum @@ -176,6 +176,8 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= @@ -198,6 +200,8 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -263,6 +267,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/server/data-transfer/internal/handler/routes.go b/server/data-transfer/internal/handler/routes.go index 31b39ef0..ed1301b9 100644 --- a/server/data-transfer/internal/handler/routes.go +++ b/server/data-transfer/internal/handler/routes.go @@ -22,7 +22,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/qrcode-set/list", Handler: GetQrCodeSetListHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/upload/qrcode", + Handler: UploadQrcodeHandler(serverCtx), + }, }, - rest.WithJwt(serverCtx.Config.Auth.AccessSecret), + //rest.WithJwt(serverCtx.Config.Auth.AccessSecret), ) } diff --git a/server/data-transfer/internal/handler/uploadqrcodehandler.go b/server/data-transfer/internal/handler/uploadqrcodehandler.go new file mode 100644 index 00000000..9667bba1 --- /dev/null +++ b/server/data-transfer/internal/handler/uploadqrcodehandler.go @@ -0,0 +1,37 @@ +package handler + +import ( + "errors" + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + + "fusenapi/server/data-transfer/internal/logic" + "fusenapi/server/data-transfer/internal/svc" + "fusenapi/server/data-transfer/internal/types" +) + +func UploadQrcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.UploadQrcodeReq + if err := httpx.Parse(r, &req); err != nil { + httpx.OkJsonCtx(r.Context(), w, &types.Response{ + Code: 510, + Message: "parameter error", + }) + logx.Info(err) + return + } + + l := logic.NewUploadQrcodeLogic(r.Context(), svcCtx) + resp := l.UploadQrcode(&req) + if resp != nil { + httpx.OkJsonCtx(r.Context(), w, resp) + } else { + err := errors.New("server logic is error, resp must not be nil") + httpx.ErrorCtx(r.Context(), w, err) + logx.Error(err) + } + } +} diff --git a/server/data-transfer/internal/logic/uploadqrcodelogic.go b/server/data-transfer/internal/logic/uploadqrcodelogic.go new file mode 100644 index 00000000..864a26b6 --- /dev/null +++ b/server/data-transfer/internal/logic/uploadqrcodelogic.go @@ -0,0 +1,66 @@ +package logic + +import ( + "context" + "errors" + "fmt" + "fusenapi/model" + "fusenapi/server/data-transfer/internal/svc" + "fusenapi/server/data-transfer/internal/types" + "fusenapi/utils/basic" + "fusenapi/utils/qrcode" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "strings" +) + +type UploadQrcodeLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewUploadQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadQrcodeLogic { + return &UploadQrcodeLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 生成base64二维码 +func (l *UploadQrcodeLogic) UploadQrcode(req *types.UploadQrcodeReq) (resp *types.Response) { + if req.Url == "" { + resp.SetStatus(basic.CodeApiErr, "param url is empty") + } + if req.QRcodeType <= 0 { + resp.SetStatus(basic.CodeApiErr, "param QRcodeType must large than 0") + } + //获取二维码模板信息 + qrCodeModel := model.NewFsQrcodeSetModel(l.svcCtx.MysqlConn) + qrCodeSet, err := qrCodeModel.FindOne(l.ctx, req.QRcodeType) + if err != nil && !errors.Is(err, sqlx.ErrNotFound) { + logx.Error(err) + resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get qrcode setting") + } + if qrCodeSet == nil { + return resp.SetStatusWithMessage(basic.CodeServiceErr, "qrcode setting is not exists") + } + qrType := qrCodeSet.SvgWebsite.String + if strings.Contains(req.Url, "www.instagram.com") { + qrType = qrCodeSet.SvgInstagram.String + } else if strings.Contains(req.Url, "www.facebook.com") { + qrType = qrCodeSet.SvgFacebook.String + } + //生成二维码 + imgBase64, err := qrcode.CreateQrCodeBs64WithLogo(req.Url, "", "", 512, int(qrCodeSet.IndexX), int(qrCodeSet.IndexY), true) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, err.Error()) + } + fmt.Println(imgBase64) + return resp.SetStatusWithMessage(basic.CodeOK, "success", types.UploadQrcodeRsp{ + Link: "", + D: qrType + " " + imgBase64, + }) +} diff --git a/server/data-transfer/internal/types/types.go b/server/data-transfer/internal/types/types.go index cbadaf3a..b213b2a9 100644 --- a/server/data-transfer/internal/types/types.go +++ b/server/data-transfer/internal/types/types.go @@ -16,6 +16,16 @@ type GetQrCodeSetListRsp struct { Name string `json:"name"` } +type UploadQrcodeReq struct { + Url string `form:"url"` + QRcodeType int64 `form:"QRcodeType"` +} + +type UploadQrcodeRsp struct { + Link string `json:"link"` + D string `json:"d"` +} + type Response struct { Code int `json:"code"` Message string `json:"msg"` diff --git a/server_api/data-transfer.api b/server_api/data-transfer.api index d3e628f9..37d09de1 100644 --- a/server_api/data-transfer.api +++ b/server_api/data-transfer.api @@ -19,6 +19,9 @@ service data-transfer { //获取二维码设置列表 @handler GetQrCodeSetListHandler get /qrcode-set/list returns (response); + //生成二维码 + @handler UploadQrcodeHandler + post /upload/qrcode (UploadQrcodeReq) returns (response); } //获取标准logo列表 @@ -31,4 +34,13 @@ type GetStandardLogoListRsp { type GetQrCodeSetListRsp { Id int64 `json:"id"` Name string `json:"name"` +} +//生成二维码 +type UploadQrcodeReq { + Url string `form:"url"` + QRcodeType int64 `form:"QRcodeType"` +} +type UploadQrcodeRsp { + Link string `json:"link"` + D string `json:"d"` } \ No newline at end of file diff --git a/utils/qrcode/creator.go b/utils/qrcode/creator.go new file mode 100644 index 00000000..2dfdf312 --- /dev/null +++ b/utils/qrcode/creator.go @@ -0,0 +1,86 @@ +package qrcode + +import ( + "bytes" + "encoding/base64" + "github.com/nfnt/resize" + "github.com/skip2/go-qrcode" + "golang.org/x/image/draw" + "image" + _ "image/jpeg" + "image/png" + "os" +) + +// 带logo的二维码图片生成 content-二维码内容 size-像素单位 logoPath-logo文件路径 x:x轴整体偏移 y:y轴整体偏移 +func CreateQrCodeBs64WithLogo(content, outPath string, logoPath string, size, x, y int, disableBorder bool) (data string, err error) { + code, err := qrcode.New(content, qrcode.High) + if err != nil { + return + } + if disableBorder { + code.DisableBorder = true + } + //设置文件大小并创建画板 + qrcodeImg := code.Image(size) + outImg := image.NewRGBA(qrcodeImg.Bounds()) + buf := new(bytes.Buffer) + //无logo + if logoPath == "" { + //图像偏移 + draw.Draw(outImg, outImg.Bounds().Add(image.Pt(x, y)), outImg, outImg.Bounds().Min, draw.Src) + _ = png.Encode(buf, qrcodeImg) + if outPath != "" { + // 写入文件 + f, _ := os.Create(outPath) + defer f.Close() + err = png.Encode(f, qrcodeImg) + if err != nil { + return "", err + } + } + return base64.StdEncoding.EncodeToString(buf.Bytes()), nil + } + //读取logo文件 + logoFile, err := os.Open(logoPath) + if err != nil { + return "", err + } + logoImg, _, err := image.Decode(logoFile) + if err != nil { + return "", err + } + logoImg = resize.Resize(uint(size/5), uint(size/5), logoImg, resize.Lanczos3) + // 添加边框 + // 图片到边框距离 + pic2FramePadding := logoImg.Bounds().Dx() / 10 + + // 新建一个边框图层 + transparentImg := image.NewRGBA(image.Rect(0, 0, logoImg.Bounds().Dx()+pic2FramePadding, logoImg.Bounds().Dy()+pic2FramePadding)) + // 图层颜色设为白色 + draw.Draw(transparentImg, transparentImg.Bounds(), image.White, image.Point{}, draw.Over) + // 将缩略图放到透明图层上 + draw.Draw(transparentImg, + image.Rect(pic2FramePadding/2, pic2FramePadding/2, transparentImg.Bounds().Dx(), transparentImg.Bounds().Dy()), + logoImg, + image.Point{}, + draw.Over) + + //logo和二维码拼接 + draw.Draw(outImg, outImg.Bounds(), qrcodeImg, image.Pt(0, 0), draw.Over) + offset := image.Pt((outImg.Bounds().Max.X-transparentImg.Bounds().Max.X)/2, (outImg.Bounds().Max.Y-transparentImg.Bounds().Max.Y)/2) + draw.Draw(outImg, outImg.Bounds().Add(offset), transparentImg, image.Pt(0, 0), draw.Over) + //图像偏移 + draw.Draw(outImg, outImg.Bounds().Add(image.Pt(x, y)), outImg, outImg.Bounds().Min, draw.Src) + err = png.Encode(buf, outImg) + if err != nil { + return "", err + } + if outPath != "" { + // 写入文件 + f, _ := os.Create(outPath) + defer f.Close() + _ = png.Encode(f, outImg) + } + return base64.StdEncoding.EncodeToString(buf.Bytes()), nil +}