fix: 当导入竞品报告字数过长,会自动截断
This commit is contained in:
parent
ce9d75ef46
commit
7898531ce1
@ -138,6 +138,9 @@ func CreateCompetitiveReportCore(ctx *gin.Context, req *cast.CreateCompetitiveRe
|
|||||||
competitorReportData.ImageURL = req.ImageUrl
|
competitorReportData.ImageURL = req.ImageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 截断超长字段(按AI生成的字段长度要求)
|
||||||
|
competitorReportData = truncateCompetitorReportData(competitorReportData)
|
||||||
|
|
||||||
zap.L().Info("解析成功", zap.Any("competitorReportData", competitorReportData))
|
zap.L().Info("解析成功", zap.Any("competitorReportData", competitorReportData))
|
||||||
|
|
||||||
today := time.Now().Format("20060102")
|
today := time.Now().Format("20060102")
|
||||||
@ -411,6 +414,9 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) {
|
|||||||
competitorReportData.HighlightAnalysis = highlightAnalysis
|
competitorReportData.HighlightAnalysis = highlightAnalysis
|
||||||
competitorReportData.DataPerformance = dataPerformance
|
competitorReportData.DataPerformance = dataPerformance
|
||||||
|
|
||||||
|
// 截断超长字段(按AI生成的字段长度要求)
|
||||||
|
competitorReportData = truncateCompetitorReportData(competitorReportData)
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
if artistNum == "" {
|
if artistNum == "" {
|
||||||
temp.Remark = "艺人编号不能为空"
|
temp.Remark = "艺人编号不能为空"
|
||||||
@ -1309,3 +1315,38 @@ func checkAndReuploadImageForReport(imageUrl string) (string, error) {
|
|||||||
|
|
||||||
return compressUrl, nil
|
return compressUrl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// truncateCompetitorReportData 截断竞品报告数据中超长的字段
|
||||||
|
// 字段长度要求参考 AI 生成竞品报告的限制
|
||||||
|
func truncateCompetitorReportData(data utils.CompetitorReportData) utils.CompetitorReportData {
|
||||||
|
// 字段长度限制
|
||||||
|
const (
|
||||||
|
MaxSummary = 78 // 概述
|
||||||
|
MaxPointField = 60 // 标题/题材/内容/文案/数据/配乐亮点
|
||||||
|
MaxViews = 60 // 浏览量
|
||||||
|
MaxCompletion = 60 // 完播率
|
||||||
|
MaxEngagement = 60 // 点赞/分享/评论
|
||||||
|
MaxOverallSummary = 300 // 整体总结及可优化建议
|
||||||
|
)
|
||||||
|
|
||||||
|
// 截断亮点分析摘要
|
||||||
|
data.HighlightAnalysis.Summary = utils.TruncateTextByRune(data.HighlightAnalysis.Summary, MaxSummary)
|
||||||
|
|
||||||
|
// 截断各亮点字段
|
||||||
|
data.HighlightAnalysis.Points.Theme = utils.TruncateTextByRune(data.HighlightAnalysis.Points.Theme, MaxPointField)
|
||||||
|
data.HighlightAnalysis.Points.Narrative = utils.TruncateTextByRune(data.HighlightAnalysis.Points.Narrative, MaxPointField)
|
||||||
|
data.HighlightAnalysis.Points.Content = utils.TruncateTextByRune(data.HighlightAnalysis.Points.Content, MaxPointField)
|
||||||
|
data.HighlightAnalysis.Points.Copywriting = utils.TruncateTextByRune(data.HighlightAnalysis.Points.Copywriting, MaxPointField)
|
||||||
|
data.HighlightAnalysis.Points.Data = utils.TruncateTextByRune(data.HighlightAnalysis.Points.Data, MaxPointField)
|
||||||
|
data.HighlightAnalysis.Points.Music = utils.TruncateTextByRune(data.HighlightAnalysis.Points.Music, MaxPointField)
|
||||||
|
|
||||||
|
// 截断数据表现字段
|
||||||
|
data.DataPerformance.Views = utils.TruncateTextByRune(data.DataPerformance.Views, MaxViews)
|
||||||
|
data.DataPerformance.Completion = utils.TruncateTextByRune(data.DataPerformance.Completion, MaxCompletion)
|
||||||
|
data.DataPerformance.Engagement = utils.TruncateTextByRune(data.DataPerformance.Engagement, MaxEngagement)
|
||||||
|
|
||||||
|
// 截断整体总结
|
||||||
|
data.OverallSummary = utils.TruncateTextByRune(data.OverallSummary, MaxOverallSummary)
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|||||||
@ -508,6 +508,30 @@ func splitTextByRune(text string, maxWidth float64) []string {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TruncateTextByRune 按字符权重截断文本
|
||||||
|
// 中文字符算1.0,英文字母/数字/英文符号算0.5
|
||||||
|
// 返回截断后的文本,确保总权重不超过maxWidth
|
||||||
|
func TruncateTextByRune(text string, maxWidth float64) string {
|
||||||
|
if text == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
runes := []rune(text)
|
||||||
|
var result []rune
|
||||||
|
currentWidth := 0.0
|
||||||
|
|
||||||
|
for _, r := range runes {
|
||||||
|
charWidth := getCharWidth(r)
|
||||||
|
if currentWidth+charWidth > maxWidth {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result = append(result, r)
|
||||||
|
currentWidth += charWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
// ConvertCompetitorReportToText 将竞品报告数据转换为文本格式
|
// ConvertCompetitorReportToText 将竞品报告数据转换为文本格式
|
||||||
// 参数:
|
// 参数:
|
||||||
// - data: 竞品报告数据
|
// - data: 竞品报告数据
|
||||||
|
|||||||
@ -221,3 +221,84 @@ func TestGenerateCompetitorReportPDFMixedContent(t *testing.T) {
|
|||||||
|
|
||||||
fmt.Printf("PDF生成成功(中英混合测试): %s\n", outputPath)
|
fmt.Printf("PDF生成成功(中英混合测试): %s\n", outputPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTruncateTextByRune 测试按字符权重截断文本
|
||||||
|
func TestTruncateTextByRune(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
text string
|
||||||
|
maxWidth float64
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "空文本",
|
||||||
|
text: "",
|
||||||
|
maxWidth: 10,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "纯中文截断",
|
||||||
|
text: "这是一段很长的中文内容需要被截断",
|
||||||
|
maxWidth: 10,
|
||||||
|
want: "这是一段很长的中文内", // 10个中文字=10,正好
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "纯英文截断(按0.5计算)",
|
||||||
|
text: "abcdefghijklmnop",
|
||||||
|
maxWidth: 5,
|
||||||
|
want: "abcdefghij", // 10个字母=5.0,正好
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "数字截断(按0.5计算)",
|
||||||
|
text: "1234567890",
|
||||||
|
maxWidth: 3,
|
||||||
|
want: "123456", // 6个数字=3.0,正好
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "中英混合截断",
|
||||||
|
text: "hello你好world世界",
|
||||||
|
maxWidth: 6,
|
||||||
|
// h(0.5)+e(0.5)+l(0.5)+l(0.5)+o(0.5)=2.5
|
||||||
|
// +你(1)+好(1)=4.5
|
||||||
|
// +w(0.5)+o(0.5)+r(0.5)+l(0.5)=2,再加就超过6了
|
||||||
|
// 所以应该是 hello你好wor
|
||||||
|
want: "hello你好wor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "符号截断(按0.5计算)",
|
||||||
|
text: "hello,world!",
|
||||||
|
maxWidth: 5,
|
||||||
|
// h(0.5)+e(0.5)+l(0.5)+l(0.5)+o(0.5)+,(0.5)=3
|
||||||
|
// +w(0.5)+o(0.5)+r(0.5)+l(0.5)+d(0.5)+!(0.5)=6,超过5
|
||||||
|
// 所以保留 hello,worl
|
||||||
|
want: "hello,worl",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "截断到正好边界",
|
||||||
|
text: "中文字符",
|
||||||
|
maxWidth: 4,
|
||||||
|
want: "中文字符", // 4个中文字=4.0,正好
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "宽度为0",
|
||||||
|
text: "hello",
|
||||||
|
maxWidth: 0,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "宽度小于单个字符",
|
||||||
|
text: "hello",
|
||||||
|
maxWidth: 0.3,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := TruncateTextByRune(tt.text, tt.maxWidth)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("TruncateTextByRune(%q, %v) = %q, want %q", tt.text, tt.maxWidth, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user