675 lines
19 KiB
Go
675 lines
19 KiB
Go
package dwbg
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"tyapi-server/internal/domains/api/dto"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// transformToSinanReport 将子接口数据组合为司南报告格式。
|
|
func transformToSinanReport(apiData map[string]interface{}, params dto.DWBG6A2CReq, log *zap.Logger) map[string]interface{} {
|
|
_ = log
|
|
report := emptySinanReport(params)
|
|
|
|
mergeInto(report, "baseInfo", buildSinanBaseInfo(apiData, params))
|
|
mergeInto(report, "standLiveInfo", buildSinanStandLiveInfo(apiData))
|
|
mergeInto(report, "antiFraudInfo", buildSinanAntiFraudInfo(apiData))
|
|
mergeInto(report, "securityInfo", buildSinanSecurityInfo(apiData))
|
|
mergeInto(report, "applicationStatistics", buildApplicationStatistics(apiData))
|
|
mergeInto(report, "lendingStatistics", buildLendingStatistics(apiData))
|
|
mergeInto(report, "performanceStatistics", buildPerformanceStatistics(apiData))
|
|
mergeInto(report, "overdueRecord", buildOverdueRecord(apiData))
|
|
mergeInto(report, "creditDetail", buildCreditDetail(apiData))
|
|
mergeInto(report, "rentalBehavior", buildRentalBehavior(apiData))
|
|
mergeInto(report, "riskSupervision", buildSinanRiskSupervision(apiData))
|
|
report["judicialLeaseReport"] = buildJudicialLeaseReport(apiData)
|
|
|
|
calcSinanRisk(report, apiData)
|
|
return report
|
|
}
|
|
|
|
func buildSinanBaseInfo(apiData map[string]interface{}, params dto.DWBG6A2CReq) map[string]interface{} {
|
|
base := emptyBaseInfo(params)
|
|
|
|
if age, sex := calculateAgeAndSex(params.IDCard); age > 0 {
|
|
base["age"] = age
|
|
base["sex"] = sex
|
|
}
|
|
|
|
if yysyh6f3 := asMap(getMapValue(apiData, "YYSYH6F3")); yysyh6f3 != nil {
|
|
if address := xypString(yysyh6f3, "address"); address != "" {
|
|
base["location"] = address
|
|
}
|
|
if sex := xypString(yysyh6f3, "sex"); sex != "" {
|
|
base["sex"] = sex
|
|
}
|
|
if birthday := xypString(yysyh6f3, "birthday"); birthday != "" {
|
|
if age := ageFromBirthday(birthday); age > 0 {
|
|
base["age"] = age
|
|
}
|
|
}
|
|
if channel := xypString(yysyh6f3, "channel"); channel != "" {
|
|
base["channel"] = convertChannelName(channel)
|
|
}
|
|
}
|
|
if base["location"] == "" {
|
|
if loc := getLocationFromIDCard(params.IDCard); loc != "" {
|
|
base["location"] = loc
|
|
}
|
|
}
|
|
|
|
if yysy35ta := asMap(getMapValue(apiData, "YYSY35TA")); yysy35ta != nil {
|
|
prov := xypString(yysy35ta, "prov")
|
|
city := xypString(yysy35ta, "city")
|
|
if prov != "" && city != "" {
|
|
base["phoneArea"] = prov + "-" + city
|
|
} else if prov != "" {
|
|
base["phoneArea"] = prov
|
|
}
|
|
if base["channel"] == "" {
|
|
if name := xypString(yysy35ta, "name"); name != "" {
|
|
base["channel"] = convertChannelName(name)
|
|
}
|
|
}
|
|
}
|
|
|
|
if yysye7v5 := asMap(getMapValue(apiData, "YYSYE7V5")); yysye7v5 != nil {
|
|
base["status"] = convertStatusFromOnlineStatus(yysye7v5)
|
|
if base["channel"] == "" {
|
|
if ch := xypString(yysye7v5, "channel"); ch != "" {
|
|
base["channel"] = convertChannelName(ch)
|
|
}
|
|
}
|
|
}
|
|
|
|
return base
|
|
}
|
|
|
|
func ageFromBirthday(birthday string) int {
|
|
birthday = strings.TrimSpace(birthday)
|
|
layouts := []string{"2006-01-02", "20060102", "2006/01/02"}
|
|
for _, layout := range layouts {
|
|
if t, err := time.Parse(layout, birthday); err == nil {
|
|
now := time.Now()
|
|
age := now.Year() - t.Year()
|
|
if now.YearDay() < t.YearDay() {
|
|
age--
|
|
}
|
|
if age > 0 {
|
|
return age
|
|
}
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func buildSinanStandLiveInfo(apiData map[string]interface{}) map[string]interface{} {
|
|
info := emptyStandLiveInfo()
|
|
|
|
if yysyh6f3 := asMap(getMapValue(apiData, "YYSYH6F3")); yysyh6f3 != nil {
|
|
finalAuth, verification := mapYYSYH6F3StandLive(xypString(yysyh6f3, "result"))
|
|
info["finalAuthResult"] = finalAuth
|
|
info["verification"] = verification
|
|
}
|
|
|
|
if yysyp0t4 := asMap(getMapValue(apiData, "YYSYP0T4")); yysyp0t4 != nil {
|
|
info["inTime"] = mapYYSYP0T4InTime(xypString(yysyp0t4, "time"), apiData)
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
func mapYYSYH6F3StandLive(result string) (finalAuthResult, verification string) {
|
|
switch result {
|
|
case "0":
|
|
return "0", "0"
|
|
case "1":
|
|
return "1", "1"
|
|
case "2":
|
|
return "1", "-1"
|
|
default:
|
|
return "1", "-1"
|
|
}
|
|
}
|
|
|
|
func mapYYSYP0T4InTime(timeRange string, apiData map[string]interface{}) string {
|
|
timeRange = strings.TrimSpace(timeRange)
|
|
if timeRange == "" {
|
|
if yysye7v5 := asMap(getMapValue(apiData, "YYSYE7V5")); yysye7v5 != nil {
|
|
status := convertStatusFromOnlineStatus(yysye7v5)
|
|
if status != 1 {
|
|
return "99"
|
|
}
|
|
}
|
|
return "-1"
|
|
}
|
|
switch timeRange {
|
|
case "[0,3)":
|
|
return "0"
|
|
case "[3,6)":
|
|
return "3"
|
|
case "[6,12)":
|
|
return "6"
|
|
case "[12,24)":
|
|
return "12"
|
|
case "[24,-1)", "[24,+)", "[24,Inf)":
|
|
return "24"
|
|
default:
|
|
return "-1"
|
|
}
|
|
}
|
|
|
|
func buildSinanAntiFraudInfo(apiData map[string]interface{}) map[string]interface{} {
|
|
info := emptyAntiFraudInfo()
|
|
flxg := asMap(getMapValue(apiData, "FLXG8B4D"))
|
|
if flxg == nil {
|
|
return info
|
|
}
|
|
data := flxg
|
|
if nested, ok := flxg["data"].(map[string]interface{}); ok {
|
|
data = nested
|
|
}
|
|
for _, field := range []string{"moneyLaundering", "deceiver", "gamblerPlayer", "gamblerBanker"} {
|
|
if val := xypString(data, field); val != "" {
|
|
info[field] = val
|
|
}
|
|
}
|
|
return info
|
|
}
|
|
|
|
func buildSinanSecurityInfo(apiData map[string]interface{}) map[string]interface{} {
|
|
info := emptySecurityInfo()
|
|
level := flxgdea9Level(apiData)
|
|
if level == "" || level == "0" {
|
|
return info
|
|
}
|
|
for k, v := range parseSecurityInfoLevel(level) {
|
|
info[k] = v
|
|
}
|
|
return info
|
|
}
|
|
|
|
func parseSecurityInfoLevel(level string) map[string]interface{} {
|
|
out := emptySecurityInfo()
|
|
for _, raw := range strings.Split(level, ",") {
|
|
t := strings.TrimSpace(raw)
|
|
if t == "" {
|
|
continue
|
|
}
|
|
switch {
|
|
case t == "A":
|
|
out["escape"] = 1
|
|
out["icase"] = 1
|
|
case t == "A1", t == "A2", t == "A3", t == "A4", t == "A5":
|
|
out["front"] = 1
|
|
out["icase"] = 1
|
|
case t == "C3":
|
|
out["drug"] = 1
|
|
out["takeDrug"] = 1
|
|
out["icase"] = 1
|
|
case t == "D", t == "D1", t == "D2", t == "D3", t == "D4", t == "D5":
|
|
out["icase"] = 1
|
|
out["ikey"] = 1
|
|
case strings.HasPrefix(t, "B") || strings.HasPrefix(t, "C"):
|
|
out["icase"] = 1
|
|
case t == "E":
|
|
out["itrancase"] = 1
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func buildApplicationStatistics(apiData map[string]interface{}) map[string]interface{} {
|
|
stats := emptyApplicationStatistics()
|
|
m := asMap(getMapValue(apiData, "JRZQ5E9F"))
|
|
if m == nil {
|
|
return stats
|
|
}
|
|
|
|
total := decodeXypInt(m, "xyp_cpl0001")
|
|
consumption := decodeXypInt(m, "xyp_cpl0007")
|
|
online := decodeXypInt(m, "xyp_cpl0008")
|
|
other := total - consumption - online
|
|
if other < 0 {
|
|
other = 0
|
|
}
|
|
|
|
v180 := decodeXypInt(m, "xyp_cpl0013")
|
|
v360 := decodeXypInt(m, "xyp_t01dezhbc") + decodeXypInt(m, "xyp_t01dezhba")
|
|
last12 := v180
|
|
if v360 > last12 {
|
|
last12 = v360
|
|
}
|
|
|
|
stats["totalApplicationCount"] = total
|
|
stats["consumptionInstallmentApplicationCount"] = consumption
|
|
stats["onlineLoanApplicationCount"] = online
|
|
stats["otherApplicationCount"] = other
|
|
stats["applicationCountLastMonth"] = decodeXypInt(m, "xyp_cpl0011")
|
|
stats["applicationCountLast3Months"] = decodeXypInt(m, "xyp_cpl0012")
|
|
stats["applicationCountLast6Months"] = v180
|
|
stats["applicationCountLast12Months"] = last12
|
|
stats["lastApplicationDate"] = reverseYYYYMMFromCpl0046(m)
|
|
stats["daysSinceLastApplication"] = formatXypInterval(m, "xyp_cpl0046")
|
|
if stats["daysSinceLastApplication"] == "0" {
|
|
stats["daysSinceLastApplication"] = "-"
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
func buildLendingStatistics(apiData map[string]interface{}) map[string]interface{} {
|
|
stats := emptyLendingStatistics()
|
|
m := asMap(getMapValue(apiData, "JRZQ5E9F"))
|
|
if m == nil {
|
|
return stats
|
|
}
|
|
|
|
v360Count := decodeXypInt(m, "xyp_t01cczhzz")
|
|
v360Amount := formatXypInterval(m, "xyp_t01achzbz")
|
|
|
|
stats["totalLendingInstitutionCount"] = decodeXypInt(m, "xyp_cpl0001")
|
|
stats["installmentLendingInstitutionCount"] = decodeXypInt(m, "xyp_cpl0007")
|
|
stats["onlineLendingInstitutionCount"] = decodeXypInt(m, "xyp_cpl0008")
|
|
stats["lastLendingDate"] = reverseYYYYMMFromCpl0046(m)
|
|
stats["daysSinceLastLending"] = formatXypInterval(m, "xyp_cpl0046")
|
|
if stats["daysSinceLastLending"] == "0" {
|
|
stats["daysSinceLastLending"] = "-"
|
|
}
|
|
stats["lendingCountLastMonth"] = decodeXypInt(m, "xyp_t01ccezzz")
|
|
stats["lendingAmountLastMonth"] = formatXypInterval(m, "xyp_t01acezzz")
|
|
stats["lendingCountLast3Months"] = decodeXypInt(m, "xyp_t01ccfzzz")
|
|
stats["lendingAmountLast3Months"] = formatXypInterval(m, "xyp_t01acfzzz")
|
|
stats["lendingCountLast6Months"] = decodeXypInt(m, "xyp_t01ccgzzz")
|
|
stats["lendingAmountLast6Months"] = formatXypInterval(m, "xyp_t01acgzzz")
|
|
stats["lendingCountLast12Months"] = v360Count
|
|
stats["lendingAmountLast12Months"] = v360Amount
|
|
stats["lendingCountLast24Months"] = v360Count
|
|
stats["lendingAmountLast24Months"] = v360Amount
|
|
|
|
return stats
|
|
}
|
|
|
|
func buildPerformanceStatistics(apiData map[string]interface{}) map[string]interface{} {
|
|
stats := emptyPerformanceStatistics()
|
|
m := asMap(getMapValue(apiData, "JRZQ5E9F"))
|
|
if m == nil {
|
|
return stats
|
|
}
|
|
|
|
v360Count := decodeXypInt(m, "xyp_t01cchzzc")
|
|
v360Amount := formatXypInterval(m, "xyp_t01achzzc")
|
|
v180Exception := decodeXypInt(m, "xyp_cpl0026")
|
|
v360Exception := decodeXypInt(m, "xyp_t01cczhza")
|
|
exception12 := v180Exception
|
|
if v360Exception > exception12 {
|
|
exception12 = v360Exception
|
|
}
|
|
|
|
stats["creditLoanDuration"] = decodeXypInt(m, "xyp_cpl0045")
|
|
stats["settledLoanOrderCount"] = decodeXypInt(m, "xyp_cpl0002")
|
|
stats["daysSinceLastPerformance"] = formatXypInterval(m, "xyp_cpl0068")
|
|
if stats["daysSinceLastPerformance"] == "0" {
|
|
stats["daysSinceLastPerformance"] = "-"
|
|
}
|
|
stats["normalRepaymentRatio"] = formatNormalRepaymentRatio(m)
|
|
stats["performanceCountLastMonth"] = decodeXypInt(m, "xyp_cpl0023")
|
|
stats["performanceAmountLastMonth"] = formatXypInterval(m, "xyp_cpl0039")
|
|
stats["repaymentExceptionCountLastMonth"] = decodeXypInt(m, "xyp_cpl0022")
|
|
stats["performanceCountLast3Months"] = decodeXypInt(m, "xyp_cpl0025")
|
|
stats["performanceAmountLast3Months"] = formatXypInterval(m, "xyp_cpl0041")
|
|
stats["repaymentExceptionCountLast3Months"] = decodeXypInt(m, "xyp_cpl0024")
|
|
stats["performanceCountLast6Months"] = decodeXypInt(m, "xyp_cpl0027")
|
|
stats["performanceAmountLast6Months"] = formatXypInterval(m, "xyp_cpl0043")
|
|
stats["repaymentExceptionCountLast6Months"] = v180Exception
|
|
stats["performanceCountLast12Months"] = v360Count
|
|
stats["performanceAmountLast12Months"] = v360Amount
|
|
stats["repaymentExceptionCountLast12Months"] = exception12
|
|
stats["performanceCountLast24Months"] = v360Count
|
|
stats["performanceAmountLast24Months"] = v360Amount
|
|
stats["repaymentExceptionCountLast24Months"] = exception12
|
|
|
|
return stats
|
|
}
|
|
|
|
func buildOverdueRecord(apiData map[string]interface{}) map[string]interface{} {
|
|
record := emptyOverdueRecord()
|
|
m := asMap(getMapValue(apiData, "JRZQ5E9F"))
|
|
if m == nil {
|
|
return record
|
|
}
|
|
|
|
currentOverdue := decodeXypInt(m, "xyp_cpl0071")
|
|
if xypString(m, "xyp_cpl0044") == "1" {
|
|
if currentOverdue > 0 {
|
|
record["currentOverdueInstitution"] = fmt.Sprintf("%d(未结清)", currentOverdue)
|
|
} else {
|
|
record["currentOverdueInstitution"] = "1(未结清)"
|
|
}
|
|
} else if currentOverdue > 0 {
|
|
record["currentOverdueInstitution"] = formatXypInterval(m, "xyp_cpl0071")
|
|
}
|
|
|
|
record["currentOverdueCount"] = currentOverdue
|
|
record["totalOverdueAmount"] = formatOverdueAmountHyphen(m, "xyp_cpl0072")
|
|
record["lastOverdueDate"] = "-"
|
|
|
|
m1Signal := 0
|
|
for _, key := range []string{"xyp_cpl0029", "xyp_cpl0030", "xyp_cpl0031"} {
|
|
if xypString(m, key) == "1" {
|
|
m1Signal++
|
|
}
|
|
}
|
|
m1Plus6 := m1Signal
|
|
if m1Plus6 > 6 {
|
|
m1Plus6 = 6
|
|
}
|
|
m1Plus12 := m1Signal
|
|
if m1Plus12 > 12 {
|
|
m1Plus12 = 12
|
|
}
|
|
record["m1PlusCountLast6Months"] = m1Plus6
|
|
record["m1PlusCountLast12Months"] = m1Plus12
|
|
record["m1PlusCountLast24Months"] = m1Plus12
|
|
|
|
repay360 := decodeXypInt(m, "xyp_t01cchzzc")
|
|
if xypString(m, "xyp_cpl0044") != "1" && repay360 > 0 {
|
|
record["m0PlusCountLast6Months"] = decodeXypInt(m, "xyp_cpl0027")
|
|
record["m0PlusCountLast12Months"] = repay360
|
|
record["m0PlusCountLast24Months"] = repay360
|
|
}
|
|
|
|
record["totalAmountLast6Months"] = formatXypInterval(m, "xyp_cpl0042")
|
|
record["totalAmountLast12Months"] = formatXypInterval(m, "xyp_t01aahzza")
|
|
record["totalAmountLast24Months"] = record["totalAmountLast12Months"]
|
|
|
|
return record
|
|
}
|
|
|
|
func buildCreditDetail(apiData map[string]interface{}) map[string]interface{} {
|
|
detail := emptyCreditDetail()
|
|
m := asMap(getMapValue(apiData, "JRZQ5E9F"))
|
|
if m == nil {
|
|
return detail
|
|
}
|
|
detail["maxOnlineLoanCredit"] = formatXypNumericString(m, "xyp_t01aahzbz")
|
|
detail["avgOnlineLoanCredit"] = formatXypNumericString(m, "xyp_t01adhzbc")
|
|
detail["maxConsumptionInstallmentCredit"] = formatXypNumericString(m, "xyp_t01aazhaz")
|
|
detail["avgConsumptionInstallmentCredit"] = formatXypNumericString(m, "xyp_t01adgzzc")
|
|
return detail
|
|
}
|
|
|
|
var rentalPeriodMap = map[string]string{
|
|
"3Days": "d3",
|
|
"7Days": "d7",
|
|
"14Days": "d14",
|
|
"1Month": "m1",
|
|
"3Months": "m3",
|
|
"6Months": "m6",
|
|
"12Months": "m12",
|
|
}
|
|
|
|
func buildRentalBehavior(apiData map[string]interface{}) map[string]interface{} {
|
|
behavior := emptyRentalBehavior()
|
|
jrzq := asMap(getMapValue(apiData, "JRZQ1D09"))
|
|
if jrzq == nil {
|
|
return behavior
|
|
}
|
|
|
|
for periodLabel, apiPeriod := range rentalPeriodMap {
|
|
behavior[fmt.Sprintf("rentalApplicationCountLast%s", periodLabel)] = formatRentalPair(
|
|
rentalNestedValue(jrzq, apiPeriod, "id", "allnum"),
|
|
rentalNestedValue(jrzq, apiPeriod, "cell", "allnum"),
|
|
)
|
|
behavior[fmt.Sprintf("rentalApplicationInstitutionsLast%s", periodLabel)] = formatRentalPair(
|
|
rentalNestedValue(jrzq, apiPeriod, "id", "orgnum"),
|
|
rentalNestedValue(jrzq, apiPeriod, "cell", "orgnum"),
|
|
)
|
|
behavior[fmt.Sprintf("rentalApplicationCountLast%sWeekend", periodLabel)] = formatRentalPair(
|
|
rentalNestedValue(jrzq, apiPeriod, "id", "weekend_allnum"),
|
|
rentalNestedValue(jrzq, apiPeriod, "cell", "weekend_allnum"),
|
|
)
|
|
behavior[fmt.Sprintf("rentalApplicationInstitutionsLast%sWeekend", periodLabel)] = formatRentalPair(
|
|
rentalNestedValue(jrzq, apiPeriod, "id", "weekend_orgnum"),
|
|
rentalNestedValue(jrzq, apiPeriod, "cell", "weekend_orgnum"),
|
|
)
|
|
behavior[fmt.Sprintf("rentalApplicationCountLast%sNight", periodLabel)] = formatRentalPair(
|
|
rentalNestedValue(jrzq, apiPeriod, "id", "night_allnum"),
|
|
rentalNestedValue(jrzq, apiPeriod, "cell", "night_allnum"),
|
|
)
|
|
behavior[fmt.Sprintf("rentalApplicationInstitutionsLast%sNight", periodLabel)] = formatRentalPair(
|
|
rentalNestedValue(jrzq, apiPeriod, "id", "night_orgnum"),
|
|
rentalNestedValue(jrzq, apiPeriod, "cell", "night_orgnum"),
|
|
)
|
|
}
|
|
|
|
return behavior
|
|
}
|
|
|
|
func rentalNestedValue(jrzq map[string]interface{}, period, dim, field string) string {
|
|
if periodData := asMap(jrzq[period]); periodData != nil {
|
|
if dimData := asMap(periodData[dim]); dimData != nil {
|
|
if val := xypString(dimData, field); val != "" {
|
|
return val
|
|
}
|
|
}
|
|
}
|
|
flatKey := fmt.Sprintf("alc_%s_%s_%s", period, dim, field)
|
|
if val := getStringValue(jrzq, flatKey); val != "" {
|
|
return val
|
|
}
|
|
return "0"
|
|
}
|
|
|
|
func formatRentalPair(idVal, cellVal string) string {
|
|
if idVal == "" {
|
|
idVal = "0"
|
|
}
|
|
if cellVal == "" {
|
|
cellVal = "0"
|
|
}
|
|
return idVal + "/" + cellVal
|
|
}
|
|
|
|
func buildSinanRiskSupervision(apiData map[string]interface{}) map[string]interface{} {
|
|
supervision := buildRiskSupervision(apiData, zap.NewNop())
|
|
idCount := parseIntValue(supervision["rentalRiskListIdCardRelationsPhones"])
|
|
phoneCount := parseIntValue(supervision["rentalRiskListPhoneRelationsIdCards"])
|
|
if idCount > 1 || phoneCount > 1 {
|
|
supervision["details"] = "存在中介关联风险"
|
|
} else {
|
|
supervision["details"] = "无"
|
|
}
|
|
return supervision
|
|
}
|
|
|
|
func asMap(val interface{}) map[string]interface{} {
|
|
if val == nil {
|
|
return nil
|
|
}
|
|
m, ok := val.(map[string]interface{})
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return m
|
|
}
|
|
|
|
func jrzqMap(apiData map[string]interface{}) map[string]interface{} {
|
|
return asMap(getMapValue(apiData, "JRZQ5E9F"))
|
|
}
|
|
|
|
func jrzq1d09Map(apiData map[string]interface{}) map[string]interface{} {
|
|
return asMap(getMapValue(apiData, "JRZQ1D09"))
|
|
}
|
|
|
|
func maxTotMons(jrzq map[string]interface{}) int {
|
|
maxVal := 0
|
|
for _, period := range []string{"m6", "m12"} {
|
|
for _, dim := range []string{"id", "cell"} {
|
|
if periodData := asMap(jrzq[period]); periodData != nil {
|
|
if dimData := asMap(periodData[dim]); dimData != nil {
|
|
maxVal = maxInt(maxVal, parseIntValue(dimData["tot_mons"]))
|
|
}
|
|
}
|
|
flatKey := fmt.Sprintf("alc_%s_%s_tot_mons", period, dim)
|
|
maxVal = maxInt(maxVal, parseIntValue(jrzq[flatKey]))
|
|
}
|
|
}
|
|
return maxVal
|
|
}
|
|
|
|
func maxInt(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func rentalHasApplication(jrzq map[string]interface{}) bool {
|
|
for _, period := range []string{"m12"} {
|
|
idVal := parseIntValue(rentalNestedValue(jrzq, period, "id", "allnum"))
|
|
cellVal := parseIntValue(rentalNestedValue(jrzq, period, "cell", "allnum"))
|
|
if idVal > 0 || cellVal > 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func hitBankOverdueRecord(m map[string]interface{}) int {
|
|
if xypString(m, "xyp_cpl0044") == "1" {
|
|
return 1
|
|
}
|
|
if decodeXypInt(m, "xyp_cpl0071") > 0 {
|
|
return 1
|
|
}
|
|
if decodeXypInt(m, "xyp_cpl0072") > 0 {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func hitRelationRisk(jrzq map[string]interface{}) int {
|
|
if jrzq == nil {
|
|
return 0
|
|
}
|
|
idCount := parseIntValue(jrzq["idcard_relation_phone"])
|
|
phoneCount := parseIntValue(jrzq["phone_relation_idard"])
|
|
if idCount > 1 || phoneCount > 1 {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func hitNewRiskFeature(m map[string]interface{}) int {
|
|
raw := xypString(m, "xyp_model_score_mid")
|
|
if raw == "" || raw == "-1" {
|
|
return 0
|
|
}
|
|
score, err := strconv.ParseFloat(raw, 64)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
const threshold = 350.0 + (950.0-350.0)/3.0
|
|
if score < threshold {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func calcMultiQuery(m map[string]interface{}) int {
|
|
windows := []struct {
|
|
months int
|
|
key string
|
|
}{
|
|
{6, "xyp_cpl0013"},
|
|
{3, "xyp_cpl0012"},
|
|
{1, "xyp_cpl0011"},
|
|
}
|
|
for _, w := range windows {
|
|
if decodeXypInt(m, w.key) > 0 {
|
|
return w.months
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func calcDeductFail(m map[string]interface{}) int {
|
|
windows := []struct {
|
|
months int
|
|
key string
|
|
}{
|
|
{6, "xyp_cpl0026"},
|
|
{3, "xyp_cpl0024"},
|
|
{1, "xyp_cpl0022"},
|
|
}
|
|
for _, w := range windows {
|
|
if decodeXypInt(m, w.key) > 0 {
|
|
return w.months
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func antiFraudHit(data map[string]interface{}) bool {
|
|
if data == nil {
|
|
return false
|
|
}
|
|
nested := data
|
|
if d, ok := data["data"].(map[string]interface{}); ok {
|
|
nested = d
|
|
}
|
|
for _, field := range []string{"moneyLaundering", "deceiver", "gamblerPlayer", "gamblerBanker"} {
|
|
val := xypString(nested, field)
|
|
if val != "" && val != "0" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func securityLevelHit(level string) bool {
|
|
level = strings.TrimSpace(level)
|
|
return level != "" && level != "0"
|
|
}
|
|
|
|
func industryBlacklistHit(level string) bool {
|
|
for _, raw := range strings.Split(level, ",") {
|
|
if isLevelDClass(strings.TrimSpace(raw)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isLevelDClass(token string) bool {
|
|
switch token {
|
|
case "D", "D1", "D2", "D3", "D4", "D5":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func groupFraudHit(data map[string]interface{}) bool {
|
|
if data == nil {
|
|
return false
|
|
}
|
|
nested := data
|
|
if d, ok := data["data"].(map[string]interface{}); ok {
|
|
nested = d
|
|
}
|
|
for _, field := range []string{"deceiver", "gamblerBanker"} {
|
|
val := strings.ToUpper(xypString(nested, field))
|
|
if val == "B" || val == "C" || val == "D" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|