一、用 Java 构建“公私联动”的信贷审批中台
在商业银行与供应链金融的信贷系统中,小微企业贷(SME Loan)的风控逻辑最为复杂。它要求系统既要像查企业一样核验工商司法信息,又要像查个人一样评估企业主(法人)的偿债能力。
天远API的“全能小微企业报告”(COMBQN13),通过单一接口聚合了QYGL3F8E(人企关系)、JRZQ7F1A(全景雷达)、JRZQ8A2D(特殊名单)和FLXG7E8F(司法涉诉)四大核心产品 111。这种“一包四查”的设计极大降低了网络交互频次,但也给 Java 后端带来了挑战:如何将接口返回的异构JSON Array解析为标准的 Java 对象,以便输入到 Drools 或 EasyRules 等规则引擎中?
本文将提供一套完整的 Java 解决方案,涵盖AES 加密工具类、组合响应的 POJO 映射策略以及核心风控指标的提取逻辑,助力开发者构建高健壮性的小微风控服务。
二、API接口调用示例(Java版)
本接口采用标准的 AES-128-CBC 加密。由于请求参数涉及authorized(授权书状态),请确保在业务流程中已留存用户的电子签名或授权日志。
1. 接口配置概览
- 服务地址:
https://api.tianyuanapi.com/api/v1/COMBQN132 - 请求方式:POST
- 鉴权:Header (
Access-Id) + Body (data密文) - 数据特点:响应体包含一个
responses数组,每个元素对应一个子产品 3。
2. Java 完整接入代码
为了处理聚合响应,本示例定义了一个SmeRiskService类。我们使用 Jackson 的JsonNode来灵活处理不同子产品的data结构,避免定义过于庞大的实体类。
Java
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.Base64; import java.util.HashMap; import java.util.Map; /** * 天远小微企业全能报告 API 服务 */ public class SmeRiskService { private static final String API_URL = "https://api.tianyuanapi.com/api/v1/COMBQN13"; private static final String ACCESS_ID = "YOUR_ACCESS_ID"; private static final String ACCESS_KEY = "YOUR_ACCESS_KEY_HEX"; // 16字节 Hex public static void main(String[] args) { try { // 1. 发起聚合查询 SmeRiskSummary summary = querySmeReport("张三", "110101199001011234", "13800138000"); if (summary != null) { System.out.println("=== 小微企业主风险摘要 ==="); System.out.println("关联企业: " + summary.getCompanyName()); System.out.println("经营状态: " + summary.getRegStatus()); System.out.println("个人借贷行为分: " + summary.getLoanBehaviorScore()); System.out.println("近半年逾期金额: " + summary.getOverdueAmount6M()); System.out.println("涉诉未结案数: " + summary.getOpenLawsuitCount()); // 简单的拒单规则演示 if ("注销".equals(summary.getRegStatus()) || summary.getOpenLawsuitCount() > 0) { System.err.println("[REJECT] 命中拒单规则:企业注销或存在未结案涉诉"); } } } catch (Exception e) { e.printStackTrace(); } } /** * 查询并清洗数据,返回领域模型对象 */ public static SmeRiskSummary querySmeReport(String name, String idCard, String mobile) throws Exception { // 1. 准备参数 Map<String, String> params = new HashMap<>(); params.put("name", name); params.put("id_card", idCard); params.put("mobile_no", mobile); params.put("authorized", "1"); // 必须获得授权 // 2. 加密 String encryptedData = AesUtil.encrypt(new ObjectMapper().writeValueAsString(params), ACCESS_KEY); // 3. 发送请求 String responseJson = sendPost(encryptedData); // 4. 解析响应结构 ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(responseJson); // 假设外层未加密,直接解析 responses 数组 // 若 data 字段加密,需先解密 data 再解析,逻辑同上 JsonNode responses = rootNode.get("responses"); if (responses == null || !responses.isArray()) { System.err.println("无效的响应格式"); return null; } // 5. 核心:从聚合数据中提取关键指标 return parseRiskSummary(responses); } /** * 数据清洗器:遍历子产品数组,提取核心字段 */ private static SmeRiskSummary parseRiskSummary(JsonNode responses) { SmeRiskSummary summary = new SmeRiskSummary(); for (JsonNode item : responses) { String apiCode = item.get("api_code").asText(); boolean success = item.get("success").asBoolean(); JsonNode data = item.get("data"); if (!success || data == null || data.isNull()) continue; // 分发处理逻辑 switch (apiCode) { case "QYGL3F8E": // 人企关系 if (data.has("items") && data.get("items").isArray() && data.get("items").size() > 0) { JsonNode company = data.get("items").get(0).get("basicInfo"); summary.setCompanyName(company.path("name").asText()); summary.setRegStatus(company.path("regStatus").asText()); } break; case "JRZQ7F1A": // 全景雷达 summary.setLoanBehaviorScore(data.path("behavior_report_detail").path("B22170001").asText("0")); summary.setOverdueAmount6M(data.path("behavior_report_detail").path("B22170031").asText("0")); break; case "JRZQ8A2D": // 特殊名单 summary.setCourtBad(data.path("id").path("court_bad").asText("未命中")); break; case "FLXG7E8F": // 司法涉诉 summary.setOpenLawsuitCount(data.path("judicial_data").path("lawsuitStat") .path("count").path("count_wei_total").asInt(0)); break; } } return summary; } // --- 领域模型 DTO --- static class SmeRiskSummary { private String companyName = "未查得"; private String regStatus = "未知"; private String loanBehaviorScore = "0"; private String overdueAmount6M = "0"; private String courtBad = "未命中"; private int openLawsuitCount = 0; // Getters & Setters 省略... public void setCompanyName(String name) { this.companyName = name; } public String getCompanyName() { return companyName; } public void setRegStatus(String status) { this.regStatus = status; } public String getRegStatus() { return regStatus; } public void setLoanBehaviorScore(String score) { this.loanBehaviorScore = score; } public String getLoanBehaviorScore() { return loanBehaviorScore; } public void setOverdueAmount6M(String amt) { this.overdueAmount6M = amt; } public String getOverdueAmount6M() { return overdueAmount6M; } public void setCourtBad(String status) { this.courtBad = status; } public void setOpenLawsuitCount(int count) { this.openLawsuitCount = count; } public int getOpenLawsuitCount() { return openLawsuitCount; } } // --- HTTP & AES Utils (简化版) --- private static String sendPost(String data) throws Exception { URL url = new URL(API_URL + "?t=" + System.currentTimeMillis()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Access-Id", ACCESS_ID); conn.setDoOutput(true); try (OutputStream os = conn.getOutputStream()) { os.write(("{\"data\":\"" + data + "\"}").getBytes(StandardCharsets.UTF_8)); } try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) sb.append(line); return sb.toString(); } } static class AesUtil { public static String encrypt(String content, String key) throws Exception { byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"), new IvParameterSpec(iv)); byte[] enc = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); byte[] combined = new byte[16 + enc.length]; System.arraycopy(iv, 0, combined, 0, 16); System.arraycopy(enc, 0, combined, 16, enc.length); return Base64.getEncoder().encodeToString(combined); } } }三、核心数据结构解析
1. 聚合响应模式
接口返回的data并非直接的业务对象,而是一个由子产品响应组成的列表。Java 开发者应使用策略模式或Switch-Case结构根据api_code字段进行分发处理。
JSON
{ "responses": [ { "api_code": "QYGL3F8E", "success": true, "data": { ... } }, // 人企关系 { "api_code": "JRZQ7F1A", "success": true, "data": { ... } }, // 全景雷达 { "api_code": "JRZQ8A2D", "success": true, "data": { ... } } // 特殊名单 ] }2. 字段映射挑战
每个子产品的字段命名风格不同:
- QYGL3F8E:使用驼峰命名(如
regStatus)4。 - JRZQ7F1A:使用大写代码(如
B22170001)55。 - JRZQ8A2D:使用下划线命名(如
id_court_bad)6。
建议在 Java 实体类(如上文的SmeRiskSummary)中统一重命名为符合业务语义的字段(如loanScore,companyStatus),屏蔽底层的异构性。
四、字段详解(Java 开发重点)
以下表格梳理了在 Java 风控系统中,用于**自动准入(Auto-Approve)或自动拒单(Auto-Reject)**的关键字段。
1. 企业经营维度 (QYGL3F8E)
| 字段路径 (JSON Path) | 业务含义 | 逻辑建议 |
|---|---|---|
items[0].basicInfo.regStatus | 经营状态 | 若包含“注销”、“吊销”,Java 逻辑应直接抛出RejectException7。 |
items[0].basicInfo.estiblishTime | 成立时间 | 计算经营年限,如< 1年则归为高风险 8。 |
2. 企业主还款能力 (JRZQ7F1A)
| 字段代码 | 字段含义 | 说明 |
|---|---|---|
| B22170001 | 贷款行为分 | 1-1000。分数越低,个人信用越差 9999。 |
| B22170031 | 近6个月累计逾期金额 | 区间值(如[5000,10000))。用于计算负债压力 10。 |
| B22170026 | 近12个月M0+逾期笔数 | 衡量还款意愿。若 > 3,建议转人工 11。 |
3. 司法与黑名单 (JRZQ8A2D / FLXG7E8F)
| 字段路径 | 字段含义 | 逻辑建议 |
|---|---|---|
id.court_bad | 法院失信人 | 值0表示命中。一票否决指标 12。 |
lawsuitStat.count.money_wei_total | 涉诉未结案金额 | 企业主的潜在负债,需计入 DTI(债务收入比)计算 13。 |
五、应用价值分析
集成天远全能小微企业报告后,Java 后端系统可实现以下核心能力:
公私联动画像构建:
在一次 API 事务中,同时获取“企业的壳”和“法人的核”。例如,如果企业经营正常(QYGL3F8E),但法人近期有大量网贷逾期(JRZQ7F1A),系统可自动判定为“经营性资金挪用风险”,拒绝放款。
供应链金融自动准入:
对于经销商融资场景,Java 服务可以配置规则链:
Step 1: 校验企业是否存续(QYGL3F8E)。
Step 2: 校验法人是否为老赖(JRZQ8A2D)。
Step 3: 校验涉诉金额是否超过注册资本的 50%(FLXG7E8F)。
全部通过后,自动触发授信流程。
贷后风险预警:
利用 Java 的 Quartz 或 Spring Scheduler 定时任务,定期调用此接口。若发现存量客户的 openLawsuitCount(未结案数)突增,系统自动生成风控工单,提示客户经理进行贷后回访。
六、总结
对于 Java 开发者而言,天远全能小微企业报告是一个典型的“胖接口”。对接的关键在于编写健壮的解析器(Parser),能够兼容子产品的成功与失败状态,并将异构数据清洗为统一的领域模型。
通过本文提供的SmeRiskService示例,您可以快速打通从数据获取、清洗到规则判定的全流程,为企业构建一个高效、智能的小微风控大脑。