package service import ( "bytes" "context" "encoding/json" "fmt" "github.com/pkg/errors" "github.com/tidwall/gjson" "github.com/zeromicro/go-zero/core/logx" "io" "net/http" "qnc-server/app/user/cmd/api/internal/config" "qnc-server/app/user/cmd/api/internal/types" "qnc-server/pkg/lzkit/crypto" "reflect" "strconv" "sync" "sync/atomic" "time" ) type WestResp struct { Message string `json:"message"` Code string `json:"code"` Data string `json:"data"` ID string `json:"id"` ErrorCode *int `json:"error_code"` Reason string `json:"reason"` } type G05HZ01WestResp struct { Message string `json:"message"` Code string `json:"code"` Data json.RawMessage `json:"data"` ID string `json:"id"` ErrorCode *int `json:"error_code"` Reason string `json:"reason"` } type WestDexService struct { config config.WestConfig } type APIResponseData struct { ApiID string `json:"apiID"` Data json.RawMessage `json:"data"` // 这里用 RawMessage 来存储原始的 data Success bool `json:"success"` Timestamp string `json:"timestamp"` Error string `json:"error,omitempty"` } // NewWestDexService 是一个构造函数,用于初始化 WestDexService func NewWestDexService(c config.Config) *WestDexService { return &WestDexService{ config: c.WestConfig, } } // CallAPI 调用西部数据的 API func (w *WestDexService) CallAPI(code string, reqData map[string]interface{}) (resp []byte, err error) { // 生成当前的13位时间戳 timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) // 构造请求URL reqUrl := fmt.Sprintf("%s/%s/%s?timestamp=%s", w.config.Url, w.config.SecretId, code, timestamp) jsonData, marshalErr := json.Marshal(reqData) if marshalErr != nil { return nil, marshalErr } // 创建HTTP POST请求 req, newRequestErr := http.NewRequest("POST", reqUrl, bytes.NewBuffer(jsonData)) if newRequestErr != nil { return nil, newRequestErr } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{} httpResp, clientDoErr := client.Do(req) if clientDoErr != nil { return nil, clientDoErr } defer func(Body io.ReadCloser) { closeErr := Body.Close() if closeErr != nil { } }(httpResp.Body) // 检查请求是否成功 if httpResp.StatusCode == 200 { // 读取响应体 bodyBytes, ReadErr := io.ReadAll(httpResp.Body) if ReadErr != nil { return nil, ReadErr } // 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法 var westDexResp WestResp UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp) if UnmarshalErr != nil { return nil, UnmarshalErr } logx.Infof("西部数据请求响应, code: %s, response: %v", code, westDexResp) if westDexResp.Code != "00000" { if westDexResp.Data == "" { return nil, errors.New(westDexResp.Message) } decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key) if DecryptErr != nil { return nil, DecryptErr } return decryptedData, errors.New(westDexResp.Message) } if westDexResp.Data == "" { return nil, errors.New(westDexResp.Message) } // 解密响应数据 decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key) if DecryptErr != nil { return nil, DecryptErr } // 输出解密后的数据 return decryptedData, nil } return nil, fmt.Errorf("西部请求失败Code: %d", httpResp.StatusCode) } // CallAPI 调用西部数据的 API func (w *WestDexService) G05HZ01CallAPI(code string, reqData map[string]interface{}) (resp []byte, err error) { // 生成当前的13位时间戳 timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) // 构造请求URL reqUrl := fmt.Sprintf("%s/%s/%s?timestamp=%s", w.config.Url, w.config.SecretSecondId, code, timestamp) jsonData, marshalErr := json.Marshal(reqData) if marshalErr != nil { return nil, marshalErr } // 创建HTTP POST请求 req, newRequestErr := http.NewRequest("POST", reqUrl, bytes.NewBuffer(jsonData)) if newRequestErr != nil { return nil, newRequestErr } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{} httpResp, clientDoErr := client.Do(req) if clientDoErr != nil { return nil, clientDoErr } defer func(Body io.ReadCloser) { closeErr := Body.Close() if closeErr != nil { } }(httpResp.Body) // 检查请求是否成功 if httpResp.StatusCode == 200 { // 读取响应体 bodyBytes, ReadErr := io.ReadAll(httpResp.Body) if ReadErr != nil { return nil, ReadErr } // 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法 var westDexResp G05HZ01WestResp UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp) if UnmarshalErr != nil { return nil, UnmarshalErr } logx.Infof("西部数据请求响应, code: %s, response: %v", code, westDexResp) if westDexResp.Code != "0000" { if westDexResp.Data == nil { return nil, errors.New(westDexResp.Message) } return westDexResp.Data, errors.New(westDexResp.Message) } if westDexResp.Data == nil { return nil, errors.New(westDexResp.Message) } return westDexResp.Data, nil } return nil, fmt.Errorf("西部请求失败Code: %d", httpResp.StatusCode) } // EncryptStructFields 加密字段的函数,处理不同类型,并跳过空值字段 func (w *WestDexService) EncryptStructFields(inputStruct interface{}) (map[string]interface{}, error) { encryptedFields := make(map[string]interface{}) // 使用反射获取结构体的类型和值 v := reflect.ValueOf(inputStruct) // 检查并解引用指针类型 if v.Kind() == reflect.Ptr { v = v.Elem() } if v.Kind() != reflect.Struct { return nil, errors.New("传入的interfact不是struct") } // 遍历结构体字段 for i := 0; i < v.NumField(); i++ { field := v.Type().Field(i) fieldValue := v.Field(i) // 检查字段的 encrypt 标签是否为 "false" encryptTag := field.Tag.Get("encrypt") if encryptTag == "false" { encryptedFields[field.Name] = fieldValue.Interface() continue } // 如果字段为空值,跳过 if fieldValue.IsZero() { continue } // 将字段的值转换为字符串进行加密 strValue := fmt.Sprintf("%v", fieldValue.Interface()) // 执行加密操作 encryptedValue, err := crypto.WestDexEncrypt(strValue, w.config.Key) if err != nil { return nil, err } // 将加密后的值存入结果映射 encryptedFields[field.Name] = encryptedValue } return encryptedFields, nil } // MapStructToAPIRequest 字段映射 func (w *WestDexService) MapStructToAPIRequest(encryptedFields map[string]interface{}, fieldMapping map[string]string, wrapField string) map[string]interface{} { apiRequest := make(map[string]interface{}) // 遍历字段映射表 for structField, apiField := range fieldMapping { // 如果加密后的字段存在,才添加到请求 if structField == "InquiredAuth" { apiRequest[apiField] = GetDateRange() } else if structField == "TimeRange" { apiRequest[apiField] = "5" } else if value, exists := encryptedFields[structField]; exists { apiRequest[apiField] = value } } // 如果 wrapField 不为空,将 apiRequest 包裹到该字段下 if wrapField != "" { return map[string]interface{}{ wrapField: apiRequest, } } return apiRequest } // ProcessRequests 批量处理 func (w *WestDexService) ProcessRequests(data interface{}, requests []types.WestDexServiceRequestParams) ([]byte, error) { var ( wg sync.WaitGroup resultsCh = make(chan APIResponseData, len(requests)) errorsCh = make(chan error, len(requests)) ctx, cancel = context.WithCancel(context.Background()) errorCount int32 errorLimit = 4 ) defer cancel() for i, req := range requests { wg.Add(1) go func(i int, req types.WestDexServiceRequestParams) { defer wg.Done() select { case <-ctx.Done(): return default: } // 请求参数预处理 apiRequest, preprocessErr := w.PreprocessRequestParams(req.ApiID, data) if preprocessErr != nil { errorsCh <- fmt.Errorf("请求预处理失败: %v", preprocessErr) atomic.AddInt32(&errorCount, 1) if atomic.LoadInt32(&errorCount) >= int32(errorLimit) { cancel() } return } var resp []byte var callApiErr error if req.ApiID == "G05HZ01" { resp, callApiErr = w.G05HZ01CallAPI(req.ApiID, apiRequest) } else { resp, callApiErr = w.CallAPI(req.ApiID, apiRequest) } timestamp := time.Now().Format("2006-01-02 15:04:05") result := APIResponseData{ ApiID: req.ApiID, Success: false, Timestamp: timestamp, } if callApiErr != nil { errorsCh <- fmt.Errorf("西部请求, 请求失败: %+v", callApiErr) atomic.AddInt32(&errorCount, 1) if atomic.LoadInt32(&errorCount) >= int32(errorLimit) { cancel() } result.Error = callApiErr.Error() result.Data = resp resultsCh <- result return } processedResp, processErr := processResponse(resp, req.ApiID) if processErr != nil { errorsCh <- fmt.Errorf("处理响应失败: %v", processErr) atomic.AddInt32(&errorCount, 1) if atomic.LoadInt32(&errorCount) >= int32(errorLimit) { cancel() } result.Error = processErr.Error() } else { result.Data = processedResp result.Success = true } resultsCh <- result }(i, req) } go func() { wg.Wait() close(resultsCh) close(errorsCh) }() // 收集所有结果并合并 var responseData []APIResponseData for result := range resultsCh { responseData = append(responseData, result) } if atomic.LoadInt32(&errorCount) >= int32(errorLimit) { var allErrors []error for err := range errorsCh { allErrors = append(allErrors, err) } return nil, fmt.Errorf("请求失败次数超过 %d 次: %v", errorLimit, allErrors) } combinedResponse, err := json.Marshal(responseData) if err != nil { return nil, fmt.Errorf("响应数据转 JSON 失败: %+v", err) } return combinedResponse, nil } // ------------------------------------请求处理器-------------------------- var requestProcessors = map[string]func(*WestDexService, interface{}) (map[string]interface{}, error){ "G09SC02": (*WestDexService).ProcessG09SC02Request, "G27BJ05": (*WestDexService).ProcessG27BJ05Request, "G26BJ05": (*WestDexService).ProcessG26BJ05Request, "G34BJ03": (*WestDexService).ProcessG34BJ03Request, "G35SC01": (*WestDexService).ProcessG35SC01Request, "G28BJ05": (*WestDexService).ProcessG28BJ05Request, "G05HZ01": (*WestDexService).ProcessG05HZ01Request, } // PreprocessRequestParams 调用指定的请求处理函数 func (w *WestDexService) PreprocessRequestParams(apiID string, params interface{}) (map[string]interface{}, error) { if processor, exists := requestProcessors[apiID]; exists { return processor(w, params) // 调用 WestDexService 方法 } var request map[string]interface{} return request, nil } // / 将处理函数作为 WestDexService 的方法 func (w *WestDexService) ProcessG09SC02Request(params interface{}) (map[string]interface{}, error) { encryptedFields, err := w.EncryptStructFields(params) if err != nil { return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err) } apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G09SC02FieldMapping, "data") return apiRequest, nil } func (w *WestDexService) ProcessG27BJ05Request(params interface{}) (map[string]interface{}, error) { encryptedFields, err := w.EncryptStructFields(params) if err != nil { return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err) } apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G27BJ05FieldMapping, "data") return apiRequest, nil } func (w *WestDexService) ProcessG26BJ05Request(params interface{}) (map[string]interface{}, error) { // 特殊名单 G26BJ05 encryptedFields, err := w.EncryptStructFields(params) if err != nil { return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err) } apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G26BJ05FieldMapping, "data") return apiRequest, nil } func (w *WestDexService) ProcessG34BJ03Request(params interface{}) (map[string]interface{}, error) { // 个人不良 G34BJ03 encryptedFields, err := w.EncryptStructFields(params) if err != nil { return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err) } apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G34BJ03FieldMapping, "data") return apiRequest, nil } func (w *WestDexService) ProcessG35SC01Request(params interface{}) (map[string]interface{}, error) { // 个人涉诉 G35SC01 encryptedFields, err := w.EncryptStructFields(params) if err != nil { return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err) } apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G35SC01FieldMapping, "data") return apiRequest, nil } func (w *WestDexService) ProcessG28BJ05Request(params interface{}) (map[string]interface{}, error) { // 借贷行为 G28BJ05 encryptedFields, err := w.EncryptStructFields(params) if err != nil { return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err) } apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G28BJ05FieldMapping, "data") return apiRequest, nil } func (w *WestDexService) ProcessG05HZ01Request(params interface{}) (map[string]interface{}, error) { // 使用 reflect 获取 params 的值和类型 val := reflect.ValueOf(params) if val.Kind() == reflect.Ptr { val = val.Elem() // 如果是指针,获取指向的实际值 } if val.Kind() != reflect.Struct { return nil, fmt.Errorf("请求参数必须是结构体类型") } // 初始化一个 map 来存储加密后的字段 encryptedFields := make(map[string]interface{}) // 遍历结构体字段,将其转换为 map[string]interface{} valType := val.Type() for i := 0; i < val.NumField(); i++ { field := val.Field(i) fieldName := valType.Field(i).Name // 如果字段名为 "IDCard",对其值进行加密 if fieldName == "IDCard" { if field.Kind() != reflect.String { return nil, fmt.Errorf("IDCard 字段不是字符串类型") } idCard := field.String() encryptedIDCard := crypto.Md5Encrypt(idCard) encryptedFields[fieldName] = encryptedIDCard } else { // 否则直接将字段值添加到 map 中 encryptedFields[fieldName] = field.Interface() } } // 使用字段映射表生成最终的 API 请求 apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G05HZ01FieldMapping, "") return apiRequest, nil } // ----------------------------------------------------------------------------- // 响应处理器 var responseProcessors = map[string]func([]byte) ([]byte, error){ "G09SC02": processG09SC02Response, // 单人婚姻 "G27BJ05": processG27BJ05Response, // 借贷意向 "G28BJ05": processG28BJ05Response, // 借贷行为 "G26BJ05": processG26BJ05Response, // 特殊名单 "G05HZ01": processG05HZ01Response, // 股东人企关系 "G34BJ03": processG34BJ03Response, // 个人不良 "G35SC01": processG35SC01Response, // 个人涉诉 } // processResponse 处理响应数据 func processResponse(resp []byte, apiID string) ([]byte, error) { if processor, exists := responseProcessors[apiID]; exists { return processor(resp) } return resp, nil } func processG09SC02Response(resp []byte) ([]byte, error) { result := gjson.GetBytes(resp, "data.0.maritalStatus") if result.Exists() { // 如果字段存在,构造包含 "status" 的 JSON 响应 responseMap := map[string]string{"status": result.String()} jsonResponse, err := json.Marshal(responseMap) if err != nil { return nil, err } return jsonResponse, nil } else { return nil, errors.New("查询为空") } } func processG27BJ05Response(resp []byte) ([]byte, error) { // 获取 code 字段 codeResult := gjson.GetBytes(resp, "code") if !codeResult.Exists() { return nil, fmt.Errorf("code 字段不存在") } if codeResult.String() != "00" { return nil, fmt.Errorf("未匹配到相关结果") } // 获取 data 字段 dataResult := gjson.GetBytes(resp, "data") if !dataResult.Exists() { return nil, fmt.Errorf("data 字段不存在") } // 将 data 字段解析为 map var dataMap map[string]interface{} if err := json.Unmarshal([]byte(dataResult.Raw), &dataMap); err != nil { return nil, fmt.Errorf("解析 data 字段失败: %v", err) } // 删除指定字段 delete(dataMap, "swift_number") delete(dataMap, "DataStrategy") // 重新编码为 JSON modifiedData, err := json.Marshal(dataMap) if err != nil { return nil, fmt.Errorf("编码修改后的 data 失败: %v", err) } return modifiedData, nil } func processG28BJ05Response(resp []byte) ([]byte, error) { // 处理借贷行为的响应数据 // 获取 code 字段 codeResult := gjson.GetBytes(resp, "code") if !codeResult.Exists() { return nil, fmt.Errorf("code 字段不存在") } if codeResult.String() != "00" { return nil, fmt.Errorf("未匹配到相关结果") } // 获取 data 字段 dataResult := gjson.GetBytes(resp, "data") if !dataResult.Exists() { return nil, fmt.Errorf("data 字段不存在") } // 将 data 字段解析为 map var dataMap map[string]interface{} if err := json.Unmarshal([]byte(dataResult.Raw), &dataMap); err != nil { return nil, fmt.Errorf("解析 data 字段失败: %v", err) } // 删除指定字段 delete(dataMap, "swift_number") delete(dataMap, "DataStrategy") // 重新编码为 JSON modifiedData, err := json.Marshal(dataMap) if err != nil { return nil, fmt.Errorf("编码修改后的 data 失败: %v", err) } return modifiedData, nil } func processG26BJ05Response(resp []byte) ([]byte, error) { // 处理特殊名单的响应数据 // 获取 code 字段 codeResult := gjson.GetBytes(resp, "code") if !codeResult.Exists() { return nil, fmt.Errorf("code 字段不存在") } if codeResult.String() != "00" { return nil, fmt.Errorf("未匹配到相关结果") } // 获取 data 字段 dataResult := gjson.GetBytes(resp, "data") if !dataResult.Exists() { return nil, fmt.Errorf("data 字段不存在") } // 将 data 字段解析为 map var dataMap map[string]interface{} if err := json.Unmarshal([]byte(dataResult.Raw), &dataMap); err != nil { return nil, fmt.Errorf("解析 data 字段失败: %v", err) } // 删除指定字段 delete(dataMap, "swift_number") delete(dataMap, "DataStrategy") // 重新编码为 JSON modifiedData, err := json.Marshal(dataMap) if err != nil { return nil, fmt.Errorf("编码修改后的 data 失败: %v", err) } return modifiedData, nil } func processG05HZ01Response(resp []byte) ([]byte, error) { // 处理股东人企关系的响应数据 code := gjson.GetBytes(resp, "code") if !code.Exists() { return nil, fmt.Errorf("响应中缺少 code 字段") } // 判断 code 是否等于 "0000" if code.String() == "0000" { // 获取 data 字段的值 data := gjson.GetBytes(resp, "data") if !data.Exists() { return nil, fmt.Errorf("响应中缺少 data 字段") } // 返回 data 字段的内容 return []byte(data.Raw), nil } // code 不等于 "0000",返回错误 return nil, fmt.Errorf("响应code错误%s", code.String()) } func processG34BJ03Response(resp []byte) ([]byte, error) { // 处理个人不良的响应数据 dataResult := gjson.GetBytes(resp, "negative_info.data.risk_level") if dataResult.Exists() { // 如果字段存在,构造包含 "status" 的 JSON 响应 responseMap := map[string]string{"risk_level": dataResult.String()} jsonResponse, err := json.Marshal(responseMap) if err != nil { return nil, err } return jsonResponse, nil } else { return nil, errors.New("查询为空") } } func processG35SC01Response(resp []byte) ([]byte, error) { // 第一步:提取外层的 data 字段 dataResult := gjson.GetBytes(resp, "data") if !dataResult.Exists() { return nil, fmt.Errorf("外层 data 字段不存在") } // 第二步:解析外层 data 的 JSON 字符串 var outerDataMap map[string]interface{} if err := json.Unmarshal([]byte(dataResult.String()), &outerDataMap); err != nil { return nil, fmt.Errorf("解析外层 data 字段失败: %v", err) } // 第三步:提取内层的 data 字段 innerData, ok := outerDataMap["data"].(string) if !ok { return nil, fmt.Errorf("内层 data 字段不存在或类型错误") } // 第四步:解析内层 data 的 JSON 字符串 var finalDataMap map[string]interface{} if err := json.Unmarshal([]byte(innerData), &finalDataMap); err != nil { return nil, fmt.Errorf("解析内层 data 字段失败: %v", err) } // 将最终的 JSON 对象编码为字节数组返回 finalDataBytes, err := json.Marshal(finalDataMap) if err != nil { return nil, fmt.Errorf("编码最终的 JSON 对象失败: %v", err) } return finalDataBytes, nil } // GetDateRange 返回今天到明天的日期范围,格式为 "yyyyMMdd-yyyyMMdd" func GetDateRange() string { today := time.Now().Format("20060102") // 获取今天的日期 tomorrow := time.Now().Add(24 * time.Hour).Format("20060102") // 获取明天的日期 return fmt.Sprintf("%s-%s", today, tomorrow) // 拼接日期范围并返回 }