fix组合包文档

This commit is contained in:
2025-12-11 11:14:31 +08:00
parent 09d7a4f076
commit 2c89b8cb26
3 changed files with 535 additions and 20 deletions

View File

@@ -15,6 +15,7 @@ import (
"tyapi-server/internal/shared/pdf"
"github.com/gin-gonic/gin"
"github.com/shopspring/decimal"
"go.uber.org/zap"
)
@@ -765,6 +766,60 @@ func (h *ProductHandler) DownloadProductDocumentation(c *gin.Context) {
docVersion = "1.0"
}
// 如果是组合包,获取子产品的文档信息
var subProductDocs []*entities.ProductDocumentation
if product.IsPackage && len(product.PackageItems) > 0 {
h.logger.Info("检测到组合包,开始获取子产品文档",
zap.String("product_id", productID),
zap.Int("sub_product_count", len(product.PackageItems)),
)
// 收集所有子产品的ID
subProductIDs := make([]string, 0, len(product.PackageItems))
for _, item := range product.PackageItems {
subProductIDs = append(subProductIDs, item.ProductID)
}
// 批量获取子产品的文档
subDocs, err := h.documentationAppService.GetDocumentationsByProductIDs(c.Request.Context(), subProductIDs)
if err != nil {
h.logger.Warn("获取组合包子产品文档失败",
zap.String("product_id", productID),
zap.Error(err),
)
} else {
// 转换为entity类型并按PackageItems的顺序排序
docMap := make(map[string]*entities.ProductDocumentation)
for i := range subDocs {
docMap[subDocs[i].ProductID] = &entities.ProductDocumentation{
ID: subDocs[i].ID,
ProductID: subDocs[i].ProductID,
RequestURL: subDocs[i].RequestURL,
RequestMethod: subDocs[i].RequestMethod,
BasicInfo: subDocs[i].BasicInfo,
RequestParams: subDocs[i].RequestParams,
ResponseFields: subDocs[i].ResponseFields,
ResponseExample: subDocs[i].ResponseExample,
ErrorCodes: subDocs[i].ErrorCodes,
Version: subDocs[i].Version,
}
}
// 按PackageItems的顺序构建子产品文档列表
for _, item := range product.PackageItems {
if subDoc, exists := docMap[item.ProductID]; exists {
subProductDocs = append(subProductDocs, subDoc)
}
}
h.logger.Info("成功获取组合包子产品文档",
zap.String("product_id", productID),
zap.Int("total_sub_products", len(product.PackageItems)),
zap.Int("docs_found", len(subProductDocs)),
)
}
}
// 尝试从缓存获取PDF
var pdfBytes []byte
var cacheHit bool
@@ -829,18 +884,61 @@ func (h *ProductHandler) DownloadProductDocumentation(c *gin.Context) {
}
}()
// 构建Product实体用于PDF生成
productEntity := &entities.Product{
ID: product.ID,
Name: product.Name,
Code: product.Code,
Description: product.Description,
Content: product.Content,
IsPackage: product.IsPackage,
Price: decimal.NewFromFloat(product.Price),
}
// 如果是组合包,添加子产品信息
if product.IsPackage && len(product.PackageItems) > 0 {
productEntity.PackageItems = make([]*entities.ProductPackageItem, len(product.PackageItems))
for i, item := range product.PackageItems {
productEntity.PackageItems[i] = &entities.ProductPackageItem{
ID: item.ID,
PackageID: product.ID,
ProductID: item.ProductID,
SortOrder: item.SortOrder,
Product: &entities.Product{
ID: item.ProductID,
Code: item.ProductCode,
Name: item.ProductName,
},
}
}
}
// 直接调用PDF生成器简化版本不使用goroutine
h.logger.Info("开始调用PDF生成器")
pdfBytes, genErr := h.pdfGenerator.GenerateProductPDF(
c.Request.Context(),
product.ID,
product.Name,
product.Code,
product.Description,
product.Content,
product.Price,
docEntity,
h.logger.Info("开始调用PDF生成器",
zap.Bool("is_package", product.IsPackage),
zap.Int("sub_product_docs_count", len(subProductDocs)),
)
// 使用重构后的生成器
refactoredGen := pdf.NewPDFGeneratorRefactored(h.logger)
var genErr error
if product.IsPackage && len(subProductDocs) > 0 {
// 组合包:使用支持子产品文档的方法
pdfBytes, genErr = refactoredGen.GenerateProductPDFWithSubProducts(
c.Request.Context(),
productEntity,
docEntity,
subProductDocs,
)
} else {
// 普通产品:使用标准方法
pdfBytes, genErr = refactoredGen.GenerateProductPDFFromEntity(
c.Request.Context(),
productEntity,
docEntity,
)
}
h.logger.Info("PDF生成器调用返回",
zap.String("product_id", productID),
zap.Bool("has_error", genErr != nil),

View File

@@ -305,6 +305,325 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
pb.addAdditionalInfo(pdf, doc, chineseFontAvailable)
}
// AddDocumentationPagesWithoutAdditionalInfo 添加接口文档页面(不包含二维码和说明)
// 用于组合包场景,在所有文档渲染完成后统一添加二维码和说明
func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
// 创建自定义的AddPage函数确保每页都有水印
addPageWithWatermark := func() {
pdf.AddPage()
pb.addHeader(pdf, chineseFontAvailable)
pb.addWatermark(pdf, chineseFontAvailable) // 每页都添加水印
}
addPageWithWatermark()
pdf.SetY(45)
pb.fontManager.SetFont(pdf, "B", 18)
_, lineHt := pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "接口文档", "", 1, "L", false, 0, "")
// 请求URL
pdf.Ln(8)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "B", 12)
pdf.CellFormat(0, lineHt, "请求URL", "", 1, "L", false, 0, "")
// URL使用黑体字体可能包含中文字符
// 先清理URL中的乱码
cleanURL := pb.textProcessor.CleanText(doc.RequestURL)
pb.fontManager.SetFont(pdf, "", 10) // 使用黑体
pdf.SetTextColor(0, 0, 0)
pdf.MultiCell(0, lineHt*1.2, cleanURL, "", "L", false)
// 请求方法
pdf.Ln(5)
pb.fontManager.SetFont(pdf, "B", 12)
pdf.CellFormat(0, lineHt, fmt.Sprintf("请求方法:%s", doc.RequestMethod), "", 1, "L", false, 0, "")
// 基本信息
if doc.BasicInfo != "" {
pdf.Ln(8)
pb.addSection(pdf, "基本信息", doc.BasicInfo, chineseFontAvailable)
}
// 请求参数
if doc.RequestParams != "" {
pdf.Ln(8)
// 显示标题
pdf.SetTextColor(0, 0, 0)
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "请求参数:", "", 1, "L", false, 0, "")
// 使用新的数据库驱动方式处理请求参数
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "request_params"); err != nil {
pb.logger.Warn("渲染请求参数表格失败,回退到文本显示", zap.Error(err))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.RequestParams)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
}
}
// 生成JSON示例
if jsonExample := pb.jsonProcessor.GenerateJSONExample(doc.RequestParams, pb.tableParser); jsonExample != "" {
pdf.Ln(5)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "B", 14)
pdf.CellFormat(0, lineHt, "请求示例:", "", 1, "L", false, 0, "")
// JSON中可能包含中文值使用黑体字体
pb.fontManager.SetFont(pdf, "", 9) // 使用黑体显示JSON支持中文
pdf.SetTextColor(0, 0, 0)
pdf.MultiCell(0, lineHt*1.3, jsonExample, "", "L", false)
}
}
// 响应示例
if doc.ResponseExample != "" {
pdf.Ln(8)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "响应示例:", "", 1, "L", false, 0, "")
// 优先尝试提取和格式化JSON
jsonContent := pb.jsonProcessor.ExtractJSON(doc.ResponseExample)
if jsonContent != "" {
// 格式化JSON
formattedJSON, err := pb.jsonProcessor.FormatJSON(jsonContent)
if err == nil {
jsonContent = formattedJSON
}
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "", 9) // 使用等宽字体显示JSON支持中文
pdf.MultiCell(0, lineHt*1.3, jsonContent, "", "L", false)
} else {
// 如果没有JSON尝试使用表格方式处理
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "response_example"); err != nil {
pb.logger.Warn("渲染响应示例表格失败,回退到文本显示", zap.Error(err))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.ResponseExample)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
}
}
}
}
// 返回字段说明
if doc.ResponseFields != "" {
pdf.Ln(8)
// 显示标题
pdf.SetTextColor(0, 0, 0)
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "返回字段说明:", "", 1, "L", false, 0, "")
// 使用新的数据库驱动方式处理返回字段(支持多个表格,带标题)
if err := pb.tableParser.ParseAndRenderTablesWithTitles(context.Background(), pdf, doc, "response_fields"); err != nil {
pb.logger.Warn("渲染返回字段表格失败,回退到文本显示",
zap.Error(err),
zap.String("content_preview", pb.getContentPreview(doc.ResponseFields, 200)))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.ResponseFields)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
} else {
pb.logger.Warn("返回字段内容为空或只有空白字符")
}
}
} else {
pb.logger.Debug("返回字段内容为空,跳过渲染")
}
// 错误代码
if doc.ErrorCodes != "" {
pdf.Ln(8)
// 显示标题
pdf.SetTextColor(0, 0, 0)
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "错误代码:", "", 1, "L", false, 0, "")
// 使用新的数据库驱动方式处理错误代码
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "error_codes"); err != nil {
pb.logger.Warn("渲染错误代码表格失败,回退到文本显示", zap.Error(err))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.ErrorCodes)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
}
}
}
// 注意:这里不添加二维码和说明,由调用方统一添加
}
// AddSubProductDocumentationPages 添加子产品的接口文档页面(用于组合包)
func (pb *PageBuilder) AddSubProductDocumentationPages(pdf *gofpdf.Fpdf, subProduct *entities.Product, doc *entities.ProductDocumentation, chineseFontAvailable bool, isLastSubProduct bool) {
// 创建自定义的AddPage函数确保每页都有水印
addPageWithWatermark := func() {
pdf.AddPage()
pb.addHeader(pdf, chineseFontAvailable)
pb.addWatermark(pdf, chineseFontAvailable) // 每页都添加水印
}
addPageWithWatermark()
pdf.SetY(45)
pb.fontManager.SetFont(pdf, "B", 18)
_, lineHt := pdf.GetFontSize()
// 显示子产品标题
subProductTitle := fmt.Sprintf("子产品接口文档:%s", subProduct.Name)
if subProduct.Code != "" {
subProductTitle = fmt.Sprintf("子产品接口文档:%s (%s)", subProduct.Name, subProduct.Code)
}
pdf.CellFormat(0, lineHt, subProductTitle, "", 1, "L", false, 0, "")
// 请求URL
pdf.Ln(8)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "B", 12)
pdf.CellFormat(0, lineHt, "请求URL", "", 1, "L", false, 0, "")
// URL使用黑体字体可能包含中文字符
// 先清理URL中的乱码
cleanURL := pb.textProcessor.CleanText(doc.RequestURL)
pb.fontManager.SetFont(pdf, "", 10) // 使用黑体
pdf.SetTextColor(0, 0, 0)
pdf.MultiCell(0, lineHt*1.2, cleanURL, "", "L", false)
// 请求方法
pdf.Ln(5)
pb.fontManager.SetFont(pdf, "B", 12)
pdf.CellFormat(0, lineHt, fmt.Sprintf("请求方法:%s", doc.RequestMethod), "", 1, "L", false, 0, "")
// 基本信息
if doc.BasicInfo != "" {
pdf.Ln(8)
pb.addSection(pdf, "基本信息", doc.BasicInfo, chineseFontAvailable)
}
// 请求参数
if doc.RequestParams != "" {
pdf.Ln(8)
// 显示标题
pdf.SetTextColor(0, 0, 0)
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "请求参数:", "", 1, "L", false, 0, "")
// 使用新的数据库驱动方式处理请求参数
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "request_params"); err != nil {
pb.logger.Warn("渲染请求参数表格失败,回退到文本显示", zap.Error(err))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.RequestParams)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
}
}
// 生成JSON示例
if jsonExample := pb.jsonProcessor.GenerateJSONExample(doc.RequestParams, pb.tableParser); jsonExample != "" {
pdf.Ln(5)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "B", 14)
pdf.CellFormat(0, lineHt, "请求示例:", "", 1, "L", false, 0, "")
// JSON中可能包含中文值使用黑体字体
pb.fontManager.SetFont(pdf, "", 9) // 使用黑体显示JSON支持中文
pdf.SetTextColor(0, 0, 0)
pdf.MultiCell(0, lineHt*1.3, jsonExample, "", "L", false)
}
}
// 响应示例
if doc.ResponseExample != "" {
pdf.Ln(8)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "响应示例:", "", 1, "L", false, 0, "")
// 优先尝试提取和格式化JSON
jsonContent := pb.jsonProcessor.ExtractJSON(doc.ResponseExample)
if jsonContent != "" {
// 格式化JSON
formattedJSON, err := pb.jsonProcessor.FormatJSON(jsonContent)
if err == nil {
jsonContent = formattedJSON
}
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pb.fontManager.SetFont(pdf, "", 9) // 使用等宽字体显示JSON支持中文
pdf.MultiCell(0, lineHt*1.3, jsonContent, "", "L", false)
} else {
// 如果没有JSON尝试使用表格方式处理
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "response_example"); err != nil {
pb.logger.Warn("渲染响应示例表格失败,回退到文本显示", zap.Error(err))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.ResponseExample)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.SetTextColor(0, 0, 0) // 确保深黑色
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
}
}
}
}
// 返回字段说明
if doc.ResponseFields != "" {
pdf.Ln(8)
// 显示标题
pdf.SetTextColor(0, 0, 0)
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "返回字段说明:", "", 1, "L", false, 0, "")
// 使用新的数据库驱动方式处理返回字段(支持多个表格,带标题)
if err := pb.tableParser.ParseAndRenderTablesWithTitles(context.Background(), pdf, doc, "response_fields"); err != nil {
pb.logger.Warn("渲染返回字段表格失败,回退到文本显示",
zap.Error(err),
zap.String("content_preview", pb.getContentPreview(doc.ResponseFields, 200)))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.ResponseFields)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
} else {
pb.logger.Warn("返回字段内容为空或只有空白字符")
}
}
} else {
pb.logger.Debug("返回字段内容为空,跳过渲染")
}
// 错误代码
if doc.ErrorCodes != "" {
pdf.Ln(8)
// 显示标题
pdf.SetTextColor(0, 0, 0)
pb.fontManager.SetFont(pdf, "B", 14)
_, lineHt = pdf.GetFontSize()
pdf.CellFormat(0, lineHt, "错误代码:", "", 1, "L", false, 0, "")
// 使用新的数据库驱动方式处理错误代码
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "error_codes"); err != nil {
pb.logger.Warn("渲染错误代码表格失败,回退到文本显示", zap.Error(err))
// 如果表格渲染失败,显示为文本
text := pb.textProcessor.CleanText(doc.ErrorCodes)
if strings.TrimSpace(text) != "" {
pb.fontManager.SetFont(pdf, "", 10)
pdf.MultiCell(0, lineHt*1.3, text, "", "L", false)
}
}
}
// 注意:这里不添加二维码和说明,由调用方统一添加
}
// addSection 添加章节
func (pb *PageBuilder) addSection(pdf *gofpdf.Fpdf, title, content string, chineseFontAvailable bool) {
_, lineHt := pdf.GetFontSize()
@@ -835,7 +1154,12 @@ func (pb *PageBuilder) safeSplitText(pdf *gofpdf.Fpdf, text string, width float6
return lines
}
// addAdditionalInfo 添加说明文字和二维码
// AddAdditionalInfo 添加说明文字和二维码(公开方法)
func (pb *PageBuilder) AddAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
pb.addAdditionalInfo(pdf, doc, chineseFontAvailable)
}
// addAdditionalInfo 添加说明文字和二维码(私有方法)
func (pb *PageBuilder) addAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
// 检查是否需要换页
pageWidth, pageHeight := pdf.GetPageSize()
@@ -925,11 +1249,36 @@ func (pb *PageBuilder) addAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.Product
// readExplanationText 读取说明文本文件
func (pb *PageBuilder) readExplanationText() string {
resourcesPDFDir := GetResourcesPDFDir()
if resourcesPDFDir == "" {
pb.logger.Error("无法获取resources/pdf目录路径")
return ""
}
textFilePath := filepath.Join(resourcesPDFDir, "后勤服务.txt")
// 记录尝试读取的文件路径
pb.logger.Debug("尝试读取说明文本文件", zap.String("path", textFilePath))
// 检查文件是否存在
if _, err := os.Stat(textFilePath); os.IsNotExist(err) {
pb.logger.Warn("说明文本文件不存在", zap.String("path", textFilePath))
fileInfo, err := os.Stat(textFilePath)
if err != nil {
if os.IsNotExist(err) {
pb.logger.Warn("说明文本文件不存在",
zap.String("path", textFilePath),
zap.String("resources_dir", resourcesPDFDir),
)
} else {
pb.logger.Error("检查说明文本文件时出错",
zap.String("path", textFilePath),
zap.Error(err),
)
}
return ""
}
// 检查文件大小
if fileInfo.Size() == 0 {
pb.logger.Warn("说明文本文件为空", zap.String("path", textFilePath))
return ""
}
@@ -946,10 +1295,21 @@ func (pb *PageBuilder) readExplanationText() string {
// 转换为字符串
text := string(content)
// 检查内容是否为空(去除空白字符后)
trimmedText := strings.TrimSpace(text)
if trimmedText == "" {
pb.logger.Warn("说明文本文件内容为空(只有空白字符)",
zap.String("path", textFilePath),
zap.Int("file_size", len(content)),
)
return ""
}
// 记录读取成功的信息
pb.logger.Info("成功读取说明文本文件",
zap.String("path", textFilePath),
zap.Int("file_size", len(content)),
zap.Int64("file_size", fileInfo.Size()),
zap.Int("content_length", len(content)),
zap.Int("text_length", len(text)),
zap.Int("line_count", len(strings.Split(text, "\n"))),
)

View File

@@ -90,16 +90,21 @@ func (g *PDFGeneratorRefactored) GenerateProductPDF(ctx context.Context, product
product.Price = decimal.NewFromFloat(price)
}
return g.generatePDF(product, doc)
return g.generatePDF(product, doc, nil)
}
// GenerateProductPDFFromEntity 从entity类型生成PDF推荐使用
func (g *PDFGeneratorRefactored) GenerateProductPDFFromEntity(ctx context.Context, product *entities.Product, doc *entities.ProductDocumentation) ([]byte, error) {
return g.generatePDF(product, doc)
return g.generatePDF(product, doc, nil)
}
// GenerateProductPDFWithSubProducts 从entity类型生成PDF支持组合包子产品文档
func (g *PDFGeneratorRefactored) GenerateProductPDFWithSubProducts(ctx context.Context, product *entities.Product, doc *entities.ProductDocumentation, subProductDocs []*entities.ProductDocumentation) ([]byte, error) {
return g.generatePDF(product, doc, subProductDocs)
}
// generatePDF 内部PDF生成方法
func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *entities.ProductDocumentation) (result []byte, err error) {
func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *entities.ProductDocumentation, subProductDocs []*entities.ProductDocumentation) (result []byte, err error) {
defer func() {
if r := recover(); r != nil {
// 将panic转换为error而不是重新抛出
@@ -174,9 +179,61 @@ func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *ent
// 添加第一页(产品信息)
pageBuilder.AddFirstPage(pdf, product, doc, chineseFontAvailable)
// 如果有关联的文档,添加接口文档页面
if doc != nil {
pageBuilder.AddDocumentationPages(pdf, doc, chineseFontAvailable)
// 如果是组合包,需要特殊处理:先渲染所有文档,最后统一添加二维码
if product.IsPackage {
// 如果有关联的文档,添加接口文档页面(但不包含二维码和说明,后面统一添加)
if doc != nil {
pageBuilder.AddDocumentationPagesWithoutAdditionalInfo(pdf, doc, chineseFontAvailable)
}
// 如果有子产品文档,为每个子产品添加接口文档页面
if len(subProductDocs) > 0 {
for i, subDoc := range subProductDocs {
// 获取子产品信息从文档中获取ProductID然后查找对应的产品信息
// 注意这里我们需要从product.PackageItems中查找对应的子产品信息
var subProduct *entities.Product
if product.PackageItems != nil && i < len(product.PackageItems) {
if product.PackageItems[i].Product != nil {
subProduct = product.PackageItems[i].Product
}
}
// 如果找不到子产品信息,创建一个基本的子产品实体
if subProduct == nil {
subProduct = &entities.Product{
ID: subDoc.ProductID,
Code: subDoc.ProductID, // 使用ProductID作为临时Code
Name: fmt.Sprintf("子产品 %d", i+1),
}
}
pageBuilder.AddSubProductDocumentationPages(pdf, subProduct, subDoc, chineseFontAvailable, false)
}
}
// 在所有接口文档渲染完成后,统一添加二维码和后勤服务说明
// 使用主产品文档(如果存在),否则使用第一个子产品文档,如果都没有则创建一个空的文档对象
var finalDoc *entities.ProductDocumentation
if doc != nil {
finalDoc = doc
} else if len(subProductDocs) > 0 {
finalDoc = subProductDocs[0]
} else {
// 如果没有文档,创建一个空的文档对象,用于添加二维码和说明
finalDoc = &entities.ProductDocumentation{
ProductID: product.ID,
RequestMethod: "POST",
Version: "1.0",
}
}
// 始终添加二维码和后勤服务说明
pageBuilder.AddAdditionalInfo(pdf, finalDoc, chineseFontAvailable)
} else {
// 普通产品:使用原来的方法(包含二维码和说明)
if doc != nil {
pageBuilder.AddDocumentationPages(pdf, doc, chineseFontAvailable)
}
}
// 生成PDF字节流