diff --git a/internal/shared/pdf/pdf_debug_tool.go b/internal/shared/pdf/pdf_debug_tool.go new file mode 100644 index 0000000..c594a01 --- /dev/null +++ b/internal/shared/pdf/pdf_debug_tool.go @@ -0,0 +1,265 @@ +package pdf + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "tyapi-server/internal/domains/product/entities" + + "go.uber.org/zap" +) + +// PDFDebugTool PDF调试工具 - 用于输出转换前后的文档 +type PDFDebugTool struct { + logger *zap.Logger + pdfGenerator *PDFGenerator + markdownConverter *MarkdownConverter + textProcessor *TextProcessor + outputDir string +} + +// NewPDFDebugTool 创建PDF调试工具 +func NewPDFDebugTool(logger *zap.Logger, outputDir string) *PDFDebugTool { + if outputDir == "" { + outputDir = "./pdf_debug_output" + } + + textProcessor := NewTextProcessor() + markdownConverter := NewMarkdownConverter(textProcessor) + pdfGenerator := NewPDFGenerator(logger) + + return &PDFDebugTool{ + logger: logger, + pdfGenerator: pdfGenerator, + markdownConverter: markdownConverter, + textProcessor: textProcessor, + outputDir: outputDir, + } +} + +// GenerateDebugDocuments 生成调试文档(转换前的markdown和转换后的PDF) +func (tool *PDFDebugTool) GenerateDebugDocuments( + ctx context.Context, + productID string, + productName, productCode, description, content string, + price float64, + doc *entities.ProductDocumentation, +) error { + // 创建输出目录 + if err := os.MkdirAll(tool.outputDir, 0755); err != nil { + return fmt.Errorf("创建输出目录失败: %w", err) + } + + timestamp := time.Now().Format("20060102_150405") + baseName := fmt.Sprintf("%s_%s", productID, timestamp) + + // 1. 保存转换前的markdown数据 + if err := tool.saveOriginalMarkdown(baseName, doc); err != nil { + tool.logger.Error("保存原始markdown失败", zap.Error(err)) + return fmt.Errorf("保存原始markdown失败: %w", err) + } + + // 2. 保存转换后的markdown数据(预处理后) + if err := tool.saveProcessedMarkdown(baseName, doc); err != nil { + tool.logger.Error("保存处理后的markdown失败", zap.Error(err)) + return fmt.Errorf("保存处理后的markdown失败: %w", err) + } + + // 3. 生成PDF文件 + pdfBytes, err := tool.pdfGenerator.GenerateProductPDF( + ctx, + productID, + productName, + productCode, + description, + content, + price, + doc, + ) + if err != nil { + tool.logger.Error("生成PDF失败", zap.Error(err)) + return fmt.Errorf("生成PDF失败: %w", err) + } + + // 4. 保存PDF文件 + pdfPath := filepath.Join(tool.outputDir, fmt.Sprintf("%s.pdf", baseName)) + if err := os.WriteFile(pdfPath, pdfBytes, 0644); err != nil { + tool.logger.Error("保存PDF文件失败", zap.Error(err)) + return fmt.Errorf("保存PDF文件失败: %w", err) + } + + tool.logger.Info("调试文档生成成功", + zap.String("product_id", productID), + zap.String("output_dir", tool.outputDir), + zap.String("base_name", baseName), + zap.Int("pdf_size", len(pdfBytes)), + ) + + return nil +} + +// saveOriginalMarkdown 保存原始markdown数据 +func (tool *PDFDebugTool) saveOriginalMarkdown(baseName string, doc *entities.ProductDocumentation) error { + if doc == nil { + return nil + } + + var content strings.Builder + content.WriteString("# 原始Markdown数据\n\n") + content.WriteString(fmt.Sprintf("生成时间: %s\n\n", time.Now().Format("2006-01-02 15:04:05"))) + content.WriteString("---\n\n") + + // 请求URL + if doc.RequestURL != "" { + content.WriteString("## 请求URL\n\n") + content.WriteString(fmt.Sprintf("```\n%s\n```\n\n", doc.RequestURL)) + } + + // 请求方法 + if doc.RequestMethod != "" { + content.WriteString("## 请求方法\n\n") + content.WriteString(fmt.Sprintf("%s\n\n", doc.RequestMethod)) + } + + // 基本信息 + if doc.BasicInfo != "" { + content.WriteString("## 基本信息\n\n") + content.WriteString(doc.BasicInfo) + content.WriteString("\n\n") + } + + // 请求参数 + if doc.RequestParams != "" { + content.WriteString("## 请求参数(原始)\n\n") + content.WriteString("```markdown\n") + content.WriteString(doc.RequestParams) + content.WriteString("\n```\n\n") + } + + // 响应示例 + if doc.ResponseExample != "" { + content.WriteString("## 响应示例(原始)\n\n") + content.WriteString("```markdown\n") + content.WriteString(doc.ResponseExample) + content.WriteString("\n```\n\n") + } + + // 返回字段 + if doc.ResponseFields != "" { + content.WriteString("## 返回字段(原始)\n\n") + content.WriteString("```markdown\n") + content.WriteString(doc.ResponseFields) + content.WriteString("\n```\n\n") + } + + // 错误代码 + if doc.ErrorCodes != "" { + content.WriteString("## 错误代码(原始)\n\n") + content.WriteString("```markdown\n") + content.WriteString(doc.ErrorCodes) + content.WriteString("\n```\n\n") + } + + // 保存文件 + filePath := filepath.Join(tool.outputDir, fmt.Sprintf("%s_original.md", baseName)) + return os.WriteFile(filePath, []byte(content.String()), 0644) +} + +// saveProcessedMarkdown 保存处理后的markdown数据 +func (tool *PDFDebugTool) saveProcessedMarkdown(baseName string, doc *entities.ProductDocumentation) error { + if doc == nil { + return nil + } + + var content strings.Builder + content.WriteString("# 转换后的Markdown数据\n\n") + content.WriteString(fmt.Sprintf("生成时间: %s\n\n", time.Now().Format("2006-01-02 15:04:05"))) + content.WriteString("---\n\n") + + // 请求URL + if doc.RequestURL != "" { + content.WriteString("## 请求URL\n\n") + content.WriteString(fmt.Sprintf("```\n%s\n```\n\n", doc.RequestURL)) + } + + // 请求方法 + if doc.RequestMethod != "" { + content.WriteString("## 请求方法\n\n") + content.WriteString(fmt.Sprintf("%s\n\n", doc.RequestMethod)) + } + + // 基本信息 + if doc.BasicInfo != "" { + content.WriteString("## 基本信息\n\n") + processedBasicInfo := tool.markdownConverter.PreprocessContent(doc.BasicInfo) + content.WriteString(processedBasicInfo) + content.WriteString("\n\n") + } + + // 请求参数(转换后) + if doc.RequestParams != "" { + content.WriteString("## 请求参数(转换后)\n\n") + processedParams := tool.markdownConverter.PreprocessContent(doc.RequestParams) + content.WriteString("```markdown\n") + content.WriteString(processedParams) + content.WriteString("\n```\n\n") + } + + // 响应示例(转换后) + if doc.ResponseExample != "" { + content.WriteString("## 响应示例(转换后)\n\n") + processedExample := tool.markdownConverter.PreprocessContent(doc.ResponseExample) + content.WriteString("```markdown\n") + content.WriteString(processedExample) + content.WriteString("\n```\n\n") + } + + // 返回字段(转换后) + if doc.ResponseFields != "" { + content.WriteString("## 返回字段(转换后)\n\n") + processedFields := tool.markdownConverter.PreprocessContent(doc.ResponseFields) + content.WriteString("```markdown\n") + content.WriteString(processedFields) + content.WriteString("\n```\n\n") + } + + // 错误代码(转换后) + if doc.ErrorCodes != "" { + content.WriteString("## 错误代码(转换后)\n\n") + processedErrorCodes := tool.markdownConverter.PreprocessContent(doc.ErrorCodes) + content.WriteString("```markdown\n") + content.WriteString(processedErrorCodes) + content.WriteString("\n```\n\n") + } + + // 保存文件 + filePath := filepath.Join(tool.outputDir, fmt.Sprintf("%s_processed.md", baseName)) + return os.WriteFile(filePath, []byte(content.String()), 0644) +} + +// GenerateDebugDocumentsFromEntity 从实体生成调试文档 +func (tool *PDFDebugTool) GenerateDebugDocumentsFromEntity( + ctx context.Context, + product *entities.Product, + doc *entities.ProductDocumentation, +) error { + var price float64 + if !product.Price.IsZero() { + price, _ = product.Price.Float64() + } + + return tool.GenerateDebugDocuments( + ctx, + product.ID, + product.Name, + product.Code, + product.Description, + product.Content, + price, + doc, + ) +}