暂时的方案

This commit is contained in:
cjy 2026-03-02 11:08:31 +08:00
parent 264114fce8
commit 9dfb751aaf

View File

@ -11,6 +11,8 @@ import (
"unicode"
"github.com/phpdave11/gofpdf"
"github.com/signintech/gopdf"
"go.uber.org/zap"
)
// cleanTextForPDF 清理文本移除PDF不支持的字符如emoji
@ -172,3 +174,178 @@ func GeneratePDF(text, imageURL, outputPath, fontPath string) error {
return nil
}
// CompetitorReportData 竞品报告数据
type CompetitorReportData struct {
HighlightAnalysis HighlightAnalysisData `json:"highlight_analysis"`
DataPerformance DataPerformanceData `json:"data_performance_analysis"`
OverallSummary string `json:"overall_summary_and_optimization"`
}
type HighlightAnalysisData struct {
Summary string `json:"summary"`
Points PointsData `json:"points"`
}
type PointsData struct {
Theme string `json:"theme"`
Narrative string `json:"narrative"`
Content string `json:"content"`
Copywriting string `json:"copywriting"`
Data string `json:"data"`
Music string `json:"music,omitempty"`
}
type DataPerformanceData struct {
Views string `json:"views"`
Completion string `json:"completion_rate,omitempty"`
Engagement string `json:"engagement"`
}
// GenerateCompetitorReportPDF 生成竞品报告PDF
// 参数:
// - templatePath: 模板文件路径
// - outputPath: 输出PDF路径
// - data: 竞品报告数据
//
// 返回: 错误信息
func GenerateCompetitorReportPDF(templatePath, outputPath string, data CompetitorReportData) error {
fmt.Println("================================templatePath:", templatePath)
fmt.Println("================================outputPath:", outputPath)
pdf := gopdf.GoPdf{}
pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
// 导入模板文件中的页面
err := pdf.ImportPagesFromSource(templatePath, "/MediaBox")
if err != nil {
return fmt.Errorf("无法导入页面: %v", err)
}
// 获取模板文件的总页数
totalPages := pdf.GetNumberOfPages()
fmt.Printf("模板文件的总页数: %d\n", totalPages)
// 根据模板路径推断字体路径假设字体文件和模板在同一目录或data目录下
dir := filepath.Dir(templatePath)
fontPath := filepath.Join(dir, "simfang.ttf")
if _, err := os.Stat(fontPath); err != nil {
// 尝试使用项目根目录下的data目录
fontPath = filepath.Join("data", "simfang.ttf")
}
fmt.Printf("字体文件路径: %s\n", fontPath)
// 加载中文字体
ttfErr := pdf.AddTTFFont("simfang", fontPath)
if ttfErr != nil {
zap.L().Error("加载字体失败", zap.String("fontPath", fontPath), zap.Error(ttfErr))
return fmt.Errorf("加载中文字体失败: %v", ttfErr)
}
// 设置字体和字号
err = pdf.SetFont("simfang", "", 10)
if err != nil {
return fmt.Errorf("设置字体失败: %v", err)
}
// 行高15pt
lineHeight := 15.0
pdf.SetPage(1)
// 概述 - 使用MultiCell自动换行一行最多35个字
pdf.SetXY(200, 104)
summaryRect := gopdf.Rect{W: 350.0, H: lineHeight}
pdf.MultiCell(&summaryRect, data.HighlightAnalysis.Summary)
// 标题亮点 - 一行最多9个字
pdf.SetXY(200, 184)
themeRect := gopdf.Rect{W: 120.0, H: lineHeight}
pdf.MultiCell(&themeRect, data.HighlightAnalysis.Points.Theme)
// 题材亮点 - 一行最多9个字
pdf.SetXY(330, 184)
narrativeRect := gopdf.Rect{W: 120.0, H: lineHeight}
pdf.MultiCell(&narrativeRect, data.HighlightAnalysis.Points.Narrative)
// 内容亮点 - 一行最多9个字
pdf.SetXY(460, 184)
contentRect := gopdf.Rect{W: 120.0, H: lineHeight}
pdf.MultiCell(&contentRect, data.HighlightAnalysis.Points.Content)
// 文案亮点 - 一行最多9个字
pdf.SetXY(200, 323)
copywritingRect := gopdf.Rect{W: 120.0, H: lineHeight}
pdf.MultiCell(&copywritingRect, data.HighlightAnalysis.Points.Copywriting)
// 数据亮点 - 一行最多9个字
pdf.SetXY(330, 323)
dataRect := gopdf.Rect{W: 120.0, H: lineHeight}
pdf.MultiCell(&dataRect, data.HighlightAnalysis.Points.Data)
// 配乐亮点(仅视频) - 一行最多9个字
if data.HighlightAnalysis.Points.Music != "" {
pdf.SetXY(460, 323)
musicRect := gopdf.Rect{W: 120.0, H: lineHeight}
pdf.MultiCell(&musicRect, data.HighlightAnalysis.Points.Music)
}
// 浏览量 - 一行最多35个字
pdf.SetXY(200, 474)
viewsRect := gopdf.Rect{W: 350.0, H: lineHeight}
pdf.MultiCell(&viewsRect, data.DataPerformance.Views)
// 完播率 - 一行最多35个字
// 始终显示在固定位置,有内容时填充内容
pdf.SetXY(200, 539)
if data.DataPerformance.Completion != "" {
completionRect := gopdf.Rect{W: 350.0, H: lineHeight}
pdf.MultiCell(&completionRect, data.DataPerformance.Completion)
}
// 点赞/分享/评论 - 一行最多35个字
// 始终固定在完播率下面的位置
pdf.SetXY(200, 600)
engagementRect := gopdf.Rect{W: 350.0, H: lineHeight}
pdf.MultiCell(&engagementRect, data.DataPerformance.Engagement)
// 整体总结及可优化建议 - 一行最多35个字
pdf.SetXY(200, 676)
summaryRect = gopdf.Rect{W: 350.0, H: lineHeight}
pdf.MultiCell(&summaryRect, data.OverallSummary)
// 生成新的 PDF
if err = pdf.WritePdf(outputPath); err != nil {
return fmt.Errorf("error writing final PDF: %v", err)
}
return nil
}
// wrapText 将文本按指定宽度换行(按字符数计算)
func wrapText(text string, maxLen int) []string {
if text == "" {
return []string{}
}
var lines []string
runes := []rune(text)
currentLine := ""
for _, r := range runes {
// 如果当前行字符数达到最大限度,换行
if len(currentLine) >= maxLen {
lines = append(lines, currentLine)
currentLine = string(r)
} else {
currentLine += string(r)
}
}
// 添加最后一行
if len(currentLine) > 0 {
lines = append(lines, currentLine)
}
return lines
}