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
|
||||
}
|
||||
|
||||
// 截断超长字段(按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
|
||||
}
|
||||
|
||||
@ -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: 竞品报告数据
|
||||
|
||||
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user