f
This commit is contained in:
154
internal/infrastructure/external/pdfgen/pdfgen_service.go
vendored
Normal file
154
internal/infrastructure/external/pdfgen/pdfgen_service.go
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package pdfgen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// PDFGenService PDF生成服务客户端
|
||||
type PDFGenService struct {
|
||||
baseURL string
|
||||
apiPath string
|
||||
logger *zap.Logger
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewPDFGenService 创建PDF生成服务客户端
|
||||
func NewPDFGenService(cfg *config.Config, logger *zap.Logger) *PDFGenService {
|
||||
// 根据环境选择服务地址
|
||||
var baseURL string
|
||||
if cfg.App.IsProduction() {
|
||||
baseURL = cfg.PDFGen.ProductionURL
|
||||
} else {
|
||||
baseURL = cfg.PDFGen.DevelopmentURL
|
||||
}
|
||||
|
||||
// 如果配置为空,使用默认值
|
||||
if baseURL == "" {
|
||||
if cfg.App.IsProduction() {
|
||||
baseURL = "http://localhost:15990"
|
||||
} else {
|
||||
baseURL = "http://1.117.67.95:15990"
|
||||
}
|
||||
}
|
||||
|
||||
// 获取API路径,如果为空使用默认值
|
||||
apiPath := cfg.PDFGen.APIPath
|
||||
if apiPath == "" {
|
||||
apiPath = "/api/v1/generate/guangzhou"
|
||||
}
|
||||
|
||||
// 获取超时时间,如果为0使用默认值
|
||||
timeout := cfg.PDFGen.Timeout
|
||||
if timeout == 0 {
|
||||
timeout = 120 * time.Second
|
||||
}
|
||||
|
||||
logger.Info("PDF生成服务已初始化",
|
||||
zap.String("base_url", baseURL),
|
||||
zap.String("api_path", apiPath),
|
||||
zap.Duration("timeout", timeout),
|
||||
)
|
||||
|
||||
return &PDFGenService{
|
||||
baseURL: baseURL,
|
||||
apiPath: apiPath,
|
||||
logger: logger,
|
||||
client: &http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GeneratePDFRequest PDF生成请求
|
||||
type GeneratePDFRequest struct {
|
||||
Data []map[string]interface{} `json:"data"`
|
||||
ReportNumber string `json:"report_number,omitempty"`
|
||||
GenerateTime string `json:"generate_time,omitempty"`
|
||||
}
|
||||
|
||||
// GeneratePDFResponse PDF生成响应
|
||||
type GeneratePDFResponse struct {
|
||||
PDFBytes []byte
|
||||
FileName string
|
||||
}
|
||||
|
||||
// GenerateGuangzhouPDF 生成广州大数据租赁风险PDF报告
|
||||
func (s *PDFGenService) GenerateGuangzhouPDF(ctx context.Context, req *GeneratePDFRequest) (*GeneratePDFResponse, error) {
|
||||
// 构建请求体
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 构建请求URL
|
||||
url := fmt.Sprintf("%s%s", s.baseURL, s.apiPath)
|
||||
|
||||
// 创建HTTP请求
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// 发送请求
|
||||
s.logger.Info("开始调用PDF生成服务",
|
||||
zap.String("url", url),
|
||||
zap.Int("data_count", len(req.Data)),
|
||||
)
|
||||
|
||||
resp, err := s.client.Do(httpReq)
|
||||
if err != nil {
|
||||
s.logger.Error("调用PDF生成服务失败",
|
||||
zap.String("url", url),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("调用PDF生成服务失败: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查HTTP状态码
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// 尝试读取错误信息
|
||||
errorBody, _ := io.ReadAll(resp.Body)
|
||||
s.logger.Error("PDF生成服务返回错误",
|
||||
zap.Int("status_code", resp.StatusCode),
|
||||
zap.String("error_body", string(errorBody)),
|
||||
)
|
||||
return nil, fmt.Errorf("PDF生成失败,状态码: %d, 错误: %s", resp.StatusCode, string(errorBody))
|
||||
}
|
||||
|
||||
// 读取PDF文件
|
||||
pdfBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取PDF文件失败: %w", err)
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
fileName := "大数据租赁风险报告.pdf"
|
||||
if req.ReportNumber != "" {
|
||||
fileName = fmt.Sprintf("%s.pdf", req.ReportNumber)
|
||||
}
|
||||
|
||||
s.logger.Info("PDF生成成功",
|
||||
zap.String("file_name", fileName),
|
||||
zap.Int("file_size", len(pdfBytes)),
|
||||
)
|
||||
|
||||
return &GeneratePDFResponse{
|
||||
PDFBytes: pdfBytes,
|
||||
FileName: fileName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -338,6 +338,43 @@ func (h *CertificationHandler) ListCertifications(c *gin.Context) {
|
||||
h.response.Success(c, result, "获取认证列表成功")
|
||||
}
|
||||
|
||||
// AdminCompleteCertificationWithoutContract 管理员代用户完成认证(暂不关联合同)
|
||||
// @Summary 管理员代用户完成认证(暂不关联合同)
|
||||
// @Description 后台补充企业信息并直接完成认证,暂时不要求上传合同
|
||||
// @Tags 认证管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body commands.AdminCompleteCertificationCommand true "管理员代用户完成认证请求"
|
||||
// @Success 200 {object} responses.CertificationResponse "代用户完成认证成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 403 {object} map[string]interface{} "权限不足"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/certifications/admin/complete-without-contract [post]
|
||||
func (h *CertificationHandler) AdminCompleteCertificationWithoutContract(c *gin.Context) {
|
||||
adminID := h.getCurrentUserID(c)
|
||||
if adminID == "" {
|
||||
h.response.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
var cmd commands.AdminCompleteCertificationCommand
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
cmd.AdminID = adminID
|
||||
|
||||
result, err := h.appService.AdminCompleteCertificationWithoutContract(c.Request.Context(), &cmd)
|
||||
if err != nil {
|
||||
h.logger.Error("管理员代用户完成认证失败", zap.Error(err), zap.String("admin_id", adminID), zap.String("user_id", cmd.UserID))
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result, "代用户完成认证成功")
|
||||
}
|
||||
|
||||
// ================ 回调处理 ================
|
||||
|
||||
// HandleEsignCallback 处理e签宝回调
|
||||
|
||||
93
internal/infrastructure/http/handlers/pdfg_handler.go
Normal file
93
internal/infrastructure/http/handlers/pdfg_handler.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
"tyapi-server/internal/shared/pdf"
|
||||
)
|
||||
|
||||
// PDFGHandler PDFG处理器
|
||||
type PDFGHandler struct {
|
||||
cacheManager *pdf.PDFCacheManager
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewPDFGHandler 创建PDFG处理器
|
||||
func NewPDFGHandler(
|
||||
cacheManager *pdf.PDFCacheManager,
|
||||
responseBuilder interfaces.ResponseBuilder,
|
||||
logger *zap.Logger,
|
||||
) *PDFGHandler {
|
||||
return &PDFGHandler{
|
||||
cacheManager: cacheManager,
|
||||
responseBuilder: responseBuilder,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadPDF 下载PDF文件
|
||||
// GET /api/v1/pdfg/download?name=xxx&id_card=xxx
|
||||
func (h *PDFGHandler) DownloadPDF(c *gin.Context) {
|
||||
name := c.Query("name")
|
||||
idCard := c.Query("id_card")
|
||||
|
||||
if name == "" || idCard == "" {
|
||||
h.responseBuilder.BadRequest(c, "姓名和身份证号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 从缓存获取PDF
|
||||
pdfBytes, hit, createdAt, err := h.cacheManager.Get(name, idCard)
|
||||
if err != nil {
|
||||
h.logger.Error("获取PDF缓存失败",
|
||||
zap.String("name", name),
|
||||
zap.String("id_card", idCard),
|
||||
zap.Error(err),
|
||||
)
|
||||
h.responseBuilder.InternalError(c, "获取PDF文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
if !hit {
|
||||
h.logger.Warn("PDF文件不存在或已过期",
|
||||
zap.String("name", name),
|
||||
zap.String("id_card", idCard),
|
||||
)
|
||||
h.responseBuilder.NotFound(c, "PDF文件不存在或已过期,请重新生成")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否过期(从文件生成时间开始算24小时)
|
||||
expiresAt := createdAt.Add(24 * time.Hour)
|
||||
if time.Now().After(expiresAt) {
|
||||
h.logger.Warn("PDF文件已过期",
|
||||
zap.String("name", name),
|
||||
zap.String("id_card", idCard),
|
||||
zap.Time("expires_at", expiresAt),
|
||||
)
|
||||
h.responseBuilder.NotFound(c, "PDF文件已过期,请重新生成")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
c.Header("Content-Type", "application/pdf")
|
||||
c.Header("Content-Disposition", `attachment; filename="大数据租赁风险报告.pdf"`)
|
||||
c.Header("Content-Length", fmt.Sprintf("%d", len(pdfBytes)))
|
||||
|
||||
// 发送PDF文件
|
||||
c.Data(http.StatusOK, "application/pdf", pdfBytes)
|
||||
|
||||
h.logger.Info("PDF文件下载成功",
|
||||
zap.String("name", name),
|
||||
zap.String("id_card", idCard),
|
||||
zap.Int("file_size", len(pdfBytes)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -826,7 +826,7 @@ func (h *ProductHandler) DownloadProductDocumentation(c *gin.Context) {
|
||||
|
||||
if h.pdfCacheManager != nil {
|
||||
var cacheErr error
|
||||
pdfBytes, cacheHit, cacheErr = h.pdfCacheManager.Get(productID, docVersion)
|
||||
pdfBytes, cacheHit, cacheErr = h.pdfCacheManager.GetByProduct(productID, docVersion)
|
||||
if cacheErr != nil {
|
||||
h.logger.Warn("从缓存获取PDF失败,将重新生成",
|
||||
zap.String("product_id", productID),
|
||||
@@ -972,7 +972,7 @@ func (h *ProductHandler) DownloadProductDocumentation(c *gin.Context) {
|
||||
// 保存到缓存(异步,不阻塞响应)
|
||||
if h.pdfCacheManager != nil {
|
||||
go func() {
|
||||
if err := h.pdfCacheManager.Set(productID, docVersion, pdfBytes); err != nil {
|
||||
if err := h.pdfCacheManager.SetByProduct(productID, docVersion, pdfBytes); err != nil {
|
||||
h.logger.Warn("保存PDF到缓存失败",
|
||||
zap.String("product_id", productID),
|
||||
zap.String("version", docVersion),
|
||||
|
||||
@@ -66,6 +66,9 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
|
||||
// 前端确认是否完成签署
|
||||
authGroup.POST("/confirm-sign", r.handler.ConfirmSign)
|
||||
|
||||
// 管理员代用户完成认证(暂不关联合同)
|
||||
authGroup.POST("/admin/complete-without-contract", r.handler.AdminCompleteCertificationWithoutContract)
|
||||
|
||||
}
|
||||
|
||||
// 回调路由(不需要认证,但需要验证签名)
|
||||
|
||||
39
internal/infrastructure/http/routes/pdfg_routes.go
Normal file
39
internal/infrastructure/http/routes/pdfg_routes.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
sharedhttp "tyapi-server/internal/shared/http"
|
||||
"tyapi-server/internal/infrastructure/http/handlers"
|
||||
)
|
||||
|
||||
// PDFGRoutes PDFG路由
|
||||
type PDFGRoutes struct {
|
||||
pdfgHandler *handlers.PDFGHandler
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewPDFGRoutes 创建PDFG路由
|
||||
func NewPDFGRoutes(
|
||||
pdfgHandler *handlers.PDFGHandler,
|
||||
logger *zap.Logger,
|
||||
) *PDFGRoutes {
|
||||
return &PDFGRoutes{
|
||||
pdfgHandler: pdfgHandler,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Register 注册相关路由
|
||||
func (r *PDFGRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
engine := router.GetEngine()
|
||||
apiGroup := engine.Group("/api/v1")
|
||||
|
||||
{
|
||||
// PDF下载接口 - 不需要认证(因为下载链接已经包含了验证信息)
|
||||
apiGroup.GET("/pdfg/download", r.pdfgHandler.DownloadPDF)
|
||||
}
|
||||
|
||||
r.logger.Info("PDFG路由注册完成")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user