This commit is contained in:
2025-12-03 18:02:49 +08:00
parent 1f06f21faf
commit b08a63fc99
2 changed files with 110 additions and 48 deletions

View File

@@ -45,7 +45,6 @@ func (pd *ProductDocumentation) IsValid() bool {
return pd.DeletedAt.Time.IsZero()
}
// UpdateContent 更新文档内容
func (pd *ProductDocumentation) UpdateContent(requestURL, requestMethod, basicInfo, requestParams, responseFields, responseExample, errorCodes string) {
pd.RequestURL = requestURL
@@ -167,7 +166,6 @@ func (pd *ProductDocumentation) UpdateDocumentation(requestURL, requestMethod, b
return nil
}
// GetDocumentationSummary 获取文档摘要
func (pd *ProductDocumentation) GetDocumentationSummary() map[string]interface{} {
return map[string]interface{}{

View File

@@ -2,7 +2,9 @@ package pdf
import (
"math"
"regexp"
"strings"
"unicode"
"github.com/jung-kurt/gofpdf/v2"
"go.uber.org/zap"
@@ -227,6 +229,101 @@ func (r *DatabaseTableRenderer) calculateColumnWidths(pdf *gofpdf.Fpdf, tableDat
return colWidths
}
// cleanTextForPDF 清理文本移除可能导致PDF生成问题的字符
func (r *DatabaseTableRenderer) cleanTextForPDF(text string) string {
// 先移除HTML标签
text = strings.TrimSpace(text)
text = strings.ReplaceAll(text, "<br>", " ")
text = strings.ReplaceAll(text, "<br/>", " ")
text = strings.ReplaceAll(text, "<br />", " ")
// 移除控制字符(除了换行符和制表符)
var cleaned strings.Builder
for _, r := range text {
// 保留可打印字符、空格、换行符、制表符
if unicode.IsPrint(r) || r == '\n' || r == '\t' || r == '\r' {
cleaned.WriteRune(r)
} else if unicode.IsSpace(r) {
// 将其他空白字符转换为空格
cleaned.WriteRune(' ')
}
}
text = cleaned.String()
// 移除多余的空白字符
text = regexp.MustCompile(`\s+`).ReplaceAllString(text, " ")
// 确保文本不为空且长度合理
if len([]rune(text)) > 10000 {
// 如果文本过长,截断
runes := []rune(text)
text = string(runes[:10000]) + "..."
}
return strings.TrimSpace(text)
}
// safeSplitText 安全地调用SplitText带错误恢复
func (r *DatabaseTableRenderer) safeSplitText(pdf *gofpdf.Fpdf, text string, width float64) []string {
// 先清理文本
text = r.cleanTextForPDF(text)
// 如果文本为空或宽度无效,返回单行
if text == "" || width <= 0 {
return []string{text}
}
// 使用匿名函数和recover保护SplitText调用
var lines []string
func() {
defer func() {
if rec := recover(); rec != nil {
r.logger.Warn("SplitText发生panic使用估算值",
zap.Any("error", rec),
zap.String("text_preview", func() string {
if len(text) > 50 {
return text[:50] + "..."
}
return text
}()),
zap.Float64("width", width))
// 如果panic发生使用估算值
charCount := len([]rune(text))
estimatedLines := math.Max(1, math.Ceil(float64(charCount)/20))
lines = make([]string, int(estimatedLines))
if estimatedLines == 1 {
lines[0] = text
} else {
// 简单分割文本
runes := []rune(text)
charsPerLine := int(math.Ceil(float64(len(runes)) / estimatedLines))
for i := 0; i < int(estimatedLines); i++ {
start := i * charsPerLine
end := start + charsPerLine
if end > len(runes) {
end = len(runes)
}
if start < len(runes) {
lines[i] = string(runes[start:end])
} else {
lines[i] = ""
}
}
}
}
}()
// 尝试调用SplitText
lines = pdf.SplitText(text, width)
}()
// 如果lines为nil或空返回单行
if lines == nil || len(lines) == 0 {
return []string{text}
}
return lines
}
// renderHeader 渲染表头
func (r *DatabaseTableRenderer) renderHeader(pdf *gofpdf.Fpdf, headers []string, colWidths []float64, startY float64) float64 {
_, lineHt := pdf.GetFontSize()
@@ -238,7 +335,8 @@ func (r *DatabaseTableRenderer) renderHeader(pdf *gofpdf.Fpdf, headers []string,
break
}
colW := colWidths[i]
headerLines := pdf.SplitText(header, colW-6) // 增加边距从4增加到6
// 使用安全的SplitText方法
headerLines := r.safeSplitText(pdf, header, colW-6)
headerHeight := float64(len(headerLines)) * lineHt
// 添加上下内边距
headerHeight += lineHt * 0.8 // 上下各0.4倍行高的内边距
@@ -263,11 +361,7 @@ func (r *DatabaseTableRenderer) renderHeader(pdf *gofpdf.Fpdf, headers []string,
colW := colWidths[i]
// 清理表头数据移除任何残留的HTML标签和样式
header = strings.TrimSpace(header)
// 移除HTML标签使用简单的替换
header = strings.ReplaceAll(header, "<br>", " ")
header = strings.ReplaceAll(header, "<br/>", " ")
header = strings.ReplaceAll(header, "<br />", " ")
header = r.cleanTextForPDF(header)
// 绘制表头背景
pdf.Rect(currentX, startY, colW, maxHeaderHeight, "FD")
@@ -275,7 +369,8 @@ func (r *DatabaseTableRenderer) renderHeader(pdf *gofpdf.Fpdf, headers []string,
// 绘制表头文本
if header != "" {
// 计算文本的实际高度(减少内边距,给文本更多空间)
headerLines := pdf.SplitText(header, colW-4)
// 使用安全的SplitText方法
headerLines := r.safeSplitText(pdf, header, colW-4)
textHeight := float64(len(headerLines)) * lineHt
if textHeight < lineHt {
textHeight = lineHt
@@ -360,31 +455,17 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
cell := row[j]
cellWidth := colWidths[j] - 4 // 减少内边距到4mm给文本更多空间
// 先清理单元格文本(在计算宽度之前)
cell = r.cleanTextForPDF(cell)
// 计算文本实际宽度,判断是否需要换行
textWidth := r.getTextWidth(pdf, cell)
var lines []string
// 只有当文本宽度超过单元格宽度时才换行
if textWidth > cellWidth {
// 文本需要换行
func() {
defer func() {
if rec := recover(); rec != nil {
r.logger.Warn("SplitText失败使用估算",
zap.Any("error", rec),
zap.Int("row_index", rowIndex),
zap.Int("col_index", j))
// 使用估算值
charCount := len([]rune(cell))
estimatedLines := math.Max(1, math.Ceil(float64(charCount)/20))
lines = make([]string, int(estimatedLines))
for i := range lines {
lines[i] = ""
}
}
}()
lines = pdf.SplitText(cell, cellWidth)
}()
// 文本需要换行使用安全的SplitText方法
lines = r.safeSplitText(pdf, cell, cellWidth)
} else {
// 文本不需要换行,单行显示
lines = []string{cell}
@@ -417,11 +498,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
cell := row[j]
// 清理单元格数据移除任何残留的HTML标签和样式
cell = strings.TrimSpace(cell)
// 移除HTML标签使用简单的正则表达式
cell = strings.ReplaceAll(cell, "<br>", " ")
cell = strings.ReplaceAll(cell, "<br/>", " ")
cell = strings.ReplaceAll(cell, "<br />", " ")
cell = r.cleanTextForPDF(cell)
// 绘制单元格背景
if fill {
@@ -440,21 +517,8 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
var textLines []string
// 只有当文本宽度超过单元格宽度时才换行
if textWidth > cellWidth {
// 文本需要换行
func() {
defer func() {
if rec := recover(); rec != nil {
// 如果SplitText失败使用估算
charCount := len([]rune(cell))
estimatedLines := math.Max(1, math.Ceil(float64(charCount)/20))
textLines = make([]string, int(estimatedLines))
for i := range textLines {
textLines[i] = ""
}
}
}()
textLines = pdf.SplitText(cell, cellWidth)
}()
// 文本需要换行使用安全的SplitText方法
textLines = r.safeSplitText(pdf, cell, cellWidth)
} else {
// 文本不需要换行,单行显示
textLines = []string{cell}