563 lines
16 KiB
Go
563 lines
16 KiB
Go
|
package children_competition
|
|||
|
|
|||
|
import (
|
|||
|
"archive/zip"
|
|||
|
"bytes"
|
|||
|
"context"
|
|||
|
"dubbo.apache.org/dubbo-go/v3/common/logger"
|
|||
|
"encoding/base64"
|
|||
|
"errors"
|
|||
|
"fmt"
|
|||
|
service "github.com/fonchain_enterprise/fonchain-main/api"
|
|||
|
"github.com/fonchain_enterprise/fonchain-main/api/children_competition"
|
|||
|
"github.com/fonchain_enterprise/fonchain-main/api/equity"
|
|||
|
"github.com/fonchain_enterprise/fonchain-main/pkg/config"
|
|||
|
"github.com/fonchain_enterprise/fonchain-main/pkg/e"
|
|||
|
"github.com/fonchain_enterprise/fonchain-main/pkg/model"
|
|||
|
service2 "github.com/fonchain_enterprise/fonchain-main/pkg/service"
|
|||
|
"github.com/fonchain_enterprise/fonchain-main/pkg/utils"
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"github.com/skip2/go-qrcode"
|
|||
|
"golang.org/x/image/font"
|
|||
|
"golang.org/x/image/font/opentype"
|
|||
|
"golang.org/x/image/math/fixed"
|
|||
|
"image"
|
|||
|
"image/color"
|
|||
|
"image/draw"
|
|||
|
"image/jpeg"
|
|||
|
"io"
|
|||
|
"os"
|
|||
|
"strings"
|
|||
|
"sync"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
// GetRegistrationRecordInfo 获取所有的作品信息
|
|||
|
func GetRegistrationRecordInfo(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.GetRegistrationRecordInfoRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("GetRegistrationRecordInfoRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.GetRegistrationRecordInfo(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
service2.ResponseQuickMsg(c, e.Ok, resp.Msg, resp)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func GetParticipantRegistrationRecords(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.GetParticipantRegistrationRecordsRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("GetParticipantRegistrationRecordsRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.GetParticipantRegistrationRecords(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
service2.ResponseQuickMsg(c, e.Ok, resp.Msg, resp)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// SetRegistrationDeadline 获取所有的作品信息
|
|||
|
func SetRegistrationDeadline(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.SetRegistrationDeadlineRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("SetRegistrationDeadlineRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.SetRegistrationDeadline(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
service2.ResponseQuickMsg(c, e.Ok, resp.Msg, resp)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// UpdateWorkStatus 更新作品状态
|
|||
|
func UpdateWorkStatus(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.UpdateWorkStatusRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("UpdateWorkStatusRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.UpdateWorkStatus(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
service2.ResponseQuickMsg(c, e.Ok, resp.Msg, resp)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// GenerateQrCode 生成投票二维码
|
|||
|
func GenerateQrCode(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.GenerateQrCodeRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("GenerateQrCode ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
getWorkInfoReq := &children_competition.GetWorkInfoRequest{
|
|||
|
WorkUid: req.WorkUid,
|
|||
|
}
|
|||
|
|
|||
|
getWorkInfoResp, err := service2.ChildrenCompetitionProvider.GetWorkInfo(context.Background(), getWorkInfoReq)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
var contentUrl string
|
|||
|
if config.Env == "test" || config.Env == "dev" {
|
|||
|
contentUrl = "https://kid-art-test.szjixun.cn/vote?uid=" + getWorkInfoResp.WorkUid
|
|||
|
} else if config.Env == "prod" {
|
|||
|
contentUrl = "https://kid-art.szjixun.cn/vote?uid=" + getWorkInfoResp.WorkUid
|
|||
|
} else {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, "未知的环境", nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
//生成图片base64编码
|
|||
|
var jpegBytes []byte
|
|||
|
var base64Str string
|
|||
|
if req.Type == 1 { // 使用模版
|
|||
|
jpegBytes, err = composeImage("./data/儿童画展模版图片.png", "", contentUrl, "《"+getWorkInfoResp.WorkName+"》", getWorkInfoResp.Name, "./data/儿童画展字体.ttf")
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
base64Str = base64.StdEncoding.EncodeToString(jpegBytes)
|
|||
|
} else if req.Type == 2 { // 不使用模版
|
|||
|
prefix := "data:image/png;base64,"
|
|||
|
base64Str, err = utils.CreateQrBase64(contentUrl, "")
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 上次生成的格式为 "data:image/png;base64,/9j/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAs" 去掉前缀才是标准的base64编码
|
|||
|
base64Str = strings.TrimPrefix(base64Str, prefix)
|
|||
|
} else {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, "未知的类型", nil)
|
|||
|
}
|
|||
|
|
|||
|
generateQrCodeRespond := &children_competition.QrCodeInfo{
|
|||
|
WorkUid: getWorkInfoResp.WorkUid,
|
|||
|
WorkName: getWorkInfoResp.WorkName,
|
|||
|
PicUrl: getWorkInfoResp.PicUrl,
|
|||
|
Name: getWorkInfoResp.Name,
|
|||
|
Phone: getWorkInfoResp.Phone,
|
|||
|
Length: getWorkInfoResp.Length,
|
|||
|
Wide: getWorkInfoResp.Wide,
|
|||
|
Status: getWorkInfoResp.Status,
|
|||
|
QrBase64: base64Str,
|
|||
|
}
|
|||
|
|
|||
|
service2.ResponseQuickMsg(c, e.Ok, "SUCCESS", generateQrCodeRespond)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// BatchGenerateQrCode 生成全部投票二维码
|
|||
|
func BatchGenerateQrCode(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.BatchGenerateQrCodeRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("BatchGenerateQrCodeRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.BatchGenerateQrCode(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
currentTimeFormat := time.Now().Format("20060102")
|
|||
|
templatePath := "./runtime/儿童美术作品" + currentTimeFormat + ".zip"
|
|||
|
zipFile, err := os.Create(templatePath)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
defer zipFile.Close()
|
|||
|
|
|||
|
zipWriter := zip.NewWriter(zipFile)
|
|||
|
defer zipWriter.Close()
|
|||
|
|
|||
|
// 接口响应时间太长,使用并发
|
|||
|
var wg sync.WaitGroup
|
|||
|
results := make(chan result, len(resp.QrCodeInfo))
|
|||
|
|
|||
|
for _, v := range resp.QrCodeInfo {
|
|||
|
wg.Add(1)
|
|||
|
//var voteUrl string
|
|||
|
//if config.Env == "test" || config.Env == "dev" || config.Env == "local" {
|
|||
|
// voteUrl = "https://kid-art-test.szjixun.cn/vote?uid=" + v.WorkUid
|
|||
|
//} else if config.Env == "prod" {
|
|||
|
// voteUrl = "https://appointteam.szjixun.cn/vote?uid=" + v.WorkUid
|
|||
|
//} else {
|
|||
|
// service2.ResponseQuickMsg(c, e.Failed, "未知的环境", nil)
|
|||
|
// return
|
|||
|
//}
|
|||
|
//
|
|||
|
//voteUrl = url.QueryEscape(voteUrl)
|
|||
|
//contentUrl := "https://appointteam.szjixun.cn/api/appointment/wx?notifyUrl=" + voteUrl
|
|||
|
var contentUrl string
|
|||
|
if config.Env == "test" || config.Env == "dev" || config.Env == "local" {
|
|||
|
contentUrl = "https://kid-art-test.szjixun.cn/vote?uid=" + v.WorkUid
|
|||
|
} else if config.Env == "prod" {
|
|||
|
contentUrl = "https://kid-art.szjixun.cn/vote?uid=" + v.WorkUid
|
|||
|
} else {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, "未知的环境", nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
prefix := "data:image/png;base64,"
|
|||
|
if req.Type == 1 { // 使用模版
|
|||
|
wgComposeImage("./data/儿童画展模版图片.png", "", contentUrl, "《"+strings.Split(v.WorkName, "(")[0]+"》", v.Name, "./data/儿童画展字体.ttf", v.WorkName, results, &wg)
|
|||
|
} else if req.Type == 2 { // 不使用模版
|
|||
|
wgCreateQrBase64(contentUrl, "", prefix, v.WorkName, results, &wg)
|
|||
|
} else {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, "未知的类型", nil)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 启动一个goroutine来等待所有worker完成
|
|||
|
//go func() {
|
|||
|
wg.Wait()
|
|||
|
close(results) // 关闭channel,表示没有更多的结果
|
|||
|
//}()
|
|||
|
|
|||
|
// 从channel中收集结果
|
|||
|
for v := range results {
|
|||
|
if v.err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
} else {
|
|||
|
var f io.Writer
|
|||
|
f, err = zipWriter.Create(v.workName + ".jpg")
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
_, err = f.Write(v.jpegBytes)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var httpType string
|
|||
|
if config.IsHttps {
|
|||
|
httpType = model.HttpsType
|
|||
|
} else {
|
|||
|
httpType = model.HttpType
|
|||
|
}
|
|||
|
var exportUrl = fmt.Sprintf("%s%s/static/儿童美术作品"+currentTimeFormat+".zip", httpType, c.Request.Host)
|
|||
|
|
|||
|
repEF := &equity.RechargeRecordManageExportFormRespond{
|
|||
|
ExportUrl: exportUrl,
|
|||
|
}
|
|||
|
|
|||
|
service.Success(c, repEF)
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// GetWorksVotesInfo 获取投票结果
|
|||
|
func GetWorksVotesInfo(c *gin.Context) {
|
|||
|
|
|||
|
var req children_competition.GetWorksVotesInfoRequest
|
|||
|
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("UpdateWorkStatusRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.GetWorksVotesInfo(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
service2.ResponseQuickMsg(c, e.Ok, resp.Msg, resp)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func WorksVotesExportForm(c *gin.Context) {
|
|||
|
var req children_competition.WorksVotesExportFormRequest
|
|||
|
if err := c.ShouldBind(&req); err != nil {
|
|||
|
logger.Errorf("WorksVotesExportFormRequest ShouldBind err", err)
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
resp, err := service2.ChildrenCompetitionProvider.WorksVotesExportForm(context.Background(), &req)
|
|||
|
if err != nil {
|
|||
|
service2.ResponseQuickMsg(c, e.Failed, err.Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
columns := []string{"参赛作品uid", "作品排名", "作品名称", "作品图片链接", "作品尺寸(cm)", "参赛人员姓名", "手机号", "总票数", "投票截止时间"}
|
|||
|
|
|||
|
var data []interface{}
|
|||
|
// 添加数据行
|
|||
|
for _, worksVotesInfo := range resp.WorksVotesInfo {
|
|||
|
var info []string
|
|||
|
info = append(info, worksVotesInfo.Uid) // 参赛作品uid
|
|||
|
info = append(info, fmt.Sprintf("%d", worksVotesInfo.Ranking)) // 作品排名
|
|||
|
info = append(info, worksVotesInfo.WorkName) // 作品名称
|
|||
|
info = append(info, worksVotesInfo.PicUrl) // 作品图片链接
|
|||
|
info = append(info, worksVotesInfo.WorkSize) // 作品尺寸(cm)
|
|||
|
info = append(info, worksVotesInfo.Name) // 参赛人员姓名
|
|||
|
info = append(info, worksVotesInfo.Phone) // 手机号
|
|||
|
info = append(info, fmt.Sprintf("%d", worksVotesInfo.VoteCount)) // 总票数
|
|||
|
info = append(info, worksVotesInfo.RegistrationDeadline) // 投票截止时间
|
|||
|
data = append(data, &info)
|
|||
|
}
|
|||
|
|
|||
|
//获取当前时间的标准时间
|
|||
|
currentTimeFormat := time.Now().Format("20060102")
|
|||
|
|
|||
|
filePath := "./runtime/儿童美术作品投票记录" + currentTimeFormat + ".xlsx"
|
|||
|
_, err = utils.ToExcelByType(columns, data, "slice", filePath)
|
|||
|
if err != nil {
|
|||
|
fmt.Println("err", err)
|
|||
|
service.ResponseQuickMsg(c, e.Failed, errors.New("转换为excel类型出错").Error(), nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
var httpType string
|
|||
|
if config.IsHttps {
|
|||
|
httpType = model.HttpsType
|
|||
|
} else {
|
|||
|
httpType = model.HttpType
|
|||
|
}
|
|||
|
var exportUrl = fmt.Sprintf("%s%s/static/儿童美术作品投票记录"+currentTimeFormat+".xlsx", httpType, c.Request.Host)
|
|||
|
|
|||
|
repEF := &equity.RechargeRecordManageExportFormRespond{
|
|||
|
ExportUrl: exportUrl,
|
|||
|
}
|
|||
|
|
|||
|
service.Success(c, repEF)
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func wgComposeImage(templatePath, outputPath, contentURL, workName, authorName, fontPath, relWorkName string, results chan<- result, wg *sync.WaitGroup) {
|
|||
|
defer wg.Done()
|
|||
|
jpegBytes, err := composeImage(templatePath, outputPath, contentURL, workName, authorName, fontPath)
|
|||
|
if err != nil {
|
|||
|
results <- result{err: err}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
results <- result{jpegBytes: jpegBytes, workName: relWorkName}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func wgCreateQrBase64(contentUrl, logoUrl, prefix, relWorkName string, results chan<- result, wg *sync.WaitGroup) {
|
|||
|
defer wg.Done()
|
|||
|
base64Str, err := utils.CreateQrBase64(contentUrl, logoUrl)
|
|||
|
if err != nil {
|
|||
|
results <- result{err: err}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 上次生成的格式为 "data:image/png;base64,/9j/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAs" 去掉前缀才是标准的base64编码
|
|||
|
jpegBytes, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(base64Str, prefix))
|
|||
|
if err != nil {
|
|||
|
results <- result{err: err}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
results <- result{jpegBytes: jpegBytes, workName: relWorkName}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 合成图像
|
|||
|
func composeImage(templatePath, outputPath, contentURL, workName, authorName, fontPath string) (jpegBytes []byte, err error) {
|
|||
|
file, err := os.Open(templatePath)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
defer file.Close()
|
|||
|
|
|||
|
img, _, err := image.Decode(file)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
bounds := img.Bounds()
|
|||
|
newImage := image.NewRGBA(bounds)
|
|||
|
draw.Draw(newImage, bounds, img, bounds.Min, draw.Src)
|
|||
|
|
|||
|
// 生成并添加带颜色的二维码
|
|||
|
qrColor := color.RGBA{30, 65, 118, 255} // #1E4176
|
|||
|
var qrImage *image.RGBA
|
|||
|
qrImage, err = generateQRCodeWithColor(contentURL, qrColor)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
qrBounds := qrImage.Bounds()
|
|||
|
qrPosition := image.Point{X: (bounds.Dx() - qrBounds.Dx()) / 2, Y: 520} // 修改二维码的位置
|
|||
|
draw.Draw(newImage, image.Rectangle{Min: qrPosition, Max: qrPosition.Add(qrBounds.Size())}, qrImage, qrBounds.Min, draw.Over)
|
|||
|
|
|||
|
workNameWidth, err := textWidth(workName, fontPath, 50)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
authorNameWidth, err := textWidth(authorName, fontPath, 50)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
// 添加作品名称和作者名字
|
|||
|
labelColor := color.RGBA{30, 65, 118, 255} // #1E4176
|
|||
|
addLabel(newImage, (bounds.Dx()-workNameWidth)/2, 1250, workName, labelColor, fontPath, 50)
|
|||
|
addLabel(newImage, (bounds.Dx()-authorNameWidth)/2, 1320, authorName, labelColor, fontPath, 50)
|
|||
|
|
|||
|
var buf bytes.Buffer
|
|||
|
|
|||
|
// 这段代码及其耗时
|
|||
|
//err = png.Encode(&buf, newImage)
|
|||
|
//if err != nil {
|
|||
|
// return
|
|||
|
//}
|
|||
|
|
|||
|
err = jpeg.Encode(&buf, newImage, nil)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if outputPath != "" { // 在指定文件路径新增图片
|
|||
|
var outFile *os.File
|
|||
|
outFile, err = os.Create(outputPath)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
defer outFile.Close()
|
|||
|
|
|||
|
err = jpeg.Encode(outFile, newImage, nil)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
} else { // 将PNG字节流
|
|||
|
//// 将PNG字节流编码为Base64
|
|||
|
//base64Str = base64.StdEncoding.EncodeToString(buf.Bytes())
|
|||
|
jpegBytes = buf.Bytes()
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 修改颜色的二维码生成函数
|
|||
|
func generateQRCodeWithColor(content string, qrColor color.RGBA) (*image.RGBA, error) {
|
|||
|
qr, err := qrcode.New(content, qrcode.Medium)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
originalImg := qr.Image(700)
|
|||
|
bounds := originalImg.Bounds()
|
|||
|
coloredImg := image.NewRGBA(bounds)
|
|||
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|||
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|||
|
if originalImg.At(x, y) == color.Black {
|
|||
|
coloredImg.Set(x, y, qrColor) // 设置为特定颜色
|
|||
|
} else {
|
|||
|
coloredImg.Set(x, y, color.White)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return coloredImg, nil
|
|||
|
}
|
|||
|
|
|||
|
// 添加文字的函数,使用外部字体
|
|||
|
func addLabel(img *image.RGBA, x, y int, label string, clr color.Color, fontPath string, fontSize float64) {
|
|||
|
fontBytes, err := os.ReadFile(fontPath)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
f, err := opentype.Parse(fontBytes)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
face, err := opentype.NewFace(f, &opentype.FaceOptions{
|
|||
|
Size: fontSize,
|
|||
|
DPI: 72,
|
|||
|
Hinting: font.HintingFull,
|
|||
|
})
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
point := fixed.P(x, y)
|
|||
|
d := &font.Drawer{
|
|||
|
Dst: img,
|
|||
|
Src: image.NewUniform(clr),
|
|||
|
Face: face,
|
|||
|
Dot: point,
|
|||
|
}
|
|||
|
d.DrawString(label)
|
|||
|
}
|
|||
|
|
|||
|
// 计算文本的宽度
|
|||
|
func textWidth(label string, fontPath string, fontSize float64) (int, error) {
|
|||
|
fontBytes, err := os.ReadFile(fontPath)
|
|||
|
if err != nil {
|
|||
|
return 0, err
|
|||
|
}
|
|||
|
f, err := opentype.Parse(fontBytes)
|
|||
|
if err != nil {
|
|||
|
return 0, err
|
|||
|
}
|
|||
|
|
|||
|
face, err := opentype.NewFace(f, &opentype.FaceOptions{
|
|||
|
Size: fontSize,
|
|||
|
DPI: 72,
|
|||
|
Hinting: font.HintingFull,
|
|||
|
})
|
|||
|
if err != nil {
|
|||
|
return 0, err
|
|||
|
}
|
|||
|
|
|||
|
d := &font.Drawer{
|
|||
|
Face: face,
|
|||
|
}
|
|||
|
return d.MeasureString(label).Ceil(), nil
|
|||
|
}
|
|||
|
|
|||
|
type result struct {
|
|||
|
jpegBytes []byte
|
|||
|
workName string
|
|||
|
err error
|
|||
|
}
|