diff --git a/data/竞品报告导入模板.xlsx b/data/竞品报告导入模板.xlsx index 0f5505dd..272f60f7 100644 Binary files a/data/竞品报告导入模板.xlsx and b/data/竞品报告导入模板.xlsx differ diff --git a/data/竞品报告导入模板_new.xlsx b/data/竞品报告导入模板_new.xlsx deleted file mode 100644 index fbfa865d..00000000 Binary files a/data/竞品报告导入模板_new.xlsx and /dev/null differ diff --git a/pkg/service/cast/report.go b/pkg/service/cast/report.go index bc47ef58..e9ce8357 100644 --- a/pkg/service/cast/report.go +++ b/pkg/service/cast/report.go @@ -372,16 +372,86 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) { 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{}, } - // 解析图片URL(E列,row[4]) - if len(row) > 4 && utils.CleanString(row[4]) != "" { - temp.ImageUrl = utils.CleanString(row[4]) + // 亮点表现分析摘要(D列,row[3]) + if len(row) > 3 { + 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]) + } + + // 图片URL(O列,row[14]) + if len(row) > 14 && utils.CleanString(row[14]) != "" { + competitorReportData.ImageURL = utils.CleanString(row[14]) + } + + competitorReportData.HighlightAnalysis = highlightAnalysis + competitorReportData.DataPerformance = dataPerformance + // 验证必填字段 if artistNum == "" { temp.Remark = "艺人编号不能为空" @@ -389,82 +459,129 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) { continue } - // 验证报告内容和图片不能同时为空 - if temp.ReportContent == "" && temp.ImageUrl == "" { - temp.Remark = "报告内容和图片不能同时为空" + // 验证亮点表现分析(D列:Summary) + if highlightAnalysis.Summary == "" { + temp.Remark = "亮点表现分析摘要不能为空" req.Reports = append(req.Reports, temp) continue } - // 如果已经有错误信息,跳过PDF生成 - if temp.Remark != "" { + // 验证标题亮点(E列) + if highlightAnalysis.Points.Theme == "" { + temp.Remark = "标题亮点不能为空" req.Reports = append(req.Reports, temp) continue } - // 检查图片URL是否包含阿里云,如果包含则下载并重新上传到OSS - if temp.ImageUrl != "" { - newImageUrl, err := checkAndReuploadImageForReport(temp.ImageUrl) + // 验证题材亮点(F列) + if highlightAnalysis.Points.Narrative == "" { + 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 { 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) continue } - temp.ImageUrl = newImageUrl + competitorReportData.ImageURL = newImageUrl } - // 如果提供了报告内容,则生成PDF并上传 - if temp.ReportContent != "" { - // 生成临时PDF文件路径 - today := time.Now().Format("20060102") - timestamp := time.Now().UnixMicro() - pdfFileName := fmt.Sprintf("%s%s老师的竞品报告%d.pdf", today, temp.ArtistName, timestamp) - pdfFilePath := "./runtime/report_pdf/" + pdfFileName + // 生成PDF并上传 + // 生成临时PDF文件路径 + today := time.Now().Format("20060102") + timestamp := time.Now().UnixMicro() + pdfFileName := fmt.Sprintf("%s%s老师的竞品报告%d.pdf", today, temp.ArtistName, timestamp) + pdfFilePath := "./runtime/report_pdf/" + pdfFileName - // 确保目录存在 - _, err = utils.CheckDirPath("./runtime/report_pdf/", true) - if err != nil { - temp.Remark = fmt.Sprintf("创建PDF目录失败: %v", err) - req.Reports = append(req.Reports, temp) - continue - } + // 确保目录存在 + _, err = utils.CheckDirPath("./runtime/report_pdf/", true) + if err != nil { + temp.Remark = fmt.Sprintf("创建PDF目录失败: %v", err) + req.Reports = append(req.Reports, temp) + continue + } - // 生成PDF文件 - fontPath := "./data/simfang.ttf" - 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 - } + // 模板路径 + templatePath := "./data/竞品报告pdf模板.pdf" - // 上传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文件 - if _, err := os.Stat(pdfFilePath); err == nil { - os.Remove(pdfFilePath) - } - continue - } - - // 将上传后的PDF链接设置到请求中 - temp.PdfUrl = pdfUrl + // 使用新的 GenerateCompetitorReportPDF 生成PDF + err = utils.GenerateCompetitorReportPDF(templatePath, pdfFilePath, competitorReportData) + if err != nil { + zap.L().Error("生成PDF失败", zap.Error(err)) + temp.Remark = "生成PDF失败" + req.Reports = append(req.Reports, temp) + continue + } + // 上传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文件 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)) - } + os.Remove(pdfFilePath) + } + 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) @@ -497,7 +614,7 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) { // 通过请求对象找到对应的Excel行号 if excelRowNum, ok := reportRowMap[reqReport]; ok { // 将错误信息写入最后一列(F列) - excelData.SetCellValue("Sheet1", fmt.Sprintf("F%d", excelRowNum), v.Remark) + excelData.SetCellValue("Sheet1", fmt.Sprintf("P%d", excelRowNum), v.Remark) hasValueRows[excelRowNum] = true } }