Files
tyapi-server/internal/domains/api/services/processors/dwbg/dwbg9fb3_ra.go
2026-06-10 17:47:36 +08:00

357 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package dwbg
import (
"math"
"strconv"
"strings"
)
const raScoreMax = 1000
const (
raWeightVerify = 0.10
raWeightJudicialBase = 0.50
raWeightCreditBase = 0.40
raHighRiskThreshold = 400
)
// buildDWBG9FB3RA 构建顶层 RA 总体安全评估(千分制,分值越高越安全)
//
// 输出字段来源:
// - ra_fraud_score → calcRAFraudScore辅助输出不参与 ra_score 加权)
// - ra_credit_score → calcRACreditScore
// - ra_judicial_score → calcRAJudicialScore
// - ra_verify_score → calcRAVerifyScore
// - ra_score → 本方法,身份 10% 固定 + 司法/借贷动态加权
// - ra_level → raLevelFromScore(ra_score),司法+借贷双高时强制 F
func buildDWBG9FB3RA(data map[string]interface{}) map[string]interface{} {
fraudScore := calcRAFraudScore(data)
creditScore := calcRACreditScore(data)
judicialScore := calcRAJudicialScore(data)
verifyScore := calcRAVerifyScore(data)
total := 0
level := "F"
if isRAForcedFGrade(judicialScore, creditScore) {
total = 0
level = "F"
} else {
wJudicial, wCredit := calcRADynamicWeights(judicialScore, creditScore)
total = int(math.Round(
float64(verifyScore)*raWeightVerify +
float64(judicialScore)*wJudicial +
float64(creditScore)*wCredit,
))
total = clampRAInt(total, 0, raScoreMax)
level = raLevelFromScore(total)
}
return map[string]interface{}{
"ra_score": total,
"ra_level": level,
"ra_fraud_score": fraudScore,
"ra_credit_score": creditScore,
"ra_judicial_score": judicialScore,
"ra_verify_score": verifyScore,
}
}
// calcRADynamicWeights 按司法/借贷风险场景分配剩余 90% 权重(身份固定 10%
func calcRADynamicWeights(judicialScore, creditScore int) (wJudicial, wCredit float64) {
hasJudicialRisk := judicialScore < raScoreMax
hasCreditRisk := creditScore < raScoreMax
switch {
case hasJudicialRisk && hasCreditRisk:
return 0.70, 0.20
case hasJudicialRisk:
return 0.65, 0.25
case hasCreditRisk:
return 0.40, 0.50
default:
return raWeightJudicialBase, raWeightCreditBase
}
}
// isRAForcedFGrade 司法 + 借贷双重高风险时强制 F 级
func isRAForcedFGrade(judicialScore, creditScore int) bool {
return judicialScore <= raHighRiskThreshold && creditScore <= raHighRiskThreshold
}
// raLevelFromScore 由 ra_score 映射等级(千分制,越高越安全)
// A: 800-1000 B: 600-799 C: 400-599 D: 200-399 E: 0-199
func raLevelFromScore(score int) string {
switch {
case score >= 800:
return "A"
case score >= 600:
return "B"
case score >= 400:
return "C"
case score >= 200:
return "D"
default:
return "E"
}
}
// toRASafetyScore 将风险扣分转为安全分:安全分 = 1000 - 风险扣分
func toRASafetyScore(riskPoints int) int {
return clampRAInt(raScoreMax-riskPoints, 0, raScoreMax)
}
// calcRAFraudScore 欺诈/黑名单维度安全分来源calcRAFraudRiskPoints
// 统计方式:满分 1000根据 behavior/complaint/fraud/special 风险信号扣分后取补集
func calcRAFraudScore(data map[string]interface{}) int {
return toRASafetyScore(calcRAFraudRiskPoints(data))
}
// calcRAFraudRiskPoints 统计欺诈维度风险扣分(仅内部使用,分值越高代表越不安全)
func calcRAFraudRiskPoints(data map[string]interface{}) int {
risk := 0
// 来源子字段 behaviorJRZQV0MD 行为黑名单)
if behavior := raAsMap(data["behavior"]); behavior != nil {
if result := raAsMap(behavior["result"]); result != nil {
// behavior.result.black_list = "1" → 命中行为黑名单,扣 500
if raAsString(result["black_list"]) == "1" {
risk += 500
}
// behavior.result.black_tag04~12 任意为 "1" → 每个扣 80
for k, v := range result {
if strings.HasPrefix(k, "black_tag") && raAsString(v) == "1" {
risk += 80
}
}
}
}
// 来源子字段 complaintJRZQVT43 投诉风险筛查)
if complaint := raAsMap(data["complaint"]); complaint != nil {
if result := raAsMap(complaint["result"]); result != nil {
// complaint.result.score × 10上限扣 300
risk += clampRAInt(raAsInt(result["score"])*10, 0, 300)
}
}
// 来源子字段 fraudJRZQV3HM 债务欺诈黑名单)
if fraud := raAsMap(data["fraud"]); fraud != nil {
// fraud.hit = 1 → 命中欺诈黑名单,扣 400
if raAsInt(fraud["hit"]) == 1 || raAsString(fraud["hit"]) == "1" {
risk += 400
}
}
// 来源子字段 specialJRZQV7MD 特殊名单)
if special := raAsMap(data["special"]); special != nil && len(special) > 0 {
switch raAsString(special["Rule_final_decision"]) {
case "Reject":
risk += 350 // 特殊名单建议拒绝
case "Review":
risk += 200 // 特殊名单建议复议
}
}
return clampRAInt(risk, 0, raScoreMax)
}
// calcRACreditScore 借贷/逾期维度安全分来源calcRACreditRiskPoints
// 统计方式:满分 1000根据 probe/intent/rating 风险信号扣分后取补集
func calcRACreditScore(data map[string]interface{}) int {
return toRASafetyScore(calcRACreditRiskPoints(data))
}
// calcRACreditRiskPoints 统计借贷维度风险扣分
func calcRACreditRiskPoints(data map[string]interface{}) int {
risk := 0
// 来源子字段 probeJRZQ4B6C 探针C
if probe := raAsMap(data["probe"]); probe != nil {
if raAsString(probe["currently_overdue"]) == "1" {
risk += 300 // 当前逾期
}
if raAsString(probe["acc_sleep"]) == "1" {
risk += 150 // 睡眠账户
}
if raAsString(probe["currently_performance"]) == "0" {
risk += 100 // 当前未履约
}
if raAsString(probe["result_code"]) == "1" {
risk += 100 // 探针命中风险
}
}
// 来源子字段 intentJRZQ3C7B 借贷意向验证)
if intent := raAsMap(data["intent"]); intent != nil {
switch raAsString(intent["Rule_final_decision"]) {
case "Reject":
risk += 400
case "Review":
risk += 250
}
// intent.Rule_final_weight × 5上限扣 250
weight := raAsInt(intent["Rule_final_weight"])
if weight > 0 {
risk += clampRAInt(weight*5, 0, 250)
}
}
// 来源子字段 ratingJRZQ5E9F 借选指数)
if rating := raAsMap(data["rating"]); rating != nil {
// rating.score 越低风险越高:扣 (500 - score),上限 300
if ratingScore := raAsInt(rating["score"]); ratingScore > 0 && ratingScore < 500 {
risk += clampRAInt(500-ratingScore, 0, 300)
}
}
return clampRAInt(risk, 0, raScoreMax)
}
// calcRAJudicialScore 司法涉诉维度安全分来源calcRAJudicialRiskPoints
// 统计方式:满分 1000根据 judicial.judicial_data 涉诉统计扣分后取补集
func calcRAJudicialScore(data map[string]interface{}) int {
return toRASafetyScore(calcRAJudicialRiskPoints(data))
}
// calcRAJudicialRiskPoints 统计司法维度风险扣分
func calcRAJudicialRiskPoints(data map[string]interface{}) int {
risk := 0
// 来源子字段 judicialFLXG7E8F 个人司法数据查询)
judicial := raAsMap(data["judicial"])
if judicial == nil {
return 0
}
judicialData := raAsMap(judicial["judicial_data"])
if judicialData == nil {
return 0
}
// lawsuitStat 下 civil/criminal/administrative/preservation 等节点累加
if lawsuitStat := raAsMap(judicialData["lawsuitStat"]); lawsuitStat != nil {
for _, section := range lawsuitStat {
sectionMap := raAsMap(section)
if sectionMap == nil {
continue
}
count := raAsMap(sectionMap["count"])
if count == nil {
continue
}
risk += clampRAInt(raAsInt(count["count_total"])*80, 0, 400) // 涉诉总件数
risk += clampRAInt(raAsInt(count["count_wei_total"])*60, 0, 300) // 未结案数
risk += clampRAInt(raAsInt(count["count_beigao"])*50, 0, 250) // 被告件数
}
}
risk += clampRAInt(len(raAsSlice(judicialData["breachCaseList"]))*150, 0, 450) // 失信案件条数
risk += clampRAInt(len(raAsSlice(judicialData["consumptionRestrictionList"]))*200, 0, 400) // 限高条数
return clampRAInt(risk, 0, raScoreMax)
}
// calcRAVerifyScore 身份/在网核验维度安全分来源calcRAVerifyRiskPoints
// 统计方式:满分 1000根据 triple/identity/presence 核验异常扣分后取补集
func calcRAVerifyScore(data map[string]interface{}) int {
return toRASafetyScore(calcRAVerifyRiskPoints(data))
}
// calcRAVerifyRiskPoints 统计核验维度风险扣分
func calcRAVerifyRiskPoints(data map[string]interface{}) int {
risk := 0
// 来源子字段 tripleYYSYK9R4 三要素验证)
if triple := raAsMap(data["triple"]); triple != nil {
if state := raAsString(triple["state"]); state != "" && state != "1" {
risk += 400 // 三要素不一致
}
}
// 来源子字段 identityIVYZN2P8 二要素认证)
if identity := raAsMap(data["identity"]); identity != nil {
if result := raAsInt(identity["result"]); result != 0 {
risk += 400 // 二要素不一致或无记录
}
}
// 来源子字段 presenceYYSYE7V5 在网状态)
if presence := raAsMap(data["presence"]); presence != nil {
desc := raAsString(presence["desc"])
if strings.Contains(desc, "停机") || strings.Contains(desc, "销号") || strings.Contains(desc, "不在网") {
risk += 80 // 在网状态异常
}
if status := raAsInt(presence["status"]); status > 1 {
risk += 50 // 在网状态码异常
}
}
return clampRAInt(risk, 0, raScoreMax)
}
func raAsMap(v interface{}) map[string]interface{} {
m, ok := v.(map[string]interface{})
if !ok || m == nil {
return nil
}
return m
}
func raAsSlice(v interface{}) []interface{} {
s, ok := v.([]interface{})
if !ok {
return nil
}
return s
}
func raAsString(v interface{}) string {
switch val := v.(type) {
case string:
return strings.TrimSpace(val)
case float64:
return strconv.FormatInt(int64(val), 10)
case int:
return strconv.Itoa(val)
case int64:
return strconv.FormatInt(val, 10)
case bool:
if val {
return "1"
}
return "0"
default:
return ""
}
}
func raAsInt(v interface{}) int {
switch val := v.(type) {
case int:
return val
case int64:
return int(val)
case float64:
return int(val)
case string:
n, err := strconv.Atoi(strings.TrimSpace(val))
if err == nil {
return n
}
case bool:
if val {
return 1
}
}
return 0
}
func clampRAInt(v, min, max int) int {
if v < min {
return min
}
if v > max {
return max
}
return v
}