ai-query对接新版freesun-agent接口的分支
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

221 lines
7.1 KiB

'use strict';
const crypto = require('crypto');
const superagent = require('superagent');
const aesEncryptToBase64 = (plainText, key, iv) => {
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
let encrypted = cipher.update(String(plainText), 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
};
const aesDecryptFromBase64 = (cipherTextBase64, key, iv) => {
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
let decrypted = decipher.update(String(cipherTextBase64), 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
};
const parseJsonSafe = (value, fallback = null) => {
try {
if (typeof value === 'string') return JSON.parse(value);
if (value && typeof value === 'object') return value;
return fallback;
} catch (_) {
return fallback;
}
};
const buildConfig = (app) => {
const conf = app?.fs?.config?.aideduct || {};
const key = String(conf.aesKey || '').trim();
const iv = String(conf.aesIv || '').trim();
if (Buffer.byteLength(key) !== 16) {
throw new Error('aideduct.aesKey 长度必须为 16 字节');
}
if (Buffer.byteLength(iv) !== 16) {
throw new Error('aideduct.aesIv 长度必须为 16 字节');
}
return {
baseUrl: String(conf.baseUrl || '').replace(/\/+$/, ''),
key,
iv,
timeout: Number(conf.timeout || 10000),
};
};
const createClient = (app) => {
const conf = buildConfig(app);
const postEncrypted = async (path, plainBody) => {
if (!conf.baseUrl) {
throw new Error('aideduct.baseUrl 未配置');
}
const plainText = JSON.stringify(plainBody || {});
const encryptedData = aesEncryptToBase64(plainText, conf.key, conf.iv);
const url = `${conf.baseUrl}/${String(path || '').replace(/^\/+/, '')}`;
const res = await superagent
.post(url)
.type('form')
.send({ data: encryptedData })
.timeout(conf.timeout);
const body = parseJsonSafe(res?.body, parseJsonSafe(res?.text, {})) || {};
if (Number(body.code) !== 200) {
throw new Error(body.msg || '飞尚接口调用失败');
}
if (typeof body.data !== 'string') {
return body.data || {};
}
const decrypted = aesDecryptFromBase64(body.data, conf.key, conf.iv);
const result = parseJsonSafe(decrypted, null);
if (!result || typeof result !== 'object') {
throw new Error('飞尚接口解密后数据格式错误');
}
return result;
};
const requireUserId = (userId) => {
const normalizedUserId = String(userId || '').trim();
if (!normalizedUserId) throw new Error('缺少 user_id');
return normalizedUserId;
};
const requireAmount = (amount) => {
const normalizedAmount = Number(amount);
if (!Number.isFinite(normalizedAmount) || normalizedAmount <= 0) {
throw new Error('amount 必须为大于 0 的数字');
}
return normalizedAmount;
};
const requireRequestId = (requestId) => {
const normalizedRequestId = String(requestId || '').trim();
if (!normalizedRequestId) throw new Error('缺少 request_id');
return normalizedRequestId;
};
const getBalance = async ({ userId }) => {
const normalizedUserId = requireUserId(userId);
const plainRes = await postEncrypted('/getBalance', {
user_id: normalizedUserId,
});
if (Number(plainRes.code) !== 200) {
throw new Error(plainRes.msg || '余额查询失败');
}
const data = plainRes.data || {};
return {
code: Number(plainRes.code),
msg: String(plainRes.msg || ''),
data: {
balance: Number(data.balance || 0),
frozen_balance: Number(data.frozen_balance || 0),
total_balance: Number(data.total_balance || 0),
},
};
};
const getUserInfo = async ({ userId }) => {
const normalizedUserId = requireUserId(userId);
const plainRes = await postEncrypted('/getuserinfo', {
user_id: normalizedUserId,
});
if (Number(plainRes.code) !== 200) {
throw new Error(plainRes.msg || '用户信息查询失败');
}
const data = plainRes.data || {};
return {
code: Number(plainRes.code),
msg: String(plainRes.msg || ''),
data: {
user: String(data.user || '').trim(),
email: String(data.email || '').trim(),
fullname: String(data.fullname || '').trim(),
create_time: String(data.create_time || '').trim(),
},
};
};
const preDeduct = async ({ userId, amount, requestId }) => {
const plainRes = await postEncrypted('/preDeduct', {
user_id: requireUserId(userId),
amount: requireAmount(amount),
request_id: requireRequestId(requestId),
});
if (Number(plainRes.code) !== 200) {
throw new Error(plainRes.msg || '预扣费失败');
}
return {
code: Number(plainRes.code),
msg: String(plainRes.msg || ''),
data: plainRes.data || {},
};
};
const confirmDeduct = async ({ userId, amount, requestId }) => {
const plainRes = await postEncrypted('/confirmDeduct', {
user_id: requireUserId(userId),
amount: requireAmount(amount),
request_id: requireRequestId(requestId),
});
if (Number(plainRes.code) !== 200) {
throw new Error(plainRes.msg || '确认扣费失败');
}
return {
code: Number(plainRes.code),
msg: String(plainRes.msg || ''),
data: plainRes.data || {},
};
};
const rollbackDeduct = async ({ userId, amount, requestId }) => {
const plainRes = await postEncrypted('/rollbackDeduct', {
user_id: requireUserId(userId),
amount: requireAmount(amount),
request_id: requireRequestId(requestId),
});
if (Number(plainRes.code) !== 200) {
throw new Error(plainRes.msg || '回退扣费失败');
}
return {
code: Number(plainRes.code),
msg: String(plainRes.msg || ''),
data: plainRes.data || {},
};
};
const rechargeBalance = async ({ userId, amount, requestId }) => {
const plainRes = await postEncrypted('/rechargeBalance', {
user_id: requireUserId(userId),
amount: requireAmount(amount),
request_id: requireRequestId(requestId),
});
if (Number(plainRes.code) !== 200) {
throw new Error(plainRes.msg || '充值失败');
}
return {
code: Number(plainRes.code),
msg: String(plainRes.msg || ''),
data: plainRes.data || {},
};
};
return {
getBalance,
getUserInfo,
preDeduct,
confirmDeduct,
rollbackDeduct,
rechargeBalance,
aesEncryptToBase64: (plainText) => aesEncryptToBase64(plainText, conf.key, conf.iv),
aesDecryptFromBase64: (cipherTextBase64) => aesDecryptFromBase64(cipherTextBase64, conf.key, conf.iv),
};
};
module.exports = async (app) => {
app.fs = app.fs || {};
app.fs.aideductService = createClient(app);
};