Files
tyapi-server/internal/shared/pdf/pdf_generator_refactored.go
2025-12-04 12:30:33 +08:00

181 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package pdf
import (
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"tyapi-server/internal/domains/product/entities"
"github.com/jung-kurt/gofpdf/v2"
"github.com/shopspring/decimal"
"go.uber.org/zap"
)
// PDFGeneratorRefactored 重构后的PDF生成器
type PDFGeneratorRefactored struct {
logger *zap.Logger
fontManager *FontManager
textProcessor *TextProcessor
markdownProc *MarkdownProcessor
tableParser *TableParser
tableRenderer *TableRenderer
jsonProcessor *JSONProcessor
logoPath string
watermarkText string
}
// NewPDFGeneratorRefactored 创建重构后的PDF生成器
func NewPDFGeneratorRefactored(logger *zap.Logger) *PDFGeneratorRefactored {
// 初始化各个模块
textProcessor := NewTextProcessor()
fontManager := NewFontManager(logger)
markdownProc := NewMarkdownProcessor(textProcessor)
tableParser := NewTableParser(logger, fontManager)
tableRenderer := NewTableRenderer(logger, fontManager, textProcessor)
jsonProcessor := NewJSONProcessor()
gen := &PDFGeneratorRefactored{
logger: logger,
fontManager: fontManager,
textProcessor: textProcessor,
markdownProc: markdownProc,
tableParser: tableParser,
tableRenderer: tableRenderer,
jsonProcessor: jsonProcessor,
watermarkText: "海南海宇大数据有限公司",
}
// 查找logo文件
gen.findLogo()
return gen
}
// findLogo 查找logo文件
func (g *PDFGeneratorRefactored) findLogo() {
// 获取当前文件所在目录
_, filename, _, _ := runtime.Caller(0)
baseDir := filepath.Dir(filename)
// 优先使用 baseDir最可靠基于当前文件位置
logoPaths := []string{
filepath.Join(baseDir, "天远数据.png"), // 相对当前文件(最优先)
}
// 尝试相对于工作目录的路径
if workDir, err := os.Getwd(); err == nil {
logoPaths = append(logoPaths,
filepath.Join(workDir, "internal", "shared", "pdf", "天远数据.png"),
filepath.Join(workDir, "tyapi-server", "internal", "shared", "pdf", "天远数据.png"),
)
}
// 尝试服务器绝对路径Linux环境优先查找 /www/tyapi-server
if runtime.GOOS == "linux" {
serverPaths := []string{
"/www/tyapi-server/internal/shared/pdf/天远数据.png",
"/app/internal/shared/pdf/天远数据.png",
}
logoPaths = append(logoPaths, serverPaths...)
}
// 尝试相对路径(作为最后的后备方案)
logoPaths = append(logoPaths,
"internal/shared/pdf/天远数据.png",
"./internal/shared/pdf/天远数据.png",
)
// 尝试所有路径
for _, logoPath := range logoPaths {
if _, err := os.Stat(logoPath); err == nil {
g.logoPath = logoPath
return
}
}
// 只记录关键错误
g.logger.Warn("未找到logo文件")
}
// GenerateProductPDF 为产品生成PDF文档接受响应类型内部转换
func (g *PDFGeneratorRefactored) GenerateProductPDF(ctx context.Context, productID string, productName, productCode, description, content string, price float64, doc *entities.ProductDocumentation) ([]byte, error) {
// 构建临时的 Product entity仅用于PDF生成
product := &entities.Product{
ID: productID,
Name: productName,
Code: productCode,
Description: description,
Content: content,
}
// 如果有价格信息,设置价格
if price > 0 {
product.Price = decimal.NewFromFloat(price)
}
return g.generatePDF(product, doc)
}
// GenerateProductPDFFromEntity 从entity类型生成PDF推荐使用
func (g *PDFGeneratorRefactored) GenerateProductPDFFromEntity(ctx context.Context, product *entities.Product, doc *entities.ProductDocumentation) ([]byte, error) {
return g.generatePDF(product, doc)
}
// generatePDF 内部PDF生成方法
func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *entities.ProductDocumentation) (result []byte, err error) {
defer func() {
if r := recover(); r != nil {
// 将panic转换为error而不是重新抛出
if e, ok := r.(error); ok {
err = fmt.Errorf("PDF生成panic: %w", e)
} else {
err = fmt.Errorf("PDF生成panic: %v", r)
}
result = nil
}
}()
// 创建PDF文档 (A4大小gofpdf v2 默认支持UTF-8)
pdf := gofpdf.New("P", "mm", "A4", "")
// 优化边距,减少空白
pdf.SetMargins(15, 25, 15)
// 加载黑体字体(用于所有内容,除了水印)
chineseFontAvailable := g.fontManager.LoadChineseFont(pdf)
// 加载水印字体(使用宋体或其他非黑体字体)
g.fontManager.LoadWatermarkFont(pdf)
// 设置文档信息
pdf.SetTitle("Product Documentation", true)
pdf.SetAuthor("TYAPI Server", true)
pdf.SetCreator("TYAPI Server", true)
// 创建页面构建器
pageBuilder := NewPageBuilder(g.logger, g.fontManager, g.textProcessor, g.markdownProc, g.tableParser, g.tableRenderer, g.jsonProcessor, g.logoPath, g.watermarkText)
// 添加第一页(产品信息)
pageBuilder.AddFirstPage(pdf, product, doc, chineseFontAvailable)
// 如果有关联的文档,添加接口文档页面
if doc != nil {
pageBuilder.AddDocumentationPages(pdf, doc, chineseFontAvailable)
}
// 生成PDF字节流
var buf bytes.Buffer
err = pdf.Output(&buf)
if err != nil {
// 错误已返回,不记录日志
return nil, fmt.Errorf("生成PDF失败: %w", err)
}
pdfBytes := buf.Bytes()
return pdfBytes, nil
}