diff --git a/internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md b/internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md deleted file mode 100644 index 6af88ba..0000000 --- a/internal/domains/api/services/processors/dwbg/谛听报告_风险标识判断标准.md +++ /dev/null @@ -1,219 +0,0 @@ -# 谛听多维报告(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/domains/api/services/processors/ivyz/ivyz5e3f_processor.go b/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go index 8453644..913c9c5 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz5e3f_processor.go @@ -7,7 +7,7 @@ import ( "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/zhicha" + "tyapi-server/internal/infrastructure/external/jiguang" ) // ProcessIVYZ5E3FRequest IVYZ5E3F API处理方法 - 婚姻评估查询 @@ -21,36 +21,57 @@ func ProcessIVYZ5E3FRequest(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, err) } - encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - + // 构建极光请求参数(使用原始身份证和姓名) reqData := map[string]interface{}{ - "name": encryptedName, - "idCard": encryptedIDCard, - "authorized": paramsDto.Authorized, + // 与 IVYZ9H2M 一致,极光使用 id_no 作为身份证字段 + "id_card": paramsDto.IDCard, + "name": paramsDto.Name, } - respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI029", reqData) + // 调用极光婚姻查询接口 + // apiCode: marriage-single(用于请求头) + // apiPath: marriage/single(用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "marriage-single", "marriage/single", reqData) if err != nil { - if errors.Is(err, zhicha.ErrDatasource) { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { return nil, errors.Join(processors.ErrDatasource, err) } else { return nil, errors.Join(processors.ErrSystem, err) } } - // 将响应数据转换为JSON字节 - respBytes, err := json.Marshal(respData) - if err != nil { + // 解析极光返回的数据 + // 现在的婚姻状态编码为(state): + // 0-结婚 1-离婚 2-未匹配(均为字符串) + var jgResp struct { + State string `json:"state"` + } + if err := json.Unmarshal(respBytes, &jgResp); err != nil { return nil, errors.Join(processors.ErrSystem, err) } - return respBytes, nil + // 将极光的编码转换为旧的 state 定义: + // 1:已婚,2:未婚/未在民政局登记,3:离异 + var state string + switch jgResp.State { + case "0": // 结婚 + state = "1" + case "1": // 离婚 + state = "3" + case "2": // 未匹配 + state = "2" + default: + // 未知状态按未匹配处理 + state = "2" + } + + // 返回与之前一致的结构:{"state": "1/2/3"} + result := map[string]string{ + "state": state, + } + + return json.Marshal(result) } diff --git a/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go b/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go index c9f6da9..3419d77 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz81nc_processor.go @@ -7,7 +7,7 @@ import ( "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/zhicha" + "tyapi-server/internal/infrastructure/external/jiguang" ) // ProcessIVYZ81NCRequest IVYZ81NC API处理方法 @@ -21,49 +21,55 @@ func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, err) } - encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - + // 构建极光请求参数(使用原始身份证和姓名) reqData := map[string]interface{}{ - "name": encryptedName, - "idCard": encryptedIDCard, - "authorized": "1", // 默认值 + // 与 IVYZ9H2M 一致,极光使用 id_no 作为身份证字段 + "id_card": paramsDto.IDCard, + "name": paramsDto.Name, } - respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI029", reqData) + // 调用极光婚姻查询接口 + // apiCode: marriage-single(用于请求头) + // apiPath: marriage/single(用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "marriage-single", "marriage/single", reqData) if err != nil { - if errors.Is(err, zhicha.ErrDatasource) { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { return nil, errors.Join(processors.ErrDatasource, err) } else { return nil, errors.Join(processors.ErrSystem, err) } } - // 解析响应数据,期望格式为 {"state": "1"} - var stateResp struct { + // 解析极光返回的数据,期望格式为 {"state": "0"|"1"|"2"} + var jgResp struct { State string `json:"state"` } - - // 将 respData 转换为 JSON 字节再解析 - respDataBytes, err := json.Marshal(respData) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - if err := json.Unmarshal(respDataBytes, &stateResp); err != nil { + if err := json.Unmarshal(respBytes, &jgResp); err != nil { return nil, errors.Join(processors.ErrSystem, err) } - // 根据 state 值转换为 81nc 格式 + // 将极光的 state 编码转换为旧的 state 语义: + // 极光:0-结婚 1-离婚 2-未匹配 + // 旧定义:1:已婚,2:未婚/未在民政局登记,3:离异 + var oldState string + switch jgResp.State { + case "0": // 结婚 + oldState = "1" + case "1": // 离婚 + oldState = "3" + case "2": // 未匹配 + oldState = "2" + default: + // 未知状态按未匹配处理 + oldState = "2" + } + + // 根据旧的 state 值转换为 81nc 格式 var opType, opTypeDesc string - switch stateResp.State { + switch oldState { case "1": // 已婚 opType = "IA" opTypeDesc = "结婚" diff --git a/internal/domains/api/services/processors/ivyz/ivyz9363_processor.go b/internal/domains/api/services/processors/ivyz/ivyz9363_processor.go index acc7a9f..0055910 100644 --- a/internal/domains/api/services/processors/ivyz/ivyz9363_processor.go +++ b/internal/domains/api/services/processors/ivyz/ivyz9363_processor.go @@ -8,7 +8,7 @@ import ( "tyapi-server/internal/domains/api/dto" "tyapi-server/internal/domains/api/services/processors" - "tyapi-server/internal/infrastructure/external/westdex" + "tyapi-server/internal/infrastructure/external/jiguang" ) // ProcessIVYZ9363Request IVYZ9363 API处理方法 @@ -17,7 +17,6 @@ func ProcessIVYZ9363Request(ctx context.Context, params []byte, deps *processors if err := json.Unmarshal(params, ¶msDto); err != nil { return nil, errors.Join(processors.ErrSystem, err) } - if err := deps.Validator.ValidateStruct(paramsDto); err != nil { return nil, errors.Join(processors.ErrInvalidParam, err) } @@ -26,43 +25,72 @@ func ProcessIVYZ9363Request(ctx context.Context, params []byte, deps *processors return nil, errors.Join(processors.ErrInvalidParam, errors.New("请正确填写身份信息")) } - encryptedManName, err := deps.WestDexService.Encrypt(paramsDto.ManName) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - encryptedManIDCard, err := deps.WestDexService.Encrypt(paramsDto.ManIDCard) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - encryptedWomanName, err := deps.WestDexService.Encrypt(paramsDto.WomanName) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - - encryptedWomanIDCard, err := deps.WestDexService.Encrypt(paramsDto.WomanIDCard) - if err != nil { - return nil, errors.Join(processors.ErrSystem, err) - } - + // 构建极光双人婚姻查询请求参数 + // 极光入参:id_card_m / name_m / id_card_w / name_w / authCode reqData := map[string]interface{}{ - "data": map[string]interface{}{ - "name_man": encryptedManName, - "idcard_man": encryptedManIDCard, - "name_woman": encryptedWomanName, - "idcard_woman": encryptedWomanIDCard, - }, + "id_card_m": paramsDto.ManIDCard, + "name_m": paramsDto.ManName, + "id_card_w": paramsDto.WomanIDCard, + "name_w": paramsDto.WomanName, + "authCode": deps.CallContext.ContractCode, } - respBytes, err := deps.WestDexService.CallAPI(ctx, "G10XM02", reqData) + // 调用极光双人婚姻查询接口 + // apiCode: marriage-double-v2(用于请求头) + // apiPath: marriage/double-v2(用于URL路径) + respBytes, err := deps.JiguangService.CallAPI(ctx, "marriage-double-v2", "marriage/double-v2", reqData) if err != nil { - if errors.Is(err, westdex.ErrDatasource) { + // 根据错误类型返回相应的错误 + if errors.Is(err, jiguang.ErrNotFound) { + return nil, errors.Join(processors.ErrNotFound, err) + } else if errors.Is(err, jiguang.ErrDatasource) { return nil, errors.Join(processors.ErrDatasource, err) } else { return nil, errors.Join(processors.ErrSystem, err) } } - return respBytes, nil + // 解析极光返回的数据,示例:"result": "0" + // 0 结婚 1 离婚 2 未匹配(均为字符串) + var jgResp struct { + Result string `json:"result"` + } + if err := json.Unmarshal(respBytes, &jgResp); err != nil { + return nil, errors.Join(processors.ErrSystem, err) + } + + // 将极光 result 映射为 81NC 业务类型 + // 0: 结婚 -> IA / 结婚 + // 1: 离婚 -> IB / 离婚 + // 2: 未匹配 -> INR / 匹配不成功 + var opType, opTypeDesc string + switch jgResp.Result { + case "0": + opType = "IA" + opTypeDesc = "结婚" + case "1": + opType = "IB" + opTypeDesc = "离婚" + case "2": + opType = "INR" + opTypeDesc = "匹配不成功" + default: + // 未知状态按未匹配处理 + opType = "INR" + opTypeDesc = "匹配不成功" + } + + // 构建当前接口对外约定的返回结构 + result := map[string]interface{}{ + "msg": "操作成功", + "code": 200, + "data": map[string]interface{}{ + "op_date": "", // 极光未提供日期,这里保持空串 + "op_type": opType, // IA / IB / INR + "op_type_desc": opTypeDesc, // 结婚 / 离婚 / 匹配不成功 + }, + "success": true, + } + + return json.Marshal(result) }