diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index 6d9ef26..12ea324 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -302,6 +302,10 @@ type QYGL7C1AReq struct { PageNum int `json:"page_num" validate:"omitempty,min=1"` } +type QYGL3F8EReq struct { + IDCard string `json:"id_card" validate:"required,validIDCard"` +} + type YYSY4F2EReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` IDCard string `json:"id_card" validate:"required,validIDCard"` diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index f4a5207..ca87a3e 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -124,6 +124,9 @@ func registerAllProcessors(combService *comb.CombService) { "QYGL8B4D": qygl.ProcessQYGL8B4DRequest, // 融资历史 "QYGL9E2F": qygl.ProcessQYGL9E2FRequest, // 行政处罚 "QYGL7C1A": qygl.ProcessQYGL7C1ARequest, // 经营异常 + "QYGL3F8E": qygl.ProcessQYGL3F8ERequest, // 人企关系加强版 + "QYGL7D9A": qygl.ProcessQYGL7D9ARequest, // 欠税公告 + "QYGL4B2E": qygl.ProcessQYGL4B2ERequest, // 税收违法 "COMENT01": qygl.ProcessCOMENT01Request, // 企业风险报告 // YYSY系列处理器 diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 1344ffd..578ba53 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -92,6 +92,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(apiCode string) interface{} { "QYGL8B4D": &dto.QYGL8B4DReq{}, "QYGL9E2F": &dto.QYGL9E2FReq{}, "QYGL7C1A": &dto.QYGL7C1AReq{}, + "QYGL3F8E": &dto.QYGL3F8EReq{}, "YYSY4B37": &dto.YYSY4B37Req{}, "YYSY4B21": &dto.YYSY4B21Req{}, "YYSY6F2E": &dto.YYSY6F2EReq{}, diff --git a/internal/domains/api/services/processors/dwbg/dwbg6a2c_processor.go b/internal/domains/api/services/processors/dwbg/dwbg6a2c_processor.go index 6124f5b..8066027 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg6a2c_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg6a2c_processor.go @@ -50,6 +50,13 @@ func ProcessDWBG6A2CRequest(ctx context.Context, params []byte, deps *processors } } + // 过滤响应数据,删除指定字段 + if respMap, ok := respData.(map[string]interface{}); ok { + delete(respMap, "reportUrl") + delete(respMap, "multCourtInfo") + delete(respMap, "judiciaRiskInfos") + } + // 将响应数据转换为JSON字节 respBytes, err := json.Marshal(respData) if err != nil { diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index 2eb4ff5..edee08e 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -50,6 +50,13 @@ func ProcessDWBG8B4DRequest(ctx context.Context, params []byte, deps *processors } } + // 过滤响应数据,删除指定字段 + if respMap, ok := respData.(map[string]interface{}); ok { + delete(respMap, "reportUrl") + delete(respMap, "multCourtInfo") + delete(respMap, "judiciaRiskInfos") + } + // 将响应数据转换为JSON字节 respBytes, err := json.Marshal(respData) if err != nil { diff --git a/internal/domains/api/services/processors/qygl/error_converter.go b/internal/domains/api/services/processors/qygl/error_converter.go new file mode 100644 index 0000000..9b6d5d9 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/error_converter.go @@ -0,0 +1,28 @@ +package qygl + +import ( + "errors" + + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/tianyancha" +) + +// convertTianYanChaError 将天眼查服务的错误转换为处理器层的标准错误 +func convertTianYanChaError(err error) error { + if err == nil { + return nil + } + + // 检查天眼查服务的错误类型,转换为处理器层的标准错误 + if errors.Is(err, tianyancha.ErrNotFound) { + return errors.Join(processors.ErrNotFound, err) + } + if errors.Is(err, tianyancha.ErrDatasource) { + return errors.Join(processors.ErrDatasource, err) + } + if errors.Is(err, tianyancha.ErrInvalidParam) { + return errors.Join(processors.ErrInvalidParam, err) + } + // 默认作为系统错误处理 + return errors.Join(processors.ErrSystem, err) +} diff --git a/internal/domains/api/services/processors/qygl/qygl23t7_processor.go b/internal/domains/api/services/processors/qygl/qygl23t7_processor.go index e8c28b7..115e2c8 100644 --- a/internal/domains/api/services/processors/qygl/qygl23t7_processor.go +++ b/internal/domains/api/services/processors/qygl/qygl23t7_processor.go @@ -33,11 +33,7 @@ func ProcessQYGL23T7Request(ctx context.Context, params []byte, deps *processors // 调用天眼查API - 使用通用的CallAPI方法 response, err := deps.TianYanChaService.CallAPI(ctx, "VerifyThreeElements", apiParams) if err != nil { - if err.Error() == "数据源异常" { // Specific error handling for data source issues - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) - } + return nil, convertTianYanChaError(err) } // 检查天眼查API调用是否成功 diff --git a/internal/domains/api/services/processors/qygl/qygl3f8e_processor.go b/internal/domains/api/services/processors/qygl/qygl3f8e_processor.go new file mode 100644 index 0000000..5cf1b8b --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl3f8e_processor.go @@ -0,0 +1,542 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "sort" + "sync" + "time" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + + "github.com/tidwall/gjson" +) + +// ProcessQYGL3F8ERequest QYGL3F8E API处理方法 - 人企关系加强版 +func ProcessQYGL3F8ERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL3F8EReq + 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) + } + + // 设置最大处理企业数量 + maxProcessCount := 3 + + // 1. 首先调用QYGLB4C0获取个人关联的企业信息 + b4c0Params := dto.QYGLB4C0Req{ + IDCard: paramsDto.IDCard, + } + b4c0ParamsBytes, err := json.Marshal(b4c0Params) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + b4c0Response, err := ProcessQYGLB4C0Request(ctx, b4c0ParamsBytes, deps) + if err != nil { + return nil, err // 错误已经是处理器标准错误,直接返回 + } + + // 2. 解析QYGLB4C0的响应,获取企业列表 + companies, err := parseCompaniesFromB4C0Response(b4c0Response) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if len(companies) == 0 { + // 没有关联企业,返回空的简化格式 + emptyResponse := map[string]interface{}{ + "items": []interface{}{}, + "total": 0, + } + return json.Marshal(emptyResponse) + } + + // 3. 对企业进行优先级排序 + sortedCompanies := sortCompaniesByPriority(companies) + + // 4. 限制处理数量 + processCount := len(sortedCompanies) + if processCount > maxProcessCount { + processCount = maxProcessCount + } + + // 5. 并发调用其他处理器获取企业详细信息 + enrichedCompanies, err := enrichCompaniesWithDetails(ctx, sortedCompanies[:processCount], deps) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 6. 构建最终响应 + finalResponse, err := buildFinalResponse(b4c0Response, enrichedCompanies, sortedCompanies[:processCount]) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return finalResponse, nil +} + +// CompanyInfo 企业信息结构 +type CompanyInfo struct { + Index int + Data gjson.Result + Name string + CreditCode string + RelationshipVal int // 关系权重值 + RelationCount int // 关系数量 + AdminPenalty int // 行政处罚数量 + Executed int // 被执行人数量 + Dishonest int // 失信被执行人数量 +} + +// parseCompaniesFromB4C0Response 从QYGLB4C0响应中解析企业列表 +func parseCompaniesFromB4C0Response(response []byte) ([]CompanyInfo, error) { + // 解析响应数据 + dataResult := gjson.GetBytes(response, "data") + if !dataResult.Exists() { + return nil, fmt.Errorf("响应中缺少data字段") + } + + datalistResult := gjson.Get(dataResult.Raw, "datalist") + if !datalistResult.Exists() { + return nil, fmt.Errorf("datalist字段不存在") + } + + companiesArray := datalistResult.Array() + companies := make([]CompanyInfo, 0, len(companiesArray)) + + for i, companyJson := range companiesArray { + // 获取企业基本信息 + name := companyJson.Get("basicInfo.name").String() + creditCode := companyJson.Get("basicInfo.creditCode").String() + + if name == "" || creditCode == "" { + continue // 跳过无效企业 + } + + // 计算各种统计数据 + adminPenalty := 0 + executed := 0 + dishonest := 0 + + // 统计行政处罚 + if adminPenaltyResult := companyJson.Get("adminPenalty"); adminPenaltyResult.Exists() && adminPenaltyResult.IsArray() { + adminPenalty = len(adminPenaltyResult.Array()) + } + + // 统计被执行人 + if executedPersonResult := companyJson.Get("executedPerson"); executedPersonResult.Exists() && executedPersonResult.IsArray() { + executed = len(executedPersonResult.Array()) + } + + // 统计失信被执行人 + if dishonestResult := companyJson.Get("dishonestExecutedPerson"); dishonestResult.Exists() && dishonestResult.IsArray() { + dishonest = len(dishonestResult.Array()) + } + + // 计算关系权重 + relationshipVal, relationCount := calculateRelationshipPriority(companyJson) + + companies = append(companies, CompanyInfo{ + Index: i, + Data: companyJson, + Name: name, + CreditCode: creditCode, + RelationshipVal: relationshipVal, + RelationCount: relationCount, + AdminPenalty: adminPenalty, + Executed: executed, + Dishonest: dishonest, + }) + } + + return companies, nil +} + +// calculateRelationshipPriority 计算关系优先级 +func calculateRelationshipPriority(companyJson gjson.Result) (int, int) { + relationshipVal := 0 + relationCount := 0 + + relationshipResult := companyJson.Get("relationship") + if !relationshipResult.Exists() || !relationshipResult.IsArray() { + return relationshipVal, relationCount + } + + relationships := relationshipResult.Array() + for _, rel := range relationships { + relationCount++ + relStr := rel.String() + + // 根据关系类型设置权重:股东(6) > 历史股东(5) > 法人(4) > 历史法人(3) > 高管(2) > 历史高管(1) + switch relStr { + case "sh": // 股东 + if relationshipVal < 6 { + relationshipVal = 6 + } + case "his_sh": // 历史股东 + if relationshipVal < 5 { + relationshipVal = 5 + } + case "lp": // 法人 + if relationshipVal < 4 { + relationshipVal = 4 + } + case "his_lp": // 历史法人 + if relationshipVal < 3 { + relationshipVal = 3 + } + case "tm": // 高管 + if relationshipVal < 2 { + relationshipVal = 2 + } + case "his_tm": // 历史高管 + if relationshipVal < 1 { + relationshipVal = 1 + } + } + } + + return relationshipVal, relationCount +} + +// sortCompaniesByPriority 按优先级对企业进行排序 +func sortCompaniesByPriority(companies []CompanyInfo) []CompanyInfo { + // 创建副本进行排序 + sorted := make([]CompanyInfo, len(companies)) + copy(sorted, companies) + + sort.Slice(sorted, func(i, j int) bool { + // 首先根据失信被执行人数量排序 + if sorted[i].Dishonest != sorted[j].Dishonest { + return sorted[i].Dishonest > sorted[j].Dishonest + } + + // 然后根据被执行人数量排序 + if sorted[i].Executed != sorted[j].Executed { + return sorted[i].Executed > sorted[j].Executed + } + + // 然后根据行政处罚数量排序 + if sorted[i].AdminPenalty != sorted[j].AdminPenalty { + return sorted[i].AdminPenalty > sorted[j].AdminPenalty + } + + // 然后按关系权重排序 + if sorted[i].RelationshipVal != sorted[j].RelationshipVal { + return sorted[i].RelationshipVal > sorted[j].RelationshipVal + } + + // 最后按关系数量排序 + return sorted[i].RelationCount > sorted[j].RelationCount + }) + + return sorted +} + +// EnrichedCompanyInfo 增强的企业信息 +type EnrichedCompanyInfo struct { + CompanyInfo + InvestHistory interface{} `json:"invest_history"` // 对外投资历史 + FinancingHistory interface{} `json:"financing_history"` // 融资历史 + PunishmentInfo interface{} `json:"punishment_info"` // 行政处罚 + AbnormalInfo interface{} `json:"abnormal_info"` // 经营异常 + LawsuitInfo interface{} `json:"lawsuit_info"` // 涉诉信息 + OwnTax interface{} `json:"own_tax"` // 欠税公告 + TaxContravention interface{} `json:"tax_contravention"` // 税收违法 +} + +// enrichCompaniesWithDetails 并发调用其他处理器获取企业详细信息 +func enrichCompaniesWithDetails(ctx context.Context, companies []CompanyInfo, deps *processors.ProcessorDependencies) ([]EnrichedCompanyInfo, error) { + var wg sync.WaitGroup + results := make(chan struct { + index int + data EnrichedCompanyInfo + err error + }, len(companies)) + + // 并发处理每个企业 + for i, company := range companies { + wg.Add(1) + go func(index int, comp CompanyInfo) { + defer wg.Done() + + enriched := EnrichedCompanyInfo{ + CompanyInfo: comp, + } + + // 并发调用多个处理器 + var detailWg sync.WaitGroup + + // 调用QYGL5A3C - 对外投资历史 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.InvestHistory = callProcessorSafely(ctx, "QYGL5A3C", comp.CreditCode, deps) + }() + + // 调用QYGL8B4D - 融资历史 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.FinancingHistory = callProcessorSafely(ctx, "QYGL8B4D", comp.CreditCode, deps) + }() + + // 调用QYGL9E2F - 行政处罚 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.PunishmentInfo = callProcessorSafely(ctx, "QYGL9E2F", comp.CreditCode, deps) + }() + + // 调用QYGL7C1A - 经营异常 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.AbnormalInfo = callProcessorSafely(ctx, "QYGL7C1A", comp.CreditCode, deps) + }() + + // 调用QYGL8271 - 涉诉信息 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.LawsuitInfo = callQYGL8271Safely(ctx, comp.Name, comp.CreditCode, deps) + }() + + // 调用QYGL7D9A - 欠税公告 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.OwnTax = callProcessorSafely(ctx, "QYGL7D9A", comp.CreditCode, deps) + }() + + // 调用QYGL4B2E - 税收违法 + detailWg.Add(1) + go func() { + defer detailWg.Done() + enriched.TaxContravention = callProcessorSafely(ctx, "QYGL4B2E", comp.CreditCode, deps) + }() + + detailWg.Wait() + + results <- struct { + index int + data EnrichedCompanyInfo + err error + }{index, enriched, nil} + }(i, company) + } + + // 等待所有goroutine完成 + go func() { + wg.Wait() + close(results) + }() + + // 收集结果 + enrichedCompanies := make([]EnrichedCompanyInfo, len(companies)) + for result := range results { + if result.err != nil { + return nil, result.err + } + enrichedCompanies[result.index] = result.data + } + + return enrichedCompanies, nil +} + +// callProcessorSafely 安全调用处理器(处理可能的错误) +func callProcessorSafely(ctx context.Context, processorType, entCode string, deps *processors.ProcessorDependencies) interface{} { + // 构建请求参数 + params := map[string]interface{}{ + "ent_code": entCode, + "page_size": 20, + "page_num": 1, + } + + paramsBytes, err := json.Marshal(params) + if err != nil { + return map[string]interface{}{} + } + + var response []byte + switch processorType { + case "QYGL5A3C": + response, err = ProcessQYGL5A3CRequest(ctx, paramsBytes, deps) + case "QYGL8B4D": + response, err = ProcessQYGL8B4DRequest(ctx, paramsBytes, deps) + case "QYGL9E2F": + response, err = ProcessQYGL9E2FRequest(ctx, paramsBytes, deps) + case "QYGL7C1A": + response, err = ProcessQYGL7C1ARequest(ctx, paramsBytes, deps) + case "QYGL7D9A": + response, err = ProcessQYGL7D9ARequest(ctx, paramsBytes, deps) + case "QYGL4B2E": + response, err = ProcessQYGL4B2ERequest(ctx, paramsBytes, deps) + default: + return map[string]interface{}{} + } + + if err != nil { + // 如果是查询为空错误,返回空对象 + if errors.Is(err, processors.ErrNotFound) { + return map[string]interface{}{} + } + // 其他错误也返回空对象,避免影响整体流程 + return map[string]interface{}{} + } + + // 解析响应 + var result interface{} + if err := json.Unmarshal(response, &result); err != nil { + return map[string]interface{}{} + } + + return result +} + +// callQYGL8271Safely 安全调用QYGL8271处理器 +func callQYGL8271Safely(ctx context.Context, entName, entCode string, deps *processors.ProcessorDependencies) interface{} { + // 构建请求参数 + params := map[string]interface{}{ + "ent_name": entName, + "ent_code": entCode, + "auth_date": generateAuthDateRange(), + } + + paramsBytes, err := json.Marshal(params) + if err != nil { + return map[string]interface{}{} + } + + response, err := ProcessQYGL8271Request(ctx, paramsBytes, deps) + if err != nil { + // 如果是查询为空错误,返回空对象 + if errors.Is(err, processors.ErrNotFound) { + return map[string]interface{}{} + } + // 其他错误也返回空对象 + return map[string]interface{}{} + } + + // 解析响应 + var result interface{} + if err := json.Unmarshal(response, &result); err != nil { + return map[string]interface{}{} + } + + return result +} + +func generateAuthDateRange() string { + now := time.Now() + start := now.AddDate(0, 0, -2).Format("20060102") + end := now.AddDate(0, 0, 2).Format("20060102") + return fmt.Sprintf("%s-%s", start, end) +} + +// buildFinalResponse 构建最终响应 +func buildFinalResponse(originalResponse []byte, enrichedCompanies []EnrichedCompanyInfo, processedCompanies []CompanyInfo) ([]byte, error) { + // 解析原始响应 + var originalData map[string]interface{} + if err := json.Unmarshal(originalResponse, &originalData); err != nil { + return nil, fmt.Errorf("解析原始响应失败: %v", err) + } + + // 获取总数,默认为0 + total := 0 + var originalDatalist []interface{} + + // 安全地获取数据,防止字段不存在的情况 + if dataField, ok := originalData["data"].(map[string]interface{}); ok { + // 获取total字段 + if totalField, exists := dataField["total"]; exists { + switch t := totalField.(type) { + case float64: + total = int(t) + case int: + total = t + case string: + // 如果total是字符串,尝试转换为整数 + if parsedTotal, err := json.Number(t).Int64(); err == nil { + total = int(parsedTotal) + } + } + } + + // 获取datalist字段 + if datalist, exists := dataField["datalist"].([]interface{}); exists { + originalDatalist = datalist + } + } + + // 如果没有企业数据,返回空列表 + if len(originalDatalist) == 0 { + finalResponse := map[string]interface{}{ + "items": []interface{}{}, + "total": 0, + } + return json.Marshal(finalResponse) + } + + // 创建增强后的企业列表 + enhancedDatalist := make([]interface{}, len(originalDatalist)) + + // 创建已处理企业的映射 + processedMap := make(map[int]EnrichedCompanyInfo) + for i, enriched := range enrichedCompanies { + if i < len(processedCompanies) { + processedMap[processedCompanies[i].Index] = enriched + } + } + + // 更新企业列表 + for i, company := range originalDatalist { + if enriched, exists := processedMap[i]; exists { + // 已处理的企业,添加详细信息 + companyMap, ok := company.(map[string]interface{}) + if ok { + companyMap["invest_history"] = enriched.InvestHistory + companyMap["financing_history"] = enriched.FinancingHistory + companyMap["punishment_info"] = enriched.PunishmentInfo + companyMap["abnormal_info"] = enriched.AbnormalInfo + companyMap["lawsuit_info"] = enriched.LawsuitInfo + companyMap["own_tax"] = enriched.OwnTax + companyMap["tax_contravention"] = enriched.TaxContravention + enhancedDatalist[i] = companyMap + } else { + enhancedDatalist[i] = company + } + } else { + // 未处理的企业,添加空的详细信息 + companyMap, ok := company.(map[string]interface{}) + if ok { + companyMap["invest_history"] = map[string]interface{}{} + companyMap["financing_history"] = map[string]interface{}{} + companyMap["punishment_info"] = map[string]interface{}{} + companyMap["abnormal_info"] = map[string]interface{}{} + companyMap["lawsuit_info"] = map[string]interface{}{} + companyMap["own_tax"] = map[string]interface{}{} + companyMap["tax_contravention"] = map[string]interface{}{} + enhancedDatalist[i] = companyMap + } else { + enhancedDatalist[i] = company + } + } + } + + // 构建最终的简化响应格式 + finalResponse := map[string]interface{}{ + "items": enhancedDatalist, + "total": total, + } + + // 序列化最终结果 + return json.Marshal(finalResponse) +} diff --git a/internal/domains/api/services/processors/qygl/qygl4b2e_processor.go b/internal/domains/api/services/processors/qygl/qygl4b2e_processor.go new file mode 100644 index 0000000..16ffc93 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl4b2e_processor.go @@ -0,0 +1,59 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL4B2ERequest QYGL4B2E API处理方法 - 税收违法 +func ProcessQYGL4B2ERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL5A3CReq + 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) + } + + // 设置默认值 + pageSize := paramsDto.PageSize + if pageSize == 0 { + pageSize = 20 + } + pageNum := paramsDto.PageNum + if pageNum == 0 { + pageNum = 1 + } + + // 构建API调用参数 + apiParams := map[string]string{ + "keyword": paramsDto.EntCode, + "pageSize": strconv.Itoa(pageSize), + "pageNum": strconv.Itoa(pageNum), + } + + // 调用天眼查API - 税收违法 + response, err := deps.TianYanChaService.CallAPI(ctx, "TaxContravention", apiParams) + if err != nil { + return nil, convertTianYanChaError(err) + } + + // 检查天眼查API调用是否成功 + if !response.Success { + return nil, errors.Join(processors.ErrDatasource, errors.New(response.Message)) + } + + // 返回天眼查响应数据 + respBytes, err := json.Marshal(response.Data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go b/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go index 32fde94..712ebac 100644 --- a/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go +++ b/internal/domains/api/services/processors/qygl/qygl5a3c_processor.go @@ -41,11 +41,7 @@ func ProcessQYGL5A3CRequest(ctx context.Context, params []byte, deps *processors // 调用天眼查API - 对外投资历史 response, err := deps.TianYanChaService.CallAPI(ctx, "InvestHistory", apiParams) if err != nil { - if err.Error() == "数据源异常" { - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) - } + return nil, convertTianYanChaError(err) } // 检查天眼查API调用是否成功 diff --git a/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go b/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go index a5508a3..a29ded0 100644 --- a/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go +++ b/internal/domains/api/services/processors/qygl/qygl7c1a_processor.go @@ -41,11 +41,7 @@ func ProcessQYGL7C1ARequest(ctx context.Context, params []byte, deps *processors // 调用天眼查API - 经营异常 response, err := deps.TianYanChaService.CallAPI(ctx, "AbnormalInfo", apiParams) if err != nil { - if err.Error() == "数据源异常" { - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) - } + return nil, convertTianYanChaError(err) } // 检查天眼查API调用是否成功 diff --git a/internal/domains/api/services/processors/qygl/qygl7d9a_processor.go b/internal/domains/api/services/processors/qygl/qygl7d9a_processor.go new file mode 100644 index 0000000..f3dac60 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygl7d9a_processor.go @@ -0,0 +1,59 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" +) + +// ProcessQYGL7D9ARequest QYGL7D9A API处理方法 - 欠税公告 +func ProcessQYGL7D9ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGL5A3CReq + 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) + } + + // 设置默认值 + pageSize := paramsDto.PageSize + if pageSize == 0 { + pageSize = 20 + } + pageNum := paramsDto.PageNum + if pageNum == 0 { + pageNum = 1 + } + + // 构建API调用参数 + apiParams := map[string]string{ + "keyword": paramsDto.EntCode, + "pageSize": strconv.Itoa(pageSize), + "pageNum": strconv.Itoa(pageNum), + } + + // 调用天眼查API - 欠税公告 + response, err := deps.TianYanChaService.CallAPI(ctx, "OwnTax", apiParams) + if err != nil { + return nil, convertTianYanChaError(err) + } + + // 检查天眼查API调用是否成功 + if !response.Success { + return nil, errors.Join(processors.ErrDatasource, errors.New(response.Message)) + } + + // 返回天眼查响应数据 + respBytes, err := json.Marshal(response.Data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go b/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go index 27af31a..1bfc4be 100644 --- a/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go +++ b/internal/domains/api/services/processors/qygl/qygl8b4d_processor.go @@ -41,11 +41,7 @@ func ProcessQYGL8B4DRequest(ctx context.Context, params []byte, deps *processors // 调用天眼查API - 融资历史 response, err := deps.TianYanChaService.CallAPI(ctx, "FinancingHistory", apiParams) if err != nil { - if err.Error() == "数据源异常" { - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) - } + return nil, convertTianYanChaError(err) } // 检查天眼查API调用是否成功 diff --git a/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go b/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go index 354ebaa..8b07c0e 100644 --- a/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go +++ b/internal/domains/api/services/processors/qygl/qygl9e2f_processor.go @@ -41,11 +41,7 @@ func ProcessQYGL9E2FRequest(ctx context.Context, params []byte, deps *processors // 调用天眼查API - 行政处罚 response, err := deps.TianYanChaService.CallAPI(ctx, "PunishmentInfo", apiParams) if err != nil { - if err.Error() == "数据源异常" { - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) - } + return nil, convertTianYanChaError(err) } // 检查天眼查API调用是否成功 diff --git a/internal/domains/api/services/processors/qygl/qyglb4c0_processor.go b/internal/domains/api/services/processors/qygl/qyglb4c0_processor.go index 733409e..3eb1bbb 100644 --- a/internal/domains/api/services/processors/qygl/qyglb4c0_processor.go +++ b/internal/domains/api/services/processors/qygl/qyglb4c0_processor.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" @@ -34,18 +33,9 @@ func ProcessQYGLB4C0Request(ctx context.Context, params []byte, deps *processors if err != nil { // 数据源错误 if errors.Is(err, westdex.ErrDatasource) { - // 如果有返回内容,优先解析返回内容 - if respBytes != nil { - var westData map[string]interface{} - if err := json.Unmarshal(respBytes, &westData); err == nil { - if code, ok := westData["code"].(string); ok && code == "1404" { - return nil, fmt.Errorf("%s: %w", processors.ErrNotFound, err) - } - } - return respBytes, errors.Join(processors.ErrDatasource, err) - } - // 没有返回内容,直接返回数据源错误 return nil, errors.Join(processors.ErrDatasource, err) + }else if errors.Is(err, westdex.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) } // 其他系统错误 return nil, errors.Join(processors.ErrSystem, err) diff --git a/internal/infrastructure/external/tianyancha/tianyancha_service.go b/internal/infrastructure/external/tianyancha/tianyancha_service.go index 745bf07..406519d 100644 --- a/internal/infrastructure/external/tianyancha/tianyancha_service.go +++ b/internal/infrastructure/external/tianyancha/tianyancha_service.go @@ -26,6 +26,8 @@ var APIEndpoints = map[string]string{ "FinancingHistory": "/open/cd/findHistoryRongzi/2.0", // 融资历史 "PunishmentInfo": "/open/mr/punishmentInfo/3.0", // 行政处罚 "AbnormalInfo": "/open/mr/abnormal/2.0", // 经营异常 + "OwnTax": "/open/mr/ownTax", // 欠税公告 + "TaxContravention": "/open/mr/taxContravention", // 税收违法 } // TianYanChaConfig 天眼查配置 @@ -74,7 +76,7 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params // 从映射中获取 API 端点 endpoint, exists := APIEndpoints[apiCode] if !exists { - return nil, fmt.Errorf("%w: 未找到 API 代码对应的端点: %s", ErrInvalidParam, apiCode) + return nil, errors.Join(ErrInvalidParam, fmt.Errorf("未找到 API 代码对应的端点: %s", apiCode)) } // 构建完整 URL @@ -82,7 +84,7 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params // 检查 Token 是否配置 if t.config.Token == "" { - return nil, fmt.Errorf("%w: 天眼查 API Token 未配置", ErrSystem) + return nil, errors.Join(ErrSystem, fmt.Errorf("天眼查 API Token 未配置")) } // 构建查询参数 @@ -100,7 +102,7 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params // 创建请求 req, err := http.NewRequestWithContext(ctx, "GET", requestURL, nil) if err != nil { - return nil, fmt.Errorf("%w: 创建请求失败: %v", ErrSystem, err) + return nil, errors.Join(ErrSystem, fmt.Errorf("创建请求失败: %v", err)) } // 设置请求头 @@ -110,29 +112,34 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params client := &http.Client{Timeout: t.config.Timeout} resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("%w: API 请求异常: %v", ErrDatasource, err) + return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求异常: %v", err)) } defer resp.Body.Close() // 检查 HTTP 状态码 if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: API 请求失败,状态码: %d", ErrDatasource, resp.StatusCode) + return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求失败,状态码: %d", resp.StatusCode)) } // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("%w: 读取响应体失败: %v", ErrSystem, err) + return nil, errors.Join(ErrSystem, fmt.Errorf("读取响应体失败: %v", err)) } // 解析 JSON 响应 var tianYanChaResp TianYanChaResponse if err := json.Unmarshal(body, &tianYanChaResp); err != nil { - return nil, fmt.Errorf("%w: 解析响应 JSON 失败: %v", ErrSystem, err) + return nil, errors.Join(ErrSystem, fmt.Errorf("解析响应 JSON 失败: %v", err)) } // 检查天眼查业务状态码 if tianYanChaResp.ErrorCode != 0 { + // 特殊处理:ErrorCode 300000 表示查询为空,返回ErrNotFound + if tianYanChaResp.ErrorCode == 300000 { + return nil, errors.Join(ErrNotFound, fmt.Errorf("天眼查查询为空: %s", tianYanChaResp.Reason)) + } + return &APIResponse{ Success: false, Code: tianYanChaResp.ErrorCode, diff --git a/internal/infrastructure/external/westdex/westdex_service.go b/internal/infrastructure/external/westdex/westdex_service.go index da45f36..06b75fa 100644 --- a/internal/infrastructure/external/westdex/westdex_service.go +++ b/internal/infrastructure/external/westdex/westdex_service.go @@ -19,6 +19,7 @@ import ( var ( ErrDatasource = errors.New("数据源异常") ErrSystem = errors.New("系统异常") + ErrNotFound = errors.New("查询为空") ) type WestResp struct { @@ -321,8 +322,8 @@ func (w *WestDexService) G05HZ01CallAPI(ctx context.Context, code string, reqDat } if westDexResp.Code != "0000" { - if westDexResp.Data == nil { - err = errors.Join(ErrSystem, fmt.Errorf(westDexResp.Message)) + if westDexResp.Data == nil || westDexResp.Code == "1404" { + err = errors.Join(ErrNotFound, fmt.Errorf(westDexResp.Message)) if w.logger != nil { w.logger.LogErrorWithResponseID(requestID, transactionID, code, err, reqData, westDexResp.ID) }