diff --git a/internal/application/api/api_application_service.go b/internal/application/api/api_application_service.go index c30506c..d9f6a48 100644 --- a/internal/application/api/api_application_service.go +++ b/internal/application/api/api_application_service.go @@ -335,7 +335,7 @@ func (s *ApiApplicationServiceImpl) validateApiCall(ctx context.Context, cmd *co parentApiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, link.ParentUserID) if err != nil { s.logger.Error("加载主账号API用户失败", - zap.String("child_user_id", apiUser.UserId), + zap.String("child_user_id", apiUser.UserId),hjk,mgumygi,b zap.String("parent_user_id", link.ParentUserID), zap.Error(err)) return nil, ErrSystem diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index a54ec87..ff79722 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -187,6 +187,9 @@ type YYSYXF7JReq struct { type QCXGA8V3Req struct { VinCode string `json:"vin_code" validate:"required"` } +type QCXGCP77Req struct { + VinCode string `json:"vin_code" validate:"required"` +} type YYSYP72DReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index 50eb262..ecdbd1a 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -414,6 +414,7 @@ func registerAllProcessors(combService *comb.CombService) { "QYGLVR76": qygl.ProcessQYGLVR76Request, //名下企业诺尔 "QCXG1S2L": qcxg.ProcessQCXG1S2LRequest, //车辆车五项信息核验V2 "QCXGA8V3": qcxg.ProcessQCXGA8V3Request, //全国车辆配置查验(车五项+使用性质+承保) + "QCXGCP77": qcxg.ProcessQCXGCP77Request, //全国车辆配置查验(车辆详情) "QCXG6U5G": qcxg.ProcessQCXG6U5GRequest, //名下车辆核验 // DWBG系列处理器 - 多维报告 "DWBG6A2C": dwbg.ProcessDWBG6A2CRequest, diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 16780b2..ce4f1f0 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -338,6 +338,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "YYSYXF7J": &dto.YYSYXF7JReq{}, //手机消费区间验证 "QCXG1S2L": &dto.QCXG1S2LReq{}, //车辆车五项信息核验V2 "QCXGA8V3": &dto.QCXGA8V3Req{}, //全国车辆配置查验(车五项+使用性质+承保) + "QCXGCP77": &dto.QCXGCP77Req{}, //全国车辆配置查验(车辆详情) "JRZQ5T8S": &dto.JRZQ5T8SReq{}, //借贷意向验证V22 "JRZQT57Z": &dto.JRZQT57ZReq{}, //团伙欺诈风险识别 "IVYZX7J9": &dto.IVYZX7J9Req{}, //学历核验政务 diff --git a/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go b/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go index 8453644..5cb22ec 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go @@ -40,10 +40,9 @@ func ProcessIVYZ5E3FRequest(ctx context.Context, params []byte, deps *processors respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI029", reqData) if err != nil { if errors.Is(err, zhicha.ErrDatasource) { - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) + return fallback5E3FViaHy87(ctx, deps, paramsDto.Name, paramsDto.IDCard) } + return nil, errors.Join(processors.ErrSystem, err) } // 将响应数据转换为JSON字节 diff --git a/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go b/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go index e99648f..639429c 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go @@ -40,57 +40,24 @@ func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI029", reqData) if err != nil { if errors.Is(err, zhicha.ErrDatasource) { - return nil, errors.Join(processors.ErrDatasource, err) - } else { - return nil, errors.Join(processors.ErrSystem, err) + return fallback81NCViaHy87(ctx, deps, paramsDto.Name, paramsDto.IDCard) } + return nil, errors.Join(processors.ErrSystem, err) } - // 解析响应数据,期望格式为 {"state": "1"} var stateResp struct { - State string `json:"state"` + State string `json:"state"` RegTime string `json:"regTime"` } - - // 将 respData 转换为 JSON 字节再解析 + respDataBytes, err := json.Marshal(respData) if err != nil { return nil, errors.Join(processors.ErrSystem, err) } - if err := json.Unmarshal(respDataBytes, &stateResp); err != nil { return nil, errors.Join(processors.ErrSystem, err) } - // 根据 state 值转换为 81nc 格式 - var opType, opTypeDesc string - switch stateResp.State { - case "1": // 已婚 - opType = "IA" - opTypeDesc = "结婚" - case "2": // 未婚/未在民政局登记 - opType = "INR" - opTypeDesc = "匹配不成功" - case "3": // 离异 - opType = "IB" - opTypeDesc = "离婚" - default: - opType = "INR" - opTypeDesc = "匹配不成功" - } - - // 构建 81nc 格式响应 - result := map[string]interface{}{ - "code": "0", - "data": map[string]interface{}{ - "op_date": stateResp.RegTime, - "op_type": opType, - "op_type_desc": opTypeDesc, - }, - "message": "成功", - "seqNo": "", - } - - // 返回 81nc 格式响应 - return json.Marshal(result) + opType, opTypeDesc := zhichaStateTo81NCOp(stateResp.State) + return json.Marshal(build81NCResponse(stateResp.RegTime, opType, opTypeDesc)) } diff --git a/internal/domains/api/services/processors/ivyz/ivyz_hy87_fallback.go b/internal/domains/api/services/processors/ivyz/ivyz_hy87_fallback.go new file mode 100644 index 0000000..fc90d68 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz_hy87_fallback.go @@ -0,0 +1,148 @@ +package ivyz + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/nuoer" +) + +const hy87MarriageAPIKey = "qualificateMarry" +const hy87MarriageAPIPath = "/v1/doCheck" + +// queryHy87MarriageStatus 调用 IVYZHY87 同源诺尔婚姻状态接口。 +// status: 0=未婚, 1=已婚, 2=离异, 3=婚姻冷静期 +func queryHy87MarriageStatus(ctx context.Context, deps *processors.ProcessorDependencies, name, idCard string) (int, error) { + if deps.NuoerService == nil { + return 0, errors.Join(processors.ErrSystem, errors.New("诺尔服务未初始化")) + } + + body := map[string]string{ + "idCard": idCard, + "name": name, + } + + resp, err := deps.NuoerService.CallAPI(ctx, hy87MarriageAPIKey, hy87MarriageAPIPath, body) + if err != nil { + if errors.Is(err, nuoer.ErrNotFound) { + return 0, errors.Join(processors.ErrNotFound, err) + } + if errors.Is(err, nuoer.ErrDatasource) { + return 0, errors.Join(processors.ErrDatasource, err) + } + return 0, errors.Join(processors.ErrSystem, err) + } + + return parseHy87MarriageStatus(resp.Data) +} + +func parseHy87MarriageStatus(data interface{}) (int, error) { + dataBytes, err := json.Marshal(data) + if err != nil { + return 0, errors.Join(processors.ErrSystem, err) + } + + var parsed struct { + Result struct { + Status int `json:"status"` + } `json:"result"` + } + if err := json.Unmarshal(dataBytes, &parsed); err != nil { + return 0, errors.Join(processors.ErrSystem, err) + } + + return parsed.Result.Status, nil +} + +// hy87StatusTo5E3FState 将 HY87 婚姻状态映射为 5E3F 的 state 字段。 +// 5E3F: 1=已婚, 2=未婚/未在民政局登记, 3=离异 +func hy87StatusTo5E3FState(status int) string { + switch status { + case 1: + return "1" + case 2: + return "3" + case 3: + return "1" + default: + return "2" + } +} + +func build5E3FResponseFromHy87(status int) map[string]string { + return map[string]string{ + "state": hy87StatusTo5E3FState(status), + } +} + +func zhichaStateTo81NCOp(state string) (opType, opTypeDesc string) { + switch state { + case "1": + return "IA", "结婚" + case "2": + return "INR", "匹配不成功" + case "3": + return "IB", "离婚" + default: + return "INR", "匹配不成功" + } +} + +func hy87StatusTo81NCOp(status int) (opType, opTypeDesc string) { + switch status { + case 1: + return "IA", "结婚" + case 2: + return "IB", "离婚" + case 3: + return "IA", "结婚" + default: + return "INR", "匹配不成功" + } +} + +func build81NCResponse(opDate, opType, opTypeDesc string) map[string]interface{} { + return map[string]interface{}{ + "code": "0", + "data": map[string]interface{}{ + "op_date": opDate, + "op_type": opType, + "op_type_desc": opTypeDesc, + }, + "message": "成功", + "seqNo": "", + } +} + +func build81NCResponseFromHy87(status int) map[string]interface{} { + opType, opTypeDesc := hy87StatusTo81NCOp(status) + return build81NCResponse("", opType, opTypeDesc) +} + +func fallback5E3FViaHy87(ctx context.Context, deps *processors.ProcessorDependencies, name, idCard string) ([]byte, error) { + status, err := queryHy87MarriageStatus(ctx, deps, name, idCard) + if err != nil { + return nil, err + } + + respBytes, err := json.Marshal(build5E3FResponseFromHy87(status)) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + return respBytes, nil +} + +func fallback81NCViaHy87(ctx context.Context, deps *processors.ProcessorDependencies, name, idCard string) ([]byte, error) { + status, err := queryHy87MarriageStatus(ctx, deps, name, idCard) + if err != nil { + return nil, err + } + + respBytes, err := json.Marshal(build81NCResponseFromHy87(status)) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/ivyz/ivyz_hy87_fallback_test.go b/internal/domains/api/services/processors/ivyz/ivyz_hy87_fallback_test.go new file mode 100644 index 0000000..bedf633 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyz_hy87_fallback_test.go @@ -0,0 +1,77 @@ +package ivyz + +import ( + "encoding/json" + "testing" +) + +func TestHy87StatusTo5E3FState(t *testing.T) { + tests := []struct { + status int + want string + }{ + {0, "2"}, + {1, "1"}, + {2, "3"}, + {3, "1"}, + {9, "2"}, + } + for _, tt := range tests { + if got := hy87StatusTo5E3FState(tt.status); got != tt.want { + t.Fatalf("status %d => %q, want %q", tt.status, got, tt.want) + } + } +} + +func TestHy87StatusTo81NCOp(t *testing.T) { + tests := []struct { + status int + wantType string + wantTypeDesc string + }{ + {0, "INR", "匹配不成功"}, + {1, "IA", "结婚"}, + {2, "IB", "离婚"}, + {3, "IA", "结婚"}, + } + for _, tt := range tests { + gotType, gotDesc := hy87StatusTo81NCOp(tt.status) + if gotType != tt.wantType || gotDesc != tt.wantTypeDesc { + t.Fatalf("status %d => (%q, %q), want (%q, %q)", tt.status, gotType, gotDesc, tt.wantType, tt.wantTypeDesc) + } + } +} + +func TestParseHy87MarriageStatus(t *testing.T) { + data := map[string]interface{}{ + "result": map[string]interface{}{ + "status": float64(1), + }, + } + status, err := parseHy87MarriageStatus(data) + if err != nil { + t.Fatalf("parseHy87MarriageStatus() error = %v", err) + } + if status != 1 { + t.Fatalf("status = %d, want 1", status) + } +} + +func TestBuild81NCResponseFromHy87(t *testing.T) { + resp := build81NCResponseFromHy87(2) + data, ok := resp["data"].(map[string]interface{}) + if !ok { + t.Fatalf("data type = %T, want map[string]interface{}", resp["data"]) + } + if data["op_type"] != "IB" || data["op_type_desc"] != "离婚" || data["op_date"] != "" { + t.Fatalf("unexpected 81nc response: %#v", resp) + } + + respBytes, err := json.Marshal(resp) + if err != nil { + t.Fatalf("json.Marshal() error = %v", err) + } + if string(respBytes) == "" { + t.Fatal("empty response bytes") + } +} diff --git a/internal/domains/api/services/processors/qcxg/qcxgcp77_processor.go b/internal/domains/api/services/processors/qcxg/qcxgcp77_processor.go new file mode 100644 index 0000000..ebee990 --- /dev/null +++ b/internal/domains/api/services/processors/qcxg/qcxgcp77_processor.go @@ -0,0 +1,44 @@ +package qcxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/haiyuapi" +) + +// ProcessQCXGCP77Request QCXGCP77 全国车辆配置查验(车辆详情)API 处理方法(海宇API) +func ProcessQCXGCP77Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QCXGCP77Req + if err := json.Unmarshal(params, ¶msDto); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if err := deps.Validator.ValidateStruct(paramsDto); err != nil { + return nil, errors.Join(processors.ErrInvalidParam, err) + } + + if deps.HaiyuapiService == nil { + return nil, errors.Join(processors.ErrSystem, errors.New("海宇API服务未初始化")) + } + + reqParams := map[string]interface{}{ + "vin_code": paramsDto.VinCode, + } + + respBytes, err := deps.HaiyuapiService.CallAPI(ctx, "QCXGCP77", reqParams) + if err != nil { + if errors.Is(err, haiyuapi.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } + if errors.Is(err, haiyuapi.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } + return nil, errors.Join(processors.ErrSystem, err) + } + + return respBytes, nil +}