Files
tyapi-server/internal/shared/pdf/table_parser.go

199 lines
5.5 KiB
Go
Raw Normal View History

2025-12-03 12:03:42 +08:00
package pdf
import (
"context"
"fmt"
"strings"
"tyapi-server/internal/domains/product/entities"
"github.com/jung-kurt/gofpdf/v2"
"go.uber.org/zap"
)
// TableBlock 表格块(用于向后兼容)
type TableBlock struct {
BeforeText string
TableData [][]string
AfterText string
}
// TableParser 表格解析器
// 从数据库读取数据并转换为表格格式
type TableParser struct {
logger *zap.Logger
fontManager *FontManager
databaseReader *DatabaseTableReader
databaseRenderer *DatabaseTableRenderer
}
// NewTableParser 创建表格解析器
func NewTableParser(logger *zap.Logger, fontManager *FontManager) *TableParser {
reader := NewDatabaseTableReader(logger)
renderer := NewDatabaseTableRenderer(logger, fontManager)
return &TableParser{
logger: logger,
fontManager: fontManager,
databaseReader: reader,
databaseRenderer: renderer,
}
}
// ParseAndRenderTable 从产品文档中解析并渲染表格(支持多个表格,带标题)
func (tp *TableParser) ParseAndRenderTable(ctx context.Context, pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, fieldType string) error {
// 从数据库读取表格数据(支持多个表格)
tableData, err := tp.databaseReader.ReadTableFromDocumentation(ctx, doc, fieldType)
if err != nil {
// 如果内容为空,不渲染,也不报错(静默跳过)
if strings.Contains(err.Error(), "内容为空") {
tp.logger.Debug("表格内容为空,跳过渲染", zap.String("field_type", fieldType))
return nil
}
return fmt.Errorf("读取表格数据失败: %w", err)
}
// 检查表格数据是否有效
if tableData == nil || len(tableData.Headers) == 0 {
tp.logger.Warn("表格数据无效,跳过渲染",
zap.String("field_type", fieldType),
zap.Bool("is_nil", tableData == nil))
return nil
}
// 渲染表格到PDF
if err := tp.databaseRenderer.RenderTable(pdf, tableData); err != nil {
2025-12-04 10:35:11 +08:00
// 错误已返回,不记录日志
2025-12-03 12:03:42 +08:00
return fmt.Errorf("渲染表格失败: %w", err)
}
return nil
}
// ParseAndRenderTablesWithTitles 从产品文档中解析并渲染多个表格(带标题)
func (tp *TableParser) ParseAndRenderTablesWithTitles(ctx context.Context, pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, fieldType string) error {
var content string
switch fieldType {
case "request_params":
content = doc.RequestParams
case "response_fields":
content = doc.ResponseFields
case "response_example":
content = doc.ResponseExample
case "error_codes":
content = doc.ErrorCodes
default:
return fmt.Errorf("未知的字段类型: %s", fieldType)
}
if strings.TrimSpace(content) == "" {
return nil
}
// 解析多个表格(带标题)
tablesWithTitles, err := tp.databaseReader.parseMarkdownTablesWithTitles(content)
if err != nil {
tp.logger.Warn("解析表格失败,回退到单个表格", zap.Error(err))
// 回退到单个表格渲染
return tp.ParseAndRenderTable(ctx, pdf, doc, fieldType)
}
if len(tablesWithTitles) == 0 {
return nil
}
// 分别渲染每个表格,并在表格前显示标题
_, lineHt := pdf.GetFontSize()
for i, twt := range tablesWithTitles {
if twt.Table == nil || len(twt.Table.Headers) == 0 {
continue
}
// 如果不是第一个表格,添加间距
if i > 0 {
pdf.Ln(5)
}
// 如果有标题,显示标题
if strings.TrimSpace(twt.Title) != "" {
pdf.SetTextColor(0, 0, 0)
tp.fontManager.SetFont(pdf, "B", 12)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, twt.Title, "", 1, "L", false, 0, "")
pdf.Ln(2)
}
// 渲染表格
if err := tp.databaseRenderer.RenderTable(pdf, twt.Table); err != nil {
tp.logger.Warn("渲染表格失败", zap.Error(err), zap.String("title", twt.Title))
continue
}
}
return nil
}
// ParseTableData 仅解析表格数据,不渲染
func (tp *TableParser) ParseTableData(ctx context.Context, doc *entities.ProductDocumentation, fieldType string) (*TableData, error) {
return tp.databaseReader.ReadTableFromDocumentation(ctx, doc, fieldType)
}
// ParseMarkdownTable 解析Markdown表格兼容方法
func (tp *TableParser) ParseMarkdownTable(text string) [][]string {
// 使用数据库读取器的markdown解析功能
tableData, err := tp.databaseReader.parseMarkdownTable(text)
if err != nil {
tp.logger.Warn("解析markdown表格失败", zap.Error(err))
return nil
}
// 转换为旧格式 [][]string
result := make([][]string, 0, len(tableData.Rows)+1)
result = append(result, tableData.Headers)
result = append(result, tableData.Rows...)
return result
}
// ExtractAllTables 提取所有表格块(兼容方法)
func (tp *TableParser) ExtractAllTables(content string) []TableBlock {
// 使用数据库读取器解析markdown表格
tableData, err := tp.databaseReader.parseMarkdownTable(content)
if err != nil {
return []TableBlock{}
}
// 转换为TableBlock格式
if len(tableData.Headers) > 0 {
rows := make([][]string, 0, len(tableData.Rows)+1)
rows = append(rows, tableData.Headers)
rows = append(rows, tableData.Rows...)
return []TableBlock{
{
BeforeText: "",
TableData: rows,
AfterText: "",
},
}
}
return []TableBlock{}
}
// IsValidTable 验证表格是否有效(兼容方法)
func (tp *TableParser) IsValidTable(tableData [][]string) bool {
if len(tableData) == 0 {
return false
}
if len(tableData[0]) == 0 {
return false
}
// 检查表头是否有有效内容
for _, cell := range tableData[0] {
if strings.TrimSpace(cell) != "" {
return true
}
}
return false
}