146 lines
5.1 KiB
JavaScript
146 lines
5.1 KiB
JavaScript
/**
|
||
* 标品PP 真实接口调试脚本(从 DB 读取完整数据)
|
||
* 使用租户 id=5 (名称:895) + 接口 id=4 (标品部门数据)
|
||
*/
|
||
|
||
const crypto = require('crypto');
|
||
const axios = require('axios');
|
||
const db = require('../db');
|
||
|
||
// ─── 工具函数 ─────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* 将私钥(多种格式)解析为 Node.js crypto 可用的 KeyObject
|
||
* 支持:
|
||
* - 裸 base64 PKCS8 DER(Java 导出格式)
|
||
* - PEM 格式
|
||
*/
|
||
function loadPrivateKey(raw) {
|
||
if (raw.includes('-----BEGIN')) {
|
||
return crypto.createPrivateKey(raw);
|
||
}
|
||
const derBuf = Buffer.from(raw.trim(), 'base64');
|
||
return crypto.createPrivateKey({ key: derBuf, format: 'der', type: 'pkcs8' });
|
||
}
|
||
|
||
function generateNonce(length = 8) {
|
||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||
return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||
}
|
||
|
||
function generateSign(params, privateKey) {
|
||
const sortedKeys = Object.keys(params).sort();
|
||
const signString = sortedKeys.map(k => `${k}=${params[k]}`).join('&');
|
||
console.log('[签名] 待签名字符串:', signString);
|
||
|
||
const signer = crypto.createSign('RSA-MD5');
|
||
signer.update(signString, 'utf8');
|
||
const signature = signer.sign(privateKey, 'base64');
|
||
console.log('[签名] Base64 (前30):', signature.substring(0, 30) + '...');
|
||
return signature;
|
||
}
|
||
|
||
// ─── 主流程 ────────────────────────────────────────────────────────────────
|
||
|
||
async function main() {
|
||
console.log('='.repeat(60));
|
||
console.log('标品PP 真实接口调试:标品部门数据');
|
||
console.log('='.repeat(60));
|
||
|
||
// 从 DB 读取完整数据
|
||
const [[tenant]] = await db.query('SELECT * FROM tenants WHERE id = ?', [5]);
|
||
const [[endpoint]] = await db.query('SELECT * FROM endpoints WHERE id = ?', [4]);
|
||
|
||
if (!tenant || !endpoint) {
|
||
console.error('❌ 租户或接口不存在');
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log('\n[租户] 名称:', tenant.name, '| 类型:', tenant.type);
|
||
console.log('[接口] 名称:', endpoint.name, '| URL:', endpoint.url);
|
||
console.log('[私钥] 长度:', tenant.private_key?.length, '字符');
|
||
|
||
// 1. 加载私钥
|
||
let privateKey;
|
||
try {
|
||
privateKey = loadPrivateKey(tenant.private_key);
|
||
console.log('✅ 私钥加载成功(类型:', privateKey.asymmetricKeyType, ')');
|
||
} catch (e) {
|
||
console.error('❌ 私钥加载失败:', e.message);
|
||
// 尝试输出私钥前后片段以诊断
|
||
console.log(' 私钥开头:', tenant.private_key.substring(0, 50));
|
||
console.log(' 私钥结尾:', tenant.private_key.slice(-50));
|
||
process.exit(1);
|
||
}
|
||
|
||
// 2. 构建 query 参数(不含 sign)
|
||
const params = {
|
||
entCode: tenant.ent_code,
|
||
apiCode: endpoint.api_code,
|
||
timestamp: Date.now().toString(),
|
||
nonce: generateNonce(8),
|
||
};
|
||
|
||
console.log('\n[参数] 基础参数:', JSON.stringify(params));
|
||
|
||
// 3. 生成签名
|
||
let signature;
|
||
try {
|
||
signature = generateSign(params, privateKey);
|
||
params.sign = signature;
|
||
console.log('✅ 签名生成成功');
|
||
} catch (e) {
|
||
console.error('❌ 签名失败:', e.message);
|
||
process.exit(1);
|
||
}
|
||
|
||
// 4. 构建请求
|
||
const encodedKey = Buffer.from(`${tenant.api_key}:`).toString('base64');
|
||
const headers = {
|
||
'Authorization': `Basic ${encodedKey}`,
|
||
'Content-Type': 'application/json',
|
||
};
|
||
|
||
const body = JSON.parse(endpoint.body || '{}');
|
||
|
||
// 5. CURL 命令
|
||
const urlObj = new URL(endpoint.url);
|
||
Object.entries(params).forEach(([k, v]) => urlObj.searchParams.set(k, v));
|
||
let curlCmd = `curl -X ${endpoint.method} '${urlObj.toString()}'`;
|
||
Object.entries(headers).forEach(([k, v]) => {
|
||
curlCmd += ` \\\n -H '${k}: ${v}'`;
|
||
});
|
||
curlCmd += ` \\\n -d '${JSON.stringify(body)}'`;
|
||
console.log('\n[CURL]\n' + curlCmd);
|
||
|
||
// 6. 发送请求
|
||
console.log('\n[请求] 发送中...');
|
||
try {
|
||
const response = await axios({
|
||
method: endpoint.method.toLowerCase(),
|
||
url: endpoint.url,
|
||
headers,
|
||
params,
|
||
data: body,
|
||
timeout: 15000,
|
||
});
|
||
|
||
console.log('\n✅ 请求成功!');
|
||
console.log('[响应] 状态:', response.status);
|
||
console.log('[响应] 数据:');
|
||
console.log(JSON.stringify(response.data, null, 2).substring(0, 1000));
|
||
} catch (err) {
|
||
console.error('\n❌ 请求失败!');
|
||
if (err.response) {
|
||
console.error('[错误] HTTP 状态:', err.response.status);
|
||
console.error('[错误] 响应:', JSON.stringify(err.response.data, null, 2));
|
||
} else {
|
||
console.error('[错误]', err.message);
|
||
}
|
||
}
|
||
|
||
process.exit(0);
|
||
}
|
||
|
||
main().catch(e => { console.error(e); process.exit(1); });
|