1. BFF 层的数据聚合之道
在现代 Web 开发架构中,BFF (Backend for Frontend) 模式越来越流行。作为连接前端 UI 与后端微服务的"胶水层",Node.js 凭借其非阻塞 I/O 和对 JSON 的原生支持,成为了处理外部 API 聚合的首选。
对于金融类应用,我们经常需要在用户注册或申请贷款的瞬间,调用第三方征信数据进行预判。天远数据 的金融借贷信用风险探查API(JRZQ2F8A)提供了基于手机号、身份证、姓名的风险画像。
对于 Node.js 开发者而言,对接该 API 的挑战主要在于处理金融级的AES-128-CBC 加密协议。本文将演示如何在 Node.js 环境中(以 Koa2 为例)优雅地封装这一服务,实现从"加密请求"到"清洗数据"的全流程。
2. API 调用示例:原生 Crypto 模块实战
Node.js 内置的 crypto 模块非常强大,足以处理工业级的加密需求,无需引入额外的重量级库。
2.1 接口契约回顾
- API 地址 :
https://api.tianyuanapi.com/api/v1/JRZQ2F8A - 加密规范 :
- 算法:AES-128-CBC。
- 填充:PKCS7(Node.js 默认支持)。
- 传输格式:
Base64(IV + 密文)。
2.2 Node.js 完整代码实现
以下代码封装了一个通用的 RiskClient 类,使用了 async/await 语法,可以直接集成到 Koa, Express 或 NestJS 中。
JavaScript
jsx
const crypto = require('crypto');
const axios = require('axios'); // 推荐使用 axios 处理 HTTP 请求
// 配置您的凭证
const CONFIG = {
apiUrl: 'https://api.tianyuanapi.com/api/v1/JRZQ2F8A',
accessId: '您的Access-Id',
accessKey: '您的16位Access-Key' // 必须是16字节
};
class TianyuanRiskClient {
constructor(accessId, accessKey) {
this.accessId = accessId;
this.accessKey = Buffer.from(accessKey, 'utf8');
this.algorithm = 'aes-128-cbc';
}
/**
* 加密数据
* 逻辑:生成IV -> AES加密 -> 拼接IV和密文 -> Base64
*/
encrypt(data) {
// 1. 生成 16 字节随机 IV
const iv = crypto.randomBytes(16);
// 2. 创建加密器 (Node.js 默认使用 PKCS7 填充)
const cipher = crypto.createCipheriv(this.algorithm, this.accessKey, iv);
// 3. 加密数据
const jsonStr = JSON.stringify(data);
let encrypted = cipher.update(jsonStr, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
// 4. 拼接 IV + 密文
const resultBuffer = Buffer.concat([iv, encrypted]);
// 5. 返回 Base64
return resultBuffer.toString('base64');
}
/**
* 解密数据
* 逻辑:Base64解码 -> 提取IV -> AES解密
*/
decrypt(base64Str) {
try {
const buffer = Buffer.from(base64Str, 'base64');
// 1. 提取前 16 字节 IV
const iv = buffer.subarray(0, 16);
// 2. 提取剩余密文
const content = buffer.subarray(16);
// 3. 创建解密器
const decipher = crypto.createDecipheriv(this.algorithm, this.accessKey, iv);
// 4. 解密
let decrypted = decipher.update(content);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return JSON.parse(decrypted.toString('utf8'));
} catch (error) {
console.error('解密失败:', error.message);
return null;
}
}
/**
* 发起风控探查
*/
async queryRisk(name, idCard, mobile) {
const payload = {
name: name,
id_card: idCard,
mobile_no: mobile,
authorized: "1" // 必须获取授权
};
const encryptedData = this.encrypt(payload);
try {
// 添加时间戳防止缓存
const url = `${CONFIG.apiUrl}?t=${Date.now()}`;
const response = await axios.post(url, {
data: encryptedData
}, {
headers: {
'Access-Id': this.accessId,
'Content-Type': 'application/json'
}
});
const resData = response.data;
if (resData.code === 0) {
// 业务成功,解密 data
return this.decrypt(resData.data);
} else {
console.warn(`API 业务错误: ${resData.code} - ${resData.message}`);
return { error: resData.message, code: resData.code };
}
} catch (error) {
console.error('HTTP 请求异常:', error.message);
throw error;
}
}
}
// --- 调用演示 ---
(async () => {
const client = new TianyuanRiskClient(CONFIG.accessId, CONFIG.accessKey);
console.log("正在查询风控数据...");
const result = await client.queryRisk("测试用户", "110101199001011234", "13800138000");
if (result && !result.error) {
console.log("探查结果:", JSON.stringify(result, null, 2));
} else {
console.log("查询失败");
}
})();
3. 核心数据结构解析:JavaScript 的灵活性
Node.js 处理 JSON 响应具有天然优势。天远API 返回的数据结构非常扁平,我们可以直接利用 JavaScript 的解构赋值(Destructuring)提取核心业务字段。
3.1 响应数据解构
JavaScript
jsx
// 假设 result 是解密后的对象
const {
result_code, // 结果编码: 1(A), 4(U), N
max_overdue_amt, // 最大逾期金额: "(1000~2000]"
max_overdue_days, // 逾期天数: "16-30"
currently_overdue, // 当前逾期机构数
latest_overdue_time // 最近逾期时间: "2023-05"
} = result;
3.2 字段业务映射表
为了让前端 UI 更好展示,建议在 BFF 层将原始数据转换为对用户友好的文案:
| 原始字段名 | 示例值 | 业务含义 | BFF 转换建议 (UI Display) |
|---|---|---|---|
result_code |
"1" | 画像完整 | { status: "success", tag: "success" } |
result_code |
"4" | 数据不足 | { status: "unknown", tag: "warning" } |
max_overdue_days |
">180" | 严重逾期 | 🔴 极高风险 (建议直接拦截) |
currently_overdue |
"0" | 无当前逾期 | 🟢 信用良好 |
acc_sleep |
"14" | 睡眠机构数 | 🟡 沉睡用户 (潜在激活目标) |
4. 应用价值分析:Node.js 生态的落地
场景一:Serverless 风控查询函数
使用阿里云 FC (Function Compute) 或腾讯云 SCF,您可以将上述 RiskClient 代码打包为一个云函数。
- 触发器:HTTP 触发器。
- 优势 :
- 按量付费:风控查询通常不是持续流量,有用户申请时才调用,Serverless 成本极低。
- IP 白名单:云函数的出口 IP 相对固定(或可配置 NAT 网关),便于在天远控制台配置 IP 白名单 4。
场景二:GraphQL 聚合层
如果您使用 GraphQL (如 Apollo Server),可以将金融借贷信用风险探查API作为一个 Resolver。
-
Schema 定义:GraphQL
jsxtype RiskProfile { isHighRisk: Boolean overdueAmount: String lastOverdue: String } -
Resolver 逻辑 :在 Resolver 中调用 API,并根据
max_overdue_amt和currently_overdue动态计算isHighRisk字段,直接返回给前端 Bool 值,屏蔽复杂的原始数据。
场景三:异常熔断与重试
Node.js 的 axios-retry 等插件可以轻松实现请求重试。
- 策略 :当 API 返回
1001(接口异常) 或 HTTP 5xx 时,自动重试 2 次。 - 降级:如果 API 暂时不可用,BFF 层可以返回一个默认的"低风险"或"需人工审核"标记,确保主业务流程(如注册)不被卡死。
5. 总结
使用 Node.js 对接天远金融借贷信用风险探查API,不仅代码量少,而且能完美融入现代化的前端技术栈。
核心要点回顾:
- Crypto 模块 :利用
crypto.createCipheriv和Buffer.concat即可轻松搞定 AES-128-CBC + IV 拼接,无需依赖第三方加密库。 - JSON 友好:Node.js 对 API 返回的 JSON 数据处理效率极高,适合做数据清洗和格式化。
- 灵活部署:无论是作为微服务、BFF 中间件还是 Serverless 函数,Node.js 都能提供轻量级的高效解决方案。
通过这套方案,您的前端应用将能即时获取用户的信用"体检报告",实现更智能的交互体验。