diff --git a/config.yaml b/config.yaml index c4ec820..6399b64 100644 --- a/config.yaml +++ b/config.yaml @@ -654,6 +654,8 @@ huibo: aes_key: "NQYN3YO+pb/GEcCBNX0ptMb7cUlnXSPvcX7VvNofBkc=" work_order_code: "gd219219725093" product_code: "22089" + baseUrl2: "https://napi.zhixin.net:9000/api/data" + app_code2: "1508795945301708800" logging: enabled: true diff --git a/internal/config/config.go b/internal/config/config.go index 19366b0..13e432e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -682,6 +682,8 @@ type HuiboConfig struct { AESKey string `mapstructure:"aes_key"` WorkOrderCode string `mapstructure:"work_order_code"` ProductCode string `mapstructure:"product_code"` + BaseURL2 string `mapstructure:"baseUrl2"` + AppCode2 string `mapstructure:"app_code2"` Logging HuiboLoggingConfig `mapstructure:"logging"` } diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index 041f2f5..6bfa06c 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -534,6 +534,12 @@ type IVYZ4Y27Req struct { IDCard string `json:"id_card" validate:"required,validIDCard"` AuthAuthorizeFileBase64 string `json:"auth_authorize_file_base64" validate:"required,validBase64PDF"` } + +type FLXGHB4FReq struct { + Name string `json:"name" validate:"required,min=1,validName"` + IDCard string `json:"id_card" validate:"required,validIDCard"` +} + type IVYZP2Q6Req struct { Name string `json:"name" validate:"required,min=1,validName"` IDCard string `json:"id_card" validate:"required,validIDCard"` @@ -572,6 +578,10 @@ type QYGL5A3CReq struct { PageNum int64 `json:"page_num" validate:"omitempty,min=1"` } +type QYGLBH7YReq struct { + EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"` +} + type QYGL2naoReq struct { EntCode string `json:"ent_code" validate:"required,validUSCI"` PageSize int64 `json:"page_size" validate:"omitempty,min=1,max=100"` diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index a3117ff..f47e630 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -186,6 +186,7 @@ func registerAllProcessors(combService *comb.CombService) { "FLXG3A9B": flxg.ProcessFLXG3A9BRequest, "FLXGK5D2": flxg.ProcessFLXGK5D2Request, "FLXGDJG3": flxg.ProcessFLXGDJG3Request, //董监高司法综合信息核验 + "FLXGHB4F": flxg.ProcessFLXGHB4FRequest, //个人涉诉案件查询汇博 // JRZQ系列处理器 "JRZQ8203": jrzq.ProcessJRZQ8203Request, "JRZQ0A03": jrzq.ProcessJRZQ0A03Request, @@ -254,6 +255,7 @@ func registerAllProcessors(combService *comb.CombService) { "QYGLDJ12": qygl.ProcessQYGLDJ12Request, //企业年报信息核验 "QYGL8848": qygl.ProcessQYGL8848Request, //企业税收违法核查 "QYGLDJ33": qygl.ProcessQYGLDJ33Request, //企业年报信息核验 + "QYGLBH7Y": qygl.ProcessQYGLBH7YRequest, //企业涉诉案件查询汇博 // YYSY系列处理器 "YYSY35TA": yysy.ProcessYYSY35TARequest, //运营商归属地数卖 diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 1bd2fc4..69cf966 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -281,6 +281,8 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "IVYZRAX1": &dto.IVYZRAX1Req{}, //融安信用分 "IVYZRAX2": &dto.IVYZRAX1Req{}, //融御反欺诈 "IVYZ2MN7": &dto.IVYZ2MN6Req{}, //学历Bzhicha + "FLXGHB4F": &dto.FLXGHB4FReq{}, //个人涉诉案件查询汇博 + "QYGLBH7Y": &dto.QYGLBH7YReq{}, //企业涉诉案件查询汇博 } // 优先返回已配置的DTO diff --git a/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go b/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go index 6c18004..b4bba6d 100644 --- a/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go +++ b/internal/domains/api/services/processors/flxg/flxg0v4b_processor.go @@ -25,7 +25,7 @@ func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, err) } // 去掉司法案件案件去掉身份证号码 - if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" { + if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" { return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) } encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name) diff --git a/internal/domains/api/services/processors/flxg/flxg5a3b_processor.go b/internal/domains/api/services/processors/flxg/flxg5a3b_processor.go index 95e289a..d6454a7 100644 --- a/internal/domains/api/services/processors/flxg/flxg5a3b_processor.go +++ b/internal/domains/api/services/processors/flxg/flxg5a3b_processor.go @@ -20,7 +20,7 @@ func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors if err := deps.Validator.ValidateStruct(paramsDto); err != nil { return nil, errors.Join(processors.ErrInvalidParam, err) } - if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" { + if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" { return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) } diff --git a/internal/domains/api/services/processors/flxg/flxg7e8f_processor.go b/internal/domains/api/services/processors/flxg/flxg7e8f_processor.go index 8b43ac1..af59fa7 100644 --- a/internal/domains/api/services/processors/flxg/flxg7e8f_processor.go +++ b/internal/domains/api/services/processors/flxg/flxg7e8f_processor.go @@ -20,7 +20,7 @@ func ProcessFLXG7E8FRequest(ctx context.Context, params []byte, deps *processors if err := deps.Validator.ValidateStruct(paramsDto); err != nil { return nil, errors.Join(processors.ErrInvalidParam, err) } - if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" { + if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" { return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) } diff --git a/internal/domains/api/services/processors/flxg/flxgca3d_processor.go b/internal/domains/api/services/processors/flxg/flxgca3d_processor.go index c355218..ab20c6c 100644 --- a/internal/domains/api/services/processors/flxg/flxgca3d_processor.go +++ b/internal/domains/api/services/processors/flxg/flxgca3d_processor.go @@ -20,7 +20,7 @@ func ProcessFLXGCA3DRequest(ctx context.Context, params []byte, deps *processors if err := deps.Validator.ValidateStruct(paramsDto); err != nil { return nil, errors.Join(processors.ErrInvalidParam, err) } - if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" { + if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" { return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) } encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name) diff --git a/internal/domains/api/services/processors/flxg/flxgdea8_processor.go b/internal/domains/api/services/processors/flxg/flxgdea8_processor.go index e30bbb0..b2c95f4 100644 --- a/internal/domains/api/services/processors/flxg/flxgdea8_processor.go +++ b/internal/domains/api/services/processors/flxg/flxgdea8_processor.go @@ -21,7 +21,7 @@ func ProcessFLXGDEA8Request(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, err) } // 去掉司法案件案件去掉身份证号码 - if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" { + if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" { return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) } diff --git a/internal/domains/api/services/processors/flxg/flxgdea9_processor.go b/internal/domains/api/services/processors/flxg/flxgdea9_processor.go index d002a68..68da3c6 100644 --- a/internal/domains/api/services/processors/flxg/flxgdea9_processor.go +++ b/internal/domains/api/services/processors/flxg/flxgdea9_processor.go @@ -25,7 +25,7 @@ func ProcessFLXGDEA9Request(ctx context.Context, params []byte, deps *processors if err != nil { return nil, errors.Join(processors.ErrSystem, err) } - if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" { + if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" { return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) } encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) diff --git a/internal/domains/api/services/processors/flxg/flxghb4f_processor.go b/internal/domains/api/services/processors/flxg/flxghb4f_processor.go new file mode 100644 index 0000000..307d36b --- /dev/null +++ b/internal/domains/api/services/processors/flxg/flxghb4f_processor.go @@ -0,0 +1,60 @@ +package flxg + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/huibo" +) + +// ProcessFLXGHB4FRequest FLXGHB4F API处理方法 - 个人涉诉案件查询汇博 +func ProcessFLXGHB4FRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.FLXGHB4FReq + if err := json.Unmarshal(params, ¶msDto); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if deps.HuiboService == nil { + return nil, errors.Join(processors.ErrSystem, errors.New("汇博服务未初始化")) + } + + // 使用 MD5 加密 name 和 idCard + encryptedName := "MD5:" + huibo.MD5Encrypt(paramsDto.Name, deps.HuiboService.GetConfig().AppKey) + encryptedIDCard := "MD5:" + huibo.MD5Encrypt(paramsDto.IDCard, deps.HuiboService.GetConfig().AppKey) + + reqdata := map[string]interface{}{ + "name": encryptedName, + "idCard": encryptedIDCard, + } + + respBytes, err := deps.HuiboService.CallAPI2(ctx, "P_004_0271", reqdata) + if err != nil { + return nil, errors.Join(processors.ErrDatasource, err) + } + + // 解析响应 + var response huibo.CallAPI2Response + if err := json.Unmarshal(respBytes, &response); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 处理状态码 + switch response.Code { + case huibo.CallAPI2StatusSuccess: + // 查询成功 + if response.Data == nil { + return []byte("{}"), nil + } + return respBytes, nil + case huibo.CallAPI2StatusNoData: + // 查询成功,无数据 - 按产品约定按调用成功计费 + return []byte("{}"), nil + default: + // 其他错误状态码 + message := huibo.GetCallAPI2StatusMessage(response.Code) + return nil, errors.Join(processors.ErrDatasource, errors.New(message)) + } +} diff --git a/internal/domains/api/services/processors/jrzq/jrzq2f8a_processor.go b/internal/domains/api/services/processors/jrzq/jrzq2f8a_processor.go index 4aed478..b4460dd 100644 --- a/internal/domains/api/services/processors/jrzq/jrzq2f8a_processor.go +++ b/internal/domains/api/services/processors/jrzq/jrzq2f8a_processor.go @@ -37,9 +37,9 @@ func ProcessJRZQ2F8ARequest(ctx context.Context, params []byte, deps *processors } reqData := map[string]interface{}{ - "name": encryptedName, - "idCard": encryptedIDCard, - "phone": encryptedMobileNo, + "name": encryptedName, + "idCard": encryptedIDCard, + "phone": encryptedMobileNo, "authorized": paramsDto.Authorized, } @@ -60,4 +60,3 @@ func ProcessJRZQ2F8ARequest(ctx context.Context, params []byte, deps *processors return respBytes, nil } - diff --git a/internal/domains/api/services/processors/qygl/qyglbh7y_processor.go b/internal/domains/api/services/processors/qygl/qyglbh7y_processor.go new file mode 100644 index 0000000..18c6d13 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qyglbh7y_processor.go @@ -0,0 +1,55 @@ +package qygl + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/huibo" +) + +// ProcessQYGLBH7YRequest QYGLBH7Y API处理方法 - 个人涉诉案件查询汇博 +func ProcessQYGLBH7YRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGLBH7YReq + if err := json.Unmarshal(params, ¶msDto); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + if deps.HuiboService == nil { + return nil, errors.Join(processors.ErrSystem, errors.New("汇博服务未初始化")) + } + + reqdata := map[string]interface{}{ + "name": paramsDto.EntName, + } + + respBytes, err := deps.HuiboService.CallAPI2(ctx, "E_004_0261", reqdata) + if err != nil { + return nil, errors.Join(processors.ErrDatasource, err) + } + + // 解析响应 + var response huibo.CallAPI2Response + if err := json.Unmarshal(respBytes, &response); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 处理状态码 + switch response.Code { + case huibo.CallAPI2StatusSuccess: + // 查询成功 + if response.Data == nil { + return []byte("{}"), nil + } + return respBytes, nil + case huibo.CallAPI2StatusNoData: + // 查询成功,无数据 - 按产品约定按调用成功计费 + return []byte("{}"), nil + default: + // 其他错误状态码 + message := huibo.GetCallAPI2StatusMessage(response.Code) + return nil, errors.Join(processors.ErrDatasource, errors.New(message)) + } +} diff --git a/internal/infrastructure/external/huibo/2.md b/internal/infrastructure/external/huibo/2.md new file mode 100644 index 0000000..507e5fd --- /dev/null +++ b/internal/infrastructure/external/huibo/2.md @@ -0,0 +1,90 @@ +自然人公开涉诉信息查询接口文档 +接口编码:BHSC-P_004_0271 +版本:V1.0 +实施日期:2026-04-13 +适用场景:合作厂家接入中胜信用平台,用于个人涉诉案件查询(含失信、限高) +1. 通信说明 +请求方式:HTTP POST +数据格式:JSON +编码格式:UTF-8 +安全要求:请求 IP 需提前绑定 +2. 请求信息 +请求地址 +http://host:port/api/data +请求头(Header) +表格 +参数名 含义 必填 类型 备注 +AppCode 接口授权码 Y String 接口服务商提供 +pcode 产品编码 Y String 固定值:P_004_0271 +请求体(Body) +表格 +参数名 含义 必填 类型 说明 +name 姓名 Y String 支持明文 / MD5 加密 +idCard 身份证号 Y String 支持明文 / MD5 加密 +请求示例(明文) +json +{ + "idCard": "2103111*****0", + "name": "*****" +} +请求示例(加密) +json +{ + "name": "MD5:3e29aae20b7b92775*****", + "idCard": "MD5:a0c28f3a5a*****14" +} +3. 响应信息 +表格 +字段名 含义 类型 备注 +code 状态码 String 参考状态码说明 +message 描述信息 String - +orderNo 订单号 String - +pcode 产品编码 String 与请求一致 +param 请求参数 Object 原样返回 +charge 是否收费 Boolean true = 收费;false = 不收费 +time 响应时间戳 String 13 位毫秒 +data 业务数据 Object 涉诉 / 失信 / 限高数据 +响应示例 +json +{ + "code": "100", + "orderNo": "1361269246899077120", + "charge": true, + "data": { + "ss": { + "preservation": { "count": {} }, + "crc": 35****4186, + "cases_tree": {}, + "administrative": {}, + "civil": {}, + "count": {}, + "implement": {}, + "criminal": {}, + "bankrupt": {} + }, + "sxbzxr": [{}], + "xgbzxr": [] + }, + "pcode": "P_004_0271", + "param": null, + "time": "1744593480538", + "message": "查询成功" +} +data 节点说明 +ss:涉诉案件(民事 / 刑事 / 执行 / 行政 / 破产等) +sxbzxr:失信被执行人 +xgbzxr:限制高消费 +4. 状态码说明 +表格 +状态码 描述 +100 查询成功 +101 参数错误 +103 账户不存在 +104 IP 限制 +105 账号已过期 +107 服务不存在 +108 产品通道已关闭 +109 账户资金不足 +110 查询成功,无数据 +500 未知请求错误 +要不要我帮你把这份接口直接写成可上线的小程序请求代码(含加密、header、异常捕获、状态码统一处理)? \ No newline at end of file diff --git a/internal/infrastructure/external/huibo/crypto.go b/internal/infrastructure/external/huibo/crypto.go new file mode 100644 index 0000000..834fbee --- /dev/null +++ b/internal/infrastructure/external/huibo/crypto.go @@ -0,0 +1,83 @@ +package huibo + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/md5" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "io" +) + +// MD5Encrypt 使用 AppKey 进行 MD5 加密 +func MD5Encrypt(data, appKey string) string { + h := md5.New() + h.Write([]byte(data + appKey)) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +// HMACSHA256Base64 使用 HMAC-SHA256 算法生成签名 +func HMACSHA256Base64(data, secret string) string { + m := hmac.New(sha256.New, []byte(secret)) + _, _ = m.Write([]byte(data)) + return base64.StdEncoding.EncodeToString(m.Sum(nil)) +} + +// EncryptAESGCMBase64 使用 AES-GCM 算法加密数据 +func EncryptAESGCMBase64(plainText, base64Key string) (string, error) { + key, err := base64.StdEncoding.DecodeString(base64Key) + if err != nil { + return "", err + } + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + iv := make([]byte, 12) + if _, err = io.ReadFull(rand.Reader, iv); err != nil { + return "", err + } + ciphertext := gcm.Seal(nil, iv, []byte(plainText), nil) + out := append(iv, ciphertext...) + return base64.StdEncoding.EncodeToString(out), nil +} + +// DecryptAESGCMBase64 使用 AES-GCM 算法解密数据 +func DecryptAESGCMBase64(encryptedBase64, base64Key string) (string, error) { + key, err := base64.StdEncoding.DecodeString(base64Key) + if err != nil { + return "", err + } + raw, err := base64.StdEncoding.DecodeString(encryptedBase64) + if err != nil { + return "", err + } + if len(raw) < 13 { + return "", errors.New("密文长度非法") + } + + iv := raw[:12] + ciphertext := raw[12:] + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + plain, err := gcm.Open(nil, iv, ciphertext, nil) + if err != nil { + return "", err + } + return string(plain), nil +} diff --git a/internal/infrastructure/external/huibo/curl_helper.go b/internal/infrastructure/external/huibo/curl_helper.go new file mode 100644 index 0000000..f7ca797 --- /dev/null +++ b/internal/infrastructure/external/huibo/curl_helper.go @@ -0,0 +1,59 @@ +package huibo + +import ( + "net/http" + "strings" +) + +// generateCurlCommand 从 HTTP 请求生成 curl 命令 +func generateCurlCommand(req *http.Request) string { + var cmd strings.Builder + cmd.WriteString("curl -X ") + cmd.WriteString(req.Method) + cmd.WriteString(" '") + cmd.WriteString(req.URL.String()) + cmd.WriteString("'") + + // 添加请求头 + for key, values := range req.Header { + for _, value := range values { + cmd.WriteString(" \\\n -H '") + cmd.WriteString(key) + cmd.WriteString(": ") + cmd.WriteString(value) + cmd.WriteString("'") + } + } + + return cmd.String() +} + +// generateCurlCommandWithBody 生成包含请求体的 curl 命令 +func generateCurlCommandWithBody(method, url string, headers map[string]string, body string) string { + var cmd strings.Builder + cmd.WriteString("curl -X ") + cmd.WriteString(method) + cmd.WriteString(" '") + cmd.WriteString(url) + cmd.WriteString("'") + + // 添加请求头 + for key, value := range headers { + cmd.WriteString(" \\\n -H '") + cmd.WriteString(key) + cmd.WriteString(": ") + cmd.WriteString(value) + cmd.WriteString("'") + } + + // 添加请求体 + if body != "" { + cmd.WriteString(" \\\n -d '") + cmd.WriteString(body) + cmd.WriteString("'") + } + + cmd.WriteString(" \\\n --compressed") + + return cmd.String() +} \ No newline at end of file diff --git a/internal/infrastructure/external/huibo/huibo_factory.go b/internal/infrastructure/external/huibo/huibo_factory.go index 4a65df3..f5e17b7 100644 --- a/internal/infrastructure/external/huibo/huibo_factory.go +++ b/internal/infrastructure/external/huibo/huibo_factory.go @@ -39,6 +39,8 @@ func NewHuiboServiceWithConfig(cfg *config.Config) (*HuiboService, error) { AESKey: cfg.Huibo.AESKey, WorkOrderCode: cfg.Huibo.WorkOrderCode, ProductCode: cfg.Huibo.ProductCode, + BaseURL2: cfg.Huibo.BaseURL2, + AppCode2: cfg.Huibo.AppCode2, }, logger) return service, nil diff --git a/internal/infrastructure/external/huibo/huibo_service.go b/internal/infrastructure/external/huibo/huibo_service.go index a9773e5..f0b93e6 100644 --- a/internal/infrastructure/external/huibo/huibo_service.go +++ b/internal/infrastructure/external/huibo/huibo_service.go @@ -6,6 +6,7 @@ import ( "crypto/aes" "crypto/cipher" "crypto/hmac" + "crypto/md5" "crypto/rand" "crypto/sha256" "encoding/base64" @@ -85,6 +86,8 @@ type HuiboConfig struct { AESKey string WorkOrderCode string ProductCode string + BaseURL2 string // CallAPI2 使用的 URL + AppCode2 string // CallAPI2 使用的 AppCode } type HuiboService struct { @@ -101,10 +104,22 @@ type responseWrapper struct { } `json:"data"` } +// CallAPI2Response CallAPI2 的响应结构体 +type CallAPI2Response struct { + Code string `json:"code"` + Data map[string]interface{} `json:"data"` + Msg string `json:"msg"` +} + func NewHuiboService(config HuiboConfig, logger *external_logger.ExternalServiceLogger) *HuiboService { return &HuiboService{config: config, logger: logger} } +// GetConfig 获取汇博配置 +func (s *HuiboService) GetConfig() HuiboConfig { + return s.config +} + // CallEducationBackgroundDetailed 教育背景(详细)查询 func (s *HuiboService) CallEducationBackgroundDetailed(ctx context.Context, name, idCard, authPDFBase64 string) ([]byte, error) { requestID := s.generateRequestID() @@ -440,3 +455,158 @@ func randomDigits(n int) string { } return string(b) } + +// MD5Encrypt 使用配置的 AppKey 进行 MD5 加密 +func (s *HuiboService) MD5Encrypt(data string) string { + h := md5.New() + h.Write([]byte(data + s.config.AppKey)) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +// CallAPI2 通用 HTTP 调用方法,返回原始响应 JSON +func (s *HuiboService) CallAPI2(ctx context.Context, pcode string, requestData map[string]interface{}) ([]byte, error) { + startTime := time.Now() + transactionID := "" + if v, ok := ctx.Value("transaction_id").(string); ok { + transactionID = v + } + + if s.logger != nil { + s.logger.LogRequest("", transactionID, "huibo_callapi2", s.config.BaseURL2) + } + + if strings.TrimSpace(s.config.BaseURL2) == "" { + return nil, errors.Join(ErrSystem, errors.New("汇博配置不完整:BaseURL2为空")) + } + + if strings.TrimSpace(s.config.AppCode2) == "" { + return nil, errors.Join(ErrSystem, errors.New("汇博配置不完整:AppCode2为空")) + } + + reqJSON, err := json.Marshal(requestData) + if err != nil { + return nil, errors.Join(ErrSystem, fmt.Errorf("请求参数序列化失败: %w", err)) + } + + // 构建 curl 命令的 headers + headers := map[string]string{ + "AppCode": s.config.AppCode2, + "pcode": pcode, + "Content-Type": "application/json", + "X-ORDER-CODE": s.config.XOrderCode, + } + + // 生成包含请求体的 curl 命令用于日志记录 + curlCmd := generateCurlCommandWithBody("POST", s.config.BaseURL2, headers, string(reqJSON)) + + // 创建 HTTP 请求 + req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.config.BaseURL2, bytes.NewBuffer(reqJSON)) + if err != nil { + return nil, errors.Join(ErrSystem, fmt.Errorf("创建HTTP请求失败: %w", err)) + } + + // req.Header.Set(headerAuthorization, s.config.AppID+"::"+s.config.AppKey) + // req.Header.Set(headerWorkOrderCode, s.config.WorkOrderCode) + // req.Header.Set(headerOrderCode, s.config.XOrderCode) + // req.Header.Set(headerSecretIDHdr, s.config.SecretID) + // req.Header.Set(headerAESKeyHdr, s.config.AESKey) + // req.Header.Set("Content-Type", writer.FormDataContentType()) + + // 设置请求头 + req.Header.Set("AppCode", s.config.AppCode2) + req.Header.Set("pcode", pcode) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-ORDER-CODE", s.config.XOrderCode) + + client := &http.Client{Timeout: 60 * time.Second} + resp, err := client.Do(req) + if err != nil { + if s.logger != nil { + s.logger.LogErrorWithFields("汇博 CallAPI2 HTTP 请求失败", + zap.String("url", s.config.BaseURL2), + zap.String("pcode", pcode), + zap.String("curl", curlCmd), + zap.Error(err), + ) + } + return nil, errors.Join(ErrDatasource, err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + if s.logger != nil { + s.logger.LogErrorWithFields("汇博 CallAPI2 读取响应体失败", + zap.String("url", s.config.BaseURL2), + zap.Int("http_status", resp.StatusCode), + zap.Error(err), + ) + } + return nil, errors.Join(ErrSystem, fmt.Errorf("读取响应体失败: %w", err)) + } + + // 解析响应以检查业务状态码 + var response CallAPI2Response + if err := json.Unmarshal(respBody, &response); err != nil { + if s.logger != nil { + s.logger.LogErrorWithFields("汇博 CallAPI2 响应解析失败", + zap.String("url", s.config.BaseURL2), + zap.String("pcode", pcode), + zap.Error(err), + ) + } + return nil, errors.Join(ErrDatasource, fmt.Errorf("响应解析失败: %w", err)) + } + + // 根据业务状态码进行处理 + switch response.Code { + case CallAPI2StatusSuccess: + // 查询成功 + if s.logger != nil { + s.logger.LogInfo( + "汇博 CallAPI2 查询成功", + zap.String("pcode", pcode), + zap.String("code", response.Code), + zap.String("transaction_id", transactionID), + ) + } + case CallAPI2StatusNoData: + // 查询成功,无数据 + if s.logger != nil { + s.logger.LogInfo( + "汇博 CallAPI2 查询成功但无数据", + zap.String("pcode", pcode), + zap.String("code", response.Code), + zap.String("transaction_id", transactionID), + ) + } + default: + // 其他错误状态码 + message := GetCallAPI2StatusMessage(response.Code) + if s.logger != nil { + s.logger.LogErrorWithFields("汇博 CallAPI2 业务状态异常", + zap.String("url", s.config.BaseURL2), + zap.String("pcode", pcode), + zap.String("code", response.Code), + zap.String("message", message), + ) + } + return nil, errors.Join(ErrDatasource, fmt.Errorf("业务状态异常(code=%s,msg=%s)", response.Code, message)) + } + + // 记录 curl 命令和响应 + if s.logger != nil { + s.logger.LogInfo( + "汇博 CallAPI2 请求响应", + zap.String("curl", curlCmd), + zap.String("response_body", string(respBody)), + zap.String("transaction_id", transactionID), + ) + } + + if s.logger != nil { + s.logger.LogResponse("", transactionID, "huibo_callapi2", http.StatusOK, time.Since(startTime)) + } + + return respBody, nil +} diff --git a/internal/infrastructure/external/huibo/status_codes.go b/internal/infrastructure/external/huibo/status_codes.go new file mode 100644 index 0000000..5a59915 --- /dev/null +++ b/internal/infrastructure/external/huibo/status_codes.go @@ -0,0 +1,52 @@ +package huibo + +// CallAPI2 状态码常量 +const ( + CallAPI2StatusSuccess = "100" // 查询成功 + CallAPI2StatusNoData = "110" // 查询成功,无数据 + CallAPI2StatusParamError = "101" // 参数错误 + CallAPI2StatusAccountError = "103" // 账户不存在 + CallAPI2StatusIPError = "104" // IP 限制 + CallAPI2StatusExpired = "105" // 账号已过期 + CallAPI2StatusServiceError = "107" // 服务不存在 + CallAPI2StatusChannelError = "108" // 产品通道已关闭 + CallAPI2StatusBalanceError = "109" // 账户资金不足 + CallAPI2StatusUnknownError = "500" // 未知请求错误 +) + +// CallAPI2 状态码对应的错误信息 +var CallAPI2StatusMessages = map[string]string{ + CallAPI2StatusSuccess: "查询成功", + CallAPI2StatusNoData: "查询成功,无数据", + CallAPI2StatusParamError: "参数错误", + CallAPI2StatusAccountError: "账户不存在", + CallAPI2StatusIPError: "IP 限制", + CallAPI2StatusExpired: "账号已过期", + CallAPI2StatusServiceError: "服务不存在", + CallAPI2StatusChannelError: "产品通道已关闭", + CallAPI2StatusBalanceError: "账户资金不足", + CallAPI2StatusUnknownError: "未知请求错误", +} + +// IsCallAPI2Success 判断 CallAPI2 状态码是否为成功(需要扣费) +func IsCallAPI2Success(code string) bool { + return code == CallAPI2StatusSuccess +} + +// IsCallAPI2NoData 判断 CallAPI2 状态码是否为无数据(需要扣费) +func IsCallAPI2NoData(code string) bool { + return code == CallAPI2StatusNoData +} + +// IsCallAPI2Billable 判断 CallAPI2 状态码是否需要扣费 +func IsCallAPI2Billable(code string) bool { + return IsCallAPI2Success(code) || IsCallAPI2NoData(code) +} + +// GetCallAPI2StatusMessage 获取 CallAPI2 状态码对应的错误信息 +func GetCallAPI2StatusMessage(code string) string { + if msg, ok := CallAPI2StatusMessages[code]; ok { + return msg + } + return "未知状态码: " + code +}