This commit is contained in:
Mrx
2026-01-28 12:21:21 +08:00
parent 2363a51a6a
commit b1f573c230
2 changed files with 330 additions and 22 deletions

View File

@@ -15,18 +15,28 @@ import (
"tyapi-server/internal/domains/api/dto"
"tyapi-server/internal/domains/api/services/processors"
"tyapi-server/internal/shared/logger"
"github.com/tidwall/gjson"
"go.uber.org/zap"
)
// ProcessQYGL3F8ERequest QYGL3F8E API处理方法 - 人企关系加强版
func ProcessQYGL3F8ERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
// 使用 zap.L() 获取全局日志器
log := logger.L()
// 记录请求开始
log.Info("QYGL3F8E处理器开始处理请求")
var paramsDto dto.QYGL3F8EReq
if err := json.Unmarshal(params, &paramsDto); err != nil {
log.Error("QYGL3F8E参数解析失败", zap.Error(err))
return nil, errors.Join(processors.ErrSystem, err)
}
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
log.Warn("QYGL3F8E参数验证失败", zap.Error(err))
return nil, errors.Join(processors.ErrInvalidParam, err)
}
@@ -40,22 +50,30 @@ func ProcessQYGL3F8ERequest(ctx context.Context, params []byte, deps *processors
}
b4c0ParamsBytes, err := json.Marshal(b4c0Params)
if err != nil {
log.Error("QYGL3F8E构建QYGL6S1B参数失败", zap.Error(err))
return nil, errors.Join(processors.ErrSystem, err)
}
b4c0Response, err := ProcessQYGL6S1BRequest(ctx, b4c0ParamsBytes, deps)
if err != nil {
log.Error("QYGL3F8E调用QYGL6S1B失败", zap.Error(err))
return nil, err // 错误已经是处理器标准错误,直接返回
}
// 2. 解析QYGL6S1B的响应获取企业列表
companies, err := parseCompaniesFrom6S1BResponse(b4c0Response)
if err != nil {
log.Error("QYGL3F8E解析QYGL6S1B响应失败", zap.Error(err))
return nil, errors.Join(processors.ErrSystem, err)
}
log.Info("QYGL3F8E解析企业列表完成",
zap.Int("total_companies", len(companies)),
)
if len(companies) == 0 {
// 没有关联企业,返回空的简化格式
log.Info("QYGL3F8E未找到关联企业返回空结果")
emptyResponse := map[string]interface{}{
"items": []interface{}{},
"total": 0,
@@ -78,15 +96,22 @@ func ProcessQYGL3F8ERequest(ctx context.Context, params []byte, deps *processors
// 5. 并发调用其他处理器获取企业详细信息
enrichedCompanies, err := enrichCompaniesWithDetails(ctx, sortedCompanies[:processCount], userIDCard, deps)
if err != nil {
log.Error("QYGL3F8E并发获取企业详细信息失败", zap.Error(err))
return nil, errors.Join(processors.ErrSystem, err)
}
// 6. 构建最终响应
finalResponse, err := buildFinalResponse(enrichedCompanies, sortedCompanies)
if err != nil {
log.Error("QYGL3F8E构建最终响应失败", zap.Error(err))
return nil, errors.Join(processors.ErrSystem, err)
}
log.Info("QYGL3F8E处理器处理完成",
zap.Int("response_size", len(finalResponse)),
zap.Int("total_companies", len(sortedCompanies)),
)
return finalResponse, nil
}
@@ -288,6 +313,8 @@ type EnrichedCompanyInfo struct {
// enrichCompaniesWithDetails 并发调用其他处理器获取企业详细信息
func enrichCompaniesWithDetails(ctx context.Context, companies []CompanyInfo, idCard string, deps *processors.ProcessorDependencies) ([]EnrichedCompanyInfo, error) {
log := logger.L()
var wg sync.WaitGroup
results := make(chan struct {
index int
@@ -307,54 +334,210 @@ func enrichCompaniesWithDetails(ctx context.Context, companies []CompanyInfo, id
// 并发调用多个处理器
var detailWg sync.WaitGroup
var detailMu sync.Mutex
// 用于跟踪每个处理器的调用结果
type processorResult struct {
processorType string
status string // "success", "empty", "error"
err error
hasData bool
}
processorResults := make([]processorResult, 0, 7)
// 调用QYGL5A3C - 对外投资历史
detailWg.Add(1)
go func() {
defer detailWg.Done()
enriched.InvestHistory = callProcessorSafely(ctx, "QYGL5A3C", comp.CreditCode, deps)
result := callProcessorSafely(ctx, "QYGL5A3C", comp.CreditCode, deps)
enriched.InvestHistory = result
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
hasData = len(resultMap) > 0
} else if resultArray, ok := result.([]interface{}); ok {
hasData = len(resultArray) > 0
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL5A3C",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
// 调用QYGL8B4D - 融资历史
detailWg.Add(1)
go func() {
defer detailWg.Done()
enriched.FinancingHistory = callProcessorSafely(ctx, "QYGL8B4D", comp.CreditCode, deps)
result := callProcessorSafely(ctx, "QYGL8B4D", comp.CreditCode, deps)
enriched.FinancingHistory = result
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
hasData = len(resultMap) > 0
} else if resultArray, ok := result.([]interface{}); ok {
hasData = len(resultArray) > 0
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL8B4D",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
// 调用QYGL9E2F - 行政处罚
detailWg.Add(1)
go func() {
defer detailWg.Done()
enriched.PunishmentInfo = callProcessorSafely(ctx, "QYGL9E2F", comp.CreditCode, deps)
result := callProcessorSafely(ctx, "QYGL9E2F", comp.CreditCode, deps)
enriched.PunishmentInfo = result
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
hasData = len(resultMap) > 0
} else if resultArray, ok := result.([]interface{}); ok {
hasData = len(resultArray) > 0
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL9E2F",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
// 调用QYGL7C1A - 经营异常
detailWg.Add(1)
go func() {
defer detailWg.Done()
enriched.AbnormalInfo = callProcessorSafely(ctx, "QYGL7C1A", comp.CreditCode, deps)
result := callProcessorSafely(ctx, "QYGL7C1A", comp.CreditCode, deps)
enriched.AbnormalInfo = result
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
hasData = len(resultMap) > 0
} else if resultArray, ok := result.([]interface{}); ok {
hasData = len(resultArray) > 0
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL7C1A",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
// 调用QYGL66SL- 企业涉诉信息
// detailWg.Add(1)
// go func() {
// defer detailWg.Done()
// enriched.LawsuitInfo = callQYGL66SLProcessorSafely(ctx, comp.CreditCode, comp.Name, deps)
// }()
detailWg.Add(1)
go func() {
defer detailWg.Done()
result := callQYGL66SLProcessorSafely(ctx, comp.CreditCode, comp.Name, deps)
enriched.LawsuitInfo = result
// QYGL66SL返回的是特殊格式需要检查是否有数据
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
for _, v := range resultMap {
if vMap, ok := v.(map[string]interface{}); ok {
if msg, ok := vMap["msg"].(string); ok && msg == "成功" {
hasData = true
break
}
}
}
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL66SL",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
// 调用QYGL7D9A - 欠税公告
detailWg.Add(1)
go func() {
defer detailWg.Done()
enriched.OwnTax = callProcessorSafely(ctx, "QYGL7D9A", comp.CreditCode, deps)
result := callProcessorSafely(ctx, "QYGL7D9A", comp.CreditCode, deps)
enriched.OwnTax = result
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
hasData = len(resultMap) > 0
} else if resultArray, ok := result.([]interface{}); ok {
hasData = len(resultArray) > 0
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL7D9A",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
// 调用QYGL4B2E - 税收违法
detailWg.Add(1)
go func() {
defer detailWg.Done()
enriched.TaxContravention = callProcessorSafely(ctx, "QYGL4B2E", comp.CreditCode, deps)
result := callProcessorSafely(ctx, "QYGL4B2E", comp.CreditCode, deps)
enriched.TaxContravention = result
hasData := false
if resultMap, ok := result.(map[string]interface{}); ok {
hasData = len(resultMap) > 0
} else if resultArray, ok := result.([]interface{}); ok {
hasData = len(resultArray) > 0
}
detailMu.Lock()
status := "success"
if !hasData {
status = "empty"
}
processorResults = append(processorResults, processorResult{
processorType: "QYGL4B2E",
status: status,
hasData: hasData,
})
detailMu.Unlock()
}()
detailWg.Wait()
@@ -377,6 +560,10 @@ func enrichCompaniesWithDetails(ctx context.Context, companies []CompanyInfo, id
enrichedCompanies := make([]EnrichedCompanyInfo, len(companies))
for result := range results {
if result.err != nil {
log.Error("QYGL3F8E企业处理失败",
zap.Int("index", result.index),
zap.Error(result.err),
)
return nil, result.err
}
enrichedCompanies[result.index] = result.data
@@ -387,6 +574,8 @@ func enrichCompaniesWithDetails(ctx context.Context, companies []CompanyInfo, id
// callProcessorSafely 安全调用处理器(处理可能的错误)
func callProcessorSafely(ctx context.Context, processorType, entCode string, deps *processors.ProcessorDependencies) interface{} {
log := logger.L()
// 构建请求参数
params := map[string]interface{}{
"ent_code": entCode,
@@ -396,6 +585,11 @@ func callProcessorSafely(ctx context.Context, processorType, entCode string, dep
paramsBytes, err := json.Marshal(params)
if err != nil {
log.Warn("QYGL3F8E构建处理器参数失败",
zap.String("processor_type", processorType),
zap.String("ent_code", entCode),
zap.Error(err),
)
return map[string]interface{}{}
}
@@ -414,21 +608,38 @@ func callProcessorSafely(ctx context.Context, processorType, entCode string, dep
case "QYGL4B2E":
response, err = ProcessQYGL4B2ERequest(ctx, paramsBytes, deps)
default:
log.Warn("QYGL3F8E未知的处理器类型",
zap.String("processor_type", processorType),
)
return map[string]interface{}{}
}
if err != nil {
// 如果是查询为空错误,返回空对象
if errors.Is(err, processors.ErrNotFound) {
log.Debug("QYGL3F8E子处理器查询结果为空正常情况",
zap.String("processor_type", processorType),
zap.String("ent_code", entCode),
)
return map[string]interface{}{}
}
// 其他错误也返回空对象,避免影响整体流程
log.Warn("QYGL3F8E子处理器调用失败已忽略错误",
zap.String("processor_type", processorType),
zap.String("ent_code", entCode),
zap.Error(err),
)
return map[string]interface{}{}
}
// 解析响应
var result interface{}
if err := json.Unmarshal(response, &result); err != nil {
log.Warn("QYGL3F8E子处理器响应解析失败",
zap.String("processor_type", processorType),
zap.String("ent_code", entCode),
zap.Error(err),
)
return map[string]interface{}{}
}
@@ -437,25 +648,99 @@ func callProcessorSafely(ctx context.Context, processorType, entCode string, dep
// callProcessorSafely 安全调用处理器
func callQYGL66SLProcessorSafely(ctx context.Context, entCode string, entName string, deps *processors.ProcessorDependencies) interface{} {
// 使用 zap.L() 获取全局日志器,它会使用 zap.ReplaceGlobals 设置的日志器
// 这样可以确保使用配置文件中的按级别分文件设置
log := logger.L()
// 参数验证:确保必填字段不为空
if entName == "" {
log.Warn("QYGL66SL处理器调用失败企业名称为空",
zap.String("ent_code", entCode),
)
return buildEmptyQYGL66SLResponse()
}
// 使用 QYGL66SLReq 的 json 键名ent_code, ent_name, auth_date, auth_authorize_file_code
// 处理器内部会再映射为 API 的 orgName/uscc/inquiredAuth/authAuthorizeFileCode
authDate := generateAuthDateRange()
authFileCode := generateAuthAuthorizeFileCode()
params := map[string]interface{}{
"orgName": entName,
"inquiredAuth": "authed:" + generateAuthDateRange(),
"uscc": entCode,
"authAuthorizeFileCode": generateAuthAuthorizeFileCode(),
"ent_code": entCode, // 可选字段,可以为空
"ent_name": entName,
"auth_date": authDate,
"auth_authorize_file_code": authFileCode,
}
paramsBytes, err := json.Marshal(params)
if err != nil {
log.Error("QYGL66SL处理器参数序列化失败",
zap.Error(err),
zap.String("ent_code", entCode),
zap.String("ent_name", entName),
zap.String("auth_date", authDate),
zap.String("auth_authorize_file_code", authFileCode),
)
return buildEmptyQYGL66SLResponse()
}
response, err := ProcessQYGL66SLRequest(ctx, paramsBytes, deps)
if err != nil {
return buildEmptyQYGL66SLResponse()
// 检查错误类型,区分不同的失败原因并记录详细日志
if errors.Is(err, processors.ErrInvalidParam) {
log.Warn("QYGL66SL处理器参数验证失败",
zap.Error(err),
zap.String("ent_code", entCode),
zap.String("ent_name", entName),
zap.String("auth_date", authDate),
zap.String("auth_authorize_file_code", authFileCode),
zap.String("error_type", "ErrInvalidParam"),
zap.String("可能原因", "auth_date格式不符合要求或ent_name格式不正确"),
)
return buildEmptyQYGL66SLResponse()
} else if errors.Is(err, processors.ErrNotFound) {
log.Error("QYGL66SL查询结果为空正常业务情况",
zap.String("ent_code", entCode),
zap.String("ent_name", entName),
zap.String("error_type", "ErrNotFound"),
)
return buildEmptyQYGL66SLResponse()
} else if errors.Is(err, processors.ErrDatasource) {
log.Error("QYGL66SL数据源错误",
zap.Error(err),
zap.String("ent_code", entCode),
zap.String("ent_name", entName),
zap.String("error_type", "ErrDatasource"),
)
return buildEmptyQYGL66SLResponse()
} else {
log.Error("QYGL66SL处理器调用失败系统错误",
zap.Error(err),
zap.String("ent_code", entCode),
zap.String("ent_name", entName),
zap.String("auth_date", authDate),
zap.String("error_type", "ErrSystem"),
zap.String("可能原因", "网络问题、超时、API调用失败等"),
)
return buildEmptyQYGL66SLResponse()
}
}
// 解析响应
var result interface{}
if err := json.Unmarshal(response, &result); err != nil {
// 只记录前200字符的响应预览
responsePreview := string(response)
if len(responsePreview) > 200 {
responsePreview = responsePreview[:200] + "..."
}
log.Error("QYGL66SL响应解析失败",
zap.Error(err),
zap.String("ent_code", entCode),
zap.String("ent_name", entName),
zap.String("response_preview", responsePreview),
zap.String("可能原因", "API返回了非JSON格式的数据"),
)
return buildEmptyQYGL66SLResponse()
}
@@ -596,6 +881,19 @@ func cleanEntoutData(data map[string]interface{}) map[string]interface{} {
"crc": cleanNumericValue,
}
// 定义需要保留结构的字段(即使清理后为空也要保留)
structuralFields := map[string]bool{
"cases_tree": true,
"civil": true,
"criminal": true,
"administrative": true,
"bankrupt": true,
"implement": true,
"preservation": true,
"count": true,
"crc": true,
}
// 处理所有字段,确保所有字段都被正确映射
for key, value := range data {
if value == nil {
@@ -608,11 +906,11 @@ func cleanEntoutData(data map[string]interface{}) map[string]interface{} {
} else {
// 对于其他字段(如 cases_tree, civil, implement 等),使用 cleanValue 处理
cleanedValue := cleanValue(value)
// 只有当清理后的值不为空时才添加
if !isEmptyValue(cleanedValue) {
// 如果是结构字段,即使清理后为空也保留
if structuralFields[key] {
cleaned[key] = cleanedValue
} else if key == "cases_tree" {
// cases_tree 即使是空对象也保留,因为它是重要的结构字段
} else if !isEmptyValue(cleanedValue) {
// 非结构字段,只有当清理后的值不为空时才添加
cleaned[key] = cleanedValue
}
}
@@ -867,6 +1165,16 @@ func isEmptyMap(data map[string]interface{}) bool {
// hasValidEntoutData 检查 entout 是否有有效数据
func hasValidEntoutData(data map[string]interface{}) bool {
// 如果数据中有任何结构字段存在,就认为有数据(即使清理后为空)
// 这样可以确保即使所有字段都是 "-",也能返回结构化的数据
structuralFields := []string{"civil", "criminal", "administrative", "bankrupt", "implement", "preservation", "cases_tree", "count", "crc"}
for _, field := range structuralFields {
if _, exists := data[field]; exists {
return true
}
}
// 检查各个案件类型是否有有效数据
caseTypes := []string{"civil", "criminal", "administrative", "bankrupt", "implement", "preservation"}

View File

@@ -139,7 +139,7 @@ func (s *ShumaiService) CallAPIForm(ctx context.Context, apiPath string, reqForm
startTime := time.Now()
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
// 根据参数选择使用的 AppID 和 AppSecret而不是依赖全局状态
var appID, appSecret string
if useGov && s.config.AppID2 != "" {
@@ -149,7 +149,7 @@ func (s *ShumaiService) CallAPIForm(ctx context.Context, apiPath string, reqForm
appID = s.config.AppID
appSecret = s.config.AppSecret
}
// 使用指定的 AppID 生成请求ID
requestID := s.generateRequestIDWithAppID(appID)
sign := GenerateSignForm(appID, timestamp, appSecret)