diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index d4b4282..208350a 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -781,28 +781,26 @@ func buildRiskWarning(apiData map[string]interface{}, log *zap.Logger) map[strin if yysy9e4aData != nil && yysyData != nil { if yysy9e4aMap, ok := yysy9e4aData.(map[string]interface{}); ok { if yysyMap, ok := yysyData.(map[string]interface{}); ok { - // 从身份证号获取省份(前2位) - var idCardProvince string + // 从身份证号获取地址 + var idCardAddress string if address, ok := yysyMap["address"].(string); ok && address != "" { - // 从地址中提取省份(简化处理,取前2-3个字符) - if len(address) >= 2 { - idCardProvince = address[:2] - // 处理"省"字 - idCardProvince = strings.TrimSuffix(idCardProvince, "省") - } + idCardAddress = address } - // 从手机号归属地获取省份 - var phoneProvince string + // 从手机号归属地获取省份和城市 + var phoneProvince, phoneCity string if provinceName, ok := yysy9e4aMap["provinceName"].(string); ok { phoneProvince = provinceName - // 处理"省"字 - phoneProvince = strings.TrimSuffix(phoneProvince, "省") + } + if cityName, ok := yysy9e4aMap["cityName"].(string); ok { + phoneCity = cityName } - // 判断省份是否一致 - if idCardProvince != "" && phoneProvince != "" && idCardProvince != phoneProvince { - riskWarning["idCardPhoneProvinceMismatch"] = 1 + // 使用智能比较函数判断是否一致 + if idCardAddress != "" && phoneProvince != "" { + if !compareLocation(idCardAddress, phoneProvince, phoneCity) { + riskWarning["idCardPhoneProvinceMismatch"] = 1 + } } } } @@ -1483,33 +1481,11 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log if yysyMapForBelong, ok := yysyDataForBelong.(map[string]interface{}); ok { // 从身份证号地址获取省份和城市 var personProvince, personCity string + var idCardAddress string if address, ok := yysyMapForBelong["address"].(string); ok && address != "" { - // 解析地址,提取省份和城市 - // 格式通常是:省份+市+县/区 - parts := strings.Split(address, "省") - if len(parts) > 1 { - personProvince = parts[0] + "省" - cityPart := parts[1] - if strings.Contains(cityPart, "市") { - cityParts := strings.Split(cityPart, "市") - if len(cityParts) > 0 { - personCity = cityParts[0] + "市" - } - } - } else { - // 尝试其他格式 - if strings.Contains(address, "省") { - provinceEnd := strings.Index(address, "省") - personProvince = address[:provinceEnd+1] - if provinceEnd+1 < len(address) { - cityPart := address[provinceEnd+1:] - if strings.Contains(cityPart, "市") { - cityEnd := strings.Index(cityPart, "市") - personCity = cityPart[:cityEnd+1] - } - } - } - } + idCardAddress = address + personProvince = extractProvinceFromAddress(address) + personCity = extractCityFromAddress(address) } // 从手机号归属地获取省份和城市 @@ -1533,15 +1509,12 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log belongRisks["phoneCardType"] = convertChannel(channel) } - // 判断归属地是否一致 - if personProvince != "" && phoneProvince != "" { - // 标准化省份名称(去除"省"字后比较) - personProvinceNorm := strings.TrimSuffix(personProvince, "省") - phoneProvinceNorm := strings.TrimSuffix(phoneProvince, "省") - if personProvinceNorm != phoneProvinceNorm { - detail["belongRiskFlag"] = 1 // 高风险 + // 判断归属地是否一致(使用智能比较函数) + if idCardAddress != "" && phoneProvince != "" { + if compareLocation(idCardAddress, phoneProvince, phoneCity) { + detail["belongRiskFlag"] = 2 // 低风险(一致) } else { - detail["belongRiskFlag"] = 2 // 低风险 + detail["belongRiskFlag"] = 1 // 高风险(不一致) } } else { detail["belongRiskFlag"] = 0 // 未查得 @@ -3162,19 +3135,19 @@ func buildTimeLoanPerformances(variableValue map[string]interface{}) []interface // checkLoanRisk 检查借贷风险 func checkLoanRisk(variableValue map[string]interface{}) bool { - // 检查近期申请频率是否过高 + // 检查近期申请频率是否过高(调高风险阈值:从>=5提高到>=10) periods := []string{"d7", "d15", "m1", "m3"} 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 >= 5 { + if count, err := strconv.Atoi(bankAllnum); err == nil && count >= 10 { return true } } if nbankAllnum != "" && nbankAllnum != "0" { - if count, err := strconv.Atoi(nbankAllnum); err == nil && count >= 5 { + if count, err := strconv.Atoi(nbankAllnum); err == nil && count >= 10 { return true } } @@ -3610,15 +3583,15 @@ func checkRecentApplicationFrequency(variableValue map[string]interface{}, riskW 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天申请次数>=5,认为是极为频繁 + // 如果近7天申请次数>=10,认为是极为频繁(调高风险阈值,进一步提高阈值) if bankAllnum != "" && bankAllnum != "0" { - if count, err := strconv.Atoi(bankAllnum); err == nil && count >= 5 { + if count, err := strconv.Atoi(bankAllnum); err == nil && count >= 10 { (*riskWarning)[period.field] = 1 break } } if nbankAllnum != "" && nbankAllnum != "0" { - if count, err := strconv.Atoi(nbankAllnum); err == nil && count >= 5 { + if count, err := strconv.Atoi(nbankAllnum); err == nil && count >= 10 { (*riskWarning)[period.field] = 1 break } @@ -3655,17 +3628,17 @@ func checkBankApplicationFrequency(variableValue map[string]interface{}, riskWar } } - // 银行申请次数极多(>=10) - if bankTotal >= 10 { + // 银行申请次数极多(>=20,调高风险阈值,进一步提高阈值) + if bankTotal >= 20 { (*riskWarning)["frequentBankApplications"] = 1 - } else if bankTotal >= 5 { + } else if bankTotal >= 15 { (*riskWarning)["moreFrequentBankApplications"] = 1 } - // 非银申请次数极多(>=10) - if nbankTotal >= 10 { + // 非银申请次数极多(>=20,调高风险阈值,进一步提高阈值) + if nbankTotal >= 20 { (*riskWarning)["frequentNonBankApplications"] = 1 - } else if nbankTotal >= 5 { + } else if nbankTotal >= 15 { (*riskWarning)["moreFrequentNonBankApplications"] = 1 } } @@ -3756,11 +3729,11 @@ func checkRentalApplicationFrequency(jrzq1d09Map map[string]interface{}, riskWar } } - // 租赁申请次数极多(>=10) - if totalCount >= 10 { + // 租赁申请次数极多(>=20,调高风险阈值,进一步提高阈值) + if totalCount >= 20 { (*riskWarning)["veryFrequentRentalApplications"] = 1 (*riskWarning)["frequentRentalApplications"] = 1 - } else if totalCount >= 5 { + } else if totalCount >= 15 { (*riskWarning)["frequentRentalApplications"] = 1 } } @@ -4011,6 +3984,208 @@ func getPhoneArea(mobile string) string { return "" } +// normalizeProvinceName 标准化省份名称,用于比较 +// 处理各种格式:广西壮族自治区 -> 广西,北京市 -> 北京,内蒙古自治区 -> 内蒙古等 +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) { + normalized = strings.TrimSuffix(normalized, suffix) + break + } + } + + return normalized +} + +// normalizeCityName 标准化城市名称,用于比较 +// 处理各种格式:南宁地区 -> 南宁,南宁市 -> 南宁等 +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) { + normalized = strings.TrimSuffix(normalized, suffix) + break + } + } + + return normalized +} + +// extractProvinceFromAddress 从完整地址中提取省份名称 +func extractProvinceFromAddress(address string) string { + if address == "" { + return "" + } + + // 特殊处理:内蒙古(必须在最前面,因为"内蒙古"可能被其他模式误匹配) + if strings.HasPrefix(address, "内蒙古") { + return "内蒙古自治区" + } + + // 处理直辖市(必须在自治区之前) + if strings.HasPrefix(address, "北京") { + return "北京市" + } + if strings.HasPrefix(address, "上海") { + return "上海市" + } + if strings.HasPrefix(address, "天津") { + return "天津市" + } + if strings.HasPrefix(address, "重庆") { + return "重庆市" + } + + // 处理各种省份格式(按长度从长到短,避免误匹配) + patterns := []struct { + pattern string + extract func(string) string + }{ + // 处理"XX壮族自治区"格式(最长,优先匹配) + {"壮族自治区", func(addr string) string { + if idx := strings.Index(addr, "壮族自治区"); idx > 0 { + return addr[:idx+len("壮族自治区")] + } + return "" + }}, + // 处理"XX回族自治区"格式 + {"回族自治区", func(addr string) string { + if idx := strings.Index(addr, "回族自治区"); idx > 0 { + return addr[:idx+len("回族自治区")] + } + return "" + }}, + // 处理"XX维吾尔自治区"格式 + {"维吾尔自治区", func(addr string) string { + if idx := strings.Index(addr, "维吾尔自治区"); idx > 0 { + return addr[:idx+len("维吾尔自治区")] + } + return "" + }}, + // 处理"XX特别行政区"格式 + {"特别行政区", func(addr string) string { + if idx := strings.Index(addr, "特别行政区"); idx > 0 { + return addr[:idx+len("特别行政区")] + } + return "" + }}, + // 处理"XX自治区"格式(如西藏、宁夏,必须在其他自治区之后) + {"自治区", func(addr string) string { + if idx := strings.Index(addr, "自治区"); idx > 0 { + return addr[:idx+len("自治区")] + } + return "" + }}, + // 处理"XX省"格式(最后处理,因为可能与其他模式冲突) + {"省", func(addr string) string { + if idx := strings.Index(addr, "省"); idx > 0 { + return addr[:idx+1] + } + return "" + }}, + } + + for _, p := range patterns { + if strings.Contains(address, p.pattern) { + if result := p.extract(address); result != "" { + return result + } + } + } + + return "" +} + +// extractCityFromAddress 从完整地址中提取城市名称 +func extractCityFromAddress(address string) string { + if address == "" { + return "" + } + + // 先提取省份,然后从剩余部分提取城市 + province := extractProvinceFromAddress(address) + if province == "" { + return "" + } + + // 去除省份部分 + cityPart := address[len(province):] + + // 处理各种城市格式 + patterns := []string{"地区", "市", "自治州", "盟"} + for _, pattern := range patterns { + if idx := strings.Index(cityPart, pattern); idx > 0 { + return cityPart[:idx+len(pattern)] + } + } + + return "" +} + +// compareLocation 比较两个地址是否一致(省份和城市) +// 使用号码归属地(格式:省份-城市)去匹配户籍所在地 +// 如果号码归属地中的省份和城市都出现在户籍所在地中,则认为一致 +// 返回true表示一致,false表示不一致 +func compareLocation(address1, province2, city2 string) bool { + if address1 == "" { + return false + } + + // 如果号码归属地只有省份,检查省份是否出现在户籍所在地中 + if province2 != "" && city2 == "" { + // 标准化省份名称,去除后缀 + province2Norm := normalizeProvinceName(province2) + // 检查标准化后的省份名称是否出现在户籍所在地中 + 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 +} + func convertChannel(channel string) string { channelMap := map[string]string{ "cmcc": "中国移动",