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 } tp.logger.Info("准备渲染表格", zap.String("field_type", fieldType), zap.Int("header_count", len(tableData.Headers)), zap.Int("row_count", len(tableData.Rows)), zap.Float64("pdf_current_y", pdf.GetY())) // 渲染表格到PDF if err := tp.databaseRenderer.RenderTable(pdf, tableData); err != nil { tp.logger.Error("渲染表格失败", zap.String("field_type", fieldType), zap.Error(err)) return fmt.Errorf("渲染表格失败: %w", err) } tp.logger.Info("表格渲染成功", zap.String("field_type", fieldType), zap.Float64("pdf_final_y", pdf.GetY())) 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 }