This commit is contained in:
@@ -5,11 +5,13 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
|
||||
"github.com/jung-kurt/gofpdf/v2"
|
||||
qrcode "github.com/skip2/go-qrcode"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -298,6 +300,9 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加说明文字和二维码
|
||||
pb.addAdditionalInfo(pdf, doc, chineseFontAvailable)
|
||||
}
|
||||
|
||||
// addSection 添加章节
|
||||
@@ -829,3 +834,248 @@ func (pb *PageBuilder) safeSplitText(pdf *gofpdf.Fpdf, text string, width float6
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
// addAdditionalInfo 添加说明文字和二维码
|
||||
func (pb *PageBuilder) addAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
|
||||
// 检查是否需要换页
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
_, _, _, bottomMargin := pdf.GetMargins()
|
||||
currentY := pdf.GetY()
|
||||
remainingHeight := pageHeight - currentY - bottomMargin
|
||||
|
||||
// 如果剩余空间不足,添加新页
|
||||
if remainingHeight < 100 {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
pdf.SetY(45)
|
||||
}
|
||||
|
||||
// 添加分隔线
|
||||
pdf.Ln(10)
|
||||
pdf.SetLineWidth(0.5)
|
||||
pdf.SetDrawColor(200, 200, 200)
|
||||
pdf.Line(15, pdf.GetY(), pageWidth-15, pdf.GetY())
|
||||
pdf.SetDrawColor(0, 0, 0)
|
||||
|
||||
// 添加说明文字标题
|
||||
pdf.Ln(15)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "B", 16)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "接入流程说明", "", 1, "L", false, 0, "")
|
||||
|
||||
// 读取说明文本文件
|
||||
explanationText := pb.readExplanationText()
|
||||
if explanationText != "" {
|
||||
pb.logger.Debug("开始渲染说明文本",
|
||||
zap.Int("text_length", len(explanationText)),
|
||||
zap.Int("line_count", len(strings.Split(explanationText, "\n"))),
|
||||
)
|
||||
|
||||
pdf.Ln(5)
|
||||
pb.fontManager.SetFont(pdf, "", 11)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
|
||||
// 处理说明文本,按行分割并显示
|
||||
lines := strings.Split(explanationText, "\n")
|
||||
renderedLines := 0
|
||||
|
||||
for i, line := range lines {
|
||||
// 保留原始行用于日志
|
||||
originalLine := line
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// 处理空行
|
||||
if line == "" {
|
||||
pdf.Ln(3)
|
||||
continue
|
||||
}
|
||||
|
||||
// 清理文本(保留中文字符和标点)
|
||||
cleanLine := pb.textProcessor.CleanText(line)
|
||||
|
||||
// 检查清理后的文本是否为空
|
||||
if strings.TrimSpace(cleanLine) == "" {
|
||||
pb.logger.Warn("文本行清理后为空,跳过渲染",
|
||||
zap.Int("line_number", i+1),
|
||||
zap.String("original_line", originalLine),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// 渲染文本行
|
||||
// 使用MultiCell自动换行,支持长文本
|
||||
pdf.MultiCell(0, lineHt*1.4, cleanLine, "", "L", false)
|
||||
renderedLines++
|
||||
}
|
||||
|
||||
pb.logger.Info("说明文本渲染完成",
|
||||
zap.Int("total_lines", len(lines)),
|
||||
zap.Int("rendered_lines", renderedLines),
|
||||
)
|
||||
} else {
|
||||
pb.logger.Warn("说明文本为空,跳过渲染")
|
||||
}
|
||||
|
||||
// 添加二维码生成方法和使用方法说明
|
||||
pb.addQRCodeSection(pdf, doc, chineseFontAvailable)
|
||||
}
|
||||
|
||||
// readExplanationText 读取说明文本文件
|
||||
func (pb *PageBuilder) readExplanationText() string {
|
||||
resourcesPDFDir := GetResourcesPDFDir()
|
||||
textFilePath := filepath.Join(resourcesPDFDir, "后勤服务.txt")
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(textFilePath); os.IsNotExist(err) {
|
||||
pb.logger.Warn("说明文本文件不存在", zap.String("path", textFilePath))
|
||||
return ""
|
||||
}
|
||||
|
||||
// 尝试读取文件(使用os.ReadFile替代已废弃的ioutil.ReadFile)
|
||||
content, err := os.ReadFile(textFilePath)
|
||||
if err != nil {
|
||||
pb.logger.Error("读取说明文本文件失败",
|
||||
zap.String("path", textFilePath),
|
||||
zap.Error(err),
|
||||
)
|
||||
return ""
|
||||
}
|
||||
|
||||
// 转换为字符串
|
||||
text := string(content)
|
||||
|
||||
// 记录读取成功的信息
|
||||
pb.logger.Info("成功读取说明文本文件",
|
||||
zap.String("path", textFilePath),
|
||||
zap.Int("file_size", len(content)),
|
||||
zap.Int("text_length", len(text)),
|
||||
zap.Int("line_count", len(strings.Split(text, "\n"))),
|
||||
)
|
||||
|
||||
// 返回文本内容
|
||||
return text
|
||||
}
|
||||
|
||||
// addQRCodeSection 添加二维码生成方法和使用方法说明
|
||||
func (pb *PageBuilder) addQRCodeSection(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
|
||||
_, pageHeight := pdf.GetPageSize()
|
||||
_, _, _, bottomMargin := pdf.GetMargins()
|
||||
currentY := pdf.GetY()
|
||||
|
||||
// 检查是否需要换页(为二维码预留空间)
|
||||
if pageHeight-currentY-bottomMargin < 120 {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
pdf.SetY(45)
|
||||
}
|
||||
|
||||
// 添加二维码标题
|
||||
pdf.Ln(15)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "B", 16)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "天远api官网二维码", "", 1, "L", false, 0, "")
|
||||
|
||||
// 先生成并添加二维码图片(确保二维码能够正常显示)
|
||||
pb.addQRCodeImage(pdf, "https://tianyuanapi.com/", chineseFontAvailable)
|
||||
|
||||
// 二维码说明文字(简化版,放在二维码之后)
|
||||
pdf.Ln(10)
|
||||
pb.fontManager.SetFont(pdf, "", 11)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
|
||||
qrCodeExplanation := "使用手机扫描上方二维码可直接跳转到天远API官网(https://tianyuanapi.com/),获取更多接口文档和资源。\n\n" +
|
||||
"二维码使用方法:\n" +
|
||||
"1. 使用手机相机或二维码扫描应用扫描二维码\n" +
|
||||
"2. 扫描后会自动跳转到天远API官网首页\n" +
|
||||
"3. 在官网可以查看完整的产品列表、接口文档和使用说明"
|
||||
|
||||
// 处理说明文本,按行分割并显示
|
||||
lines := strings.Split(qrCodeExplanation, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
pdf.Ln(2)
|
||||
continue
|
||||
}
|
||||
|
||||
// 普通文本行
|
||||
cleanLine := pb.textProcessor.CleanText(line)
|
||||
if strings.TrimSpace(cleanLine) != "" {
|
||||
pdf.MultiCell(0, lineHt*1.3, cleanLine, "", "L", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// addQRCodeImage 生成并添加二维码图片到PDF
|
||||
func (pb *PageBuilder) addQRCodeImage(pdf *gofpdf.Fpdf, content string, chineseFontAvailable bool) {
|
||||
// 检查是否需要换页
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
_, _, _, bottomMargin := pdf.GetMargins()
|
||||
currentY := pdf.GetY()
|
||||
|
||||
// 二维码大小(40mm)
|
||||
qrSize := 40.0
|
||||
if pageHeight-currentY-bottomMargin < qrSize+20 {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
pdf.SetY(45)
|
||||
}
|
||||
|
||||
// 生成二维码
|
||||
qr, err := qrcode.New(content, qrcode.Medium)
|
||||
if err != nil {
|
||||
pb.logger.Warn("生成二维码失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 将二维码转换为PNG字节
|
||||
qrBytes, err := qr.PNG(256)
|
||||
if err != nil {
|
||||
pb.logger.Warn("转换二维码为PNG失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建临时文件保存二维码(使用os.CreateTemp替代已废弃的ioutil.TempFile)
|
||||
tmpFile, err := os.CreateTemp("", "qrcode_*.png")
|
||||
if err != nil {
|
||||
pb.logger.Warn("创建临时文件失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpFile.Name()) // 清理临时文件
|
||||
|
||||
// 写入二维码数据
|
||||
if _, err := tmpFile.Write(qrBytes); err != nil {
|
||||
pb.logger.Warn("写入二维码数据失败", zap.Error(err))
|
||||
tmpFile.Close()
|
||||
return
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
// 添加二维码说明
|
||||
pdf.Ln(10)
|
||||
pb.fontManager.SetFont(pdf, "", 10)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "官网二维码:", "", 1, "L", false, 0, "")
|
||||
|
||||
// 计算二维码位置(居中)
|
||||
qrX := (pageWidth - qrSize) / 2
|
||||
|
||||
// 添加二维码图片
|
||||
pdf.Ln(5)
|
||||
pdf.ImageOptions(tmpFile.Name(), qrX, pdf.GetY(), qrSize, qrSize, false, gofpdf.ImageOptions{}, 0, "")
|
||||
|
||||
// 添加二维码下方的说明文字
|
||||
pdf.SetY(pdf.GetY() + qrSize + 5)
|
||||
pb.fontManager.SetFont(pdf, "", 9)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
qrNote := "使用手机扫描上方二维码可访问官网获取更多详情"
|
||||
noteWidth := pdf.GetStringWidth(qrNote)
|
||||
noteX := (pageWidth - noteWidth) / 2
|
||||
pdf.SetX(noteX)
|
||||
pdf.CellFormat(noteWidth, lineHt, qrNote, "", 1, "C", false, 0, "")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user