Files
tyapi-server/internal/infrastructure/http/handlers/qygl_report_handler.go

225 lines
7.1 KiB
Go
Raw Normal View History

2026-03-10 17:28:04 +08:00
package handlers
import (
"encoding/json"
2026-03-11 15:21:53 +08:00
"fmt"
2026-03-10 17:28:04 +08:00
"html/template"
"net/http"
2026-03-11 15:21:53 +08:00
"net/url"
2026-03-10 17:28:04 +08:00
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"tyapi-server/internal/application/api/commands"
"tyapi-server/internal/domains/api/dto"
api_repositories "tyapi-server/internal/domains/api/repositories"
api_services "tyapi-server/internal/domains/api/services"
"tyapi-server/internal/domains/api/services/processors"
"tyapi-server/internal/domains/api/services/processors/qygl"
2026-03-11 15:21:53 +08:00
"tyapi-server/internal/shared/pdf"
2026-03-10 17:28:04 +08:00
)
// QYGLReportHandler 企业全景报告页面渲染处理器
// 使用 QYGLJ1U9 聚合接口生成企业报告数据,并通过模板引擎渲染 qiye.html
type QYGLReportHandler struct {
apiRequestService *api_services.ApiRequestService
logger *zap.Logger
2026-03-11 15:35:50 +08:00
reportRepo api_repositories.ReportRepository
pdfCacheManager *pdf.PDFCacheManager
2026-03-10 17:28:04 +08:00
}
// NewQYGLReportHandler 创建企业报告页面处理器
func NewQYGLReportHandler(
apiRequestService *api_services.ApiRequestService,
logger *zap.Logger,
reportRepo api_repositories.ReportRepository,
2026-03-11 15:35:50 +08:00
pdfCacheManager *pdf.PDFCacheManager,
2026-03-10 17:28:04 +08:00
) *QYGLReportHandler {
return &QYGLReportHandler{
apiRequestService: apiRequestService,
logger: logger,
reportRepo: reportRepo,
2026-03-11 15:35:50 +08:00
pdfCacheManager: pdfCacheManager,
2026-03-10 17:28:04 +08:00
}
}
// GetQYGLReportPage 企业全景报告页面
// GET /reports/qygl?ent_code=xxx&ent_name=yyy&ent_reg_no=zzz
func (h *QYGLReportHandler) GetQYGLReportPage(c *gin.Context) {
// 读取查询参数
entCode := c.Query("ent_code")
entName := c.Query("ent_name")
entRegNo := c.Query("ent_reg_no")
// 组装 QYGLUY3S 入参
req := dto.QYGLUY3SReq{
EntName: entName,
EntRegno: entRegNo,
EntCode: entCode,
}
params, err := json.Marshal(req)
if err != nil {
h.logger.Error("序列化企业全景报告入参失败", zap.Error(err))
c.String(http.StatusInternalServerError, "生成企业报告失败,请稍后重试")
return
}
// 通过 ApiRequestService 调用 QYGLJ1U9 聚合处理器
options := &commands.ApiCallOptions{}
callCtx := &processors.CallContext{}
ctx := c.Request.Context()
respBytes, err := h.apiRequestService.PreprocessRequestApi(ctx, "QYGLJ1U9", params, options, callCtx)
if err != nil {
h.logger.Error("调用企业全景报告处理器失败", zap.Error(err))
c.String(http.StatusInternalServerError, "生成企业报告失败,请稍后重试")
return
}
var report map[string]interface{}
if err := json.Unmarshal(respBytes, &report); err != nil {
h.logger.Error("解析企业全景报告结果失败", zap.Error(err))
c.String(http.StatusInternalServerError, "生成企业报告失败,请稍后重试")
return
}
reportJSONBytes, err := json.Marshal(report)
if err != nil {
h.logger.Error("序列化企业全景报告结果失败", zap.Error(err))
c.String(http.StatusInternalServerError, "生成企业报告失败,请稍后重试")
return
}
// 使用 template.JS 避免在脚本中被转义,直接作为 JS 对象字面量注入
reportJSON := template.JS(reportJSONBytes)
c.HTML(http.StatusOK, "qiye.html", gin.H{
"ReportJSON": reportJSON,
})
}
// GetQYGLReportPageByID 通过编号查看企业全景报告页面
// GET /reports/qygl/:id
func (h *QYGLReportHandler) GetQYGLReportPageByID(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.String(http.StatusBadRequest, "报告编号不能为空")
return
}
// 优先从数据库中查询报告记录
if h.reportRepo != nil {
if entity, err := h.reportRepo.FindByReportID(c.Request.Context(), id); err == nil && entity != nil {
reportJSON := template.JS(entity.ReportData)
c.HTML(http.StatusOK, "qiye.html", gin.H{
"ReportJSON": reportJSON,
})
return
}
}
// 回退到进程内存缓存(兼容老的访问方式)
report, ok := qygl.GetQYGLReport(id)
if !ok {
c.String(http.StatusNotFound, "报告不存在或已过期")
return
}
reportJSONBytes, err := json.Marshal(report)
if err != nil {
h.logger.Error("序列化企业全景报告结果失败", zap.Error(err))
c.String(http.StatusInternalServerError, "生成企业报告失败,请稍后再试")
return
}
reportJSON := template.JS(reportJSONBytes)
c.HTML(http.StatusOK, "qiye.html", gin.H{
"ReportJSON": reportJSON,
})
}
2026-03-11 15:21:53 +08:00
// GetQYGLReportPDFByID 通过编号导出企业全景报告 PDF基于 headless Chrome 渲染 HTML
// GET /reports/qygl/:id/pdf
func (h *QYGLReportHandler) GetQYGLReportPDFByID(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.String(http.StatusBadRequest, "报告编号不能为空")
return
}
// 可选:从数据库查一次,用于生成更友好的文件名
var fileName = "企业全景报告.pdf"
if h.reportRepo != nil {
if entity, err := h.reportRepo.FindByReportID(c.Request.Context(), id); err == nil && entity != nil {
if entity.EntName != "" {
fileName = fmt.Sprintf("%s_企业全景报告.pdf", entity.EntName)
}
}
}
2026-03-11 15:35:50 +08:00
// 先尝试从缓存中读取基于报告ID
if h.pdfCacheManager != nil {
if cachedBytes, hit, _, err := h.pdfCacheManager.GetByReportID(id); err == nil && hit && len(cachedBytes) > 0 {
h.logger.Info("企业全景报告 PDF 缓存命中",
zap.String("report_id", id),
zap.Int("pdf_size", len(cachedBytes)),
)
encodedFileName := url.QueryEscape(fileName)
c.Header("Content-Type", "application/pdf")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", encodedFileName))
c.Data(http.StatusOK, "application/pdf", cachedBytes)
return
}
}
2026-03-11 15:21:53 +08:00
// 根据当前请求推断访问协议(支持通过反向代理的 X-Forwarded-Proto
scheme := "http"
if c.Request.TLS != nil {
scheme = "https"
} else if forwardedProto := c.Request.Header.Get("X-Forwarded-Proto"); forwardedProto != "" {
scheme = forwardedProto
}
// 构建用于 headless 浏览器访问的完整报告页面 URL
reportURL := fmt.Sprintf("%s://%s/reports/qygl/%s", scheme, c.Request.Host, id)
h.logger.Info("开始生成企业全景报告 PDFheadless Chrome",
zap.String("report_id", id),
zap.String("url", reportURL),
)
pdfGen := pdf.NewHTMLPDFGenerator(h.logger)
pdfBytes, err := pdfGen.GenerateFromURL(c.Request.Context(), reportURL)
if err != nil {
h.logger.Error("生成企业全景报告 PDF 失败", zap.String("report_id", id), zap.Error(err))
c.String(http.StatusInternalServerError, "生成企业报告 PDF 失败,请稍后重试")
return
}
if len(pdfBytes) == 0 {
h.logger.Error("生成的企业全景报告 PDF 为空", zap.String("report_id", id))
c.String(http.StatusInternalServerError, "生成的企业报告 PDF 为空,请稍后重试")
return
}
2026-03-11 15:35:50 +08:00
// 将生成结果写入缓存(忽略写入错误,不影响主流程)
if h.pdfCacheManager != nil {
if err := h.pdfCacheManager.SetByReportID(id, pdfBytes); err != nil {
h.logger.Warn("保存企业全景报告 PDF 到缓存失败",
zap.String("report_id", id),
zap.Error(err),
)
}
}
2026-03-11 15:21:53 +08:00
encodedFileName := url.QueryEscape(fileName)
c.Header("Content-Type", "application/pdf")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", encodedFileName))
c.Data(http.StatusOK, "application/pdf", pdfBytes)
}