f
This commit is contained in:
@@ -0,0 +1,466 @@
|
||||
package pdfg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/config"
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/pdfgen"
|
||||
"tyapi-server/internal/shared/logger"
|
||||
"tyapi-server/internal/shared/pdf"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ProcessPDFG01GZRequest PDFG01GZ 处理器 - 大数据租赁风险PDF报告
|
||||
func ProcessPDFG01GZRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.PDFG01GZReq
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 获取全局logger
|
||||
zapLogger := logger.GetGlobalLogger()
|
||||
|
||||
// 从context获取config(如果存在)
|
||||
var cacheTTL time.Duration = 24 * time.Hour
|
||||
var cacheDir 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
|
||||
}
|
||||
|
||||
// 获取最大缓存大小
|
||||
var maxSize int64
|
||||
if cfg, ok := ctx.Value("config").(*config.Config); ok && cfg != nil {
|
||||
maxSize = cfg.PDFGen.Cache.MaxSize
|
||||
}
|
||||
|
||||
// 创建PDF缓存管理器
|
||||
cacheManager, err := pdf.NewPDFCacheManager(zapLogger, cacheDir, cacheTTL, maxSize)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, fmt.Errorf("创建PDF缓存管理器失败: %w", err))
|
||||
}
|
||||
|
||||
// 从context获取config创建PDF生成服务
|
||||
var pdfGenService *pdfgen.PDFGenService
|
||||
if cfg, ok := ctx.Value("config").(*config.Config); ok && cfg != nil {
|
||||
pdfGenService = pdfgen.NewPDFGenService(cfg, zapLogger)
|
||||
} else {
|
||||
// 如果无法获取config,使用默认配置
|
||||
defaultCfg := &config.Config{
|
||||
App: config.AppConfig{Env: "development"},
|
||||
PDFGen: config.PDFGenConfig{
|
||||
DevelopmentURL: "http://pdfg.tianyuanapi.com",
|
||||
ProductionURL: "http://localhost:15990",
|
||||
APIPath: "/api/v1/generate/guangzhou",
|
||||
Timeout: 120 * time.Second,
|
||||
},
|
||||
}
|
||||
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",
|
||||
zap.String("name", paramsDto.Name),
|
||||
zap.String("id_card", paramsDto.IDCard),
|
||||
)
|
||||
|
||||
// 调用多个处理器获取数据(即使部分失败也继续)
|
||||
apiData := collectAPIData(ctx, paramsDto, deps, zapLogger)
|
||||
|
||||
// 格式化数据为PDF生成服务需要的格式(为缺失的数据提供默认值)
|
||||
formattedData := formatDataForPDF(apiData, paramsDto, zapLogger)
|
||||
|
||||
// 从APPLICANT_BASIC_INFO中提取报告编号(如果存在)
|
||||
var reportNumber string
|
||||
if len(formattedData) > 0 {
|
||||
if basicInfo, ok := formattedData[0]["data"].(map[string]interface{}); ok {
|
||||
if rn, ok := basicInfo["report_number"].(string); ok {
|
||||
reportNumber = rn
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果没有提取到,生成新的报告编号
|
||||
if reportNumber == "" {
|
||||
reportNumber = generateReportNumber()
|
||||
}
|
||||
|
||||
// 构建PDF生成请求
|
||||
pdfReq := &pdfgen.GeneratePDFRequest{
|
||||
Data: formattedData,
|
||||
ReportNumber: reportNumber,
|
||||
GenerateTime: time.Now().Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
// 调用PDF生成服务
|
||||
// 即使部分子处理器失败,只要有APPLICANT_BASIC_INFO就可以生成PDF
|
||||
pdfResp, err := pdfGenService.GenerateGuangzhouPDF(ctx, pdfReq)
|
||||
if err != nil {
|
||||
zapLogger.Error("生成PDF失败",
|
||||
zap.Error(err),
|
||||
zap.Int("data_items", len(formattedData)),
|
||||
)
|
||||
return nil, errors.Join(processors.ErrSystem, fmt.Errorf("生成PDF失败: %w", err))
|
||||
}
|
||||
|
||||
// 保存到缓存
|
||||
if err := cacheManager.Set(paramsDto.Name, paramsDto.IDCard, pdfResp.PDFBytes); err != nil {
|
||||
zapLogger.Warn("保存PDF到缓存失败", zap.Error(err))
|
||||
// 不影响返回结果,只记录警告
|
||||
}
|
||||
|
||||
// 生成下载链接
|
||||
downloadURL := generateDownloadURL(paramsDto.Name, paramsDto.IDCard)
|
||||
|
||||
zapLogger.Info("PDF生成成功",
|
||||
zap.String("name", paramsDto.Name),
|
||||
zap.String("id_card", paramsDto.IDCard),
|
||||
zap.String("report_number", reportNumber),
|
||||
zap.String("download_url", downloadURL),
|
||||
)
|
||||
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"download_url": downloadURL,
|
||||
"report_number": reportNumber,
|
||||
"cached": false,
|
||||
})
|
||||
}
|
||||
|
||||
// collectAPIData 收集所有需要的API数据
|
||||
// 即使部分或全部子处理器失败,也会返回结果(失败的设为nil),确保流程继续
|
||||
func collectAPIData(ctx context.Context, params dto.PDFG01GZReq, deps *processors.ProcessorDependencies, logger *zap.Logger) map[string]interface{} {
|
||||
apiData := make(map[string]interface{})
|
||||
|
||||
// 并发调用多个处理器
|
||||
type processorResult struct {
|
||||
apiCode string
|
||||
data interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
results := make(chan processorResult, 5)
|
||||
|
||||
// 调用IVYZ5A9O - 需要: name, id_card, auth_authorize_file_code
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("调用IVYZ5A9O处理器时发生panic",
|
||||
zap.Any("panic", r),
|
||||
)
|
||||
results <- processorResult{"IVYZ5A9O", 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
|
||||
}
|
||||
ivyzParams := map[string]interface{}{
|
||||
"name": params.Name,
|
||||
"id_card": params.IDCard,
|
||||
"auth_authorize_file_code": params.AuthAuthorizeFileCode,
|
||||
}
|
||||
paramsBytes, err := json.Marshal(ivyzParams)
|
||||
if err != nil {
|
||||
logger.Warn("序列化IVYZ5A9O参数失败", zap.Error(err))
|
||||
results <- processorResult{"IVYZ5A9O", nil, err}
|
||||
return
|
||||
}
|
||||
data, err := callProcessor(ctx, "IVYZ5A9O", paramsBytes, deps)
|
||||
results <- processorResult{"IVYZ5A9O", data, err}
|
||||
}()
|
||||
|
||||
// 调用JRZQ8A2D - 需要: name, id_card, mobile_no, authorized
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("调用JRZQ8A2D处理器时发生panic",
|
||||
zap.Any("panic", r),
|
||||
)
|
||||
results <- processorResult{"JRZQ8A2D", nil, fmt.Errorf("处理器panic: %v", r)}
|
||||
}
|
||||
}()
|
||||
jrzq8a2dParams := map[string]interface{}{
|
||||
"name": params.Name,
|
||||
"id_card": params.IDCard,
|
||||
"mobile_no": params.MobileNo,
|
||||
"authorized": params.Authorized,
|
||||
}
|
||||
paramsBytes, err := json.Marshal(jrzq8a2dParams)
|
||||
if err != nil {
|
||||
logger.Warn("序列化JRZQ8A2D参数失败", zap.Error(err))
|
||||
results <- processorResult{"JRZQ8A2D", nil, err}
|
||||
return
|
||||
}
|
||||
data, err := callProcessor(ctx, "JRZQ8A2D", paramsBytes, deps)
|
||||
results <- processorResult{"JRZQ8A2D", data, err}
|
||||
}()
|
||||
|
||||
// 调用FLXGDEA9 - 需要: name, id_card, authorized
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("调用FLXGDEA9处理器时发生panic",
|
||||
zap.Any("panic", r),
|
||||
)
|
||||
results <- processorResult{"FLXGDEA9", nil, fmt.Errorf("处理器panic: %v", r)}
|
||||
}
|
||||
}()
|
||||
flxgParams := map[string]interface{}{
|
||||
"name": params.Name,
|
||||
"id_card": params.IDCard,
|
||||
"authorized": params.Authorized,
|
||||
}
|
||||
paramsBytes, err := json.Marshal(flxgParams)
|
||||
if err != nil {
|
||||
logger.Warn("序列化FLXGDEA9参数失败", zap.Error(err))
|
||||
results <- processorResult{"FLXGDEA9", nil, err}
|
||||
return
|
||||
}
|
||||
data, err := callProcessor(ctx, "FLXGDEA9", paramsBytes, deps)
|
||||
results <- processorResult{"FLXGDEA9", data, err}
|
||||
}()
|
||||
|
||||
// 调用JRZQ1D09 - 需要: name, id_card, mobile_no, authorized
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("调用JRZQ1D09处理器时发生panic",
|
||||
zap.Any("panic", r),
|
||||
)
|
||||
results <- processorResult{"JRZQ1D09", nil, fmt.Errorf("处理器panic: %v", r)}
|
||||
}
|
||||
}()
|
||||
jrzq1d09Params := map[string]interface{}{
|
||||
"name": params.Name,
|
||||
"id_card": params.IDCard,
|
||||
"mobile_no": params.MobileNo,
|
||||
"authorized": params.Authorized,
|
||||
}
|
||||
paramsBytes, err := json.Marshal(jrzq1d09Params)
|
||||
if err != nil {
|
||||
logger.Warn("序列化JRZQ1D09参数失败", zap.Error(err))
|
||||
results <- processorResult{"JRZQ1D09", nil, err}
|
||||
return
|
||||
}
|
||||
data, err := callProcessor(ctx, "JRZQ1D09", paramsBytes, deps)
|
||||
results <- processorResult{"JRZQ1D09", data, err}
|
||||
}()
|
||||
|
||||
// 调用JRZQ8B3C - 需要: name, id_card, mobile_no (不需要authorized)
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("调用JRZQ8B3C处理器时发生panic",
|
||||
zap.Any("panic", r),
|
||||
)
|
||||
results <- processorResult{"JRZQ8B3C", nil, fmt.Errorf("处理器panic: %v", r)}
|
||||
}
|
||||
}()
|
||||
jrzq8b3cParams := map[string]interface{}{
|
||||
"name": params.Name,
|
||||
"id_card": params.IDCard,
|
||||
"mobile_no": params.MobileNo,
|
||||
}
|
||||
paramsBytes, err := json.Marshal(jrzq8b3cParams)
|
||||
if err != nil {
|
||||
logger.Warn("序列化JRZQ8B3C参数失败", zap.Error(err))
|
||||
results <- processorResult{"JRZQ8B3C", nil, err}
|
||||
return
|
||||
}
|
||||
data, err := callProcessor(ctx, "JRZQ8B3C", paramsBytes, deps)
|
||||
results <- processorResult{"JRZQ8B3C", data, err}
|
||||
}()
|
||||
|
||||
// 收集结果,即使所有处理器都失败也继续
|
||||
successCount := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
result := <-results
|
||||
if result.err != nil {
|
||||
// 记录错误但不中断流程,允许部分数据缺失
|
||||
logger.Warn("调用处理器失败,将使用默认值",
|
||||
zap.String("api_code", result.apiCode),
|
||||
zap.Error(result.err),
|
||||
)
|
||||
apiData[result.apiCode] = nil
|
||||
} else {
|
||||
apiData[result.apiCode] = result.data
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("子处理器调用完成",
|
||||
zap.Int("total", 5),
|
||||
zap.Int("success", successCount),
|
||||
zap.Int("failed", 5-successCount),
|
||||
)
|
||||
|
||||
return apiData
|
||||
}
|
||||
|
||||
// callProcessor 调用指定的处理器
|
||||
func callProcessor(ctx context.Context, apiCode string, params []byte, deps *processors.ProcessorDependencies) (interface{}, error) {
|
||||
// 通过CombService获取处理器
|
||||
if combSvc, ok := deps.CombService.(interface {
|
||||
GetProcessor(apiCode string) (processors.ProcessorFunc, bool)
|
||||
}); ok {
|
||||
processor, exists := combSvc.GetProcessor(apiCode)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("未找到处理器: %s", apiCode)
|
||||
}
|
||||
respBytes, err := processor(ctx, params, deps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data interface{}
|
||||
if err := json.Unmarshal(respBytes, &data); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// 如果无法通过CombService获取,返回错误
|
||||
return nil, fmt.Errorf("无法获取处理器: %s,CombService不支持GetProcessor方法", apiCode)
|
||||
}
|
||||
|
||||
// formatDataForPDF 格式化数据为PDF生成服务需要的格式
|
||||
// 为所有子处理器提供数据,即使失败也提供默认值,确保PDF生成服务能收到完整结构
|
||||
func formatDataForPDF(apiData map[string]interface{}, params dto.PDFG01GZReq, logger *zap.Logger) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0)
|
||||
|
||||
// 1. APPLICANT_BASIC_INFO - 申请人基本信息(始终存在)
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "APPLICANT_BASIC_INFO",
|
||||
"data": map[string]interface{}{
|
||||
"name": params.Name,
|
||||
"id_card": params.IDCard,
|
||||
"mobile": params.MobileNo,
|
||||
"query_time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
"report_number": generateReportNumber(),
|
||||
"generate_time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
})
|
||||
|
||||
// 2. IVYZ5A9O - 自然人综合风险智能评估模型
|
||||
if data, ok := apiData["IVYZ5A9O"]; ok && data != nil {
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "IVYZ5A9O",
|
||||
"data": data,
|
||||
})
|
||||
} else {
|
||||
// 子处理器失败或无数据时,返回空对象 {}
|
||||
logger.Debug("IVYZ5A9O数据缺失,使用空对象")
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "IVYZ5A9O",
|
||||
"data": map[string]interface{}{},
|
||||
})
|
||||
}
|
||||
|
||||
// 3. JRZQ8A2D - 特殊名单验证B
|
||||
if data, ok := apiData["JRZQ8A2D"]; ok && data != nil {
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "JRZQ8A2D",
|
||||
"data": data,
|
||||
})
|
||||
} else {
|
||||
logger.Debug("JRZQ8A2D数据缺失,使用空对象")
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "JRZQ8A2D",
|
||||
"data": map[string]interface{}{},
|
||||
})
|
||||
}
|
||||
|
||||
// 4. FLXGDEA9 - 公安不良人员名单
|
||||
if data, ok := apiData["FLXGDEA9"]; ok && data != nil {
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "FLXGDEA9",
|
||||
"data": data,
|
||||
})
|
||||
} else {
|
||||
logger.Debug("FLXGDEA9数据缺失,使用空对象")
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "FLXGDEA9",
|
||||
"data": map[string]interface{}{},
|
||||
})
|
||||
}
|
||||
|
||||
// 5. JRZQ1D09 - 3C租赁申请意向
|
||||
if data, ok := apiData["JRZQ1D09"]; ok && data != nil {
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "JRZQ1D09",
|
||||
"data": data,
|
||||
})
|
||||
} else {
|
||||
logger.Debug("JRZQ1D09数据缺失,使用空对象")
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "JRZQ1D09",
|
||||
"data": map[string]interface{}{},
|
||||
})
|
||||
}
|
||||
|
||||
// 6. JRZQ8B3C - 个人消费能力等级
|
||||
if data, ok := apiData["JRZQ8B3C"]; ok && data != nil {
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "JRZQ8B3C",
|
||||
"data": data,
|
||||
})
|
||||
} else {
|
||||
logger.Debug("JRZQ8B3C数据缺失,使用空对象")
|
||||
result = append(result, map[string]interface{}{
|
||||
"apiID": "JRZQ8B3C",
|
||||
"data": map[string]interface{}{},
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// generateReportNumber 生成报告编号
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user