This commit is contained in:
Mrx
2026-06-02 12:26:22 +08:00
parent aed109d589
commit 6d892643ba
9 changed files with 228 additions and 65 deletions

View File

@@ -7,7 +7,7 @@ import (
"tyapi-server/internal/domains/api/dto"
"tyapi-server/internal/domains/api/services/processors"
"tyapi-server/internal/infrastructure/external/xingwei"
"tyapi-server/internal/infrastructure/external/nuoer"
)
// ProcessIVYZ8I9JRequest IVYZ8I9J API处理方法 - 互联网行为推测
@@ -21,27 +21,37 @@ func ProcessIVYZ8I9JRequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrInvalidParam, err)
}
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
reqData := map[string]interface{}{
"name": paramsDto.Name,
"idCardNum": paramsDto.IDCard,
"phoneNumber": paramsDto.MobileNo,
body := map[string]string{
"name": paramsDto.Name,
"idCard": paramsDto.IDCard,
"mobile": paramsDto.MobileNo,
}
// 调用行为数据API使用指定的project_id
projectID := "CDJ-1074522823015198720"
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
nuoerDoCheckAPIKey := "kunyu_fix_v3_tg_model"
ApiPath := "/v1/doCheck"
resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
if err != nil {
if errors.Is(err, xingwei.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
} else if errors.Is(err, xingwei.ErrDatasource) {
if errors.Is(err, nuoer.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err)
} else if errors.Is(err, xingwei.ErrSystem) {
return nil, errors.Join(processors.ErrSystem, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
}
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err)
}
return respBytes, nil
// 将响应数据序列化为 JSON
respBytes, err := json.Marshal(resp.Data)
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
}
// 使用 transform 函数转换响应数据
transformedBytes, err := IVYZ8I9JTransformResponseFromBytes(respBytes)
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
}
return transformedBytes, nil
}

View File

@@ -0,0 +1,108 @@
package ivyz
import (
"encoding/json"
"strings"
"github.com/tidwall/gjson"
)
// RiskCodeMapping 风险码到字段的映射
// 注意:一个风险码可能映射到多个字段
var RiskCodeMapping = map[string][]string{
"11001": {"sjbq_ychy", "sjbq_swhjyc"}, // 疑似涉黑涉诈 → 异常行业、上网环境异常
"11002": {"sjbq_ycxw", "sjbq_qtyc", "sjbq_swhjyc"}, // 疑似网络投机 → 严重异常行为、其他异常行为、上网环境异常
"11003": {"sjbq_ymd"}, // 疑似营销欺诈 → 羊毛党
"11004": {"sjbq_zlbz", "sjbq_swhjyc"}, // 疑似黑中介包装 → 资料包装中介、上网环境异常
"11005": {"sjbq_zfyc"}, // 疑似恶意套现 → 支付异常行为
"12001": {"sjbq_sfcy", "sjbq_swhjyc"}, // 疑似黑产设备 → 身份信息存疑、上网环境异常
"12002": {"sjbq_xjzl", "sjbq_swhjyc"}, // 疑似黑产账号 → 虚假资料、上网环境异常
"21001": {"sjbq_ycxw"}, // 疑似恶意借贷 → 严重异常行为
"21002": {"sjbq_sxxw"}, // 疑似职业撸口子 → 失信行为
}
// IVYZ8I9JTransformResponseFromBytes 将包含 riskCode 的格式转换为多个布尔字段格式
func IVYZ8I9JTransformResponseFromBytes(respBytes []byte) ([]byte, error) {
// 获取 result 字段中的 riskCode
riskCodeResult := gjson.GetBytes(respBytes, "result.riskCode")
if !riskCodeResult.Exists() {
// 如果没有 riskCode 字段,返回默认值
return createDefaultResponse()
}
riskCode := riskCodeResult.String()
result, err := parseRiskCodeToFields(riskCode)
if err != nil {
return createDefaultResponse()
}
return json.Marshal(result)
}
// parseRiskCodeToFields 解析 riskCode 并转换为多个字段
// riskCode 格式: "11001-3|21001-2"
// 其中 "11001" 是风险码,"3" 是风险等级1低风险、2中风险、3高风险
// 只要风险码存在,就认为命中并映射到对应字段
func parseRiskCodeToFields(riskCode string) (map[string]string, error) {
// 初始化所有字段为 "0"
result := map[string]string{
"sjbq_zlbz": "0", // 资料包装中介 - 对应 11004 疑似黑中介包装
"sjbq_ychy": "0", // 异常行业 - 对应 11001 疑似涉黑涉诈
"sjbq_xjzl": "0", // 虚假资料 - 对应 12002 疑似黑产账号
"sjbq_ymd": "0", // 羊毛党 - 对应 11003 疑似营销欺诈
"sjbq_sfcy": "0", // 身份信息存疑 - 对应 12001 疑似黑产设备
"sjbq_ycxw": "0", // 严重异常行为 - 对应 21001 疑似恶意借贷
"sjbq_sxxw": "0", // 失信行为 - 对应 21002 疑似职业撸口子
"sjbq_zfyc": "0", // 支付异常行为 - 对应 11005 疑似恶意套现
"sjbq_qtyc": "0", // 其他异常行为 - 对应 11002 疑似网络涉赌
"sjbq_swhjyc": "0", // 上网环境异常 - 对应 11001/11004/11002 涉黑涉诈/黑中介包装/网络涉赌
}
if riskCode == "" || riskCode == "-1" {
return result, nil
}
// 分割多个风险码(用 | 分隔)
riskCodes := strings.Split(riskCode, "|")
for _, rc := range riskCodes {
rc = strings.TrimSpace(rc)
if rc == "" {
continue
}
// 解析风险码和风险等级(用 - 分隔)
parts := strings.Split(rc, "-")
if len(parts) < 1 {
continue
}
code := parts[0]
// 只要风险码存在,就认为命中(不管风险等级是 1、2 还是 3
// 一个风险码可能映射到多个字段
if fieldNames, ok := RiskCodeMapping[code]; ok {
for _, fieldName := range fieldNames {
result[fieldName] = "1"
}
}
}
return result, nil
}
// createDefaultResponse 创建默认响应(所有字段为 "0"
func createDefaultResponse() ([]byte, error) {
result := map[string]string{
"sjbq_zlbz": "0",
"sjbq_ychy": "0",
"sjbq_xjzl": "0",
"sjbq_ymd": "0",
"sjbq_sfcy": "0",
"sjbq_ycxw": "0",
"sjbq_sxxw": "0",
"sjbq_zfyc": "0",
"sjbq_qtyc": "0",
"sjbq_swhjyc": "0",
}
return json.Marshal(result)
}