fix: 当导入竞品报告字数过长,会自动截断

This commit is contained in:
cjy 2026-03-03 14:04:41 +08:00
parent ce9d75ef46
commit 7898531ce1
3 changed files with 146 additions and 0 deletions

View File

@ -138,6 +138,9 @@ func CreateCompetitiveReportCore(ctx *gin.Context, req *cast.CreateCompetitiveRe
competitorReportData.ImageURL = req.ImageUrl
}
// 截断超长字段按AI生成的字段长度要求
competitorReportData = truncateCompetitorReportData(competitorReportData)
zap.L().Info("解析成功", zap.Any("competitorReportData", competitorReportData))
today := time.Now().Format("20060102")
@ -411,6 +414,9 @@ func ImportCompetitiveReportBatch(ctx *gin.Context) {
competitorReportData.HighlightAnalysis = highlightAnalysis
competitorReportData.DataPerformance = dataPerformance
// 截断超长字段按AI生成的字段长度要求
competitorReportData = truncateCompetitorReportData(competitorReportData)
// 验证必填字段
if artistNum == "" {
temp.Remark = "艺人编号不能为空"
@ -1309,3 +1315,38 @@ func checkAndReuploadImageForReport(imageUrl string) (string, error) {
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
}

View File

@ -508,6 +508,30 @@ func splitTextByRune(text string, maxWidth float64) []string {
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 将竞品报告数据转换为文本格式
// 参数:
// - data: 竞品报告数据

View File

@ -221,3 +221,84 @@ func TestGenerateCompetitorReportPDFMixedContent(t *testing.T) {
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)
}
})
}
}