| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | package tianyancha | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | import ( | 
					
						
							|  |  |  |  | 	"context" | 
					
						
							|  |  |  |  | 	"encoding/json" | 
					
						
							|  |  |  |  | 	"errors" | 
					
						
							|  |  |  |  | 	"fmt" | 
					
						
							|  |  |  |  | 	"io" | 
					
						
							|  |  |  |  | 	"net/http" | 
					
						
							|  |  |  |  | 	"net/url" | 
					
						
							|  |  |  |  | 	"strings" | 
					
						
							|  |  |  |  | 	"time" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | var ( | 
					
						
							|  |  |  |  | 	ErrDatasource   = errors.New("数据源异常") | 
					
						
							|  |  |  |  | 	ErrNotFound     = errors.New("查询为空") | 
					
						
							|  |  |  |  | 	ErrSystem       = errors.New("系统异常") | 
					
						
							|  |  |  |  | 	ErrInvalidParam = errors.New("参数错误") | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // APIEndpoints 天眼查 API 端点映射 | 
					
						
							|  |  |  |  | var APIEndpoints = map[string]string{ | 
					
						
							| 
									
										
										
										
											2025-09-20 23:29:49 +08:00
										 |  |  |  | 	"VerifyThreeElements": "/open/ic/verify/2.0",              // 企业三要素验证 | 
					
						
							|  |  |  |  | 	"InvestHistory":       "/open/hi/invest/2.0",              // 对外投资历史 | 
					
						
							|  |  |  |  | 	"FinancingHistory":    "/open/cd/findHistoryRongzi/2.0",   // 融资历史 | 
					
						
							|  |  |  |  | 	"PunishmentInfo":      "/open/mr/punishmentInfo/3.0",      // 行政处罚 | 
					
						
							|  |  |  |  | 	"AbnormalInfo":        "/open/mr/abnormal/2.0",            // 经营异常 | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 	"OwnTax":              "/open/mr/ownTax",                  // 欠税公告 | 
					
						
							|  |  |  |  | 	"TaxContravention":    "/open/mr/taxContravention",        // 税收违法 | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // TianYanChaConfig 天眼查配置 | 
					
						
							|  |  |  |  | type TianYanChaConfig struct { | 
					
						
							|  |  |  |  | 	BaseURL string | 
					
						
							|  |  |  |  | 	Token   string | 
					
						
							|  |  |  |  | 	Timeout time.Duration | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // TianYanChaService 天眼查服务 | 
					
						
							|  |  |  |  | type TianYanChaService struct { | 
					
						
							|  |  |  |  | 	config TianYanChaConfig | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // APIResponse 标准API响应结构 | 
					
						
							|  |  |  |  | type APIResponse struct { | 
					
						
							|  |  |  |  | 	Success bool        `json:"success"` | 
					
						
							|  |  |  |  | 	Code    int         `json:"code"` | 
					
						
							|  |  |  |  | 	Message string      `json:"message"` | 
					
						
							|  |  |  |  | 	Data    interface{} `json:"data"` | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // TianYanChaResponse 天眼查原始响应结构 | 
					
						
							|  |  |  |  | type TianYanChaResponse struct { | 
					
						
							|  |  |  |  | 	ErrorCode int         `json:"error_code"` | 
					
						
							|  |  |  |  | 	Reason    string      `json:"reason"` | 
					
						
							|  |  |  |  | 	Result    interface{} `json:"result"` | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | // NewTianYanChaService 创建天眼查服务实例 | 
					
						
							|  |  |  |  | func NewTianYanChaService(baseURL, token string, timeout time.Duration) *TianYanChaService { | 
					
						
							|  |  |  |  | 	if timeout == 0 { | 
					
						
							|  |  |  |  | 		timeout = 30 * time.Second | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return &TianYanChaService{ | 
					
						
							|  |  |  |  | 		config: TianYanChaConfig{ | 
					
						
							|  |  |  |  | 			BaseURL: baseURL, | 
					
						
							|  |  |  |  | 			Token:   token, | 
					
						
							|  |  |  |  | 			Timeout: timeout, | 
					
						
							|  |  |  |  | 		}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // CallAPI 调用天眼查API - 通用方法,由外部处理器传入具体参数 | 
					
						
							|  |  |  |  | func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params map[string]string) (*APIResponse, error) { | 
					
						
							|  |  |  |  | 	// 从映射中获取 API 端点 | 
					
						
							|  |  |  |  | 	endpoint, exists := APIEndpoints[apiCode] | 
					
						
							|  |  |  |  | 	if !exists { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrInvalidParam, fmt.Errorf("未找到 API 代码对应的端点: %s", apiCode)) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 构建完整 URL | 
					
						
							|  |  |  |  | 	fullURL := strings.TrimRight(t.config.BaseURL, "/") + "/" + strings.TrimLeft(endpoint, "/") | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 检查 Token 是否配置 | 
					
						
							|  |  |  |  | 	if t.config.Token == "" { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrSystem, fmt.Errorf("天眼查 API Token 未配置")) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 构建查询参数 | 
					
						
							|  |  |  |  | 	queryParams := url.Values{} | 
					
						
							|  |  |  |  | 	for key, value := range params { | 
					
						
							|  |  |  |  | 		queryParams.Set(key, value) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 构建完整URL | 
					
						
							|  |  |  |  | 	requestURL := fullURL | 
					
						
							|  |  |  |  | 	if len(queryParams) > 0 { | 
					
						
							|  |  |  |  | 		requestURL += "?" + queryParams.Encode() | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 创建请求 | 
					
						
							|  |  |  |  | 	req, err := http.NewRequestWithContext(ctx, "GET", requestURL, nil) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrSystem, fmt.Errorf("创建请求失败: %v", err)) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 设置请求头 | 
					
						
							|  |  |  |  | 	req.Header.Set("Authorization", t.config.Token) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 发送请求 | 
					
						
							|  |  |  |  | 	client := &http.Client{Timeout: t.config.Timeout} | 
					
						
							|  |  |  |  | 	resp, err := client.Do(req) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求异常: %v", err)) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 	defer resp.Body.Close() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 检查 HTTP 状态码 | 
					
						
							|  |  |  |  | 	if resp.StatusCode != http.StatusOK { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求失败,状态码: %d", resp.StatusCode)) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 读取响应体 | 
					
						
							|  |  |  |  | 	body, err := io.ReadAll(resp.Body) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrSystem, fmt.Errorf("读取响应体失败: %v", err)) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 解析 JSON 响应 | 
					
						
							|  |  |  |  | 	var tianYanChaResp TianYanChaResponse | 
					
						
							|  |  |  |  | 	if err := json.Unmarshal(body, &tianYanChaResp); err != nil { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		return nil, errors.Join(ErrSystem, fmt.Errorf("解析响应 JSON 失败: %v", err)) | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 检查天眼查业务状态码 | 
					
						
							|  |  |  |  | 	if tianYanChaResp.ErrorCode != 0 { | 
					
						
							| 
									
										
										
										
											2025-09-30 12:03:51 +08:00
										 |  |  |  | 		// 特殊处理:ErrorCode 300000 表示查询为空,返回ErrNotFound | 
					
						
							|  |  |  |  | 		if tianYanChaResp.ErrorCode == 300000 { | 
					
						
							|  |  |  |  | 			return nil, errors.Join(ErrNotFound, fmt.Errorf("天眼查查询为空: %s", tianYanChaResp.Reason)) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2025-07-30 00:51:22 +08:00
										 |  |  |  | 		return &APIResponse{ | 
					
						
							|  |  |  |  | 			Success: false, | 
					
						
							|  |  |  |  | 			Code:    tianYanChaResp.ErrorCode, | 
					
						
							|  |  |  |  | 			Message: tianYanChaResp.Reason, | 
					
						
							|  |  |  |  | 			Data:    tianYanChaResp.Result, | 
					
						
							|  |  |  |  | 		}, nil | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 成功情况 | 
					
						
							|  |  |  |  | 	return &APIResponse{ | 
					
						
							|  |  |  |  | 		Success: true, | 
					
						
							|  |  |  |  | 		Code:    0, | 
					
						
							|  |  |  |  | 		Message: tianYanChaResp.Reason, | 
					
						
							|  |  |  |  | 		Data:    tianYanChaResp.Result, | 
					
						
							|  |  |  |  | 	}, nil | 
					
						
							|  |  |  |  | } |