feat: add toolbox query, upload module, update config and gitignore

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Mrx
2026-05-21 17:29:35 +08:00
parent 0a0ca7bf9b
commit 144d5507dd
22 changed files with 11184 additions and 8 deletions

View File

@@ -2,12 +2,14 @@ package service
import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"qnc-server/app/main/api/internal/config"
tianyuanapi "qnc-server/app/main/api/internal/service/tianyuanapi_sdk"
"qnc-server/app/main/model"
"qnc-server/pkg/lzkit/crypto"
"sort"
"strings"
"sync"
@@ -75,7 +77,8 @@ type APIResponseData struct {
}
// ProcessRequests 处理请求
func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]byte, error) {
// orderNo: 当前查询订单号,供异步车辆类接口生成 return_url 回调地址
func (a *ApiRequestService) ProcessRequests(params []byte, productID string, orderNo string) ([]byte, error) {
var ctx, cancel = context.WithCancel(context.Background())
defer cancel()
@@ -112,6 +115,25 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
if len(featureList) == 0 {
return nil, errors.New("处理请求错误,产品无对应接口功能")
}
// 在原始 params 上附加 order_no供异步车辆类接口自动生成回调地址
var baseParams map[string]interface{}
if err := json.Unmarshal(params, &baseParams); err != nil {
logx.Errorf("解析查询参数失败, Params: %s, Error: %v", string(params), err)
return nil, fmt.Errorf("解析查询参数失败: %w", err)
}
if mobile, exists := baseParams["mobile"]; exists {
baseParams["mobile_no"] = mobile
}
if orderNo != "" {
baseParams["order_no"] = orderNo
}
paramsWithOrder, err := json.Marshal(baseParams)
if err != nil {
logx.Errorf("序列化查询参数失败, Params: %s, Error: %v", string(params), err)
return nil, fmt.Errorf("序列化查询参数失败: %w", err)
}
var (
wg sync.WaitGroup
resultsCh = make(chan APIResponseData, len(featureList))
@@ -152,7 +174,7 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
tryCount := 0
for {
tryCount++
resp, preprocessErr = a.PreprocessRequestApi(params, feature.ApiId)
resp, preprocessErr = a.PreprocessRequestApi(paramsWithOrder, feature.ApiId)
if preprocessErr == nil {
break
}
@@ -224,7 +246,21 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
"QYGL6F2D": (*ApiRequestService).ProcessQYGL6F2DRequest,
"JRZQ8203": (*ApiRequestService).ProcessJRZQ8203Request,
"JRZQ4AA8": (*ApiRequestService).ProcessJRZQ4AA8Request,
"QCXGGB2Q": (*ApiRequestService).ProcessQCXGGB2QRequest,
"QCXGYTS2": (*ApiRequestService).ProcessQCXGYTS2Request,
"QCXG5F3A": (*ApiRequestService).ProcessQCXG5F3ARequest,
"QCXG7A2B": (*ApiRequestService).ProcessQCXG7A2BRequest,
"QCXG9P1C": (*ApiRequestService).ProcessQCXG9P1CFRequest,
"QCXG4D2E": (*ApiRequestService).ProcessQCXG4D2ERequest,
"QCXG5U0Z": (*ApiRequestService).ProcessQCXG5U0ZRequest,
"QCXG1U4U": (*ApiRequestService).ProcessQCXG1U4URequest,
"QCXGY7F2": (*ApiRequestService).ProcessQCXGY7F2Request,
"QCXG1H7Y": (*ApiRequestService).ProcessQCXG1H7YRequest,
"QCXG4I1Z": (*ApiRequestService).ProcessQCXG4I1ZRequest,
"QCXG3Y6B": (*ApiRequestService).ProcessQCXG3Y6BRequest,
"QCXG3Z3L": (*ApiRequestService).ProcessQCXG3Z3LRequest,
"QCXGP00W": (*ApiRequestService).ProcessQCXGP00WRequest,
"QCXG6B4E": (*ApiRequestService).ProcessQCXG6B4ERequest,
"DWBG8B4D": (*ApiRequestService).ProcessDWBG8B4DRequest,
"DWBG6A2C": (*ApiRequestService).ProcessDWBG6A2CRequest,
"JRZQ4B6C": (*ApiRequestService).ProcessJRZQ4B6CRequest,
@@ -1126,24 +1162,290 @@ func (a *ApiRequestService) ProcessQYGL6F2DRequest(params []byte) ([]byte, error
return nil, fmt.Errorf("响应code错误%s", code.String())
}
// ProcessQCXGGB2QRequest 人车核验简版
func (a *ApiRequestService) ProcessQCXGGB2QRequest(params []byte) ([]byte, error) {
plateNo := gjson.GetBytes(params, "plate_no")
carplateType := gjson.GetBytes(params, "carplate_type")
name := gjson.GetBytes(params, "name")
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
return nil, errors.New("api请求, QCXGGB2Q, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXGGB2Q", map[string]interface{}{
"plate_no": plateNo.String(),
"carplate_type": carplateType.String(),
"name": name.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// ProcessQCXGYTS2Request 人车核验详版
func (a *ApiRequestService) ProcessQCXGYTS2Request(params []byte) ([]byte, error) {
plateNo := gjson.GetBytes(params, "plate_no")
carplateType := gjson.GetBytes(params, "carplate_type")
name := gjson.GetBytes(params, "name")
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
return nil, errors.New("api请求, QCXGYTS2, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXGYTS2", map[string]interface{}{
"plate_no": plateNo.String(),
"carplate_type": carplateType.String(),
"name": name.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// ProcessQCXG5F3ARequest 名下车辆(车牌)
func (a *ApiRequestService) ProcessQCXG5F3ARequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
if !idCard.Exists() || !name.Exists() {
return nil, errors.New("api请求, QCXG5F3A, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXG5F3A", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// ProcessQCXG7A2BRequest 名下车辆
func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
if !idCard.Exists() {
return nil, errors.New("api请求, QCXG7A2B, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXG7A2B", map[string]interface{}{
"id_card": idCard.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// ProcessQCXG9P1CFRequest 名下车辆车牌查询 A
func (a *ApiRequestService) ProcessQCXG9P1CFRequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
mobile := gjson.GetBytes(params, "mobile")
if !idCard.Exists() || !name.Exists() || !mobile.Exists() {
return nil, errors.New("api请求, QCXG9P1C, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("QCXG9P1C", map[string]interface{}{
"id_card": idCard.String(),
"authorized": "1",
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG4D2ERequest(params []byte) ([]byte, error) {
m := map[string]interface{}{}
if err := json.Unmarshal(params, &m); err != nil {
return nil, fmt.Errorf("api请求, QCXG4D2E, 解析参数失败: %w", err)
}
body := map[string]interface{}{}
if v, ok := m["user_type"].(string); ok && v != "" {
body["user_type"] = v
} else {
body["user_type"] = "1"
}
if v, ok := m["id_card"].(string); ok {
body["id_card"] = v
} else {
return nil, errors.New("api请求, QCXG4D2E, 缺少 id_card")
}
resp, err := a.tianyuanapi.CallInterface("QCXG4D2E", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG5U0ZRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
if !vin.Exists() || vin.String() == "" {
return nil, errors.New("api请求, QCXG5U0Z, 缺少 vin_code")
}
resp, err := a.tianyuanapi.CallInterface("QCXG5U0Z", map[string]interface{}{"vin_code": vin.String()})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG1U4URequest(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code", "image_url"}, nil)
orderNo := gjson.GetBytes(params, "order_no").String()
if body["vin_code"] == nil || body["image_url"] == nil || orderNo == "" {
return nil, errors.New("api请求, QCXG1U4U, 缺少必填参数 vin_code/image_url/order_no")
}
logx.Infof("vehicle api request QCXG1U4U, order_no=%s, vin_code=%v, image_url=%v", orderNo, body["vin_code"], body["image_url"])
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG1U4U")
resp, err := a.tianyuanapi.CallInterface("QCXG1U4U", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXGY7F2Request(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code", "vehicle_location", "first_registrationdate"}, nil)
if body["vin_code"] == nil || body["vehicle_location"] == nil || body["first_registrationdate"] == nil {
return nil, errors.New("api请求, QCXGY7F2, 缺少必填参数 vin_code/vehicle_location/first_registrationdate")
}
logx.Infof("vehicle api request QCXGY7F2, vin_code=%v, vehicle_location=%v, first_registrationdate=%v", body["vin_code"], body["vehicle_location"], body["first_registrationdate"])
resp, err := a.tianyuanapi.CallInterface("QCXGY7F2", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG1H7YRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
plate := gjson.GetBytes(params, "car_license")
if !vin.Exists() || vin.String() == "" || !plate.Exists() || plate.String() == "" {
return nil, errors.New("api请求, QCXG1H7Y, 缺少 vin_code 或 car_license")
}
body := map[string]interface{}{
"vin_code": vin.String(),
"plate_no": plate.String(),
}
logx.Infof("vehicle api request QCXG1H7Y, vin_code=%s, plate_no=%s", vin.String(), plate.String())
resp, err := a.tianyuanapi.CallInterface("QCXG1H7Y", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG4I1ZRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
if !vin.Exists() || vin.String() == "" {
return nil, errors.New("api请求, QCXG4I1Z, 缺少 vin_code")
}
resp, err := a.tianyuanapi.CallInterface("QCXG4I1Z", map[string]interface{}{"vin_code": vin.String()})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG3Y6BRequest(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code"}, nil)
orderNo := gjson.GetBytes(params, "order_no").String()
if body["vin_code"] == nil || orderNo == "" {
return nil, errors.New("api请求, QCXG3Y6B, 缺少必填参数 vin_code/order_no")
}
logx.Infof("vehicle api request QCXG3Y6B, order_no=%s, vin_code=%v", orderNo, body["vin_code"])
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG3Y6B")
resp, err := a.tianyuanapi.CallInterface("QCXG3Y6B", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG3Z3LRequest(params []byte) ([]byte, error) {
body := buildVehicleBody(params, []string{"vin_code"}, nil)
orderNo := gjson.GetBytes(params, "order_no").String()
if body["vin_code"] == nil || orderNo == "" {
return nil, errors.New("api请求, QCXG3Z3L, 缺少必填参数 vin_code/order_no")
}
logx.Infof("vehicle api request QCXG3Z3L, order_no=%s, vin_code=%v", orderNo, body["vin_code"])
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG3Z3L")
resp, err := a.tianyuanapi.CallInterface("QCXG3Z3L", body)
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXGP00WRequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
orderNo := gjson.GetBytes(params, "order_no").String()
vlphoto := gjson.GetBytes(params, "vlphoto_data")
if !vin.Exists() || vin.String() == "" || orderNo == "" || !vlphoto.Exists() || vlphoto.String() == "" {
return nil, errors.New("api请求, QCXGP00W, 缺少必填参数 vin_code/order_no/vlphoto_data")
}
logx.Infof("vehicle api request QCXGP00W, order_no=%s, vin_code=%s, vlphoto_data_len=%d", orderNo, vin.String(), len(vlphoto.String()))
key, err := hex.DecodeString(a.config.Encrypt.SecretKey)
if err != nil {
return nil, fmt.Errorf("api请求, QCXGP00W, 密钥解析失败: %w", err)
}
encData, err := crypto.AesEncrypt([]byte(vlphoto.String()), key)
if err != nil {
return nil, fmt.Errorf("api请求, QCXGP00W, 加密行驶证数据失败: %w", err)
}
resp, err := a.tianyuanapi.CallInterface("QCXGP00W", map[string]interface{}{
"vin_code": vin.String(),
"return_url": a.buildVehicleCallbackURL(orderNo, "QCXGP00W"),
"data": encData,
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
func (a *ApiRequestService) ProcessQCXG6B4ERequest(params []byte) ([]byte, error) {
vin := gjson.GetBytes(params, "vin_code")
if !vin.Exists() || vin.String() == "" {
return nil, errors.New("api请求, QCXG6B4E, 缺少 vin_code")
}
auth := gjson.GetBytes(params, "authorized").String()
if auth == "" {
auth = "1"
}
resp, err := a.tianyuanapi.CallInterface("QCXG6B4E", map[string]interface{}{
"vin_code": vin.String(),
"authorized": auth,
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}
// buildVehicleBody 从 params 中取 required 与 optional 键,仅非空才写入 body
func buildVehicleBody(params []byte, required, optional []string) map[string]interface{} {
body := make(map[string]interface{})
for _, k := range required {
v := gjson.GetBytes(params, k)
if v.Exists() {
body[k] = v.String()
}
}
for _, k := range optional {
v := gjson.GetBytes(params, k)
if v.Exists() && v.String() != "" {
body[k] = v.String()
}
}
return body
}
// buildVehicleCallbackURL 生成车辆类接口的异步回调地址
func (a *ApiRequestService) buildVehicleCallbackURL(orderNo, apiID string) string {
base := strings.TrimRight(a.config.Promotion.OfficialDomain, "/")
if base == "" {
return fmt.Sprintf("/api/v1/tianyuan/vehicle/callback?order_no=%s&api_id=%s", orderNo, apiID)
}
return fmt.Sprintf("%s/api/v1/tianyuan/vehicle/callback?order_no=%s&api_id=%s", base, orderNo, apiID)
}
// ProcessYYSY09CDRequest 三要素
func (a *ApiRequestService) ProcessYYSY09CDRequest(params []byte) ([]byte, error) {
name := gjson.GetBytes(params, "name")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
package tianxingjuhe
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
// Client 天行聚合API客户端
type Client struct {
baseURL string
key string
client *http.Client
}
// Config 客户端配置
type Config struct {
BaseURL string // API基础URL
Key string // API密钥
Timeout int // 超时时间(秒)
}
// Response 通用API响应结构
type Response struct {
Code int `json:"code"` // 状态码200表示成功
Msg string `json:"msg"` // 返回说明
Result interface{} `json:"result"` // 返回结果集,具体内容根据接口而定
}
// NewClient 创建新的客户端实例
func NewClient(config Config) (*Client, error) {
if config.BaseURL == "" {
return nil, fmt.Errorf("baseURL不能为空")
}
if config.Key == "" {
return nil, fmt.Errorf("key不能为空")
}
return &Client{
baseURL: config.BaseURL,
key: config.Key,
client: &http.Client{
Timeout: time.Duration(config.Timeout) * time.Second,
},
}, nil
}
// Get 发送GET请求
func (c *Client) Get(endpoint string, params map[string]interface{}) (*Response, error) {
// 构建完整URL
fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint)
// 添加请求参数
queryParams := url.Values{}
for key, value := range params {
queryParams.Set(key, fmt.Sprintf("%v", value))
}
// 添加key参数
queryParams.Set("key", c.key)
// 拼接查询参数
if len(queryParams) > 0 {
fullURL = fmt.Sprintf("%s?%s", fullURL, queryParams.Encode())
}
// 创建HTTP请求
req, err := http.NewRequest("GET", fullURL, nil)
if err != nil {
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0")
// 发送请求
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
// 解析响应
var apiResp Response
if err := json.Unmarshal(body, &apiResp); err != nil {
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
}
return &apiResp, nil
}
// Post 发送POST请求
func (c *Client) Post(endpoint string, params map[string]interface{}) (*Response, error) {
// 构建完整URL
fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint)
// 添加key参数
if params == nil {
params = make(map[string]interface{})
}
params["key"] = c.key
// 序列化请求体
requestBody, err := json.Marshal(params)
if err != nil {
return nil, fmt.Errorf("序列化请求体失败: %v", err)
}
// 创建HTTP请求
req, err := http.NewRequest("POST", fullURL, bytes.NewBuffer(requestBody))
if err != nil {
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0")
// 发送请求
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
// 解析响应
var apiResp Response
if err := json.Unmarshal(body, &apiResp); err != nil {
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
}
return &apiResp, nil
}

View File

@@ -7,9 +7,10 @@ import (
"fmt"
"time"
"qnc-server/app/main/model"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/logx"
"qnc-server/app/main/model"
)
// TianyuanapiCallLogService 天元API调用记录服务

File diff suppressed because it is too large Load Diff