From 8d5da9d88ef619cc5432ab2e03a01a73b38fed45 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Fri, 27 Feb 2026 16:42:38 +0800 Subject: [PATCH 1/4] f --- .../processors/dwbg/dwbg8b4d_processor.go | 85 ++++++++++++------- .../external/zhicha/zhicha_test.go | 9 +- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index 3cb9dc9..e19183e 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -3862,40 +3862,63 @@ func buildLeasingRiskAssessment(apiData map[string]interface{}, log *zap.Logger) assessment[fieldNameNight] = fmt.Sprintf("%s/%s", idNightAllnum, cellNightAllnum) } - // 判断风险标识:如果有申请记录,设置为高风险 - hasApplication := false - for _, apiPeriod := range periodMap { - fieldName := fmt.Sprintf("alc_%s_id_allnum", apiPeriod) - // 尝试多种类型:string、int、float64 - if allnum, ok := jrzq1d09Map[fieldName].(string); ok && allnum != "" && allnum != "0" { - hasApplication = true - break - } else if allnum, ok := jrzq1d09Map[fieldName].(int); ok && allnum > 0 { - hasApplication = true - break - } else if allnum, ok := jrzq1d09Map[fieldName].(float64); ok && allnum > 0 { - hasApplication = true - break - } - // 也检查cell字段 - fieldNameCell := fmt.Sprintf("alc_%s_cell_allnum", apiPeriod) - if allnum, ok := jrzq1d09Map[fieldNameCell].(string); ok && allnum != "" && allnum != "0" { - hasApplication = true - break - } else if allnum, ok := jrzq1d09Map[fieldNameCell].(int); ok && allnum > 0 { - hasApplication = true - break - } else if allnum, ok := jrzq1d09Map[fieldNameCell].(float64); ok && allnum > 0 { - hasApplication = true - break + // 仅使用近12月(Last12,对应 m12)的总次数作为判断依据, + // 因为近3月等短周期已经被近12月统计包含,避免重复放大 + totalCount := 0 + + idKey12 := "alc_m12_id_allnum" + cellKey12 := "alc_m12_cell_allnum" + + // 身份证维度(近12月) + if v, ok := jrzq1d09Map[idKey12]; ok { + switch vv := v.(type) { + case string: + if vv != "" && vv != "0" { + if parsed, err := strconv.Atoi(vv); err == nil { + totalCount += parsed + } + } + case float64: + if vv > 0 { + totalCount += int(vv) + } + case int: + if vv > 0 { + totalCount += vv + } } } - // 风险标识:如果有申请记录,风险较高;如果没有申请记录,风险较低 - // 注意:riskFlag的值:0=未查得,1=低风险,2=高风险 - if hasApplication { - assessment["riskFlag"] = 2 // 有申请记录,风险较高 + + // 手机号维度(近12月) + if v, ok := jrzq1d09Map[cellKey12]; ok { + switch vv := v.(type) { + case string: + if vv != "" && vv != "0" { + if parsed, err := strconv.Atoi(vv); err == nil { + totalCount += parsed + } + } + case float64: + if vv > 0 { + totalCount += int(vv) + } + case int: + if vv > 0 { + totalCount += vv + } + } + } + + // 根据近12月总申请次数设置风险标识: + // - totalCount == 0 -> 0 无风险 / 未查得 + // - 0 < totalCount <= 10 -> 2 低风险(有少量租赁申请) + // - totalCount > 10 -> 1 高风险(租赁申请很多) + if totalCount == 0 { + assessment["riskFlag"] = 0 + } else if totalCount <= 10 { + assessment["riskFlag"] = 2 } else { - assessment["riskFlag"] = 1 // 无申请记录,风险较低 + assessment["riskFlag"] = 1 } } } diff --git a/internal/infrastructure/external/zhicha/zhicha_test.go b/internal/infrastructure/external/zhicha/zhicha_test.go index 6d58eb2..3ca5e50 100644 --- a/internal/infrastructure/external/zhicha/zhicha_test.go +++ b/internal/infrastructure/external/zhicha/zhicha_test.go @@ -84,20 +84,25 @@ func TestEncryptWithInvalidKey(t *testing.T) { } func TestDecryptWithInvalidData(t *testing.T) { - key := "1234567890abcdef1234567890abcdef" + key := "af4ca0098e6a202a5c08c413ebd9fd62" // 测试无效的加密数据 invalidData := []string{ "", // 空数据 "invalid_base64", // 无效的Base64 "dGVzdA==", // 有效的Base64但不是AES加密数据 + "ZmJIKRQz6j+IboulkSfq0g==", + "qBijvRmmm3bbLEdaCMw2XvHc8SDq3oGh6lD6BwELyhU=", + "ITtZBkPMZQ88UTHWJuWjSA==", } for _, data := range invalidData { - _, err := Decrypt(data, key) + decrypted, err := Decrypt(data, key) if err == nil { t.Errorf("使用无效数据 %s 应该返回错误", data) } + fmt.Println("data: ", data) + fmt.Println("decrypted: ", decrypted) } } From a54a19e4396762c018ddb685f5beb15474b41cca Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Fri, 27 Feb 2026 16:49:45 +0800 Subject: [PATCH 2/4] f --- .../processors/dwbg/dwbg8b4d_processor.go | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index e19183e..e68d878 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -3862,29 +3862,32 @@ func buildLeasingRiskAssessment(apiData map[string]interface{}, log *zap.Logger) assessment[fieldNameNight] = fmt.Sprintf("%s/%s", idNightAllnum, cellNightAllnum) } - // 仅使用近12月(Last12,对应 m12)的总次数作为判断依据, - // 因为近3月等短周期已经被近12月统计包含,避免重复放大 + // 仅使用近12月(Last12,对应 m12)的次数作为判断依据, + // totalCount 取身份证维度和手机号维度中的较大值,而不是两者相加 totalCount := 0 idKey12 := "alc_m12_id_allnum" cellKey12 := "alc_m12_cell_allnum" + idCount := 0 + cellCount := 0 + // 身份证维度(近12月) if v, ok := jrzq1d09Map[idKey12]; ok { switch vv := v.(type) { case string: if vv != "" && vv != "0" { if parsed, err := strconv.Atoi(vv); err == nil { - totalCount += parsed + idCount = parsed } } case float64: if vv > 0 { - totalCount += int(vv) + idCount = int(vv) } case int: if vv > 0 { - totalCount += vv + idCount = vv } } } @@ -3895,20 +3898,27 @@ func buildLeasingRiskAssessment(apiData map[string]interface{}, log *zap.Logger) case string: if vv != "" && vv != "0" { if parsed, err := strconv.Atoi(vv); err == nil { - totalCount += parsed + cellCount = parsed } } case float64: if vv > 0 { - totalCount += int(vv) + cellCount = int(vv) } case int: if vv > 0 { - totalCount += vv + cellCount = vv } } } + // 使用身份证和手机号两个维度中的较大值作为近12月总次数 + if idCount >= cellCount { + totalCount = idCount + } else { + totalCount = cellCount + } + // 根据近12月总申请次数设置风险标识: // - totalCount == 0 -> 0 无风险 / 未查得 // - 0 < totalCount <= 10 -> 2 低风险(有少量租赁申请) From dedd4a60a4f2eb62c67df514c7445f689fd65f15 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sat, 28 Feb 2026 14:05:54 +0800 Subject: [PATCH 3/4] f --- .../processors/dwbg/dwbg8b4d_processor.go | 45 +++- .../dwbg/谛听报告_风险标识判断标准.md | 219 ++++++++++++++++++ .../external/zhicha/zhicha_test.go | 6 +- 3 files changed, 256 insertions(+), 14 deletions(-) create mode 100644 internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md diff --git a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go index e68d878..72f4b3c 100644 --- a/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go +++ b/internal/domains/api/services/processors/dwbg/dwbg8b4d_processor.go @@ -1524,7 +1524,7 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log } detail["belongRisks"] = belongRisks - // 公安重点人员核验产品(highRiskFlag 和 keyPersonCheckList) + // 公安重点人员核验产品(highRiskFlag 由 keyPersonCheckList 五项是否命中决定) flxgdea9Data := getMapValue(apiData, "FLXGDEA9") keyPersonCheckList := make(map[string]interface{}) keyPersonCheckList["num"] = "1" @@ -1538,7 +1538,6 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log if flxgdea9Map, ok := flxgdea9Data.(map[string]interface{}); ok { level, ok := flxgdea9Map["level"].(string) if !ok { - // 尝试从其他可能的字段获取 if levelVal, exists := flxgdea9Map["level"]; exists { if levelStr, ok := levelVal.(string); ok { level = levelStr @@ -1548,19 +1547,14 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log } } + // 仅根据 level 解析并填充 keyPersonCheckList 五项 if level != "" && level != "0" { - // 有风险,设置highRiskFlag为1(高风险) - detail["highRiskFlag"] = 1 - - // 解析level字段,填充keyPersonCheckList levelParts := strings.Split(level, ",") for _, part := range levelParts { part = strings.TrimSpace(part) if part == "" || part == "0" { continue } - - // 根据level代码判断风险类型 if strings.HasPrefix(part, "A") { keyPersonCheckList["fontFlag"] = 1 } @@ -1577,14 +1571,27 @@ func buildElementVerificationDetail(apiData map[string]interface{}, log *zap.Log keyPersonCheckList["sheJiaoTongFlag"] = 1 } } - } else { - // 无风险,设置highRiskFlag为2(低风险) - detail["highRiskFlag"] = 2 } + } } detail["keyPersonCheckList"] = keyPersonCheckList + // 仅根据 keyPersonCheckList 五项判定 highRiskFlag(不看是否有数据): + // 五项均未命中 → 0 无风险;仅 sheJiaoTongFlag 命中 → 2 低风险;其他任一项命中 → 1 高风险 + otherHit := keyPersonFlagEq1(keyPersonCheckList, "fontFlag") || + keyPersonFlagEq1(keyPersonCheckList, "jingJiFontFlag") || + keyPersonFlagEq1(keyPersonCheckList, "fangAiFlag") || + keyPersonFlagEq1(keyPersonCheckList, "zhongDianFlag") + sheJiaoHit := keyPersonFlagEq1(keyPersonCheckList, "sheJiaoTongFlag") + if otherHit { + detail["highRiskFlag"] = 1 // 高风险 + } else if sheJiaoHit { + detail["highRiskFlag"] = 2 // 低风险:仅涉交通命中 + } else { + detail["highRiskFlag"] = 0 // 无风险:五项均未命中 + } + // 设置默认值 if _, exists := detail["sjsysFlag"]; !exists { detail["sjsysFlag"] = 0 @@ -3945,6 +3952,22 @@ func getMapValue(data map[string]interface{}, key string) interface{} { return nil } +// keyPersonFlagEq1 判断 keyPersonCheckList 中某标识是否为 1(支持 int/float64) +func keyPersonFlagEq1(m map[string]interface{}, key string) bool { + v, ok := m[key] + if !ok { + return false + } + switch vv := v.(type) { + case int: + return vv == 1 + case float64: + return vv == 1 + default: + return false + } +} + func maskName(name string) string { if name == "" { return "" diff --git a/internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md b/internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md new file mode 100644 index 0000000..6af88ba --- /dev/null +++ b/internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md @@ -0,0 +1,219 @@ +# 谛听多维报告(DWBG8B4D)风险标识判断标准 + +本文档汇总 `dwbg8b4d_processor.go` 中所有风险标识(*Flag / riskFlag)的判定规则与数据来源,便于查阅和评审。 + +--- + +## 一、elementVerificationDetail(要素核验产品)内风险标识 + +### 1. sjsysFlag(手机三要素简版风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 YYSYH6D2 数据或无法解析 | +| 1 | 高风险 | YYSYH6D2.result ≠ "0"(身份证+手机号+姓名 不一致) | +| 2 | 低风险 | YYSYH6D2.result == "0"(三要素一致) | + +**数据源**:YYSYH6D2(手机三要素简版) + +--- + +### 2. sfzeysFlag(身份证二要素风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 IVYZ9K7F 数据或 status/desc/result 无法解析 | +| 1 | 高风险 | 核验结果不为「一致」 | +| 2 | 低风险 | 核验结果为「一致」 | + +**数据源**:IVYZ9K7F(身份证二要素)。status 来自 data.status、desc 或 result(0=一致,非0=不一致)。 + +--- + +### 3. onlineRiskFlag(手机在网时长风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 YYSY8B1C 数据或 inTime 为空 | +| 1 | 高风险 | inTime == "0" 或 "3"(在网时长极短) | +| 2 | 低风险 | inTime 为其他非空值 | + +**数据源**:YYSY8B1C(手机在网时长) + +--- + +### 4. phoneVailRiskFlag(手机信息验证风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 YYSYE7V5 数据或 status 无法解析 | +| 1 | 高风险 | status == 1(不在网) | +| 2 | 低风险 | status == 0(在网/实号) | + +**数据源**:YYSYE7V5(手机在网状态等) + +--- + +### 5. belongRiskFlag(身份证号与手机号归属地风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 YYSY9E4A 或 YYSYH6D2,或户籍/归属地任一方为空 | +| 1 | 高风险 | 户籍所在地与号码归属地**不一致**(经智能比较) | +| 2 | 低风险 | 户籍所在地与号码归属地**一致** | + +**数据源**:YYSY9E4A(号码归属地)、YYSYH6D2(address 作户籍)。使用 `compareLocation` 做省/市归一化后比较。 + +--- + +### 6. highRiskFlag(公安重点人员核验风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 无风险 | keyPersonCheckList 五项(fontFlag、jingJiFontFlag、fangAiFlag、zhongDianFlag、sheJiaoTongFlag)**均为 0** | +| 1 | 高风险 | **任一项** fontFlag / jingJiFontFlag / fangAiFlag / zhongDianFlag 为 1(涉刑/经侦/妨害/重点/涉交通以外) | +| 2 | 低风险 | **仅** sheJiaoTongFlag 为 1(仅涉交通命中),其余为 0 | + +**数据源**:FLXGDEA9 的 level 解析后填充 keyPersonCheckList(A→fontFlag, B→jingJiFontFlag, C→fangAiFlag, D→zhongDianFlag, E→sheJiaoTongFlag)。**不看是否有数据**,仅根据五项当前值判定。 + +--- + +## 二、overdueRiskProduct(逾期风险产品)内风险标识 + +### 7. lyjlhyFlag(履约/借贷逾期类风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 JRZQ5E9F 数据(默认) | +| 1 | 高风险 | 当前存在未结清逾期 **或** 近 7/14/30 天有逾期(hasUnsettledOverdue=="逾期" 或 overdueLast7Days/14Days/30Days 任一为「逾期」) | +| 2 | 低风险 | 有 JRZQ5E9F 数据且无上述逾期情况 | + +**数据源**:JRZQ5E9F(借选指数评估),字段:xyp_cpl0044、xyp_cpl0029、xyp_cpl0030、xyp_cpl0031 等。 + +--- + +### 8. dkzhktjFlag(贷款综合情况风险标识) + +与 **lyjlhyFlag** 使用同一套逻辑,同时赋值:有逾期或近期逾期则为 1,否则为 2。 + +**数据源**:JRZQ5E9F。 + +--- + +### 9. tsmdyzFlag(特殊名单验证风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 JRZQ8A2D 数据(默认) | +| 1 | 高风险 | specialListVerification 列表**非空**(身份证或手机号任一侧命中:法院失信人/被执行人、银行/非银中风险/一般风险/高风险等任一) | +| 2 | 低风险 | 有 JRZQ8A2D 数据且 specialListVerification 为空 | + +**数据源**:JRZQ8A2D(特殊名单验证 B)。命中规则:id/cell 下对应字段值为 "0" 表示命中(如 court_bad、court_executed、bank_lost 等)。 + +--- + +## 三、multCourtInfo(司法风险核验产品)内风险标识 + +### 10. legalCasesFlag(涉案公告案件风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 无 | 无 FLXG7E8F 或 lawsuitStat 下无民事/刑事/行政/保全/破产案件 | +| 1 | 高风险 | legalCases 列表**非空**(民事、刑事、行政、保全、破产任一类 cases 非空) | + +**数据源**:FLXG7E8F(个人司法涉诉查询)→ judicial_data.lawsuitStat → civil/criminal/administrative/preservation/bankrupt.cases。 + +--- + +### 11. executionCasesFlag(执行案件风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 无 | 无 FLXG7E8F 或 implement.cases 为空 | +| 1 | 高风险 | executionCases 列表**非空** | + +**数据源**:FLXG7E8F → judicial_data.lawsuitStat.implement.cases。 + +--- + +### 12. disinCasesFlag(失信案件风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 无 | 无 FLXG7E8F 或 breachCaseList 为空 | +| 1 | 高风险 | disinCases 列表**非空** | + +**数据源**:FLXG7E8F → judicial_data.breachCaseList。 + +--- + +### 13. limitCasesFlag(限高案件风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 无 | 无 FLXG7E8F 或 consumptionRestrictionList 为空 | +| 1 | 高风险 | limitCases 列表**非空** | + +**数据源**:FLXG7E8F → judicial_data.consumptionRestrictionList。 + +--- + +## 四、loanEvaluationVerificationDetail(借贷评估产品)内风险标识 + +### 14. riskFlag(借贷评估风险标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 未查得 | 无 JRZQ6F2A 或 risk_screen_v2.variables[0].variableValue 无法解析 | +| 1 | 高风险 | checkLoanRisk 为 true:**任一时间段**(d7/d15/m1/m3)下,银行或非银申请次数 **≥ 10**(als_{period}_id_bank_allnum / als_{period}_id_nbank_allnum) | +| 2 | 低风险 | 有数据且 checkLoanRisk 为 false | + +**数据源**:JRZQ6F2A(借贷意向验证 A)→ risk_screen_v2.variables[0].variableValue。 + +--- + +## 五、leasingRiskAssessment(租赁风险评估产品)内风险标识 + +### 15. riskFlag(租赁风险评估标识) + +| 取值 | 含义 | 判断条件 | +|------|--------|----------| +| 0 | 无风险 | 近 12 月(m12)**总次数为 0**(身份证维度 alc_m12_id_allnum 与手机号维度 alc_m12_cell_allnum 取**较大值**后为 0) | +| 1 | 高风险 | 近 12 月总次数 **> 10** | +| 2 | 低风险 | 近 12 月总次数 **1~10**(含 10) | + +**数据源**:JRZQ1D09(3C 租赁申请意向)。**仅用近 12 月(Last12 / m12)**,totalCount = max(idCount, cellCount)。 + +--- + +## 六、riskWarning(规则风险提示)内各项命中规则摘要 + +以下为「命中即置 1」的规则,未命中或未查得均为 0。不改变上述各产品内 *Flag 的取值含义。 + +| 类别 | 字段示例 | 命中条件(简要) | +|----------------|----------|------------------| +| 要素核查 | idCardTwoElementMismatch | IVYZ9K7F 二要素结果非「一致」 | +| | phoneThreeElementMismatch | YYSYH6D2.result ≠ "0" | +| 运营商/在网 | shortPhoneDuration | YYSY8B1C.inTime == "0" | +| | shortPhoneDurationSlight | YYSY8B1C.inTime == "3" | +| | noPhoneDuration | YYSYE7V5.status==1 且 desc 含「风险」 | +| 归属地 | idCardPhoneProvinceMismatch | 户籍与号码归属地智能比较不一致 | +| 公安重点人员 | hasCriminalRecord / isEconomyFront / isDisrupSocial / isKeyPerson / isTrafficRelated | FLXGDEA9.level 解析后 A/B/C/D/E 前缀 | +| 涉赌涉诈 | isAntiFraudInfo | FLXG8B4D 中 moneyLaundering/deceiver/gamblerPlayer/gamblerBanker 任一项非空且非"0" | +| 逾期/名单 | hitHighRiskBankLastTwoYears / hitHighRiskNonBankLastTwoYears / hitCurrentOverdue | JRZQ8A2D.id 下 bank_lost/nbank_lost/bank_overdue/nbank_overdue 等字段值为 "0"(命中) | +| 司法 | hitCivilCase / hitCriminalRisk / hitExecutionCase / hitAdministrativeCase / hitPreservationReview / hitBankruptcyAndLiquidation | FLXG7E8F 对应案件类型 **cases 数组非空**(不依 count) | +| 借贷意向 | frequentApplicationRecent | 近 d7/d15/m1 银行或非银申请次数 **≥ 10**(JRZQ6F2A) | +| | frequentBankApplications / moreFrequentBankApplications | 近 m6+m12 银行申请总次数 **≥ 20** / **≥ 15** | +| | frequentNonBankApplications / moreFrequentNonBankApplications | 近 m6+m12 非银申请总次数 **≥ 20** / **≥ 15** | +| 租赁申请 | veryFrequentRentalApplications / frequentRentalApplications | JRZQ1D09 近 m3+m6+m12(每期取 id/cell 较大值再累加)总次数 **≥ 20** / **≥ 15** | +| 偿债压力 | highDebtPressure | JRZQ5E9F 当前逾期金额或当前逾期机构数非空且非 "0"/"1" | + +--- + +## 七、风险取值约定(通用) + +- **0**:未查得 / 无风险 / 无命中(依字段语义) +- **1**:高风险 / 命中 +- **2**:低风险(仅部分 *Flag 使用,如 sjsysFlag、sfzeysFlag、onlineRiskFlag、phoneVailRiskFlag、belongRiskFlag、highRiskFlag、lyjlhyFlag、dkzhktjFlag、tsmdyzFlag、loanEvaluation riskFlag、leasingRiskAssessment riskFlag) + +文档版本:根据当前 `dwbg8b4d_processor.go` 逻辑整理,若有代码变更请同步更新本文档。 diff --git a/internal/infrastructure/external/zhicha/zhicha_test.go b/internal/infrastructure/external/zhicha/zhicha_test.go index 3ca5e50..651dd19 100644 --- a/internal/infrastructure/external/zhicha/zhicha_test.go +++ b/internal/infrastructure/external/zhicha/zhicha_test.go @@ -91,9 +91,9 @@ func TestDecryptWithInvalidData(t *testing.T) { "", // 空数据 "invalid_base64", // 无效的Base64 "dGVzdA==", // 有效的Base64但不是AES加密数据 - "ZmJIKRQz6j+IboulkSfq0g==", - "qBijvRmmm3bbLEdaCMw2XvHc8SDq3oGh6lD6BwELyhU=", - "ITtZBkPMZQ88UTHWJuWjSA==", + "i96w+SDjwENjuvsokMFbLw==", + "oaihmICgEcszWMk0gXoB12E/ygF4g78x0/sC3/KHnBk=", + "5bx+WvXvdNRVVOp9UuNFHg==", } for _, data := range invalidData { From 927b08b8719d8531b1eac06a1bbd4d69f7d0db97 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sat, 28 Feb 2026 14:49:42 +0800 Subject: [PATCH 4/4] f --- config.yaml | 2 ++ configs/env.development.yaml | 2 ++ configs/env.production.yaml | 4 ++- .../dto/responses/finance_responses.go | 6 ++-- .../finance_application_service_impl.go | 31 ++++++++++++------- internal/config/config.go | 12 ++++--- .../services/recharge_record_service.go | 4 +-- .../services/recharge_record_service_test.go | 17 +++++++++- 8 files changed, 56 insertions(+), 22 deletions(-) diff --git a/config.yaml b/config.yaml index 04fcb45..364e8ac 100644 --- a/config.yaml +++ b/config.yaml @@ -276,6 +276,8 @@ wallet: default_credit_limit: 50.00 min_amount: "100.00" # 生产环境最低充值金额 max_amount: "100000.00" # 单次最高充值金额 + recharge_bonus_enabled: true # 是否启用充值赠送,设为 false 时仅展示商务洽谈提示 + api_store_recharge_tip: "" # 关闭赠送时展示的提示文案,为空则使用默认文案 # 支付宝充值赠送配置 alipay_recharge_bonus: - recharge_amount: 1000.00 # 充值1000元 diff --git a/configs/env.development.yaml b/configs/env.development.yaml index 2c5f9ad..a35c4b2 100644 --- a/configs/env.development.yaml +++ b/configs/env.development.yaml @@ -108,6 +108,8 @@ wallet: default_credit_limit: 0.01 min_amount: "0.01" # 生产环境最低充值金额 max_amount: "100000.00" # 单次最高充值金额 + recharge_bonus_enabled: false # 开发环境可设为 true 测试赠送 + api_store_recharge_tip: "尊敬的客户,若您的充值金额较大或有批量调价需求,为获取专属商务优惠方案,请直接联系我司商务团队进行洽谈。感谢您的支持!" # 支付宝充值赠送配置 alipay_recharge_bonus: - recharge_amount: 0.01 # 充值1000元 diff --git a/configs/env.production.yaml b/configs/env.production.yaml index 3159cdb..198eba5 100644 --- a/configs/env.production.yaml +++ b/configs/env.production.yaml @@ -109,7 +109,9 @@ wallet: default_credit_limit: 50.00 min_amount: "100.00" # 生产环境最低充值金额 max_amount: "100000.00" # 单次最高充值金额 - # 支付宝充值赠送配置 + recharge_bonus_enabled: false # 暂不赠送,展示商务洽谈提示 + api_store_recharge_tip: "尊敬的客户,若您的充值金额较大或有批量调价需求,为获取专属商务优惠方案,请直接联系我司商务团队进行洽谈。感谢您的支持!" + # 支付宝充值赠送配置(recharge_bonus_enabled 为 true 时生效) alipay_recharge_bonus: - recharge_amount: 1000.00 # 充值1000元 bonus_amount: 50.00 # 赠送50元 diff --git a/internal/application/finance/dto/responses/finance_responses.go b/internal/application/finance/dto/responses/finance_responses.go index 556bd35..34a2262 100644 --- a/internal/application/finance/dto/responses/finance_responses.go +++ b/internal/application/finance/dto/responses/finance_responses.go @@ -108,8 +108,10 @@ type AlipayRechargeOrderResponse struct { // RechargeConfigResponse 充值配置响应 type RechargeConfigResponse struct { - MinAmount string `json:"min_amount"` // 最低充值金额 - MaxAmount string `json:"max_amount"` // 最高充值金额 + MinAmount string `json:"min_amount"` // 最低充值金额 + MaxAmount string `json:"max_amount"` // 最高充值金额 + RechargeBonusEnabled bool `json:"recharge_bonus_enabled"` // 是否启用充值赠送 + ApiStoreRechargeTip string `json:"api_store_recharge_tip"` // API 商店充值提示(大额/批量联系商务) AlipayRechargeBonus []AlipayRechargeBonusRuleResponse `json:"alipay_recharge_bonus"` } diff --git a/internal/application/finance/finance_application_service_impl.go b/internal/application/finance/finance_application_service_impl.go index 2b8eab2..d5d6d52 100644 --- a/internal/application/finance/finance_application_service_impl.go +++ b/internal/application/finance/finance_application_service_impl.go @@ -1337,17 +1337,26 @@ func (s *FinanceApplicationServiceImpl) GetAdminRechargeRecords(ctx context.Cont // GetRechargeConfig 获取充值配置 func (s *FinanceApplicationServiceImpl) GetRechargeConfig(ctx context.Context) (*responses.RechargeConfigResponse, error) { - bonus := make([]responses.AlipayRechargeBonusRuleResponse, 0, len(s.config.Wallet.AliPayRechargeBonus)) - for _, rule := range s.config.Wallet.AliPayRechargeBonus { - bonus = append(bonus, responses.AlipayRechargeBonusRuleResponse{ - RechargeAmount: rule.RechargeAmount, - BonusAmount: rule.BonusAmount, - }) + bonus := make([]responses.AlipayRechargeBonusRuleResponse, 0) + if s.config.Wallet.RechargeBonusEnabled && len(s.config.Wallet.AliPayRechargeBonus) > 0 { + bonus = make([]responses.AlipayRechargeBonusRuleResponse, 0, len(s.config.Wallet.AliPayRechargeBonus)) + for _, rule := range s.config.Wallet.AliPayRechargeBonus { + bonus = append(bonus, responses.AlipayRechargeBonusRuleResponse{ + RechargeAmount: rule.RechargeAmount, + BonusAmount: rule.BonusAmount, + }) + } + } + tip := s.config.Wallet.ApiStoreRechargeTip + if tip == "" && !s.config.Wallet.RechargeBonusEnabled { + tip = "尊敬的客户,若您的充值金额较大或有批量调价需求,为获取专属商务优惠方案,请直接联系我司商务团队进行洽谈。感谢您的支持!" } return &responses.RechargeConfigResponse{ - MinAmount: s.config.Wallet.MinAmount, - MaxAmount: s.config.Wallet.MaxAmount, - AlipayRechargeBonus: bonus, + MinAmount: s.config.Wallet.MinAmount, + MaxAmount: s.config.Wallet.MaxAmount, + RechargeBonusEnabled: s.config.Wallet.RechargeBonusEnabled, + ApiStoreRechargeTip: tip, + AlipayRechargeBonus: bonus, }, nil } @@ -1651,9 +1660,9 @@ func (s *FinanceApplicationServiceImpl) processWechatPaymentSuccess(ctx context. return nil } - // 计算充值赠送金额(复用支付宝的赠送逻辑) + // 计算充值赠送金额(复用支付宝的赠送逻辑,受 recharge_bonus_enabled 开关控制) bonusAmount := decimal.Zero - if len(s.config.Wallet.AliPayRechargeBonus) > 0 { + if s.config.Wallet.RechargeBonusEnabled && len(s.config.Wallet.AliPayRechargeBonus) > 0 { for i := len(s.config.Wallet.AliPayRechargeBonus) - 1; i >= 0; i-- { rule := s.config.Wallet.AliPayRechargeBonus[i] if amount.GreaterThanOrEqual(decimal.NewFromFloat(rule.RechargeAmount)) { diff --git a/internal/config/config.go b/internal/config/config.go index 61739ec..e865834 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -331,11 +331,13 @@ type SignConfig struct { // WalletConfig 钱包配置 type WalletConfig struct { - DefaultCreditLimit float64 `mapstructure:"default_credit_limit"` - MinAmount string `mapstructure:"min_amount"` // 最低充值金额 - MaxAmount string `mapstructure:"max_amount"` // 最高充值金额 - AliPayRechargeBonus []AliPayRechargeBonusRule `mapstructure:"alipay_recharge_bonus"` - BalanceAlert BalanceAlertConfig `mapstructure:"balance_alert"` + DefaultCreditLimit float64 `mapstructure:"default_credit_limit"` + MinAmount string `mapstructure:"min_amount"` // 最低充值金额 + MaxAmount string `mapstructure:"max_amount"` // 最高充值金额 + RechargeBonusEnabled bool `mapstructure:"recharge_bonus_enabled"` // 是否启用充值赠送,关闭后仅展示商务洽谈提示 + ApiStoreRechargeTip string `mapstructure:"api_store_recharge_tip"` // API 商店充值提示文案(大额/批量需求联系商务) + AliPayRechargeBonus []AliPayRechargeBonusRule `mapstructure:"alipay_recharge_bonus"` + BalanceAlert BalanceAlertConfig `mapstructure:"balance_alert"` } // BalanceAlertConfig 余额预警配置 diff --git a/internal/domains/finance/services/recharge_record_service.go b/internal/domains/finance/services/recharge_record_service.go index e69a8c1..1657d94 100644 --- a/internal/domains/finance/services/recharge_record_service.go +++ b/internal/domains/finance/services/recharge_record_service.go @@ -15,9 +15,9 @@ import ( "tyapi-server/internal/shared/interfaces" ) -// calculateAlipayRechargeBonus 计算支付宝充值赠送金额 +// calculateAlipayRechargeBonus 计算支付宝充值赠送金额(受 recharge_bonus_enabled 开关控制) func calculateAlipayRechargeBonus(rechargeAmount decimal.Decimal, walletConfig *config.WalletConfig) decimal.Decimal { - if walletConfig == nil || len(walletConfig.AliPayRechargeBonus) == 0 { + if walletConfig == nil || !walletConfig.RechargeBonusEnabled || len(walletConfig.AliPayRechargeBonus) == 0 { return decimal.Zero } diff --git a/internal/domains/finance/services/recharge_record_service_test.go b/internal/domains/finance/services/recharge_record_service_test.go index 489f322..64dbaa7 100644 --- a/internal/domains/finance/services/recharge_record_service_test.go +++ b/internal/domains/finance/services/recharge_record_service_test.go @@ -10,8 +10,9 @@ import ( ) func TestCalculateAlipayRechargeBonus(t *testing.T) { - // 创建测试配置 + // 创建测试配置(开启赠送) walletConfig := &config.WalletConfig{ + RechargeBonusEnabled: true, AliPayRechargeBonus: []config.AliPayRechargeBonusRule{ {RechargeAmount: 1000.00, BonusAmount: 50.00}, // 充1000送50 {RechargeAmount: 5000.00, BonusAmount: 300.00}, // 充5000送300 @@ -74,6 +75,7 @@ func TestCalculateAlipayRechargeBonus(t *testing.T) { func TestCalculateAlipayRechargeBonus_EmptyConfig(t *testing.T) { // 测试空配置 walletConfig := &config.WalletConfig{ + RechargeBonusEnabled: true, AliPayRechargeBonus: []config.AliPayRechargeBonusRule{}, } @@ -85,4 +87,17 @@ func TestCalculateAlipayRechargeBonus_EmptyConfig(t *testing.T) { assert.True(t, bonus.Equal(decimal.Zero), "nil配置应该返回零赠送金额") } +func TestCalculateAlipayRechargeBonus_Disabled(t *testing.T) { + // 关闭赠送时,任意金额均不赠送 + walletConfig := &config.WalletConfig{ + RechargeBonusEnabled: false, + AliPayRechargeBonus: []config.AliPayRechargeBonusRule{ + {RechargeAmount: 1000.00, BonusAmount: 50.00}, + {RechargeAmount: 10000.00, BonusAmount: 800.00}, + }, + } + bonus := calculateAlipayRechargeBonus(decimal.NewFromFloat(10000.00), walletConfig) + assert.True(t, bonus.Equal(decimal.Zero), "关闭赠送时应返回零") +} + \ No newline at end of file