@@ -1,14 +1,18 @@
package comb
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"tyapi-server/internal/shared/logger"
"go.uber.org/zap"
)
// =======================
@@ -468,46 +472,105 @@ type sourceContext struct {
// =======================
// buildSourceContext 根据 source.json 解析出各子产品的结构化数据
func buildSourceContext ( src sourceFile ) ( * sourceContext , error ) {
ctx := & sourceContext { }
func buildSourceContext ( ctx context . Context , src sourceFile ) ( * sourceContext , error ) {
log := logger . GetGlobalLogger ( )
result := & sourceContext { }
for _ , resp := range src . Responses {
if ! resp . Success {
continue
}
// 检查数据是否为空
if len ( resp . Data ) == 0 {
log . Warn ( "子产品数据为空,跳过解析" ,
zap . String ( "api_code" , resp . ApiCode ) ,
zap . String ( "parent_api_code" , "COMBHZY2" ) ,
)
continue
}
switch strings . ToUpper ( resp . ApiCode ) {
case "DWBG8B4D" :
var data baseProductData
if err := json . Unmarshal ( resp . Data , & data ) ; err != nil {
return nil , fmt . Errorf ( "解析DWBG8B4D数据失败: %w" , err )
log . Error ( "解析DWBG8B4D数据失败,使用兼容处理" ,
zap . Error ( err ) ,
zap . String ( "api_code" , resp . ApiCode ) ,
zap . String ( "data_preview" , string ( resp . Data [ : min ( 100 , len ( resp . Data ) ) ] ) ) ,
)
// 尝试部分解析,即使失败也继续
if partialErr := json . Unmarshal ( resp . Data , & data ) ; partialErr != nil {
log . Warn ( "DWBG8B4D数据格式异常, 将使用空结构" ,
zap . Error ( partialErr ) ,
zap . String ( "api_code" , resp . ApiCode ) ,
)
// 使用空结构,不返回错误
data = baseProductData { }
}
ctx . BaseData = & data
}
result . BaseData = & data
case "FLXG7E8F" :
var data judicialProductData
if err := json . Unmarshal ( resp . Data , & data ) ; err != nil {
return nil , fmt . Errorf ( "解析FLXG7E8F数据失败: %w" , err )
log . Warn ( "解析FLXG7E8F数据失败,使用兼容处理" ,
zap . Error ( err ) ,
zap . String ( "api_code" , resp . ApiCode ) ,
zap . String ( "data_preview" , string ( resp . Data [ : min ( 100 , len ( resp . Data ) ) ] ) ) ,
)
// 使用空结构,不返回错误
data = judicialProductData { }
}
ctx . JudicialData = & data
result . JudicialData = & data
case "JRZQ9D4E" :
var data contentsProductData
if err := json . Unmarshal ( resp . Data , & data ) ; err != nil {
return nil , fmt . Errorf ( "解析JRZQ9D4E数据失败: %w" , err )
log . Warn ( "解析JRZQ9D4E数据失败,使用兼容处理" ,
zap . Error ( err ) ,
zap . String ( "api_code" , resp . ApiCode ) ,
zap . String ( "data_preview" , string ( resp . Data [ : min ( 100 , len ( resp . Data ) ) ] ) ) ,
)
// 使用空结构,不返回错误
data = contentsProductData { }
}
ctx . ContentsData = & data
result . ContentsData = & data
case "JRZQ6F2A" :
var data riskScreenProductData
if err := json . Unmarshal ( resp . Data , & data ) ; err != nil {
return nil , fmt . Errorf ( "解析JRZQ6F2A数据失败: %w" , err )
log . Warn ( "解析JRZQ6F2A数据失败,使用兼容处理" ,
zap . Error ( err ) ,
zap . String ( "api_code" , resp . ApiCode ) ,
zap . String ( "data_preview" , string ( resp . Data [ : min ( 100 , len ( resp . Data ) ) ] ) ) ,
)
// 使用空结构,不返回错误
data = riskScreenProductData { }
}
ctx . RiskScreen = & data
result . RiskScreen = & data
default :
log . Debug ( "未知的子产品API代码, 跳过" ,
zap . String ( "api_code" , resp . ApiCode ) ,
zap . String ( "parent_api_code" , "COMBHZY2" ) ,
)
}
}
if ctx . BaseData == nil {
return nil , errors . New ( "未获取到DWBG8B4D核心数据" )
if result . BaseData == nil {
log . Warn ( "未获取到DWBG8B4D核心数据,将使用空结构 " ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
// 使用空结构,不返回错误,让后续处理能够继续
result . BaseData = & baseProductData { }
}
return ctx , nil
return result , nil
}
// min 辅助函数,返回两个整数中的较小值
func min ( a , b int ) int {
if a < b {
return a
}
return b
}
// =======================
@@ -515,22 +578,35 @@ func buildSourceContext(src sourceFile) (*sourceContext, error) {
// =======================
// buildTargetReport 将上下文数据映射为 target.json 完整结构
func buildTargetReport ( ctx * sourceContext ) targetReport {
func buildTargetReport ( ctx context . Context , sourceCtx * sourceContext ) targetReport {
log := logger . GetGlobalLogger ( )
// 使用recover捕获panic, 确保不会导致整个请求失败
defer func ( ) {
if r := recover ( ) ; r != nil {
log . Error ( "构建目标报告时发生panic, 使用默认值" ,
zap . Any ( "panic" , r ) ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
}
} ( )
report := targetReport {
ReportSummary : buildReportSummary ( ctx ) ,
BasicInfo : buildBasicInfo ( ctx ) ,
RiskIdentification : buildRiskIdentification ( ctx ) ,
CreditAssessment : buildCreditAssessment ( ctx ) ,
LeasingRiskAssessment : buildLeasingRiskAssessment ( ctx ) ,
ComprehensiveAnalysis : buildComprehensiveAnalysis ( ctx ) ,
ReportFooter : buildReportFooter ( ctx ) ,
ReportSummary : buildReportSummary ( ctx , sourceCtx ),
BasicInfo : buildBasicInfo ( ctx , sourceCtx ),
RiskIdentification : buildRiskIdentification ( ctx , sourceCtx ),
CreditAssessment : buildCreditAssessment ( ctx , sourceCtx ),
LeasingRiskAssessment : buildLeasingRiskAssessment ( ctx , sourceCtx ),
ComprehensiveAnalysis : buildComprehensiveAnalysis ( ctx , sourceCtx ),
ReportFooter : buildReportFooter ( ctx , sourceCtx ),
}
return report
}
// buildReportSummary 组装 reportSummary, 包括规则、反欺诈信息
func buildReportSummary ( ctx * sourceContext ) reportSummary {
func buildReportSummary ( ctx context . Context , sourceCtx * sourceContext ) reportSummary {
log := logger . GetGlobalLogger ( )
const strategyCode = "STR0042314/贷前-经营性租赁全量策略"
summary := reportSummary {
@@ -551,40 +627,66 @@ func buildReportSummary(ctx *sourceContext) reportSummary {
} ,
}
if c tx. BaseData == nil {
if sourceCtx == nil || sourceC tx. BaseData == nil {
log . Warn ( "BaseData为空, 使用默认summary值" ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
return summary
}
verifyResult := strings . TrimSpace ( ctx . BaseData . VerifyRule )
// 兼容处理:安全访问字段
verifyResult := ""
if sourceCtx . BaseData . VerifyRule != "" {
verifyResult = strings . TrimSpace ( sourceCtx . BaseData . VerifyRule )
}
if verifyResult == "" {
verifyResult = "未命中"
}
summary . RuleValidation . Result = verifyResult
scoreLevel := riskLevelFromScore ( c tx. BaseData . FraudScore )
scoreLevel := riskLevelFromScore ( sourceC tx. BaseData . FraudScore )
summary . AntiFraudScore . Level = scoreLevel
fraudRuleLevel := strings . TrimSpace ( ctx . BaseData . FraudRule )
fraudRuleLevel := ""
if sourceCtx . BaseData . FraudRule != "" {
fraudRuleLevel = strings . TrimSpace ( sourceCtx . BaseData . FraudRule )
}
if fraudRuleLevel == "" || fraudRuleLevel == "-" {
fraudRuleLevel = "未命中"
}
summary . AntiFraudRule . Level = fraudRuleLevel
riskCount := ctx . BaseData . RiskWarning . TotalRiskCounts
// 兼容处理: 安全访问RiskWarning
riskCount := 0
if sourceCtx . BaseData . RiskWarning . TotalRiskCounts > 0 {
riskCount = sourceCtx . BaseData . RiskWarning . TotalRiskCounts
}
summary . AbnormalRulesHit . Count = riskCount
summary . AbnormalRulesHit . Alert = buildRiskWarningAlert ( c tx. BaseData . RiskWarning )
summary . AbnormalRulesHit . Alert = buildRiskWarningAlert ( sourceC tx. BaseData . RiskWarning )
return summary
}
// buildBasicInfo 组装 basicInfo, 包含基础信息与核验列表
func buildBasicInfo ( ctx * sourceContext ) reportBasicInfo {
base := ctx . BaseData . BaseInfo
func buildBasicInfo ( ctx context . Context , sourceCtx * sourceContext ) reportBasicInfo {
log := logger . GetGlobalLogger ( )
// 兼容处理: 安全访问BaseData和BaseInfo
var base baseInfo
if sourceCtx != nil && sourceCtx . BaseData != nil {
base = sourceCtx . BaseData . BaseInfo
} else {
log . Warn ( "BaseData或BaseInfo为空, 使用默认值" ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
base = baseInfo { }
}
reportID := generateReportID ( )
verifications := make ( [ ] verificationItem , 0 , 5 )
elementResult , elementDetails := buildElementVerificationResult ( c tx)
elementResult , elementDetails := buildElementVerificationResult ( sourceC tx)
verifications = append ( verifications , verificationItem {
Item : "要素核查" ,
Description : "使用姓名、手机号、身份证信息进行三要素核验" ,
@@ -592,7 +694,7 @@ func buildBasicInfo(ctx *sourceContext) reportBasicInfo {
Details : elementDetails ,
} )
carrierResult , carrierDetails := buildCarrierVerificationResult ( c tx)
carrierResult , carrierDetails := buildCarrierVerificationResult ( sourceC tx)
verifications = append ( verifications , verificationItem {
Item : "运营商检验" ,
Description : "检查手机号在运营商处的状态及在线时长" ,
@@ -600,25 +702,27 @@ func buildBasicInfo(ctx *sourceContext) reportBasicInfo {
Details : carrierDetails ,
} )
// 兼容处理: 安全访问JudicialData
var stat * lawsuitStat
if c tx. JudicialData != nil {
stat = & c tx. JudicialData . JudicialData . LawsuitStat
if sourceCtx != nil && sourceC tx. JudicialData != nil {
stat = & sourceC tx. JudicialData . JudicialData . LawsuitStat
}
totalCaseCount := 0
totalCriminal := 0
totalExecution := 0
if stat != nil {
totalCriminal = len ( stat . Criminal . Cases)
totalCaseCount = totalCriminal + len ( stat . Civil . Cases ) + len ( stat . Administrative . Cases ) + len ( stat . Preservation . Cases ) + len ( stat . Bankrupt . Cases )
totalExecution = len ( stat . Implemen t . Cases )
// 兼容处理:安全访问 Cases数组
totalCriminal = safeLen ( stat . Criminal . Cases )
totalCaseCount = totalCriminal + safeLen ( stat . Civil . Cases ) + safeLen ( stat . Administrative . Cases ) + safeLen ( stat . Preservation . Cases ) + safeLen ( stat . Bankrup t . Cases )
totalExecution = safeLen ( stat . Implement . Cases )
}
totalDishonest := 0
totalRestriction := 0
if c tx. JudicialData != nil {
totalDishonest = l en( c tx. JudicialData . JudicialData . BreachCaseList )
totalRestriction = l en( c tx. JudicialData . JudicialData . ConsumptionRestrictionList )
if sourceCtx != nil && sourceC tx. JudicialData != nil {
totalDishonest = safeL en( sourceC tx. JudicialData . JudicialData . BreachCaseList )
totalRestriction = safeL en( sourceC tx. JudicialData . JudicialData . ConsumptionRestrictionList )
}
if totalCaseCount > 0 || totalExecution > 0 || totalDishonest > 0 || totalRestriction > 0 {
@@ -629,11 +733,11 @@ func buildBasicInfo(ctx *sourceContext) reportBasicInfo {
detailParts = append ( detailParts , fmt . Sprintf ( "%s%d条" , label , count ) )
}
}
addCaseDetail ( "刑事案件" , l en( stat . Criminal . Cases ) )
addCaseDetail ( "民事案件" , l en( stat . Civil . Cases ) )
addCaseDetail ( "行政案件" , l en( stat . Administrative . Cases ) )
addCaseDetail ( "非诉保全审查案件" , l en( stat . Preservation . Cases ) )
addCaseDetail ( "强制清算与破产案件" , l en( stat . Bankrupt . Cases ) )
addCaseDetail ( "刑事案件" , safeL en( stat . Criminal . Cases ) )
addCaseDetail ( "民事案件" , safeL en( stat . Civil . Cases ) )
addCaseDetail ( "行政案件" , safeL en( stat . Administrative . Cases ) )
addCaseDetail ( "非诉保全审查案件" , safeL en( stat . Preservation . Cases ) )
addCaseDetail ( "强制清算与破产案件" , safeL en( stat . Bankrupt . Cases ) )
}
if totalExecution > 0 {
detailParts = append ( detailParts , fmt . Sprintf ( "执行案件%d条" , totalExecution ) )
@@ -659,7 +763,7 @@ func buildBasicInfo(ctx *sourceContext) reportBasicInfo {
} )
}
loanRiskResult , loanRiskDetails := buildLoanRiskResult ( c tx)
loanRiskResult , loanRiskDetails := buildLoanRiskResult ( sourceC tx)
verifications = append ( verifications , verificationItem {
Item : "借贷评估" ,
Description : "综合近12个月借贷申请情况评估风险" ,
@@ -667,7 +771,7 @@ func buildBasicInfo(ctx *sourceContext) reportBasicInfo {
Details : loanRiskDetails ,
} )
otherDetails := gatherOtherRiskDetails ( c tx)
otherDetails := gatherOtherRiskDetails ( sourceC tx)
if otherDetails != "" {
verifications = append ( verifications , verificationItem {
Item : "其他" ,
@@ -685,8 +789,18 @@ func buildBasicInfo(ctx *sourceContext) reportBasicInfo {
}
}
// safeLen 安全获取切片长度, 避免nil指针
func safeLen [ T any ] ( s [ ] T ) int {
if s == nil {
return 0
}
return len ( s )
}
// buildRiskIdentification 组装 riskIdentification, 各列表对标 target.json
func buildRiskIdentification ( ctx * sourceContext ) riskIdentification {
func buildRiskIdentification ( ctx context . Context , sourceCtx * sourceContext ) riskIdentification {
log := logger . GetGlobalLogger ( )
identification := riskIdentification {
Title : "风险识别产品" ,
CaseAnnouncements : caseAnnouncementSection {
@@ -707,35 +821,58 @@ func buildRiskIdentification(ctx *sourceContext) riskIdentification {
} ,
}
if ctx . JudicialData == nil {
// 兼容处理: 安全访问JudicialData
if sourceCtx == nil || sourceCtx . JudicialData == nil {
log . Debug ( "JudicialData为空, 返回空的风险识别数据" ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
return identification
}
stat := c tx. JudicialData . JudicialData . LawsuitStat
stat := sourceC tx. JudicialData . JudicialData . LawsuitStat
baseName := ""
baseID := ""
if c tx. BaseData != nil {
baseName = c tx. BaseData . BaseInfo . Name
baseID = c tx. BaseData . BaseInfo . IdCard
if sourceC tx. BaseData != nil {
baseName = sourceC tx. BaseData . BaseInfo . Name
baseID = sourceC tx. BaseData . BaseInfo . IdCard
}
// 兼容处理: 安全访问Cases数组
caseRecords := make ( [ ] caseAnnouncementRecord , 0 )
if stat . Civil . Cases != nil {
caseRecords = append ( caseRecords , convertCaseAnnouncements ( stat . Civil . Cases , "民事案件" ) ... )
}
if stat . Criminal . Cases != nil {
caseRecords = append ( caseRecords , convertCaseAnnouncements ( stat . Criminal . Cases , "刑事案件" ) ... )
}
if stat . Administrative . Cases != nil {
caseRecords = append ( caseRecords , convertCaseAnnouncements ( stat . Administrative . Cases , "行政案件" ) ... )
}
if stat . Preservation . Cases != nil {
caseRecords = append ( caseRecords , convertCaseAnnouncements ( stat . Preservation . Cases , "非诉保全审查" ) ... )
}
if stat . Bankrupt . Cases != nil {
caseRecords = append ( caseRecords , convertCaseAnnouncements ( stat . Bankrupt . Cases , "强制清算与破产" ) ... )
}
identification . CaseAnnouncements . Records = caseRecords
if stat . Implement . Cases != nil {
identification . EnforcementAnnouncements . Records = convertEnforcementAnnouncements ( stat . Implement . Cases )
identification . DishonestAnnouncements . Records = convertDishonestAnnouncements ( ctx . JudicialData . JudicialData . BreachCaseList , baseName , baseID )
identification . HighConsumptionRestrictionAnn . Records = convertConsumptionRestrictions ( ctx . JudicialData . JudicialData . ConsumptionRestrictionList , baseName , baseID )
}
if sourceCtx . JudicialData . JudicialData . BreachCaseList != nil {
identification . DishonestAnnouncements . Records = convertDishonestAnnouncements ( sourceCtx . JudicialData . JudicialData . BreachCaseList , baseName , baseID )
}
if sourceCtx . JudicialData . JudicialData . ConsumptionRestrictionList != nil {
identification . HighConsumptionRestrictionAnn . Records = convertConsumptionRestrictions ( sourceCtx . JudicialData . JudicialData . ConsumptionRestrictionList , baseName , baseID )
}
return identification
}
// buildCreditAssessment 组装 creditAssessment, 包含客户类型与异常时间段
func buildCreditAssessment ( ctx * sourceContext ) creditAssessment {
func buildCreditAssessment ( ctx context . Context , sourceCtx * sourceContext ) creditAssessment {
log := logger . GetGlobalLogger ( )
assessment := creditAssessment {
Title : "信贷评估产品" ,
LoanIntentionByCustomerType : assessmentSection [ loanIntentionRecord ] {
@@ -748,8 +885,12 @@ func buildCreditAssessment(ctx *sourceContext) creditAssessment {
} ,
}
metrics := extractApplyLoanMetrics ( ctx )
// 兼容处理:安全提取指标
metrics := extractApplyLoanMetrics ( sourceCtx )
if len ( metrics ) == 0 {
log . Debug ( "未获取到借贷指标数据,返回空的信贷评估" ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
return assessment
}
@@ -854,7 +995,9 @@ func buildCreditAssessment(ctx *sourceContext) creditAssessment {
}
// buildLeasingRiskAssessment 组装 leasingRiskAssessment 中的 3C 多头信息
func buildLeasingRiskAssessment ( ctx * sourceContext ) leasingRiskAssessment {
func buildLeasingRiskAssessment ( ctx context . Context , sourceCtx * sourceContext ) leasingRiskAssessment {
log := logger . GetGlobalLogger ( )
assessment := leasingRiskAssessment {
Title : "租赁风险评估产品" ,
MultiLender3C : assessmentSection [ multiLenderRecord ] {
@@ -863,11 +1006,15 @@ func buildLeasingRiskAssessment(ctx *sourceContext) leasingRiskAssessment {
} ,
}
if ctx . ContentsData == nil || len ( ctx . ContentsData . Contents ) == 0 {
// 兼容处理: 安全访问ContentsData
if sourceCtx == nil || sourceCtx . ContentsData == nil || len ( sourceCtx . ContentsData . Contents ) == 0 {
log . Debug ( "ContentsData为空或Contents为空, 返回空的租赁风险评估" ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
return assessment
}
contents := c tx. ContentsData . Contents
contents := sourceC tx. ContentsData . Contents
// 消费金融机构指标, 优先使用近12个月的机构数, 其次退化到近一年内的其它统计
consumerApplied := pickFirstInt ( contents , "BH_A074" , "BH_A065" , "BH_A055" )
@@ -935,13 +1082,18 @@ func buildLeasingRiskAssessment(ctx *sourceContext) leasingRiskAssessment {
}
// buildComprehensiveAnalysis 汇总最终的文字结论(仅输出存在风险的要点)
func buildComprehensiveAnalysis ( ctx * sourceContext ) [ ] string {
func buildComprehensiveAnalysis ( ctx context . Context , sourceCtx * sourceContext ) [ ] string {
log := logger . GetGlobalLogger ( )
analysis := make ( [ ] string , 0 , 8 )
if c tx. BaseData == nil {
if sourceCtx == nil || sourceC tx. BaseData == nil {
log . Debug ( "BaseData为空, 返回空的综合分析" ,
zap . String ( "api_code" , "COMBHZY2" ) ,
)
return analysis
}
summary := buildReportSummary ( ctx )
summary := buildReportSummary ( ctx , sourceCtx )
highRiskDetected := false
mediumRiskDetected := false
@@ -957,32 +1109,33 @@ func buildComprehensiveAnalysis(ctx *sourceContext) []string {
mediumRiskDetected = mediumRiskDetected || medium
}
judicialBullet , judicialHigh , judicialMedium := buildJudicialBullet ( c tx)
judicialBullet , judicialHigh , judicialMedium := buildJudicialBullet ( sourceC tx)
if judicialBullet != "" {
analysis = append ( analysis , judicialBullet )
highRiskDetected = highRiskDetected || judicialHigh
mediumRiskDetected = mediumRiskDetected || judicialMedium
}
if msg , high , medium := buildLoanAssessmentBullet ( ctx ) ; msg != "" {
if msg , high , medium := buildLoanAssessmentBullet ( ctx , sourceCtx ); msg != "" {
analysis = append ( analysis , msg )
highRiskDetected = highRiskDetected || high
mediumRiskDetected = mediumRiskDetected || medium
}
if msg , high , medium := buildOtherRiskBullet ( c tx, judicialBullet != "" ) ; msg != "" {
if msg , high , medium := buildOtherRiskBullet ( sourceC tx, judicialBullet != "" ) ; msg != "" {
analysis = append ( analysis , msg )
highRiskDetected = highRiskDetected || high
mediumRiskDetected = mediumRiskDetected || medium
}
if msg , high , medium := buildMultiLenderBullet ( ctx ) ; msg != "" {
if msg , high , medium := buildMultiLenderBullet ( ctx , sourceCtx ); msg != "" {
analysis = append ( analysis , msg )
highRiskDetected = highRiskDetected || high
mediumRiskDetected = mediumRiskDetected || medium
}
risk := ctx . BaseData . RiskWarning
// 兼容处理:安全访问 RiskWarning
risk := sourceCtx . BaseData . RiskWarning
if hasHighRiskHit ( risk ) {
highRiskDetected = true
} else if hasMediumRiskHit ( risk ) {
@@ -999,7 +1152,7 @@ func buildComprehensiveAnalysis(ctx *sourceContext) []string {
}
// buildReportFooter 填充数据来源与免责声明
func buildReportFooter ( ctx * sourceContext ) reportFooter {
func buildReportFooter ( ctx context . Context , sourceCtx * sourceContext ) reportFooter {
return reportFooter {
DataSource : "天远数据报告" ,
GenerationTime : time . Now ( ) . Format ( "2006-01-02" ) ,
@@ -1109,12 +1262,13 @@ func mapRiskFlag(flag int) string {
}
// gatherOtherRiskDetails 汇总其它风险命中项的说明文本
func gatherOtherRiskDetails ( c tx * sourceContext ) string {
if c tx. BaseData == nil {
func gatherOtherRiskDetails ( sourceC tx * sourceContext ) string {
if sourceCtx == nil || sourceC tx. BaseData == nil {
return ""
}
hits := make ( [ ] string , 0 , 4 )
risk := ctx . BaseData . RiskWarning
// 兼容处理:安全访问 RiskWarning
risk := sourceCtx . BaseData . RiskWarning
if risk . IsAntiFraudInfo > 0 {
hits = append ( hits , "涉赌涉诈风险" )
}
@@ -1145,12 +1299,13 @@ func gatherOtherRiskDetails(ctx *sourceContext) string {
if risk . VeryFrequentRentalApplications > 0 {
hits = append ( hits , "租赁机构申请次数极多" )
}
if ctx . JudicialData != nil {
stat : = c tx. JudicialData . JudicialData . LawsuitStat
totalCase := len ( stat . Civil . Cases ) + len ( stat . Criminal . Cases ) + len ( s tat . Administrative . Cases ) + len ( stat . Preservation . Cases ) + len ( stat . Bankrupt . Cases )
totalExecution := len ( stat . Implemen t . Cases )
totalDishonest := l en( ctx . JudicialD ata . JudicialData . BreachCaseList )
totalRestriction := l en( c tx. JudicialData . JudicialData . ConsumptionRestriction List)
// 兼容处理: 安全访问JudicialData
if sourceCtx ! = nil && sourceC tx. JudicialData != nil {
stat := sourceCtx . JudicialDa ta. JudicialData . LawsuitStat
totalCase := safeLen ( stat . Civil . Cases ) + safeLen ( stat . Criminal . Cases ) + safeLen ( stat . Administrative . Cases ) + safeLen ( stat . Preservation . Cases ) + safeLen ( stat . Bankrup t . Cases )
totalExecution := safeL en( st at. Implement . Cases )
totalDishonest := safeL en( sourceC tx. JudicialData . JudicialData . BreachCase List)
totalRestriction := safeLen ( sourceCtx . JudicialData . JudicialData . ConsumptionRestrictionList )
if totalCase > 0 || totalExecution > 0 || totalDishonest > 0 || totalRestriction > 0 {
hits = append ( hits , "存在司法风险记录" )
}
@@ -1266,8 +1421,8 @@ func buildJudicialBullet(ctx *sourceContext) (string, bool, bool) {
return sentence , true , false
}
func buildLoanAssessmentBullet ( ctx * sourceContext ) ( string , bool , bool ) {
assessment := buildCreditAssessment ( ctx )
func buildLoanAssessmentBullet ( ctx context . Context , sourceCtx * sourceContext ) ( string , bool , bool ) {
assessment := buildCreditAssessment ( ctx , sourceCtx )
records := assessment . LoanIntentionByCustomerType . Records
if len ( records ) == 0 {
return "" , false , false
@@ -1311,8 +1466,8 @@ func buildLoanAssessmentBullet(ctx *sourceContext) (string, bool, bool) {
return sentence , high , medium
}
func buildMultiLenderBullet ( ctx * sourceContext ) ( string , bool , bool ) {
assessment := buildCreditAssessment ( ctx )
func buildMultiLenderBullet ( ctx context . Context , sourceCtx * sourceContext ) ( string , bool , bool ) {
assessment := buildCreditAssessment ( ctx , sourceCtx )
records := assessment . LoanIntentionAbnormalTimes . Records
if len ( records ) == 0 {
return "" , false , false
@@ -1596,12 +1751,13 @@ func addThousandsSeparator(value string) string {
}
// buildLoanRiskResult 根据 riskWarning 命中情况生成借贷评估结果
func buildLoanRiskResult ( c tx * sourceContext ) ( string , string ) {
if c tx. BaseData == nil {
func buildLoanRiskResult ( sourceC tx * sourceContext ) ( string , string ) {
if sourceCtx == nil || sourceC tx. BaseData == nil {
return "正常" , ""
}
risk := ctx . BaseData . RiskWarning
// 兼容处理:安全访问 RiskWarning
risk := sourceCtx . BaseData . RiskWarning
hits := make ( [ ] string , 0 , 3 )
if risk . HitHighRiskBankLastTwoYears > 0 {
@@ -1639,11 +1795,12 @@ func buildCaseDetails(parts []string) string {
}
// buildElementVerificationResult 根据 riskWarning 的要素相关项生成结果
func buildElementVerificationResult ( c tx * sourceContext ) ( string , string ) {
if c tx. BaseData == nil {
func buildElementVerificationResult ( sourceC tx * sourceContext ) ( string , string ) {
if sourceCtx == nil || sourceC tx. BaseData == nil {
return "正常" , ""
}
risk := ctx . BaseData . RiskWarning
// 兼容处理:安全访问 RiskWarning
risk := sourceCtx . BaseData . RiskWarning
hits := make ( [ ] string , 0 , 2 )
if risk . IdCardTwoElementMismatch > 0 {
hits = append ( hits , "身份证二要素不一致" )
@@ -1658,11 +1815,12 @@ func buildElementVerificationResult(ctx *sourceContext) (string, string) {
}
// buildCarrierVerificationResult 根据 riskWarning 的运营商相关项生成结果
func buildCarrierVerificationResult ( c tx * sourceContext ) ( string , string ) {
if c tx. BaseData == nil {
func buildCarrierVerificationResult ( sourceC tx * sourceContext ) ( string , string ) {
if sourceCtx == nil || sourceC tx. BaseData == nil {
return "正常" , ""
}
risk := ctx . BaseData . RiskWarning
// 兼容处理:安全访问 RiskWarning
risk := sourceCtx . BaseData . RiskWarning
hits := make ( [ ] string , 0 , 4 )
if risk . ShortPhoneDuration > 0 {
hits = append ( hits , "手机在网时长极短" )
@@ -1742,13 +1900,23 @@ func hasMediumRiskHit(r riskWarning) bool {
r . MoreFrequentNonBankApplications > 0
}
func extractApplyLoanMetrics ( c tx * sourceContext ) map [ string ] int {
if ctx . RiskScreen == nil {
func extractApplyLoanMetrics ( sourceC tx * sourceContext ) map [ string ] int {
// 兼容处理: 安全访问RiskScreen
if sourceCtx == nil || sourceCtx . RiskScreen == nil {
return nil
}
for _ , variable := range ctx . RiskScreen . RiskScreenV2 . Variables {
// 兼容处理:安全访问 Variables数组
if sourceCtx . RiskScreen . RiskScreenV2 . Variables == nil {
return nil
}
for _ , variable := range sourceCtx . RiskScreen . RiskScreenV2 . Variables {
if strings . EqualFold ( variable . VariableName , "bairong_applyloan_extend" ) {
// 兼容处理: 安全访问VariableValue
if variable . VariableValue == nil {
continue
}
results := make ( map [ string ] int , len ( variable . VariableValue ) )
for key , val := range variable . VariableValue {
results [ key ] = parseMetricValue ( val )
@@ -1821,3 +1989,4 @@ func riskLevelFromStrictCount(count int) string {
return "高风险"
}
}