diff --git a/internal/domains/api/services/processors/ivyz/ivyzrax1_processor.go b/internal/domains/api/services/processors/ivyz/ivyzrax1_processor.go index c028c10..0c96cc8 100644 --- a/internal/domains/api/services/processors/ivyz/ivyzrax1_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyzrax1_processor.go @@ -7,7 +7,7 @@ import ( "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/zhicha" + "tyapi-server/internal/infrastructure/external/nuoer" ) // ProcessIVYZRAX1Request IVYZRAX1 API处理方法 - 融安信用分 @@ -21,45 +21,34 @@ func ProcessIVYZRAX1Request(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, err) } - // encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) - // if err != nil { - // return nil, errors.Join(processors.ErrSystem, err) - // } - - // encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) - // if err != nil { - // return nil, errors.Join(processors.ErrSystem, err) - // } - // encryptedMoblie, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo) - // if err != nil { - // return nil, errors.Join(processors.ErrSystem, err) - // } - - md5Name := deps.ZhichaService.MD5(paramsDto.Name) - md5IDCard := deps.ZhichaService.MD5(paramsDto.IDCard) - md5Mobile := deps.ZhichaService.MD5(paramsDto.MobileNo) - - reqData := map[string]interface{}{ - // "name": encryptedName, - // "idCard": encryptedIDCard, - // "phone": encryptedMoblie, - "authorized": paramsDto.Authorized, - "name": md5Name, - "idCard": md5IDCard, - "phone": md5Mobile, + body := map[string]string{ + "name": paramsDto.Name, + "idCard": paramsDto.IDCard, + "mobile": paramsDto.MobileNo, } - respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI084", reqData) + nuoerDoCheckAPIKey := "zhiTongModelG" + ApiPath := "/v1/doCheck" + + resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body) if err != nil { - if errors.Is(err, zhicha.ErrDatasource) { + if errors.Is(err, nuoer.ErrDatasource) { return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) } + if errors.Is(err, nuoer.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } + return nil, errors.Join(processors.ErrSystem, err) } - // 将响应数据转换为JSON字节 - respBytes, err := json.Marshal(respData) + rawData, ok := resp.Data.(map[string]interface{}) + if !ok { + return processors.MarshalRawResponse(resp.Data) + } + + result := mapNuoerZhiTongModelGToIVYZRAX1Response(rawData) + + respBytes, err := json.Marshal(result) if err != nil { return nil, errors.Join(processors.ErrSystem, err) } diff --git a/internal/domains/api/services/processors/ivyz/ivyzrax1_transform.go b/internal/domains/api/services/processors/ivyz/ivyzrax1_transform.go new file mode 100644 index 0000000..4529de2 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyzrax1_transform.go @@ -0,0 +1,74 @@ +package ivyz + +import ( + "math" + "strconv" +) + +const ( + rax1ScoreYwBaseMin = 300 + rax1ScoreYwBaseMax = 1000 + rax1ScoreYwBaseIntercept = 984.0 + rax1ScoreYwBaseSlope = 6.32 +) + +// mapNuoerZhiTongModelGToIVYZRAX1Response 将 nuoer zhiTongModelG 响应转为 IVYZRAX1 对外结构。 +// nuoer score 为百分制(0~100,越低越安全),映射为 scoreywbase 千分制(300~1000,越高越安全)。 +// 同分下 scoreywbase 高于 JRZQ0L85 的 score_120_General。未匹配到数据时返回空对象 {}。 +func mapNuoerZhiTongModelGToIVYZRAX1Response(data map[string]interface{}) map[string]interface{} { + score, ok := extractRax1Score(data) + if !ok { + return map[string]interface{}{} + } + return map[string]interface{}{ + "scoreywbase": mapRax1ScoreToScoreYwBase(score), + } +} + +func extractRax1Score(data map[string]interface{}) (float64, bool) { + if data == nil { + return 0, false + } + payload := data + if result, ok := data["result"].(map[string]interface{}); ok { + payload = result + } + return parseRax1Score(payload["score"]) +} + +func parseRax1Score(v interface{}) (float64, bool) { + if v == nil { + return 0, false + } + switch val := v.(type) { + case float64: + return val, true + case int: + return float64(val), true + case int64: + return float64(val), true + case string: + if val == "" { + return 0, false + } + f, err := strconv.ParseFloat(val, 64) + if err != nil { + return 0, false + } + return f, true + default: + return 0, false + } +} + +// mapRax1ScoreToScoreYwBase 百分制 score 转千分制 scoreywbase。 +// 样本标定:40.49→728,50.46→665,即 round(984 - score*6.32),结果钳制到 [300, 1000]。 +func mapRax1ScoreToScoreYwBase(score float64) string { + v := int(math.Round(rax1ScoreYwBaseIntercept - score*rax1ScoreYwBaseSlope)) + if v < rax1ScoreYwBaseMin { + v = rax1ScoreYwBaseMin + } else if v > rax1ScoreYwBaseMax { + v = rax1ScoreYwBaseMax + } + return strconv.Itoa(v) +} diff --git a/internal/domains/api/services/processors/jrzq/1.md b/internal/domains/api/services/processors/jrzq/1.md new file mode 100644 index 0000000..3b9b432 --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/1.md @@ -0,0 +1,20 @@ + { + "scoreywbase": "728" +} +{ + "result": { + "score": "40.49" + } +} + + + +{ + "result": { + "score": "50.46" + } +} + +{ + "scoreywbase": "665" +} \ No newline at end of file diff --git a/internal/domains/api/services/processors/jrzq/jrzq0l85_processor.go b/internal/domains/api/services/processors/jrzq/jrzq0l85_processor.go index fb41183..d4f8fb2 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq0l85_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq0l85_processor.go @@ -4,12 +4,10 @@ import ( "context" "encoding/json" "errors" - "math" - "strconv" "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/zhicha" + "tyapi-server/internal/infrastructure/external/nuoer" ) // ProcessJRZQ0L85Request JRZQ0L85 API处理方法 - 个人信用分 @@ -22,39 +20,32 @@ func ProcessJRZQ0L85Request(ctx context.Context, params []byte, deps *processors if err := deps.Validator.ValidateStruct(paramsDto); err != nil { return nil, errors.Join(processors.ErrInvalidParam, err) } - - md5Name := deps.ZhichaService.MD5(paramsDto.Name) - md5IDCard := deps.ZhichaService.MD5(paramsDto.IDCard) - md5MobileNo := deps.ZhichaService.MD5(paramsDto.MobileNo) - - reqData := map[string]interface{}{ - "name": md5Name, - "idCard": md5IDCard, - "phone": md5MobileNo, - "authorized": "1", + body := map[string]string{ + "name": paramsDto.Name, + "idCard": paramsDto.IDCard, + "mobile": paramsDto.MobileNo, } - respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI084", reqData) + nuoerDoCheckAPIKey := "zhiTongModelG" + ApiPath := "/v1/doCheck" + + resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body) if err != nil { - if errors.Is(err, zhicha.ErrDatasource) { + if errors.Is(err, nuoer.ErrDatasource) { return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) } + if errors.Is(err, nuoer.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } + return nil, errors.Join(processors.ErrSystem, err) } - score := "-1" - if m, ok := respData.(map[string]interface{}); ok { - if rawScore, exists := m["scoreywbase"]; exists { - if v, ok := parseToFloat64(rawScore); ok { - score = mapScoreAfywBaseToGeneralScore(v) - } - } + rawData, ok := resp.Data.(map[string]interface{}) + if !ok { + return processors.MarshalRawResponse(resp.Data) } - result := map[string]interface{}{ - "score_120_General": score, - } + result := mapNuoerZhiTongModelGToResponse(rawData) respBytes, err := json.Marshal(result) if err != nil { @@ -63,50 +54,3 @@ func ProcessJRZQ0L85Request(ctx context.Context, params []byte, deps *processors return respBytes, nil } - -func parseToFloat64(v interface{}) (float64, bool) { - switch value := v.(type) { - case float64: - return value, true - case string: - if value == "" { - return 0, false - } - f, err := strconv.ParseFloat(value, 64) - if err != nil { - return 0, false - } - return f, true - case json.Number: - f, err := value.Float64() - if err != nil { - return 0, false - } - return f, true - default: - return 0, false - } -} - -func mapScoreAfywBaseToGeneralScore(scoreAfywBase float64) string { - // scoreafywbase: 300~1000,分值越高违约概率越低。 - // score_120_General: 300~900,分值越高信用越好。 - if scoreAfywBase < 300 { - scoreAfywBase = 300 - } - if scoreAfywBase > 1000 { - scoreAfywBase = 1000 - } - - // 线性映射:300->300, 1000->900 - score := 300 + (scoreAfywBase-300)*600/700 - scoreInt := int(math.Round(score)) - if scoreInt < 300 { - scoreInt = 300 - } - if scoreInt > 900 { - scoreInt = 900 - } - - return strconv.Itoa(scoreInt) -} diff --git a/internal/domains/api/services/processors/jrzq/jrzq0l85_transform.go b/internal/domains/api/services/processors/jrzq/jrzq0l85_transform.go new file mode 100644 index 0000000..906cf6b --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/jrzq0l85_transform.go @@ -0,0 +1,37 @@ +package jrzq + +import ( + "math" + "strconv" +) + +const ( + score120GeneralMin = 300 + score120GeneralMax = 900 + score120GeneralIntercept = 932.0 + score120GeneralSlope = 6.32 +) + +// mapNuoerZhiTongModelGToResponse 将 nuoer zhiTongModelG 响应转为 JRZQ0L85 对外结构。 +// nuoer score 为百分制(0~100,越低越安全),映射为 score_120_General 千分制(300~900,越高越安全;-1 表示未命中)。 +func mapNuoerZhiTongModelGToResponse(data map[string]interface{}) map[string]interface{} { + score, ok := extractConsumerTagScore(data) + return map[string]interface{}{ + "score_120_General": mapZhiTongModelGScoreToScore120General(score, ok), + } +} + +// mapZhiTongModelGScoreToScore120General 百分制 score 转千分制 score_120_General。 +// 样本标定:50.46→613,即 round(932 - score*6.32),结果钳制到 [300, 900]。 +func mapZhiTongModelGScoreToScore120General(score float64, ok bool) string { + if !ok { + return "-1" + } + v := int(math.Round(score120GeneralIntercept - score*score120GeneralSlope)) + if v < score120GeneralMin { + v = score120GeneralMin + } else if v > score120GeneralMax { + v = score120GeneralMax + } + return strconv.Itoa(v) +} diff --git a/internal/infrastructure/external/nuoer/nuoer_service.go b/internal/infrastructure/external/nuoer/nuoer_service.go index aae046e..b9c0583 100644 --- a/internal/infrastructure/external/nuoer/nuoer_service.go +++ b/internal/infrastructure/external/nuoer/nuoer_service.go @@ -31,6 +31,7 @@ var queryBillingAPIKeys = map[string]struct{}{ "loanRiskTagV8": {}, // 风险变量V8 loanRiskTagV8 "loanRiskTagV9": {}, // 风险变量V9 loanRiskTagV9 "loanRiskTagV10": {}, // 风险变量V10 loanRiskTagV10 + "zhiTongModelG": {}, // 智瞳分G版 zhiTongModelG } func isQueryBillingAPIKey(apiKey string) bool {