项目背景
在自研的系统,需要实现类似草料二维码的功能
将我们自己的小程序,通过代码生成相想要的小程序二维码
代码已经上传到 Github 需要的朋友可以自取
https://github.com/ctra-wang/wechat-mini-qrcode
一、生成Qrcode并提交到对象存储
通过源生API实现对小程序二维码的生成
1、s3上传多云对象存储桶
感谢
suyuan32
同学对代码的开源: https://github.com/suyuan32/simple-admin-file
让我们一起支持群主维护simple-admin 社群吧!!!
不能加入星球的朋友记得来点个Star!!
https://github.com/suyuan32/simple-admin-core
2、源码
下面为示意代码,
需要的package如下:
- github.com/silenceper/wechat/v2
- github.com/aws/aws-sdk-go/aws
func NewVerifyRaceSignTeamLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VerifyRaceSignTeamLogic {
return &VerifyRaceSignTeamLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx}
}
func (l *VerifyRaceSignTeamLogic) VerifyRaceSignTeam(req *types.RaceSignTeamInfo) (resp *types.BaseMsgResp, err error) {
deptId := ctxdata.GetIntParamFromJwt(l.ctx, "deptId")
// 根据具体赛事id查询
raceSignTeam, err := l.svcCtx.CtraGoRaceRpc.GetRaceSignTeamById(l.ctx, &ctragorace.IDReq{
Id: *req.Id,
})
if err != nil {
return nil, err
}
flag := false
if int(deptId) != constant.ORG_TYPE_ADMIN {
// 根据 deptid 换 orgInfo
orgRes, err := l.svcCtx.CtraGoRaceRpc.GetRaceOrgInfoList(l.ctx, &ctragorace.RaceOrgInfoListReq{
DeptId: &deptId,
})
if err != nil {
return nil, err
}
if orgRes.Total > 0 {
// 非管理员,查看该权限下所有赛事
raceList, err := l.svcCtx.CtraGoRaceRpc.GetRaceInfoList(l.ctx, &ctragorace.RaceInfoListReq{
RaceOrgInfoId: *orgRes.Data[0].Id,
})
if err != nil {
return nil, err
}
if raceList.Total > 0 {
for _, datum := range raceList.Data {
if *datum.Id == *raceSignTeam.RaceInfoId {
flag = true
break
}
}
}
}
} else {
flag = true
}
if !flag {
return nil, errorx.NewCodeAbortedError("该用户暂无权限进行审核!")
}
// ----------------------- 生成 微信小程序二维码 使用:github.com/silenceper/wechat/v2 -----------------------
// 初始化 Wechat 实例
wc := wechat.NewWechat()
//这里本地内存保存access_token,也可选择redis,memcache或者自定cache
memory := cache.NewMemory()
cfg := &miniConfig.Config{
AppID: constant.MINI_APP_LATEST_ID,
AppSecret: constant.MINI_APP_LATEST_SECRET,
Cache: memory,
}
mini := wc.GetMiniProgram(cfg)
qr := mini.GetQRCode()
qrRes, err := qr.CreateWXAQRCode(qrcode.QRCoder{
Page: l.svcCtx.Config.QrCode.Page,
Path: fmt.Sprintf(l.svcCtx.Config.QrCode.Path, raceSignTeam.RaceInfoId),
//CheckPath: nil,
Width: l.svcCtx.Config.QrCode.Width,
//Scene: "pathName=/race/pages/group&id=58",
//AutoColor: false,
//LineColor: nil,
//IsHyaline: false,
//EnvVersion: "",
})
if err != nil {
return nil, errorx.NewCodeAbortedError("通过微信小程序API生成小程序二维码失败!")
}
// ----------------------- 将 微信小程序二维码 生成为本地文件用于存入对象存储cos -----------------------
// 拼接文件路径
fileDir := fmt.Sprintf("/upload/race-info/%s/", time.Now().Format("2006_01"))
newFileName := generatoruuid.GetUuid() + ".jpg"
// 获取当前 Pod 的工作目录
pwd, err := os.Getwd()
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件夹失败!")
}
// 创建文件路径
err = os.MkdirAll(pwd+fileDir, 0755)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件夹失败!")
}
// 复制文件(空文件)到该路径
qrcodeFile, err := os.Create(pwd + fileDir + newFileName)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件失败!")
}
// 将通过API生成小程序二维码->写入到文件中
_, err = qrcodeFile.Write(qrRes)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件失败!")
}
defer qrcodeFile.Close()
// 删除文件
defer os.RemoveAll(pwd + fileDir + newFileName)
// ----------------------- 推送文件到tencent对象存储cos -----------------------
// 创建s3实例
sess := session.Must(session.NewSession(
&aws.Config{
Region: aws.String(l.svcCtx.Config.QrCode.Region),
Credentials: credentials.NewStaticCredentials(l.svcCtx.Config.QrCode.SecretId, l.svcCtx.Config.QrCode.SecretKey, ""),
Endpoint: aws.String(l.svcCtx.Config.QrCode.EndPoint),
},
))
CloudStorage := s3.New(sess)
qrFile, err := os.Open(pwd + fileDir + newFileName)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("读取新创建qrcode失败!")
}
defer qrFile.Close()
// 处理存放cos的路径
// - C 端
// - 赛事报名:race-sign/赛事id/
// - 赛事信息:race-info/赛事id/ ✅
// - 用户里程碑:member-moment/用户id/
// - 用户个人信息: member-info/用户id/
// - B 端 (不在这里使用)
// - banner:banner/
relativeSrc := fmt.Sprintf("%s/%s/%d/%s",
l.svcCtx.Config.QrCode.Folder,
"race-info",
*raceSignTeam.RaceInfoId,
newFileName)
// 发送到tencent-cos
_, err = CloudStorage.PutObjectWithContext(context.Background(), &s3.PutObjectInput{
Bucket: aws.String(l.svcCtx.Config.QrCode.BucketName),
Key: aws.String(relativeSrc),
Body: qrFile,
})
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("文件上传到tencent-cos失败!")
}
fmt.Println("QR code upload tencent cos successfully!")
// ----------------------- 更新数据库表 race_sign_team -----------------------
// 更新团报状态
res1, err := l.svcCtx.CtraGoRaceRpc.UpdateRaceSignTeam(l.ctx,
&ctragorace.RaceSignTeamInfo{
Id: req.Id,
IsValidate: pointy.GetPointer(int32(constant.SIGN_TEAM_IS_VALIDATE_ACCESS)),
TeamQrcode: &relativeSrc,
})
if err != nil {
return nil, err
}
return &types.BaseMsgResp{Msg: res1.Msg}, nil
}
3、源码分析
3.1、拿到qrcode二进制
qrRes 为拿到的图片二进制
// ----------------------- 生成 微信小程序二维码 使用:github.com/silenceper/wechat/v2 -----------------------
// 初始化 Wechat 实例
wc := wechat.NewWechat()
//这里本地内存保存access_token,也可选择redis,memcache或者自定cache
memory := cache.NewMemory()
cfg := &miniConfig.Config{
AppID: constant.MINI_APP_LATEST_ID,
AppSecret: constant.MINI_APP_LATEST_SECRET,
Cache: memory,
}
mini := wc.GetMiniProgram(cfg)
qr := mini.GetQRCode()
qrRes, err := qr.CreateWXAQRCode(qrcode.QRCoder{
Page: l.svcCtx.Config.QrCode.Page,
Path: fmt.Sprintf(l.svcCtx.Config.QrCode.Path, raceSignTeam.RaceInfoId),
//CheckPath: nil,
Width: l.svcCtx.Config.QrCode.Width,
//Scene: "pathName=/race/pages/group&id=58",
//AutoColor: false,
//LineColor: nil,
//IsHyaline: false,
//EnvVersion: "",
})
if err != nil {
return nil, errorx.NewCodeAbortedError("通过微信小程序API生成小程序二维码失败!")
}
3.2、二维码存为本地文件
将 微信小程序二维码 生成为本地文件用于存入对象存储cos
// ----------------------- 将 微信小程序二维码 生成为本地文件用于存入对象存储cos -----------------------
// 拼接文件路径
fileDir := fmt.Sprintf("/upload/contracts/%s/", time.Now().Format("2006_01"))
newFileName := generatoruuid.GetUuid() + ".jpg"
// 获取当前 Pod 的工作目录
pwd, err := os.Getwd()
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件夹失败!")
}
// 创建文件路径
err = os.MkdirAll(pwd+fileDir, 0755)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件夹失败!")
}
// 复制文件(空文件)到该路径
qrcodeFile, err := os.Create(pwd + fileDir + newFileName)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件失败!")
}
// 将通过API生成小程序二维码->写入到文件中
_, err = qrcodeFile.Write(qrRes)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("os创建文件失败!")
}
defer qrcodeFile.Close()
// 删除文件
defer os.RemoveAll(pwd + fileDir + newFileName)
3.3、推送文件到tencent对象存储cos
通过s3协议上传到对象存储桶 | 腾讯云 cos
这里注意要将刚才保存的图片再次打开os.Open()
// ----------------------- 推送文件到tencent对象存储cos -----------------------
// 创建s3实例
sess := session.Must(session.NewSession(
&aws.Config{
Region: aws.String(l.svcCtx.Config.QrCode.Region),
Credentials: credentials.NewStaticCredentials(l.svcCtx.Config.QrCode.SecretId, l.svcCtx.Config.QrCode.SecretKey, ""),
Endpoint: aws.String(l.svcCtx.Config.QrCode.EndPoint),
},
))
CloudStorage := s3.New(sess)
qrFile, err := os.Open(pwd + fileDir + newFileName)
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("读取新创建qrcode失败!")
}
defer qrFile.Close()
// 处理存放cos的路径
// - C 端
// - 赛事报名:race-sign/赛事id/
// - 赛事信息:race-info/赛事id/ ✅
// - 用户里程碑:member-moment/用户id/
// - 用户个人信息: member-info/用户id/
// - B 端 (不在这里使用)
// - banner:banner/
relativeSrc := fmt.Sprintf("%s/%s/%d/%s",
l.svcCtx.Config.QrCode.Folder,
"race-info",
*raceSignTeam.RaceInfoId,
newFileName)
// 发送到tencent-cos
_, err = CloudStorage.PutObjectWithContext(context.Background(), &s3.PutObjectInput{
Bucket: aws.String(l.svcCtx.Config.QrCode.BucketName),
Key: aws.String(relativeSrc),
Body: qrFile,
})
if err != nil {
l.Logger.Errorf(err.Error())
return nil, errorx.NewCodeAbortedError("文件上传到tencent-cos失败!")
}
fmt.Println("QR code upload tencent cos successfully!")