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
|
||
}
|