159 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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{
 | ||
| 	"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",            // 经营异常
 | ||
| 	"OwnTax":              "/open/mr/ownTax",                  // 欠税公告
 | ||
| 	"TaxContravention":    "/open/mr/taxContravention",        // 税收违法
 | ||
| }
 | ||
| 
 | ||
| // 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 {
 | ||
| 		return nil, errors.Join(ErrInvalidParam, fmt.Errorf("未找到 API 代码对应的端点: %s", apiCode))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 构建完整 URL
 | ||
| 	fullURL := strings.TrimRight(t.config.BaseURL, "/") + "/" + strings.TrimLeft(endpoint, "/")
 | ||
| 
 | ||
| 	// 检查 Token 是否配置
 | ||
| 	if t.config.Token == "" {
 | ||
| 		return nil, errors.Join(ErrSystem, fmt.Errorf("天眼查 API Token 未配置"))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 构建查询参数
 | ||
| 	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 {
 | ||
| 		return nil, errors.Join(ErrSystem, fmt.Errorf("创建请求失败: %v", err))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 设置请求头
 | ||
| 	req.Header.Set("Authorization", t.config.Token)
 | ||
| 
 | ||
| 	// 发送请求
 | ||
| 	client := &http.Client{Timeout: t.config.Timeout}
 | ||
| 	resp, err := client.Do(req)
 | ||
| 	if err != nil {
 | ||
| 		return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求异常: %v", err))
 | ||
| 	}
 | ||
| 	defer resp.Body.Close()
 | ||
| 
 | ||
| 	// 检查 HTTP 状态码
 | ||
| 	if resp.StatusCode != http.StatusOK {
 | ||
| 		return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求失败,状态码: %d", resp.StatusCode))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 读取响应体
 | ||
| 	body, err := io.ReadAll(resp.Body)
 | ||
| 	if err != nil {
 | ||
| 		return nil, errors.Join(ErrSystem, fmt.Errorf("读取响应体失败: %v", err))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 解析 JSON 响应
 | ||
| 	var tianYanChaResp TianYanChaResponse
 | ||
| 	if err := json.Unmarshal(body, &tianYanChaResp); err != nil {
 | ||
| 		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,
 | ||
| 			Message: tianYanChaResp.Reason,
 | ||
| 			Data:    tianYanChaResp.Result,
 | ||
| 		}, nil
 | ||
| 	}
 | ||
| 
 | ||
| 	// 成功情况
 | ||
| 	return &APIResponse{
 | ||
| 		Success: true,
 | ||
| 		Code:    0,
 | ||
| 		Message: tianYanChaResp.Reason,
 | ||
| 		Data:    tianYanChaResp.Result,
 | ||
| 	}, nil
 | ||
| }
 |