f
This commit is contained in:
@@ -781,28 +781,26 @@ func buildRiskWarning(apiData map[string]interface{}, log *zap.Logger) map[strin
|
|||||||
if yysy9e4aData != nil && yysyData != nil {
|
if yysy9e4aData != nil && yysyData != nil {
|
||||||
if yysy9e4aMap, ok := yysy9e4aData.(map[string]interface{}); ok {
|
if yysy9e4aMap, ok := yysy9e4aData.(map[string]interface{}); ok {
|
||||||
if yysyMap, ok := yysyData.(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 != "" {
|
if address, ok := yysyMap["address"].(string); ok && address != "" {
|
||||||
// 从地址中提取省份(简化处理,取前2-3个字符)
|
idCardAddress = address
|
||||||
if len(address) >= 2 {
|
|
||||||
idCardProvince = address[:2]
|
|
||||||
// 处理"省"字
|
|
||||||
idCardProvince = strings.TrimSuffix(idCardProvince, "省")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从手机号归属地获取省份
|
// 从手机号归属地获取省份和城市
|
||||||
var phoneProvince string
|
var phoneProvince, phoneCity string
|
||||||
if provinceName, ok := yysy9e4aMap["provinceName"].(string); ok {
|
if provinceName, ok := yysy9e4aMap["provinceName"].(string); ok {
|
||||||
phoneProvince = provinceName
|
phoneProvince = provinceName
|
||||||
// 处理"省"字
|
}
|
||||||
phoneProvince = strings.TrimSuffix(phoneProvince, "省")
|
if cityName, ok := yysy9e4aMap["cityName"].(string); ok {
|
||||||
|
phoneCity = cityName
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断省份是否一致
|
// 使用智能比较函数判断是否一致
|
||||||
if idCardProvince != "" && phoneProvince != "" && idCardProvince != phoneProvince {
|
if idCardAddress != "" && phoneProvince != "" {
|
||||||
riskWarning["idCardPhoneProvinceMismatch"] = 1
|
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 {
|
if yysyMapForBelong, ok := yysyDataForBelong.(map[string]interface{}); ok {
|
||||||
// 从身份证号地址获取省份和城市
|
// 从身份证号地址获取省份和城市
|
||||||
var personProvince, personCity string
|
var personProvince, personCity string
|
||||||
|
var idCardAddress string
|
||||||
if address, ok := yysyMapForBelong["address"].(string); ok && address != "" {
|
if address, ok := yysyMapForBelong["address"].(string); ok && address != "" {
|
||||||
// 解析地址,提取省份和城市
|
idCardAddress = address
|
||||||
// 格式通常是:省份+市+县/区
|
personProvince = extractProvinceFromAddress(address)
|
||||||
parts := strings.Split(address, "省")
|
personCity = extractCityFromAddress(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]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从手机号归属地获取省份和城市
|
// 从手机号归属地获取省份和城市
|
||||||
@@ -1533,15 +1509,12 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log
|
|||||||
belongRisks["phoneCardType"] = convertChannel(channel)
|
belongRisks["phoneCardType"] = convertChannel(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断归属地是否一致
|
// 判断归属地是否一致(使用智能比较函数)
|
||||||
if personProvince != "" && phoneProvince != "" {
|
if idCardAddress != "" && phoneProvince != "" {
|
||||||
// 标准化省份名称(去除"省"字后比较)
|
if compareLocation(idCardAddress, phoneProvince, phoneCity) {
|
||||||
personProvinceNorm := strings.TrimSuffix(personProvince, "省")
|
detail["belongRiskFlag"] = 2 // 低风险(一致)
|
||||||
phoneProvinceNorm := strings.TrimSuffix(phoneProvince, "省")
|
|
||||||
if personProvinceNorm != phoneProvinceNorm {
|
|
||||||
detail["belongRiskFlag"] = 1 // 高风险
|
|
||||||
} else {
|
} else {
|
||||||
detail["belongRiskFlag"] = 2 // 低风险
|
detail["belongRiskFlag"] = 1 // 高风险(不一致)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
detail["belongRiskFlag"] = 0 // 未查得
|
detail["belongRiskFlag"] = 0 // 未查得
|
||||||
@@ -3162,19 +3135,19 @@ func buildTimeLoanPerformances(variableValue map[string]interface{}) []interface
|
|||||||
|
|
||||||
// checkLoanRisk 检查借贷风险
|
// checkLoanRisk 检查借贷风险
|
||||||
func checkLoanRisk(variableValue map[string]interface{}) bool {
|
func checkLoanRisk(variableValue map[string]interface{}) bool {
|
||||||
// 检查近期申请频率是否过高
|
// 检查近期申请频率是否过高(调高风险阈值:从>=5提高到>=10)
|
||||||
periods := []string{"d7", "d15", "m1", "m3"}
|
periods := []string{"d7", "d15", "m1", "m3"}
|
||||||
for _, period := range periods {
|
for _, period := range periods {
|
||||||
bankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_bank_allnum", period))
|
bankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_bank_allnum", period))
|
||||||
nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period))
|
nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period))
|
||||||
|
|
||||||
if bankAllnum != "" && bankAllnum != "0" {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if nbankAllnum != "" && nbankAllnum != "0" {
|
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
|
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))
|
bankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_bank_allnum", period.apiPeriod))
|
||||||
nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period.apiPeriod))
|
nbankAllnum := getStringValue(variableValue, fmt.Sprintf("als_%s_id_nbank_allnum", period.apiPeriod))
|
||||||
|
|
||||||
// 如果近7天申请次数>=5,认为是极为频繁
|
// 如果近7天申请次数>=10,认为是极为频繁(调高风险阈值,进一步提高阈值)
|
||||||
if bankAllnum != "" && bankAllnum != "0" {
|
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
|
(*riskWarning)[period.field] = 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if nbankAllnum != "" && nbankAllnum != "0" {
|
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
|
(*riskWarning)[period.field] = 1
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -3655,17 +3628,17 @@ func checkBankApplicationFrequency(variableValue map[string]interface{}, riskWar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 银行申请次数极多(>=10)
|
// 银行申请次数极多(>=20,调高风险阈值,进一步提高阈值)
|
||||||
if bankTotal >= 10 {
|
if bankTotal >= 20 {
|
||||||
(*riskWarning)["frequentBankApplications"] = 1
|
(*riskWarning)["frequentBankApplications"] = 1
|
||||||
} else if bankTotal >= 5 {
|
} else if bankTotal >= 15 {
|
||||||
(*riskWarning)["moreFrequentBankApplications"] = 1
|
(*riskWarning)["moreFrequentBankApplications"] = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非银申请次数极多(>=10)
|
// 非银申请次数极多(>=20,调高风险阈值,进一步提高阈值)
|
||||||
if nbankTotal >= 10 {
|
if nbankTotal >= 20 {
|
||||||
(*riskWarning)["frequentNonBankApplications"] = 1
|
(*riskWarning)["frequentNonBankApplications"] = 1
|
||||||
} else if nbankTotal >= 5 {
|
} else if nbankTotal >= 15 {
|
||||||
(*riskWarning)["moreFrequentNonBankApplications"] = 1
|
(*riskWarning)["moreFrequentNonBankApplications"] = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3756,11 +3729,11 @@ func checkRentalApplicationFrequency(jrzq1d09Map map[string]interface{}, riskWar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 租赁申请次数极多(>=10)
|
// 租赁申请次数极多(>=20,调高风险阈值,进一步提高阈值)
|
||||||
if totalCount >= 10 {
|
if totalCount >= 20 {
|
||||||
(*riskWarning)["veryFrequentRentalApplications"] = 1
|
(*riskWarning)["veryFrequentRentalApplications"] = 1
|
||||||
(*riskWarning)["frequentRentalApplications"] = 1
|
(*riskWarning)["frequentRentalApplications"] = 1
|
||||||
} else if totalCount >= 5 {
|
} else if totalCount >= 15 {
|
||||||
(*riskWarning)["frequentRentalApplications"] = 1
|
(*riskWarning)["frequentRentalApplications"] = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4011,6 +3984,208 @@ func getPhoneArea(mobile string) string {
|
|||||||
return ""
|
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 {
|
func convertChannel(channel string) string {
|
||||||
channelMap := map[string]string{
|
channelMap := map[string]string{
|
||||||
"cmcc": "中国移动",
|
"cmcc": "中国移动",
|
||||||
|
|||||||
Reference in New Issue
Block a user