This commit is contained in:
@@ -2,12 +2,17 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"tyapi-server/internal/application/product"
|
||||
"tyapi-server/internal/application/product/dto/commands"
|
||||
"tyapi-server/internal/application/product/dto/queries"
|
||||
_ "tyapi-server/internal/application/product/dto/responses"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
"tyapi-server/internal/shared/pdf"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
@@ -15,14 +20,15 @@ import (
|
||||
|
||||
// ProductHandler 产品相关HTTP处理器
|
||||
type ProductHandler struct {
|
||||
appService product.ProductApplicationService
|
||||
apiConfigService product.ProductApiConfigApplicationService
|
||||
categoryService product.CategoryApplicationService
|
||||
subAppService product.SubscriptionApplicationService
|
||||
appService product.ProductApplicationService
|
||||
apiConfigService product.ProductApiConfigApplicationService
|
||||
categoryService product.CategoryApplicationService
|
||||
subAppService product.SubscriptionApplicationService
|
||||
documentationAppService product.DocumentationApplicationServiceInterface
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
validator interfaces.RequestValidator
|
||||
logger *zap.Logger
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
validator interfaces.RequestValidator
|
||||
pdfGenerator *pdf.PDFGenerator
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewProductHandler 创建产品HTTP处理器
|
||||
@@ -34,17 +40,19 @@ func NewProductHandler(
|
||||
documentationAppService product.DocumentationApplicationServiceInterface,
|
||||
responseBuilder interfaces.ResponseBuilder,
|
||||
validator interfaces.RequestValidator,
|
||||
pdfGenerator *pdf.PDFGenerator,
|
||||
logger *zap.Logger,
|
||||
) *ProductHandler {
|
||||
return &ProductHandler{
|
||||
appService: appService,
|
||||
apiConfigService: apiConfigService,
|
||||
categoryService: categoryService,
|
||||
subAppService: subAppService,
|
||||
appService: appService,
|
||||
apiConfigService: apiConfigService,
|
||||
categoryService: categoryService,
|
||||
subAppService: subAppService,
|
||||
documentationAppService: documentationAppService,
|
||||
responseBuilder: responseBuilder,
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
responseBuilder: responseBuilder,
|
||||
validator: validator,
|
||||
pdfGenerator: pdfGenerator,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,3 +638,168 @@ func (h *ProductHandler) GetProductDocumentation(c *gin.Context) {
|
||||
|
||||
h.responseBuilder.Success(c, doc, "获取文档成功")
|
||||
}
|
||||
|
||||
// DownloadProductDocumentation 下载产品接口文档(PDF文件)
|
||||
// @Summary 下载产品接口文档
|
||||
// @Description 根据产品ID从数据库获取产品信息和文档信息,动态生成PDF文档并下载。
|
||||
// @Tags 数据大厅
|
||||
// @Accept json
|
||||
// @Produce application/pdf
|
||||
// @Param id path string true "产品ID"
|
||||
// @Success 200 {file} file "PDF文档文件"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 404 {object} map[string]interface{} "产品不存在"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/products/{id}/documentation/download [get]
|
||||
func (h *ProductHandler) DownloadProductDocumentation(c *gin.Context) {
|
||||
productID := c.Param("id")
|
||||
if productID == "" {
|
||||
h.responseBuilder.BadRequest(c, "产品ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查PDF生成器是否可用
|
||||
if h.pdfGenerator == nil {
|
||||
h.logger.Error("PDF生成器未初始化")
|
||||
h.responseBuilder.InternalError(c, "PDF生成器未初始化")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取产品信息
|
||||
product, err := h.appService.GetProductByID(c.Request.Context(), &queries.GetProductQuery{ID: productID})
|
||||
if err != nil {
|
||||
h.logger.Error("获取产品信息失败", zap.Error(err))
|
||||
h.responseBuilder.NotFound(c, "产品不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查产品编码是否存在
|
||||
if product.Code == "" {
|
||||
h.logger.Warn("产品编码为空", zap.String("product_id", productID))
|
||||
h.responseBuilder.BadRequest(c, "产品编码不存在")
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("开始生成PDF文档",
|
||||
zap.String("product_id", productID),
|
||||
zap.String("product_code", product.Code),
|
||||
zap.String("product_name", product.Name),
|
||||
)
|
||||
|
||||
// 获取产品文档信息
|
||||
doc, docErr := h.documentationAppService.GetDocumentationByProductID(c.Request.Context(), productID)
|
||||
if docErr != nil {
|
||||
h.logger.Warn("获取产品文档失败,将只生成产品基本信息",
|
||||
zap.String("product_id", productID),
|
||||
zap.Error(docErr),
|
||||
)
|
||||
}
|
||||
|
||||
// 将响应类型转换为entity类型
|
||||
var docEntity *entities.ProductDocumentation
|
||||
if doc != nil {
|
||||
docEntity = &entities.ProductDocumentation{
|
||||
ID: doc.ID,
|
||||
ProductID: doc.ProductID,
|
||||
RequestURL: doc.RequestURL,
|
||||
RequestMethod: doc.RequestMethod,
|
||||
BasicInfo: doc.BasicInfo,
|
||||
RequestParams: doc.RequestParams,
|
||||
ResponseFields: doc.ResponseFields,
|
||||
ResponseExample: doc.ResponseExample,
|
||||
ErrorCodes: doc.ErrorCodes,
|
||||
Version: doc.Version,
|
||||
}
|
||||
}
|
||||
|
||||
// 使用数据库数据生成PDF
|
||||
h.logger.Info("准备调用PDF生成器",
|
||||
zap.String("product_id", productID),
|
||||
zap.String("product_name", product.Name),
|
||||
zap.Bool("has_doc", docEntity != nil),
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
h.logger.Error("PDF生成过程中发生panic",
|
||||
zap.String("product_id", productID),
|
||||
zap.Any("panic_value", r),
|
||||
)
|
||||
// 确保在panic时也能返回响应
|
||||
if !c.Writer.Written() {
|
||||
h.responseBuilder.InternalError(c, fmt.Sprintf("生成PDF文档时发生错误: %v", r))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 直接调用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.String("product_id", productID),
|
||||
zap.Bool("has_error", genErr != nil),
|
||||
zap.Int("pdf_size", len(pdfBytes)),
|
||||
)
|
||||
|
||||
if genErr != nil {
|
||||
h.logger.Error("生成PDF文档失败",
|
||||
zap.String("product_id", productID),
|
||||
zap.String("product_code", product.Code),
|
||||
zap.Error(genErr),
|
||||
)
|
||||
h.responseBuilder.InternalError(c, fmt.Sprintf("生成PDF文档失败: %s", genErr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("PDF生成器调用完成",
|
||||
zap.String("product_id", productID),
|
||||
zap.Int("pdf_size", len(pdfBytes)),
|
||||
)
|
||||
|
||||
if len(pdfBytes) == 0 {
|
||||
h.logger.Error("生成的PDF文档为空",
|
||||
zap.String("product_id", productID),
|
||||
zap.String("product_code", product.Code),
|
||||
)
|
||||
h.responseBuilder.InternalError(c, "生成的PDF文档为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 生成文件名(清理文件名中的非法字符)
|
||||
fileName := fmt.Sprintf("%s_接口文档.pdf", product.Name)
|
||||
if product.Name == "" {
|
||||
fileName = fmt.Sprintf("%s_接口文档.pdf", product.Code)
|
||||
}
|
||||
// 清理文件名中的非法字符
|
||||
fileName = strings.ReplaceAll(fileName, "/", "_")
|
||||
fileName = strings.ReplaceAll(fileName, "\\", "_")
|
||||
fileName = strings.ReplaceAll(fileName, ":", "_")
|
||||
fileName = strings.ReplaceAll(fileName, "*", "_")
|
||||
fileName = strings.ReplaceAll(fileName, "?", "_")
|
||||
fileName = strings.ReplaceAll(fileName, "\"", "_")
|
||||
fileName = strings.ReplaceAll(fileName, "<", "_")
|
||||
fileName = strings.ReplaceAll(fileName, ">", "_")
|
||||
fileName = strings.ReplaceAll(fileName, "|", "_")
|
||||
|
||||
h.logger.Info("成功生成PDF文档",
|
||||
zap.String("product_id", productID),
|
||||
zap.String("product_code", product.Code),
|
||||
zap.String("file_name", fileName),
|
||||
zap.Int("file_size", len(pdfBytes)),
|
||||
)
|
||||
|
||||
// 设置响应头并返回PDF文件
|
||||
c.Header("Content-Type", "application/pdf")
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
|
||||
c.Header("Content-Length", fmt.Sprintf("%d", len(pdfBytes)))
|
||||
c.Data(http.StatusOK, "application/pdf", pdfBytes)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ func (r *ProductRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
products.GET("/:id", r.productHandler.GetProductDetail)
|
||||
products.GET("/:id/api-config", r.productHandler.GetProductApiConfig)
|
||||
products.GET("/:id/documentation", r.productHandler.GetProductDocumentation)
|
||||
products.GET("/:id/documentation/download", r.productHandler.DownloadProductDocumentation)
|
||||
|
||||
// 订阅产品(需要认证)
|
||||
products.POST("/:id/subscribe", r.auth.Handle(), r.productHandler.SubscribeProduct)
|
||||
|
||||
Reference in New Issue
Block a user