From 7a957a6b8784ad6ef45f1a731b422fabb106df3d Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Wed, 4 Mar 2026 12:39:01 +0800 Subject: [PATCH] f --- internal/domains/api/dto/api_request_dto.go | 18 ++ .../api/services/api_request_service.go | 3 + .../api/services/form_config_service.go | 11 +- .../processors/dwbg/dwbg8b4d_processor.go | 260 +++++++++--------- .../processors/ivyz/ivyzocr2_processor.go | 48 ++++ .../processors/jrzq/jrzqocre_processor.go | 52 ++++ .../processors/jrzq/jrzqocry_processor.go | 48 ++++ .../processors/qygl/qyglj0q1_processor.go | 50 ++++ .../processors/qygl/qygluy3s_processor.go | 50 ++++ 9 files changed, 408 insertions(+), 132 deletions(-) create mode 100644 internal/domains/api/services/processors/ivyz/ivyzocr2_processor.go create mode 100644 internal/domains/api/services/processors/jrzq/jrzqocre_processor.go create mode 100644 internal/domains/api/services/processors/jrzq/jrzqocry_processor.go create mode 100644 internal/domains/api/services/processors/qygl/qyglj0q1_processor.go create mode 100644 internal/domains/api/services/processors/qygl/qygluy3s_processor.go diff --git a/internal/domains/api/dto/api_request_dto.go b/internal/domains/api/dto/api_request_dto.go index 2de8555..217aeeb 100644 --- a/internal/domains/api/dto/api_request_dto.go +++ b/internal/domains/api/dto/api_request_dto.go @@ -113,6 +113,19 @@ type QYGL2ACDReq struct { LegalPerson string `json:"legal_person" validate:"required,min=1,validName"` EntCode string `json:"ent_code" validate:"required,validUSCI"` } +type QYGLUY3SReq struct { + EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"` + EntRegno string `json:"ent_reg_no" validate:"omitempty"` + EntCode string `json:"ent_code" validate:"omitempty,validUSCI"` +} + +type JRZQOCRYReq struct { + PhotoData string `json:"photo_data" validate:"required,validBase64Image"` +} +type JRZQOCREReq struct { + PhotoData string `json:"photo_data" validate:"omitempty,validBase64Image"` + ImageUrl string `json:"image_url" validate:"omitempty,url"` +} type YYSYK9R4Req struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` @@ -591,6 +604,11 @@ type YYSY8B1CReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` } +type QYGLJ0Q1Req struct { + MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` + IDCard string `json:"id_card" validate:"required,validIDCard"` + Name string `json:"name" validate:"required,min=1,validName"` +} type YYSY6D9AReq struct { MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"` IDCard string `json:"id_card" validate:"required,validIDCard"` diff --git a/internal/domains/api/services/api_request_service.go b/internal/domains/api/services/api_request_service.go index 70b4cd6..6d80c90 100644 --- a/internal/domains/api/services/api_request_service.go +++ b/internal/domains/api/services/api_request_service.go @@ -178,6 +178,8 @@ func registerAllProcessors(combService *comb.CombService) { "QYGLNIO8": qygl.ProcessQYGLNIO8Request, //企业基本信息 "QYGLP0HT": qygl.ProcessQYGLP0HTRequest, //股权穿透 "QYGL5S1I": qygl.ProcessQYGL5S1IRequest, //企业司法涉诉I + "QYGLJ0Q1": qygl.ProcessQYGLJ0Q1Request, //企业股权结构全景查询 + "QYGLUY3S": qygl.ProcessQYGLUY3SRequest, //企业经营状态全景查询 // YYSY系列处理器 "YYSYD50F": yysy.ProcessYYSYD50FRequest, @@ -249,6 +251,7 @@ func registerAllProcessors(combService *comb.CombService) { "IVYZX5QZ": ivyz.ProcessIVYZX5QZRequest, //活体检测 "IVYZX5Q2": ivyz.ProcessIVYZX5Q2Request, //活体识别步骤二 "IVYZOCR1": ivyz.ProcessIVYZOCR1Request, //身份证OCR + "IVYZOCR2": ivyz.ProcessIVYZOCR2Request, //身份证OCR2数卖 // COMB系列处理器 - 只注册有自定义逻辑的组合包 "COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode diff --git a/internal/domains/api/services/form_config_service.go b/internal/domains/api/services/form_config_service.go index 2f321d1..f6f2fc4 100644 --- a/internal/domains/api/services/form_config_service.go +++ b/internal/domains/api/services/form_config_service.go @@ -256,6 +256,9 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string "QCXG3M7Z": &dto.QCXG3M7ZReq{}, //人车关系核验(ETC)10093 月更 "JRZQ1P5G": &dto.JRZQ1P5GReq{}, //全国自然人借贷压力指数查询(2) "IVYZOCR1": &dto.IVYZOCR1Req{}, //身份证OCR + "IVYZOCR2": &dto.IVYZOCR1Req{}, //身份证OCR2数卖 + "QYGLJ0Q1": &dto.QYGLJ0Q1Req{}, //企业股权结构全景查询 + "QYGLUY3S": &dto.QYGLUY3SReq{}, //企业经营状态全景查询 } // 优先返回已配置的DTO @@ -439,6 +442,7 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string { "ent_name": "企业名称", "legal_person": "法人姓名", "ent_code": "企业代码", + "ent_reg_no": "企业注册号", "auth_date": "授权日期", "date_range": "日期范围", "time_range": "时间范围", @@ -472,7 +476,7 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string { "notice_model": "车辆型号", "vlphoto_data": "行驶证图片", "carplate_type": "车辆号牌类型", - "image_url": "行驶证图片地址", + "image_url": "入参图片地址", "reg_url": "车辆登记证图片地址", "token": "token采集及获取结果时所使用的凭证,有效期2个小时,在此时效内,应用侧可以发起采集请求(重复的采集所触发的结果会被忽略)和结果查询", "vehicle_name": "车型名称", @@ -501,6 +505,7 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso "ent_name": "示例企业有限公司", "legal_person": "王五", "ent_code": "91110000123456789X", + "ent_reg_no": "110000000123456", "auth_date": "20240101-20241231", "date_range": "20240101-20241231", "time_range": "09:00-18:00", @@ -572,6 +577,7 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st "ent_name": "请输入企业全称", "legal_person": "请输入法人真实姓名", "ent_code": "请输入统一社会信用代码", + "ent_reg_no": "请输入企业注册号(统一社会信用代码)", "auth_date": "请输入授权日期范围(YYYYMMDD-YYYYMMDD)", "date_range": "请输入日期范围(YYYYMMDD-YYYYMMDD)", "time_range": "请输入时间范围(HH:MM-HH:MM)", @@ -593,7 +599,7 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st "plate_type": "请选择号牌类型(01或02)", "vin_code": "请输入17位车辆识别代号VIN码", "return_type": "请选择返回类型", - "photo_data": "请输入base64编码的人脸图片(支持JPG、BMP、PNG格式)", + "photo_data": "请输入base64编码的入参图片(支持JPG、BMP、PNG格式)", "ownerType": "请选择企业主类型", "type": "请选择查询类型", "query_reason_id": "请选择查询原因ID", @@ -645,6 +651,7 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s "ent_name": "请输入企业全称", "legal_person": "请输入法人真实姓名", "ent_code": "请输入统一社会信用代码", + "ent_reg_no": "请输入企业注册号(统一社会信用代码)", "auth_date": "请输入授权日期范围,格式:YYYYMMDD-YYYYMMDD,且日期范围必须包括今天", "date_range": "请输入日期范围,格式:YYYYMMDD-YYYYMMDD", "time_range": "请输入时间范围,格式:HH:MM-HH:MM", diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index 72f4b3c..caf2bb1 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -109,7 +109,7 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor { apiCode: "FLXG8B4D", params: map[string]interface{}{ - "mobile_no": params.MobileNo, + "mobile_no": params.MobileNo, "authorized": "1", }, }, @@ -117,9 +117,9 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor { apiCode: "JRZQ8A2D", params: map[string]interface{}{ - "mobile_no": params.MobileNo, - "id_card": params.IDCard, - "name": params.Name, + "mobile_no": params.MobileNo, + "id_card": params.IDCard, + "name": params.Name, "authorized": "1", }, }, @@ -127,9 +127,9 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor { apiCode: "JRZQ1D09", params: map[string]interface{}{ - "mobile_no": params.MobileNo, - "id_card": params.IDCard, - "name": params.Name, + "mobile_no": params.MobileNo, + "id_card": params.IDCard, + "name": params.Name, "authorized": "1", }, }, @@ -155,9 +155,9 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor { apiCode: "JRZQ5E9F", params: map[string]interface{}{ - "mobile_no": params.MobileNo, - "id_card": params.IDCard, - "name": params.Name, + "mobile_no": params.MobileNo, + "id_card": params.IDCard, + "name": params.Name, "authorized": "1", }, }, @@ -165,8 +165,8 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor { apiCode: "FLXGDEA9", params: map[string]interface{}{ - "id_card": params.IDCard, - "name": params.Name, + "id_card": params.IDCard, + "name": params.Name, "authorized": "1", }, }, @@ -211,7 +211,7 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor }() paramsBytes, err := json.Marshal(ac.params) - if err != nil { + if err != nil { log.Warn("序列化API参数失败", zap.String("api_code", ac.apiCode), zap.Error(err), @@ -266,7 +266,7 @@ func callProcessor(ctx context.Context, apiCode string, params []byte, deps *pro return nil, fmt.Errorf("未找到处理器: %s", apiCode) } respBytes, err := processor(ctx, params, deps) - if err != nil { + if err != nil { return nil, err } var data interface{} @@ -387,7 +387,7 @@ func buildBaseInfo(apiData map[string]interface{}, params dto.DWBG8B4DReq, log * // 从运营商三要素获取户籍所在地(address字段) if address, ok := yysyMap["address"].(string); ok && address != "" { baseInfo["location"] = address - } else { + } else { // 如果address不存在,尝试从身份证号获取 if location := getLocationFromIDCard(params.IDCard); location != "" { baseInfo["location"] = location @@ -496,7 +496,7 @@ func buildStandLiveInfo(apiData map[string]interface{}, log *zap.Logger) map[str } } } - + if status != "" { // "一致" -> "0", "不一致" -> "1" if status == "一致" { @@ -629,7 +629,7 @@ func buildVerifyRule(apiData map[string]interface{}, log *zap.Logger) string { // buildFraudRule 构建反欺诈规则(综合考虑fraudScore、特殊名单和借选指数风险) func buildFraudRule(apiData map[string]interface{}, log *zap.Logger) string { fraudScore := getFraudScore(apiData) - + // 如果fraudScore无效,检查特殊名单和借选指数 if fraudScore == -1 { // 检查特殊名单 @@ -654,7 +654,7 @@ func buildFraudRule(apiData map[string]interface{}, log *zap.Logger) string { } return "低风险" } - + // 根据fraudScore判断风险等级 if fraudScore >= 80 { return "高风险" @@ -1039,13 +1039,13 @@ func buildRiskWarning(apiData map[string]interface{}, log *zap.Logger) map[strin if variableValue, ok := variable["variableValue"].(map[string]interface{}); ok { // 检查近期申请频率(近7天、近15天、近1个月) checkRecentApplicationFrequency(variableValue, &riskWarning) - + // 检查银行/非银申请次数 checkBankApplicationFrequency(variableValue, &riskWarning) - + // 检查偿债压力(从借选指数评估获取) checkDebtPressure(apiData, &riskWarning) - + // 计算借贷评估风险计数 calculateLoanRiskCounts(variableValue, &riskWarning) } @@ -1152,21 +1152,21 @@ func buildRiskWarning(apiData map[string]interface{}, log *zap.Logger) map[strin // 计算总风险点数量(排除Counts字段和level字段) totalRiskCounts := 0 excludeFields := map[string]bool{ - "totalRiskCounts": true, + "totalRiskCounts": true, "level": true, - "sfhyfxRiskCounts": true, - "sfhyfxRiskHighCounts": true, - "sfhyfxRiskMiddleCounts": true, - "gazdyrhyRiskCounts": true, + "sfhyfxRiskCounts": true, + "sfhyfxRiskHighCounts": true, + "sfhyfxRiskMiddleCounts": true, + "gazdyrhyRiskCounts": true, "gazdyrhyRiskHighCounts": true, "gazdyrhyRiskMiddleCounts": true, - "yqfxRiskCounts": true, - "yqfxRiskHighCounts": true, - "yqfxRiskMiddleCounts": true, - "zlfxpgRiskCounts": true, - "zlfxpgRiskHighCounts": true, - "zlfxpgRiskMiddleCounts": true, - "jdpgRiskCounts": true, + "yqfxRiskCounts": true, + "yqfxRiskHighCounts": true, + "yqfxRiskMiddleCounts": true, + "zlfxpgRiskCounts": true, + "zlfxpgRiskHighCounts": true, + "zlfxpgRiskMiddleCounts": true, + "jdpgRiskCounts": true, "jdpgRiskHighCounts": true, "jdpgRiskMiddleCounts": true, "idCardRiskCounts": true, @@ -1277,7 +1277,7 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log } } } - + if status != "" { if status == "一致" { detail["sfzeysFlag"] = 2 // 低风险 @@ -1313,7 +1313,7 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log } } } - + if status != "" { personCheckDetails["result"] = status } @@ -1365,7 +1365,7 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log // 如果没有data字段,直接使用flxgMap(新格式) data = flxgMap } - + if moneyLaundering, ok := data["moneyLaundering"].(string); ok { antiFraudInfo["moneyLaundering"] = moneyLaundering } @@ -1448,7 +1448,7 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log if channel, ok := yysy4b21Map["channel"].(string); ok { phoneVailRisks["phoneCompany"] = convertChannelName(channel) } - + // 获取在网时长(status=0时也要提取) if yysy8b1cData != nil { if yysy8b1cMap, ok := yysy8b1cData.(map[string]interface{}); ok { @@ -1471,11 +1471,11 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log // 身份证号手机号归属地(belongRiskFlag 和 belongRisks) belongRisks := make(map[string]interface{}) belongRisks["num"] = "1" - + // 重新获取数据 yysy9e4aDataForBelong := getMapValue(apiData, "YYSY9E4A") yysyDataForBelong := getMapValue(apiData, "YYSYH6D2") - + if yysy9e4aDataForBelong != nil && yysyDataForBelong != nil { if yysy9e4aMap, ok := yysy9e4aDataForBelong.(map[string]interface{}); ok { if yysyMapForBelong, ok := yysyDataForBelong.(map[string]interface{}); ok { @@ -1621,7 +1621,7 @@ func buildRiskSupervision(apiData map[string]interface{}, log *zap.Logger) map[s // 从3C租赁申请意向获取数据 jrzq1d09Data := getMapValue(apiData, "JRZQ1D09") - + // 设置默认值 riskSupervision["rentalRiskListIdCardRelationsPhones"] = 0 riskSupervision["rentalRiskListPhoneRelationsIdCards"] = 0 @@ -1896,15 +1896,15 @@ func buildOverdueRiskProduct(apiData map[string]interface{}, log *zap.Logger) ma // 判断风险标识 hasOverdue := overdueRiskProduct["hasUnsettledOverdue"] == "逾期" - hasRecentOverdue := overdueRiskProduct["overdueLast7Days"] == "逾期" || - overdueRiskProduct["overdueLast14Days"] == "逾期" || + hasRecentOverdue := overdueRiskProduct["overdueLast7Days"] == "逾期" || + overdueRiskProduct["overdueLast14Days"] == "逾期" || overdueRiskProduct["overdueLast30Days"] == "逾期" - + if hasOverdue || hasRecentOverdue { - overdueRiskProduct["lyjlhyFlag"] = 1 // 高风险 + overdueRiskProduct["lyjlhyFlag"] = 1 // 高风险 overdueRiskProduct["dkzhktjFlag"] = 1 // 高风险 } else { - overdueRiskProduct["lyjlhyFlag"] = 2 // 低风险 + overdueRiskProduct["lyjlhyFlag"] = 2 // 低风险 overdueRiskProduct["dkzhktjFlag"] = 2 // 低风险 } } @@ -2028,7 +2028,7 @@ func buildMultCourtInfo(apiData map[string]interface{}, log *zap.Logger) map[str if lawsuitStat, ok := judicialData["lawsuitStat"].(map[string]interface{}); ok { // 收集所有涉案公告案件(民事、刑事、行政、保全、破产)- 都归到legalCases legalCases := make([]interface{}, 0) - + // 处理民事案件(civil)- 结构:civil.cases[] if civilVal, exists := lawsuitStat["civil"]; exists && civilVal != nil { if civil, ok := civilVal.(map[string]interface{}); ok && len(civil) > 0 { @@ -2178,7 +2178,7 @@ func buildMultCourtInfo(apiData map[string]interface{}, log *zap.Logger) map[str // convertCivilCase 转换民事案件 func convertCivilCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if cAh, ok := caseMap["c_ah"].(string); ok { caseInfo["caseNumber"] = cAh @@ -2259,7 +2259,7 @@ func convertCivilCase(caseMap map[string]interface{}) map[string]interface{} { // convertExecutionCase 转换执行案件 func convertExecutionCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if cAh, ok := caseMap["c_ah"].(string); ok { caseInfo["caseNumber"] = cAh @@ -2393,7 +2393,7 @@ func convertExecutionCase(caseMap map[string]interface{}) map[string]interface{} // convertBreachCase 转换失信案件 func convertBreachCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if caseNumber, ok := caseMap["caseNumber"].(string); ok { caseInfo["caseNumber"] = caseNumber @@ -2470,7 +2470,7 @@ func convertBreachCase(caseMap map[string]interface{}) map[string]interface{} { // convertLimitCase 转换限高案件 func convertLimitCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if caseNumber, ok := caseMap["caseNumber"].(string); ok { caseInfo["caseNumber"] = caseNumber @@ -2513,7 +2513,7 @@ func convertLimitCase(caseMap map[string]interface{}) map[string]interface{} { // convertPreservationCase 转换保全审查案件 func convertPreservationCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if cAh, ok := caseMap["c_ah"].(string); ok { caseInfo["caseNumber"] = cAh @@ -2607,7 +2607,7 @@ func convertPreservationCase(caseMap map[string]interface{}) map[string]interfac // convertCriminalCase 转换刑事案件 func convertCriminalCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if cAh, ok := caseMap["c_ah"].(string); ok { caseInfo["caseNumber"] = cAh @@ -2689,7 +2689,7 @@ func convertCriminalCase(caseMap map[string]interface{}) map[string]interface{} // convertAdministrativeCase 转换行政案件 func convertAdministrativeCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if cAh, ok := caseMap["c_ah"].(string); ok { caseInfo["caseNumber"] = cAh @@ -2772,7 +2772,7 @@ func convertAdministrativeCase(caseMap map[string]interface{}) map[string]interf // convertBankruptCase 转换破产清算案件 func convertBankruptCase(caseMap map[string]interface{}) map[string]interface{} { caseInfo := make(map[string]interface{}) - + // 案号 if cAh, ok := caseMap["c_ah"].(string); ok { caseInfo["caseNumber"] = cAh @@ -2919,11 +2919,11 @@ func buildOrganLoanPerformances(variableValue map[string]interface{}) []interfac // 时间周期映射 periodMap := map[string]string{ - "last7Day": "d7", - "last15Day": "d15", - "last1Month": "m1", - "last3Month": "m3", - "last6Month": "m6", + "last7Day": "d7", + "last15Day": "d15", + "last1Month": "m1", + "last3Month": "m3", + "last6Month": "m6", "last12Month": "m12", } @@ -2983,11 +2983,11 @@ func buildCustomerLoanPerformances(variableValue map[string]interface{}) []inter } periodMap := map[string]string{ - "last7Day": "d7", - "last15Day": "d15", - "last1Month": "m1", - "last3Month": "m3", - "last6Month": "m6", + "last7Day": "d7", + "last15Day": "d15", + "last1Month": "m1", + "last3Month": "m3", + "last6Month": "m6", "last12Month": "m12", } @@ -3001,7 +3001,7 @@ func buildCustomerLoanPerformances(variableValue map[string]interface{}) []inter cellOrgnum := getStringValue(variableValue, fmt.Sprintf("als_%s_cell_%s_orgnum", apiPeriod, customerType.prefix)) idAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_%s_allnum", apiPeriod, customerType.prefix)) cellAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_cell_%s_allnum", apiPeriod, customerType.prefix)) - + if idOrgnum == "" { idOrgnum = "0" } @@ -3014,7 +3014,7 @@ func buildCustomerLoanPerformances(variableValue map[string]interface{}) []inter if cellAllnum == "" { cellAllnum = "0" } - + perf[period] = fmt.Sprintf("%s/%s", idOrgnum, cellOrgnum) perf[period+"Count"] = fmt.Sprintf("%s/%s", idAllnum, cellAllnum) } @@ -3043,11 +3043,11 @@ func buildBusinessLoanPerformances(variableValue map[string]interface{}) []inter } periodMap := map[string]string{ - "last7Day": "d7", - "last15Day": "d15", - "last1Month": "m1", - "last3Month": "m3", - "last6Month": "m6", + "last7Day": "d7", + "last15Day": "d15", + "last1Month": "m1", + "last3Month": "m3", + "last6Month": "m6", "last12Month": "m12", } @@ -3061,7 +3061,7 @@ func buildBusinessLoanPerformances(variableValue map[string]interface{}) []inter cellOrgnum := getStringValue(variableValue, fmt.Sprintf("als_%s_cell_%s_orgnum", apiPeriod, businessType.prefix)) idAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_%s_allnum", apiPeriod, businessType.prefix)) cellAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_cell_%s_allnum", apiPeriod, businessType.prefix)) - + if idOrgnum == "" { idOrgnum = "0" } @@ -3074,7 +3074,7 @@ func buildBusinessLoanPerformances(variableValue map[string]interface{}) []inter if cellAllnum == "" { cellAllnum = "0" } - + perf[period] = fmt.Sprintf("%s/%s", idOrgnum, cellOrgnum) perf[period+"Count"] = fmt.Sprintf("%s/%s", idAllnum, cellAllnum) } @@ -3102,11 +3102,11 @@ func buildTimeLoanPerformances(variableValue map[string]interface{}) []interface } periodMap := map[string]string{ - "last7Day": "d7", - "last15Day": "d15", - "last1Month": "m1", - "last3Month": "m3", - "last6Month": "m6", + "last7Day": "d7", + "last15Day": "d15", + "last1Month": "m1", + "last3Month": "m3", + "last6Month": "m6", "last12Month": "m12", } @@ -3120,7 +3120,7 @@ func buildTimeLoanPerformances(variableValue map[string]interface{}) []interface cellOrgnum := getStringValue(variableValue, fmt.Sprintf("als_%s_cell_%s_%s_orgnum", apiPeriod, timeType.orgType, timeType.timeType)) idAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_%s_%s_allnum", apiPeriod, timeType.orgType, timeType.timeType)) cellAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_cell_%s_%s_allnum", apiPeriod, timeType.orgType, timeType.timeType)) - + if idOrgnum == "" { idOrgnum = "0" } @@ -3133,7 +3133,7 @@ func buildTimeLoanPerformances(variableValue map[string]interface{}) []interface if cellAllnum == "" { cellAllnum = "0" } - + perf[period] = fmt.Sprintf("%s/%s", idOrgnum, cellOrgnum) perf[period+"Count"] = fmt.Sprintf("%s/%s", idAllnum, cellAllnum) } @@ -3151,7 +3151,7 @@ func checkLoanRisk(variableValue map[string]interface{}) bool { for _, period := range periods { bankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_bank_allnum", period)) nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period)) - + if bankAllnum != "" && bankAllnum != "0" { if count, err := strconv.Atoi(bankAllnum); err == nil && count >= 10 { return true @@ -3593,7 +3593,7 @@ func checkRecentApplicationFrequency(variableValue map[string]interface{}, riskW for _, period := range periods { bankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_bank_allnum", period.apiPeriod)) nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period.apiPeriod)) - + // 如果近7天申请次数>=10,认为是极为频繁(调高风险阈值,进一步提高阈值) if bankAllnum != "" && bankAllnum != "0" { if count, err := strconv.Atoi(bankAllnum); err == nil && count >= 10 { @@ -3626,7 +3626,7 @@ func checkBankApplicationFrequency(variableValue map[string]interface{}, riskWar for _, period := range periods { bankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_bank_allnum", period.apiPeriod)) nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period.apiPeriod)) - + if bankAllnum != "" && bankAllnum != "0" { if count, err := strconv.Atoi(bankAllnum); err == nil { bankTotal += count @@ -3663,7 +3663,7 @@ func checkDebtPressure(apiData map[string]interface{}, riskWarning *map[string]i // 检查当前逾期金额和机构数 currentOverdueAmount := getStringValue(jrzq5e9fMap, "xyp_cpl0072") currentOverdueInstitutionCount := getStringValue(jrzq5e9fMap, "xyp_cpl0071") - + // 如果当前逾期金额较大或机构数较多,认为偿债压力极高 if currentOverdueAmount != "" && currentOverdueAmount != "0" && currentOverdueAmount != "1" { (*riskWarning)["highDebtPressure"] = 1 @@ -3718,7 +3718,7 @@ func checkRentalApplicationFrequency(jrzq1d09Map map[string]interface{}, riskWar for _, period := range periods { idAllnum := getStringValue(jrzq1d09Map, fmt.Sprintf("alc_%s_id_allnum", period.apiPeriod)) cellAllnum := getStringValue(jrzq1d09Map, fmt.Sprintf("alc_%s_cell_allnum", period.apiPeriod)) - + // 取较大的值 idCount := 0 cellCount := 0 @@ -3732,7 +3732,7 @@ func checkRentalApplicationFrequency(jrzq1d09Map map[string]interface{}, riskWar cellCount = count } } - + if idCount > cellCount { totalCount += idCount } else { @@ -3779,15 +3779,15 @@ func buildLeasingRiskAssessment(apiData map[string]interface{}, log *zap.Logger) // - Institution(3C机构): 使用id(身份证)查询的数据 // - Platform(3C平台): 使用cell(手机号)查询的数据 // 格式: "身份证/手机号",如果没有匹配的就是0 - + // 时间周期映射:3Days->d3, 7Days->d7, 14Days->d14, Month->m1, 3Months->m3, 6Months->m6, 12Months->m12 periodMap := map[string]string{ - "3Days": "d3", - "7Days": "d7", - "14Days": "d14", - "Month": "m1", - "3Months": "m3", - "6Months": "m6", + "3Days": "d3", + "7Days": "d7", + "14Days": "d14", + "Month": "m1", + "3Months": "m3", + "6Months": "m6", "12Months": "m12", } @@ -4050,21 +4050,21 @@ func normalizeProvinceName(province string) string { if province == "" { return "" } - + // 去除前后空格 province = strings.TrimSpace(province) - + // 特殊处理:内蒙古(必须在最前面) if strings.Contains(province, "内蒙古") { return "内蒙古" } - + // 处理自治区、特别行政区、直辖市等后缀 suffixes := []string{ "壮族自治区", "回族自治区", "维吾尔自治区", "自治区", "特别行政区", "省", "市", } - + normalized := province for _, suffix := range suffixes { if strings.HasSuffix(normalized, suffix) { @@ -4072,7 +4072,7 @@ func normalizeProvinceName(province string) string { break } } - + return normalized } @@ -4082,15 +4082,15 @@ func normalizeCityName(city string) string { if city == "" { return "" } - + // 去除前后空格 city = strings.TrimSpace(city) - + // 处理各种后缀 suffixes := []string{ "地区", "市", "自治州", "盟", "县", "区", } - + normalized := city for _, suffix := range suffixes { if strings.HasSuffix(normalized, suffix) { @@ -4098,7 +4098,7 @@ func normalizeCityName(city string) string { break } } - + return normalized } @@ -4107,12 +4107,12 @@ func extractProvinceFromAddress(address string) string { if address == "" { return "" } - + // 特殊处理:内蒙古(必须在最前面,因为"内蒙古"可能被其他模式误匹配) if strings.HasPrefix(address, "内蒙古") { return "内蒙古自治区" } - + // 处理直辖市(必须在自治区之前) if strings.HasPrefix(address, "北京") { return "北京市" @@ -4126,7 +4126,7 @@ func extractProvinceFromAddress(address string) string { if strings.HasPrefix(address, "重庆") { return "重庆市" } - + // 处理各种省份格式(按长度从长到短,避免误匹配) patterns := []struct { pattern string @@ -4170,12 +4170,12 @@ func extractProvinceFromAddress(address string) string { // 处理"XX省"格式(最后处理,因为可能与其他模式冲突) {"省", func(addr string) string { if idx := strings.Index(addr, "省"); idx > 0 { - return addr[:idx+1] + return addr[:idx+len("省")] } return "" }}, } - + for _, p := range patterns { if strings.Contains(address, p.pattern) { if result := p.extract(address); result != "" { @@ -4183,7 +4183,7 @@ func extractProvinceFromAddress(address string) string { } } } - + return "" } @@ -4192,16 +4192,16 @@ func extractCityFromAddress(address string) string { if address == "" { return "" } - + // 先提取省份,然后从剩余部分提取城市 province := extractProvinceFromAddress(address) if province == "" { return "" } - + // 去除省份部分 cityPart := address[len(province):] - + // 处理各种城市格式 patterns := []string{"地区", "市", "自治州", "盟"} for _, pattern := range patterns { @@ -4209,7 +4209,7 @@ func extractCityFromAddress(address string) string { return cityPart[:idx+len(pattern)] } } - + return "" } @@ -4221,7 +4221,7 @@ func compareLocation(address1, province2, city2 string) bool { if address1 == "" { return false } - + // 如果号码归属地只有省份,检查省份是否出现在户籍所在地中 if province2 != "" && city2 == "" { // 标准化省份名称,去除后缀 @@ -4229,20 +4229,20 @@ func compareLocation(address1, province2, city2 string) bool { // 检查标准化后的省份名称是否出现在户籍所在地中 return strings.Contains(address1, province2Norm) } - + // 如果号码归属地有省份和城市,检查两者是否都出现在户籍所在地中 if province2 != "" && city2 != "" { // 标准化省份和城市名称,去除后缀 province2Norm := normalizeProvinceName(province2) city2Norm := normalizeCityName(city2) - + // 检查省份和城市是否都出现在户籍所在地中 provinceMatch := strings.Contains(address1, province2Norm) cityMatch := strings.Contains(address1, city2Norm) - + return provinceMatch && cityMatch } - + return false } @@ -4262,7 +4262,7 @@ func convertChannel(channel string) string { // convertChannelName 转换运营商名称(处理中文名称) func convertChannelName(channel string) string { // 如果已经是中文名称,直接返回 - if strings.Contains(channel, "移动") || strings.Contains(channel, "联通") || + if strings.Contains(channel, "移动") || strings.Contains(channel, "联通") || strings.Contains(channel, "电信") || strings.Contains(channel, "广电") { return channel } @@ -4276,7 +4276,7 @@ func convertStatusFromOnlineStatus(data map[string]interface{}) int { // 获取status字段(0-在网,1-不在网) var statusVal interface{} var ok bool - + // status可能是int或float64类型 if statusVal, ok = data["status"]; !ok { return -1 // 未查得 @@ -4362,7 +4362,7 @@ func getFraudScore(apiData map[string]interface{}) int { // 从涉赌涉诈风险评估获取基础反欺诈评分 flxgData := getMapValue(apiData, "FLXG8B4D") baseScore := -1 - + if flxgData != nil { flxgMap, ok := flxgData.(map[string]interface{}) if ok { @@ -4505,7 +4505,7 @@ func getCreditScore(apiData map[string]interface{}) int { return 900 } parsed, err := strconv.ParseFloat(v, 64) - if err != nil { + if err != nil { // 解析失败,返回900(默认信用较好) return 900 } @@ -4546,7 +4546,7 @@ func isDevelopmentMode() bool { if useMock := os.Getenv("DWBG_USE_MOCK_DATA"); useMock != "" { return useMock == "1" || useMock == "true" || useMock == "yes" } - + env := os.Getenv("ENV") if env == "" { env = os.Getenv("CONFIG_ENV") @@ -4554,12 +4554,12 @@ func isDevelopmentMode() bool { if env == "" { env = os.Getenv("APP_ENV") } - + // 默认开发环境 if env == "" { return true } - + return env == "development" } @@ -4571,32 +4571,32 @@ func loadMockAPIDataFromFile(log *zap.Logger) map[string]interface{} { // 默认使用最新的导出文件 mockDataPath = "api_data_export/api_data_4522_20260214_152339.json" } - + // 检查文件是否存在 if _, err := os.Stat(mockDataPath); os.IsNotExist(err) { log.Warn("开发模式:模拟数据文件不存在", zap.String("path", mockDataPath)) return nil } - + // 读取文件内容 fileData, err := os.ReadFile(mockDataPath) if err != nil { log.Warn("开发模式:读取模拟数据文件失败", zap.String("path", mockDataPath), zap.Error(err)) return nil } - + // 解析JSON var apiData map[string]interface{} if err := json.Unmarshal(fileData, &apiData); err != nil { log.Warn("开发模式:解析模拟数据JSON失败", zap.String("path", mockDataPath), zap.Error(err)) return nil } - - log.Info("开发模式:成功加载模拟数据", + + log.Info("开发模式:成功加载模拟数据", zap.String("path", mockDataPath), zap.Int("api_count", len(apiData)), ) - + return apiData } diff --git a/internal/domains/api/services/processors/ivyz/ivyzocr2_processor.go b/internal/domains/api/services/processors/ivyz/ivyzocr2_processor.go new file mode 100644 index 0000000..7f4c7b3 --- /dev/null +++ b/internal/domains/api/services/processors/ivyz/ivyzocr2_processor.go @@ -0,0 +1,48 @@ +package ivyz + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/shumai" +) + +// ProcessIVYZOCR2Request IVYZOCR2 OCR识别API处理方法 +func ProcessIVYZOCR2Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.IVYZOCR1Req + 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) + } + + reqFormData := map[string]interface{}{ + "photo_data": paramsDto.PhotoData, + } + + // 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded + apiPath := "/v4/idcard/ocr" // 接口路径,根据数脉文档填写(如 v4/xxx) + respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData) + if err != nil { + if errors.Is(err, shumai.ErrNotFound) { + // 查无记录情况 + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, shumai.ErrDatasource) { + // 数据源错误 + return nil, errors.Join(processors.ErrDatasource, err) + } else if errors.Is(err, shumai.ErrSystem) { + // 系统错误 + return nil, errors.Join(processors.ErrSystem, err) + } else { + // 其他未知错误 + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/jrzq/jrzqocre_processor.go b/internal/domains/api/services/processors/jrzq/jrzqocre_processor.go new file mode 100644 index 0000000..b9e9eb5 --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/jrzqocre_processor.go @@ -0,0 +1,52 @@ +package jrzq + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/shumai" +) + +// ProcessJRZQOCREERequest JRZQOCRE 银行卡OCR API 数卖服务示例 +func ProcessJRZQOCREERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.JRZQOCREReq + 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 paramsDto.PhotoData == "" && paramsDto.ImageUrl == "" { + return nil, errors.Join(processors.ErrInvalidParam, errors.New("photo_data or image_url is required")) + } + + // 2选1:有值的用对应 key,空则用另一个 + reqFormData := make(map[string]interface{}) + if paramsDto.PhotoData != "" { + reqFormData["image"] = paramsDto.PhotoData + } else { + reqFormData["url"] = paramsDto.ImageUrl + } + // 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded + apiPath := "/v2/bankcard/ocr" // 接口路径,根据数脉文档填写(如 v4/xxx) + respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData) + if err != nil { + if errors.Is(err, shumai.ErrDatasource) { + // 数据源错误 + return nil, errors.Join(processors.ErrDatasource, err) + } else if errors.Is(err, shumai.ErrSystem) { + // 系统错误 + return nil, errors.Join(processors.ErrSystem, err) + } else { + // 其他未知错误 + return nil, errors.Join(processors.ErrSystem, err) + } + } + + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/jrzq/jrzqocry_processor.go b/internal/domains/api/services/processors/jrzq/jrzqocry_processor.go new file mode 100644 index 0000000..414c9d3 --- /dev/null +++ b/internal/domains/api/services/processors/jrzq/jrzqocry_processor.go @@ -0,0 +1,48 @@ +package jrzq + +import ( + "context" + "encoding/json" + "errors" + + "tyapi-server/internal/domains/api/dto" + "tyapi-server/internal/domains/api/services/processors" + "tyapi-server/internal/infrastructure/external/shujubao" +) + +// ProcessJRZQOCRYERequest JRZQOCRY 银行卡OCR API 处理方法(使用数据宝服务示例) +func ProcessJRZQOCRYERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.JRZQOCRYReq + 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) + } + + // 构建数据宝入参:姓名、身份证、手机号、银行卡号(sign 外的业务参数可按需 AES 加密后作为 bodyData) + reqParams := map[string]interface{}{ + "key": "3ee8e7a7a71870db2c0bf98e7e6b8b5c", + "imageId": paramsDto.PhotoData, + } + + // 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197 + apiPath := "/trade/user/1986" + data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams) + if err != nil { + if errors.Is(err, shujubao.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } + if errors.Is(err, shujubao.ErrQueryEmpty) { + return nil, errors.Join(processors.ErrNotFound, err) + } + return nil, errors.Join(processors.ErrSystem, err) + } + + respBytes, err := json.Marshal(data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qyglj0q1_processor.go b/internal/domains/api/services/processors/qygl/qyglj0q1_processor.go new file mode 100644 index 0000000..3a4b185 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qyglj0q1_processor.go @@ -0,0 +1,50 @@ +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/shujubao" +) + +// ProcessQYGLJ0Q1Request QYGLJ0Q1 企业股权结构全景查询 API 处理方法(使用数据宝服务示例) +func ProcessQYGLJ0Q1Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGLJ0Q1Req + 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) + } + + // 构建数据宝入参:姓名、身份证、手机号、银行卡号(sign 外的业务参数可按需 AES 加密后作为 bodyData) + reqParams := map[string]interface{}{ + "key": "adac456f7b4ced764b606c8b07fed4d3", + "mobile": paramsDto.MobileNo, + "idcard": paramsDto.IDCard, + "name": paramsDto.Name, + } + + // 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197 + apiPath := "/communication/personal/1979" + data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams) + if err != nil { + if errors.Is(err, shujubao.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } + if errors.Is(err, shujubao.ErrQueryEmpty) { + return nil, errors.Join(processors.ErrNotFound, err) + } + return nil, errors.Join(processors.ErrSystem, err) + } + + respBytes, err := json.Marshal(data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + return respBytes, nil +} diff --git a/internal/domains/api/services/processors/qygl/qygluy3s_processor.go b/internal/domains/api/services/processors/qygl/qygluy3s_processor.go new file mode 100644 index 0000000..2cd8500 --- /dev/null +++ b/internal/domains/api/services/processors/qygl/qygluy3s_processor.go @@ -0,0 +1,50 @@ +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/shujubao" +) + +// ProcessQYGLUY3SRequest QYGLUY3S 企业经营状态全景查询 API 处理方法(使用数据宝服务示例) +func ProcessQYGLUY3SRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) { + var paramsDto dto.QYGLUY3SReq + 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) + } + + // 构建数据宝入参:姓名、身份证、手机号、银行卡号(sign 外的业务参数可按需 AES 加密后作为 bodyData) + reqParams := map[string]interface{}{ + "key": "5131227a847c06c111f624a22ebacc06", + "entName": paramsDto.EntName, + "regno": paramsDto.EntRegno, + "creditcode": paramsDto.EntCode, + } + + // 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197 + apiPath := "/communication/personal/10195" + data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams) + if err != nil { + if errors.Is(err, shujubao.ErrDatasource) { + return nil, errors.Join(processors.ErrDatasource, err) + } + if errors.Is(err, shujubao.ErrQueryEmpty) { + return nil, errors.Join(processors.ErrNotFound, err) + } + return nil, errors.Join(processors.ErrSystem, err) + } + + respBytes, err := json.Marshal(data) + if err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + return respBytes, nil +}