This commit is contained in:
Mrx
2026-01-29 16:51:15 +08:00
6 changed files with 116 additions and 106 deletions

View File

@@ -2,6 +2,8 @@ package pdfg
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -31,24 +33,25 @@ func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors
// 获取全局logger
zapLogger := logger.GetGlobalLogger()
// Debug记录入口参数
zapLogger.Debug("PDFG01GZ请求开始",
// Debug记录入口参数(使用 Info 级别便于线上查看)
logger.L().Info("PDFG01GZ请求开始",
zap.String("name", paramsDto.Name),
zap.String("id_card", paramsDto.IDCard),
zap.String("mobile_no", paramsDto.MobileNo),
zap.String("authorized", paramsDto.Authorized),
zap.String("auth_authorize_file_code", paramsDto.AuthAuthorizeFileCode),
)
// 从context获取config如果存在
var cacheTTL time.Duration = 24 * time.Hour
var cacheDir string
var apiDomain string
if cfg, ok := ctx.Value("config").(*config.Config); ok && cfg != nil {
cacheTTL = cfg.PDFGen.Cache.TTL
if cacheTTL == 0 {
cacheTTL = 24 * time.Hour
}
cacheDir = cfg.PDFGen.Cache.CacheDir
apiDomain = cfg.API.Domain
}
// 获取最大缓存大小
@@ -56,8 +59,8 @@ func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors
if cfg, ok := ctx.Value("config").(*config.Config); ok && cfg != nil {
maxSize = cfg.PDFGen.Cache.MaxSize
// Debug记录PDF生成服务配置
zapLogger.Debug("PDFG01GZ加载配置",
// Debug记录PDF生成服务配置(使用 Info 级别便于线上查看)
logger.L().Info("PDFG01GZ加载配置",
zap.String("env", cfg.App.Env),
zap.String("pdfgen_production_url", cfg.PDFGen.ProductionURL),
zap.String("pdfgen_development_url", cfg.PDFGen.DevelopmentURL),
@@ -93,31 +96,8 @@ func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors
pdfGenService = pdfgen.NewPDFGenService(defaultCfg, zapLogger)
}
// 检查缓存
_, hit, createdAt, err := cacheManager.Get(paramsDto.Name, paramsDto.IDCard)
if err != nil {
zapLogger.Warn("检查缓存失败继续生成PDF", zap.Error(err))
} else if hit {
// 缓存命中,模拟慢几秒
zapLogger.Info("PDF缓存命中返回缓存文件",
zap.String("name", paramsDto.Name),
zap.String("id_card", paramsDto.IDCard),
zap.Time("created_at", createdAt),
)
// 模拟慢几秒2-4秒
time.Sleep(2 * time.Second)
// 生成下载链接
downloadURL := generateDownloadURL(paramsDto.Name, paramsDto.IDCard)
return json.Marshal(map[string]interface{}{
"download_url": downloadURL,
"cached": true,
"created_at": createdAt.Format(time.RFC3339),
})
}
// 缓存未命中需要生成PDF
zapLogger.Info("PDF缓存未命中开始生成PDF",
// 直接生成PDF不检查缓存每次都重新生成
zapLogger.Info("开始生成PDF",
zap.String("name", paramsDto.Name),
zap.String("id_card", paramsDto.IDCard),
)
@@ -130,7 +110,7 @@ func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors
// 打印完整的apiData为避免日志过大这里直接序列化为JSON字符串
apiDataBytes, _ := json.Marshal(apiData)
zapLogger.Debug("PDFG01GZ数据准备完成",
logger.L().Info("PDFG01GZ数据准备完成",
zap.Int("api_data_count", len(apiData)),
zap.Int("formatted_items", len(formattedData)),
zap.ByteString("api_data", apiDataBytes),
@@ -159,11 +139,10 @@ func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors
// 调用PDF生成服务
// 即使部分子处理器失败只要有APPLICANT_BASIC_INFO就可以生成PDF
zapLogger.Debug("PDFG01GZ开始调用PDF生成服务",
logger.L().Info("PDFG01GZ开始调用PDF生成服务",
zap.String("report_number", reportNumber),
zap.Int("data_items", len(formattedData)),
)
pdfResp, err := pdfGenService.GenerateGuangzhouPDF(ctx, pdfReq)
if err != nil {
zapLogger.Error("生成PDF失败",
@@ -173,26 +152,33 @@ func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrSystem, fmt.Errorf("生成PDF失败: %w", err))
}
// 保存到缓存
if err := cacheManager.Set(paramsDto.Name, paramsDto.IDCard, pdfResp.PDFBytes); err != nil {
// 生成报告ID每次请求都生成唯一的ID
reportID := generateReportID()
// 保存到缓存基于报告ID文件名包含时间戳确保唯一性
if err := cacheManager.SetByReportID(reportID, pdfResp.PDFBytes); err != nil {
zapLogger.Warn("保存PDF到缓存失败", zap.Error(err))
// 不影响返回结果,只记录警告
}
// 生成下载链接
downloadURL := generateDownloadURL(paramsDto.Name, paramsDto.IDCard)
// 生成下载链接基于报告ID
downloadURL := generateDownloadURL(apiDomain, reportID)
expiresAt := time.Now().Add(cacheTTL)
zapLogger.Info("PDF生成成功",
zap.String("name", paramsDto.Name),
zap.String("id_card", paramsDto.IDCard),
zap.String("report_id", reportID),
zap.String("report_number", reportNumber),
zap.String("download_url", downloadURL),
)
return json.Marshal(map[string]interface{}{
"download_url": downloadURL,
"report_id": reportID,
"report_number": reportNumber,
"cached": false,
"expires_at": expiresAt.Format(time.RFC3339),
"ttl_seconds": int(cacheTTL.Seconds()),
})
}
@@ -210,35 +196,29 @@ func collectAPIData(ctx context.Context, params dto.PDFG01GZReq, deps *processor
results := make(chan processorResult, 5)
// 调用IVYZ5A9O - 需要: name, id_card, auth_authorize_file_code
// 调用JRZQ0L85 - 需要: name, id_card, mobile_no
go func() {
defer func() {
if r := recover(); r != nil {
logger.Error("调用IVYZ5A9O处理器时发生panic",
logger.Error("调用JRZQ0L85处理器时发生panic",
zap.Any("panic", r),
)
results <- processorResult{"IVYZ5A9O", nil, fmt.Errorf("处理器panic: %v", r)}
results <- processorResult{"JRZQ0L85", nil, fmt.Errorf("处理器panic: %v", r)}
}
}()
// 检查必需字段
if params.AuthAuthorizeFileCode == "" {
logger.Warn("IVYZ5A9O缺少auth_authorize_file_code字段跳过调用")
results <- processorResult{"IVYZ5A9O", nil, fmt.Errorf("缺少必需字段: auth_authorize_file_code")}
return
jrzq0l85Params := map[string]interface{}{
"name": params.Name,
"id_card": params.IDCard,
"mobile_no": params.MobileNo,
}
ivyzParams := map[string]interface{}{
"name": params.Name,
"id_card": params.IDCard,
"auth_authorize_file_code": params.AuthAuthorizeFileCode,
}
paramsBytes, err := json.Marshal(ivyzParams)
paramsBytes, err := json.Marshal(jrzq0l85Params)
if err != nil {
logger.Warn("序列化IVYZ5A9O参数失败", zap.Error(err))
results <- processorResult{"IVYZ5A9O", nil, err}
logger.Warn("序列化JRZQ0L85参数失败", zap.Error(err))
results <- processorResult{"JRZQ0L85", nil, err}
return
}
data, err := callProcessor(ctx, "IVYZ5A9O", paramsBytes, deps)
results <- processorResult{"IVYZ5A9O", data, err}
data, err := callProcessor(ctx, "JRZQ0L85", paramsBytes, deps)
results <- processorResult{"JRZQ0L85", data, err}
}()
// 调用JRZQ8A2D - 需要: name, id_card, mobile_no, authorized
@@ -412,17 +392,17 @@ func formatDataForPDF(apiData map[string]interface{}, params dto.PDFG01GZReq, lo
},
})
// 2. IVYZ5A9O - 自然人综合风险智能评估模型
if data, ok := apiData["IVYZ5A9O"]; ok && data != nil {
// 2. JRZQ0L85 - 自然人综合风险智能评估模型替代原IVYZ5A9O
if data, ok := apiData["JRZQ0L85"]; ok && data != nil {
result = append(result, map[string]interface{}{
"apiID": "IVYZ5A9O",
"apiID": "JRZQ0L85",
"data": data,
})
} else {
// 子处理器失败或无数据时,返回空对象 {}
logger.Debug("IVYZ5A9O数据缺失,使用空对象")
logger.Debug("JRZQ0L85数据缺失,使用空对象")
result = append(result, map[string]interface{}{
"apiID": "IVYZ5A9O",
"apiID": "JRZQ0L85",
"data": map[string]interface{}{},
})
}
@@ -491,10 +471,29 @@ func generateReportNumber() string {
return fmt.Sprintf("RPT%s", time.Now().Format("20060102150405"))
}
// generateDownloadURL 生成下载链接
func generateDownloadURL(name, idCard string) string {
// 这里应该生成实际的下载URL
// 暂时返回一个占位符,实际应该根据服务器配置生成
return fmt.Sprintf("/api/v1/pdfg/download?name=%s&id_card=%s", name, idCard)
// generateReportID 生成对外可见的报告ID
// 每次请求都生成唯一的ID格式{report_number}-{随机字符串}
// 注意不再包含cacheKey因为每次请求都会重新生成不需要通过ID定位缓存文件
func generateReportID() string {
reportNumber := generateReportNumber()
// 生成8字节随机字符串确保每次请求ID都不同
randomBytes := make([]byte, 8)
if _, err := rand.Read(randomBytes); err != nil {
// 如果随机数生成失败,使用纳秒时间戳作为后备
randomBytes = []byte(fmt.Sprintf("%d", time.Now().UnixNano()))
}
randomStr := hex.EncodeToString(randomBytes)
return fmt.Sprintf("%s-%s", reportNumber, randomStr)
}
// generateDownloadURL 生成下载链接基于报告ID/缓存键)
// apiDomain: 外部可访问的API域名如 api.tianyuanapi.com
func generateDownloadURL(apiDomain, reportID string) string {
if apiDomain == "" {
// 兜底:保留相对路径,方便本地/测试环境使用
return fmt.Sprintf("/api/v1/pdfg/download?id=%s", reportID)
}
// 生成完整链接例如https://api.tianyuanapi.com/api/v1/pdfg/download?id=xxx
return fmt.Sprintf("https://%s/api/v1/pdfg/download?id=%s", apiDomain, reportID)
}