一次 AI API 请求的计费往往需要把用量、单价和其他倍率相乘。常规样本没有问题,不代表极端输入也安全:数值转换可能越界,非有限值可能进入计算,异常上游用量也可能被当成正常结果。
把数值限制在存储范围内,只解决“能存进去”;有没有资格结算,需要另一个明确判断。 本文用一个完整的 Node.js 教学程序演示:保留原始计算值、拒绝越界扣费,并留下可以序列化的审计记录。全部样本由人工构造,不连接模型、不操作账户、不代表线上真实账单。
先分清三个不同的上限
输入上限用于拒绝不合理参数,例如过大的生成数量或时长;数值上限来自存储字段和计算表示;业务限额则来自账户预算和授权。三者不能互相代替。
假设教学系统用有符号 32 位整数存储非负配额,最大值是 2147483647。一次计算得到 4294967294 时,不能把它强制转成较窄整数后继续走正常扣费,也不能因为能截到最大值,就认为这笔最大值扣款合法。
饱和处理是把超出表示范围的值限制到边界,用来避免溢出产生不可控结果。它属于数值保护;一旦发生,业务侧必须收到异常信号。面向请求入口,可以在预扣前拒绝;如果已经调用上游,则还需要保留已发生的成本和原预扣记录,进入专门的差额处理流程,不能直接把账目清零。
教学实现:越界返回拒绝,审计值用字符串保存
下面采用非负整数字符串作为输入,用 BigInt 完成乘法。示例故意不处理小数价格、汇率或缓存公式,避免把一个算术边界演示误当完整计费系统。真实业务可先约定最小配额单位和舍入方法,再实现对应的精确计算。
保存为 quota-audit-demo.mjs,执行 node quota-audit-demo.mjs。无需额外安装依赖,使用支持 BigInt 和 ES 模块的 Node.js 即可。
import assert from 'node:assert/strict';
const MAX_QUOTA = 2147483647n; // 教学存储边界,不是人民币金额
const MAX_INPUT = 10n ** 30n; // 教学输入上限,不是模型参数限制
function inspectCharge(quantity, unitQuota) {
const values = [quantity, unitQuota];
if (values.some(v => typeof v !== 'string' || !/^(0|[1-9]\\d{0,30})$/.test(v))) {
return { accepted: false, reason: 'invalid_integer', charge: null };
}
const [q, u] = values.map(BigInt);
if (q > MAX_INPUT || u > MAX_INPUT) {
return { accepted: false, reason: 'input_limit', charge: null };
}
const calculated = q * u;
if (calculated > MAX_QUOTA) {
return {
accepted: false, reason: 'quota_overflow', charge: null,
audit: {
operation: 'multiply', original: calculated.toString(),
boundary: MAX_QUOTA.toString(), decision: 'reject',
},
};
}
return { accepted: true, reason: null, charge: calculated.toString() };
}
const normal = inspectCharge('3', '20');
const edge = inspectCharge('1', '2147483647');
const overflow = inspectCharge('2', '2147483647');
assert.equal(normal.charge, '60');
assert.equal(edge.accepted, true);
assert.equal(edge.charge, '2147483647');
assert.equal(overflow.accepted, false);
assert.equal(overflow.charge, null);
assert.equal(overflow.audit.original, '4294967294');
for (const value of ['-1', '1.5', 'NaN', 'Infinity', '1e30', '', '9'.repeat(100)]) {
assert.equal(inspectCharge(value, '1').accepted, false);
}
assert.equal(inspectCharge('0', '20').charge, '0');
assert.equal(inspectCharge('1000000000000000000000000000001', '1').reason, 'input_limit');
assert.doesNotThrow(() => JSON.stringify(overflow));
console.log(JSON.stringify({ normal, edge, overflow }, null, 2));
console.log('PASS: 正常、精确边界、越界拒绝、非法输入、零值、JSON审计');
这里有两层输入限制:先限制字符串格式和长度,再限制转换后的数值。直接拿任意长度字符串构造大整数,会把“避免溢出”变成另一种资源消耗问题。测试中的超长输入只用于本地拒绝路径,不发送到任何服务。
遇到越界时,charge 明确为 null,表示没有产生可用于扣款的值;它不是免费调用的 0。audit.original 用十进制字符串保存,既保留精确数值,也避免直接把 BigInt 交给 JSON.stringify。审计记录只含算术操作、原始值、边界和决定,不包含请求正文、密钥或客户标识。
我参与 FishAI 的运营。需要了解本站接入方式的开发者,可以先从 https://yufish.cc/docs/ 核对应用所需协议,再在自己的测试环境建立输入边界和费用核对流程。本文程序是独立教学实现,不能据此推定本站线上字段、实际扣费路径或管理员审计权限已经过本文验收。
本地运行究竟证明了什么
2026-09-13 本地实际执行输出如下:
{
"normal": {
"accepted": true,
"reason": null,
"charge": "60"
},
"edge": {
"accepted": true,
"reason": null,
"charge": "2147483647"
},
"overflow": {
"accepted": false,
"reason": "quota_overflow",
"charge": null,
"audit": {
"operation": "multiply",
"original": "4294967294",
"boundary": "2147483647",
"decision": "reject"
}
}
}
PASS: 正常、精确边界、越界拒绝、非法输入、零值、JSON审计
正常样本得到 60;精确上限仍被接受;超过上限的乘积被拒绝,同时保留 4294967294 的完整原始值。非法格式、负数、小数、非有限值文本、超长输入和输入上限越界均走拒绝路径。零用量独立验证为合法的 0,避免和拒绝状态混在一起。
这里证明的是教学函数的返回契约和 JSON 可记录性。没有测试数据库事务、并发扣费、网络重试或真实模型计费,也没有得到任何“已经替用户节省费用”的业务结果。
接进实际账务链路,还要补四件事
预扣与最终结算分别校验。 预估输入合法,不代表上游返回用量也合法。最终结算可能走另一条代码路径,必须再次检查输入、计算结果和范围,不能仅在最前端做一次校验。
业务调用必须处理拒绝信号。 即使函数给出 accepted=false,调用者如果仍读取其他字段更新余额,保护也会失效。扣费入口应显式要求 accepted=true;异常进入独立记录和后续处理,不把 null、缺失值或解析失败默认转换成 0 后继续。
退款要追溯原始预扣。 拒绝最终数值不等于可以忘掉已经预扣的配额。退款和差额处理应关联原账务事件,并确保重复执行不会再次退款。本文没有实现事务和幂等性,不能直接复制该函数作为线上结算器。
审计写入失败也要被看见。 控制台打印成功只适合教学。生产中要明确异常记录的保存位置、访问者、保存期限和写入失败处理;不能请求完成后才发现审计出口丢了一整批事件。常规用户页面与受限审计字段也应分别设计。
一次越界事件至少需要回答:输入是什么类型的量、使用哪个公式版本、计算得到什么、为什么拒绝、预扣是否发生、后续如何处理。数值保护防止算错方向,明确的业务决定防止把异常当正常扣款,审计记录让两者可以追溯。
本文由 AI 辅助整理,完整教学程序已本地运行。样本不是生产故障证据,不涉及客户数据。
网硕互联帮助中心




评论前必须登录!
注册