feat: 实现新模板下的竞品报告批量导入

This commit is contained in:
cjy 2026-03-02 16:25:10 +08:00
parent 6242962b43
commit 9c4c4b1c66
3 changed files with 178 additions and 61 deletions

Binary file not shown.

View File

@ -372,16 +372,86 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) {
temp.Title = nowDate + temp.ArtistName + "老师竞品报告" temp.Title = nowDate + temp.ArtistName + "老师竞品报告"
} }
} }
// 解析报告内容D列row[3]
if len(row) > 3 { // 构建竞品报告数据(新模板格式)
temp.ReportContent = row[3] // D列row[3]):亮点表现分析 - 对应Summary字段
// E列row[4]):标题亮点
// F列row[5]):题材亮点
// G列row[6]):内容亮点
// H列row[7]):文案亮点
// I列row[8]):数据亮点
// J列row[9]):配乐亮点
// K列row[10]):浏览量
// L列row[11]):完播率
// M列row[12]):点赞/分享/评论
// N列row[13]):整体总结及可优化建议
// O列row[14]):图片
var competitorReportData utils.CompetitorReportData
// 解析亮点表现分析
highlightAnalysis := utils.HighlightAnalysisData{
Points: utils.PointsData{},
} }
// 解析图片URLE列row[4] // 亮点表现分析摘要D列row[3]
if len(row) > 4 && utils.CleanString(row[4]) != "" { if len(row) > 3 {
temp.ImageUrl = utils.CleanString(row[4]) highlightAnalysis.Summary = utils.CleanString(row[3])
} }
// 标题亮点E列row[4]
if len(row) > 4 {
highlightAnalysis.Points.Theme = utils.CleanString(row[4])
}
// 题材亮点F列row[5]
if len(row) > 5 {
highlightAnalysis.Points.Narrative = utils.CleanString(row[5])
}
// 内容亮点G列row[6]
if len(row) > 6 {
highlightAnalysis.Points.Content = utils.CleanString(row[6])
}
// 文案亮点H列row[7]
if len(row) > 7 {
highlightAnalysis.Points.Copywriting = utils.CleanString(row[7])
}
// 数据亮点I列row[8]
if len(row) > 8 {
highlightAnalysis.Points.Data = utils.CleanString(row[8])
}
// 配乐亮点J列row[9]
if len(row) > 9 {
highlightAnalysis.Points.Music = utils.CleanString(row[9])
}
// 解析数据表现
dataPerformance := utils.DataPerformanceData{}
// 浏览量K列row[10]
if len(row) > 10 {
dataPerformance.Views = utils.CleanString(row[10])
}
// 完播率L列row[11]
if len(row) > 11 {
dataPerformance.Completion = utils.CleanString(row[11])
}
// 点赞/分享/评论M列row[12]
if len(row) > 12 {
dataPerformance.Engagement = utils.CleanString(row[12])
}
// 整体总结及可优化建议N列row[13]
if len(row) > 13 {
competitorReportData.OverallSummary = utils.CleanString(row[13])
}
// 图片URLO列row[14]
if len(row) > 14 && utils.CleanString(row[14]) != "" {
competitorReportData.ImageURL = utils.CleanString(row[14])
}
competitorReportData.HighlightAnalysis = highlightAnalysis
competitorReportData.DataPerformance = dataPerformance
// 验证必填字段 // 验证必填字段
if artistNum == "" { if artistNum == "" {
temp.Remark = "艺人编号不能为空" temp.Remark = "艺人编号不能为空"
@ -389,82 +459,129 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) {
continue continue
} }
// 验证报告内容和图片不能同时为空 // 验证亮点表现分析D列Summary
if temp.ReportContent == "" && temp.ImageUrl == "" { if highlightAnalysis.Summary == "" {
temp.Remark = "报告内容和图片不能同时为空" temp.Remark = "亮点表现分析摘要不能为空"
req.Reports = append(req.Reports, temp) req.Reports = append(req.Reports, temp)
continue continue
} }
// 如果已经有错误信息跳过PDF生成 // 验证标题亮点E列
if temp.Remark != "" { if highlightAnalysis.Points.Theme == "" {
temp.Remark = "标题亮点不能为空"
req.Reports = append(req.Reports, temp) req.Reports = append(req.Reports, temp)
continue continue
} }
// 检查图片URL是否包含阿里云如果包含则下载并重新上传到OSS // 验证题材亮点F列
if temp.ImageUrl != "" { if highlightAnalysis.Points.Narrative == "" {
newImageUrl, err := checkAndReuploadImageForReport(temp.ImageUrl) temp.Remark = "题材亮点不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 验证内容亮点G列
if highlightAnalysis.Points.Content == "" {
temp.Remark = "内容亮点不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 验证文案亮点H列
if highlightAnalysis.Points.Copywriting == "" {
temp.Remark = "文案亮点不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 验证数据亮点I列
if highlightAnalysis.Points.Data == "" {
temp.Remark = "数据亮点不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 验证浏览量K列
if dataPerformance.Views == "" {
temp.Remark = "浏览量不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 验证点赞/分享/评论M列
if dataPerformance.Engagement == "" {
temp.Remark = "点赞/分享/评论不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 验证整体总结及可优化建议N列
if competitorReportData.OverallSummary == "" {
temp.Remark = "整体总结及可优化建议不能为空"
req.Reports = append(req.Reports, temp)
continue
}
// 处理图片URL
if competitorReportData.ImageURL != "" {
newImageUrl, err := checkAndReuploadImageForReport(competitorReportData.ImageURL)
if err != nil { if err != nil {
temp.Remark = fmt.Sprintf("图片处理失败: %v", err) temp.Remark = fmt.Sprintf("图片处理失败: %v", err)
zap.L().Error("图片重新上传失败", zap.String("imageUrl", temp.ImageUrl), zap.Error(err)) zap.L().Error("图片重新上传失败", zap.String("imageUrl", competitorReportData.ImageURL), zap.Error(err))
req.Reports = append(req.Reports, temp) req.Reports = append(req.Reports, temp)
continue continue
} }
temp.ImageUrl = newImageUrl competitorReportData.ImageURL = newImageUrl
} }
// 如果提供了报告内容则生成PDF并上传 // 生成PDF并上传
if temp.ReportContent != "" { // 生成临时PDF文件路径
// 生成临时PDF文件路径 today := time.Now().Format("20060102")
today := time.Now().Format("20060102") timestamp := time.Now().UnixMicro()
timestamp := time.Now().UnixMicro() pdfFileName := fmt.Sprintf("%s%s老师的竞品报告%d.pdf", today, temp.ArtistName, timestamp)
pdfFileName := fmt.Sprintf("%s%s老师的竞品报告%d.pdf", today, temp.ArtistName, timestamp) pdfFilePath := "./runtime/report_pdf/" + pdfFileName
pdfFilePath := "./runtime/report_pdf/" + pdfFileName
// 确保目录存在 // 确保目录存在
_, err = utils.CheckDirPath("./runtime/report_pdf/", true) _, err = utils.CheckDirPath("./runtime/report_pdf/", true)
if err != nil { if err != nil {
temp.Remark = fmt.Sprintf("创建PDF目录失败: %v", err) temp.Remark = fmt.Sprintf("创建PDF目录失败: %v", err)
req.Reports = append(req.Reports, temp) req.Reports = append(req.Reports, temp)
continue continue
} }
// 生成PDF文件 // 模板路径
fontPath := "./data/simfang.ttf" templatePath := "./data/竞品报告pdf模板.pdf"
err = utils.GeneratePDF(temp.ReportContent, temp.ImageUrl, pdfFilePath, fontPath)
if err != nil {
zap.L().Error("生成PDF失败", zap.Error(err))
temp.Remark = "生成PDF失败"
req.Reports = append(req.Reports, temp)
continue
}
// 上传PDF到OSS // 使用新的 GenerateCompetitorReportPDF 生成PDF
pdfUrl, uploadErr := upload.PutBos(pdfFilePath, upload.PdfType, true) err = utils.GenerateCompetitorReportPDF(templatePath, pdfFilePath, competitorReportData)
if uploadErr != nil { if err != nil {
zap.L().Error("上传PDF失败", zap.Error(uploadErr)) zap.L().Error("生成PDF失败", zap.Error(err))
temp.Remark = "上传PDF失败" temp.Remark = "生成PDF失败"
req.Reports = append(req.Reports, temp) req.Reports = append(req.Reports, temp)
// 清理临时PDF文件 continue
if _, err := os.Stat(pdfFilePath); err == nil { }
os.Remove(pdfFilePath)
}
continue
}
// 将上传后的PDF链接设置到请求中
temp.PdfUrl = pdfUrl
// 上传PDF到OSS
pdfUrl, uploadErr := upload.PutBos(pdfFilePath, upload.PdfType, true)
if uploadErr != nil {
zap.L().Error("上传PDF失败", zap.Error(uploadErr))
temp.Remark = "上传PDF失败"
req.Reports = append(req.Reports, temp)
// 清理临时PDF文件 // 清理临时PDF文件
if _, err := os.Stat(pdfFilePath); err == nil { if _, err := os.Stat(pdfFilePath); err == nil {
if err := os.Remove(pdfFilePath); err != nil { os.Remove(pdfFilePath)
zap.L().Warn("删除临时PDF文件失败", zap.String("path", pdfFilePath), zap.Error(err)) }
} continue
}
// 将上传后的PDF链接设置到请求中
temp.PdfUrl = pdfUrl
// 清理临时PDF文件
if _, err := os.Stat(pdfFilePath); err == nil {
if err := os.Remove(pdfFilePath); err != nil {
zap.L().Warn("删除临时PDF文件失败", zap.String("path", pdfFilePath), zap.Error(err))
} }
} else {
// 如果没有报告内容则将图片URL设置为PDF URL
temp.PdfUrl = temp.ImageUrl
} }
req.Reports = append(req.Reports, temp) req.Reports = append(req.Reports, temp)
@ -497,7 +614,7 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) {
// 通过请求对象找到对应的Excel行号 // 通过请求对象找到对应的Excel行号
if excelRowNum, ok := reportRowMap[reqReport]; ok { if excelRowNum, ok := reportRowMap[reqReport]; ok {
// 将错误信息写入最后一列F列 // 将错误信息写入最后一列F列
excelData.SetCellValue("Sheet1", fmt.Sprintf("F%d", excelRowNum), v.Remark) excelData.SetCellValue("Sheet1", fmt.Sprintf("P%d", excelRowNum), v.Remark)
hasValueRows[excelRowNum] = true hasValueRows[excelRowNum] = true
} }
} }