超额计费

场景

账单系统上线后,运行正常。

但运营团队发现了一些问题:

Ops Chat
周二 11:40
运

用户反馈:

  1. 为什么我的账单有超额费用?
  2. 超额费用是怎么计算的?
  3. 我什么时候超的限?

技术问题:

  1. 统计数据延迟(月度统计不是实时的)
  2. 无法精确计算超额时间点
  3. 用户无法实时查看已用量

问题分析

我查了一下当前的计费逻辑:

现在的链路是「调用记录 → 月度统计表 → 账单」三段式,套餐内的免费额度靠事后聚合的结果反推。这条链路能算出月底的总数,却答不出用户最关心的三个问题:超额是从哪一次调用开始的、此刻到底用了多少、超额费用究竟怎么算出来的。要改就得先说清楚这条链路究竟在哪几个环节上不够用。

设计流程

问题分析

  1. 步骤 1:梳理「调用记录 → 月度统计表 → 账单」的现有计费链路
  2. 步骤 2:核对统计表的聚合周期,确认数据比真实调用落后多少
  3. 步骤 3:逐条比对调用记录与账单明细,定位超额起点无法追溯的原因
  4. 步骤 4:归纳三个待改进点:统计延迟、超额时点查不到、用户看不到实时用量

关注点:月度统计的滞后、超额起点不可追溯、实时用量缺失。

问题:

  1. 使用月度统计表,数据有延迟
  2. 无法精确计算超额时间点
  3. 用户无法实时监控自己的用量

改进方案

实时用量追踪

不再依赖月度统计表,而是实时追踪:

第一刀切在数据来源上:把月底的聚合换成调用时的实时累加,让「已用量」成为一份随时可查的状态。累加口径必须和套餐额度完全对齐——按什么维度计数、要不要区分 API、跨月怎么归集,都要在这一步定死,否则后面算超额和出账单会各说各话。

设计流程

实时用量追踪

  1. 步骤 1:确定用量累加维度:按 API Key、用户和计费周期分别计数
  2. 步骤 2:在请求链路上实时累加调用次数,并同步更新当前周期已用量
  3. 步骤 3:抽样比对实时计数与原始调用记录,确认无重复、无漏计
  4. 步骤 4:让实时用量成为唯一权威口径,月度统计表降级为对账用途

关注点:累加口径、套餐额度一致性、计数归属与可回溯性。

实时查询用量

实时追踪解决了数据准不准,但用户还需要一个入口看得到自己的用量。查询接口要直接给出当前周期已用次数、套餐上限、剩余额度和超额费用预估,并且能下钻到按 API、按天的分布——只有这样用户才能自己判断该不该继续调用,而不是等账单出来才发现超了。

设计流程

实时查询用量

  1. 步骤 1:确定查询接口返回字段:已用量、套餐上限、剩余额度与超额预估
  2. 步骤 2:按用户和计费周期读取实时用量,并补充按 API、按天的分布
  3. 步骤 3:校验返回值与限流计数一致,剩余额度不得出现负数或漂移
  4. 步骤 4:让用户在超额发生前就能自行查看并调整调用节奏

关注点:查询口径、剩余额度准确性、用户可自助下钻。

超额预警

用量达到 80% 时预警

实时用量最有价值的用法是提前预警,而不是事后追责。设一条 80% 的阈值线,用量跨过它就通知用户:剩余额度还能支撑多久、按当前速度预计何时用完、要不要升级套餐。预警只触发一次,并且必须带上触发那一刻的用量快照,否则用户点进来看到的是另一个数字,信任就没了。

设计流程

用量达到 80% 时预警

  1. 步骤 1:为每个计费周期配置 80% 预警阈值,并登记触发时的用量快照
  2. 步骤 2:用量累加后判断是否跨越阈值,只在首次跨越时发出通知
  3. 步骤 3:在预警内容中附上已用量、剩余额度与预计耗尽时间
  4. 步骤 4:保证同一周期不重复提醒,并记录通知状态供后续追溯

关注点:阈值触发时机、重复通知抑制、快照可核对。

超额限制

硬限额 vs 软限额

超额之后是继续服务还是直接拒绝,是产品必须明确的取舍。软限额只提醒不拦截,靠预警把成本压力转给用户;硬限额直接拒绝超额请求,能把成本和外部依赖牢牢锁住,但要先确认用户已经收到过预警、并且有升级或提额的入口。两种策略可以并存:默认软限额,由用户自己选择是否开启硬限额。

设计流程

硬限额 vs 软限额

  1. 步骤 1:为每个套餐标明采用软限额还是硬限额,并写明超额后的服务行为
  2. 步骤 2:软限额只记录超额用量并持续预警,不中断用户请求
  3. 步骤 3:硬限额在用量越界后拒绝新增请求,并返回可升级的错误信息
  4. 步骤 4:为用户提供切换限额方式和临时提额的入口,避免被硬拦截卡死

关注点:拦截与提醒的边界、用户体验与成本控制的平衡。

精确账单生成

相关数据模型

invoicesFields
字段名类型约束 / 说明
idINTPRIMARY KEY, AUTO_INCREMENT
user_idINTFK -> users.id
subscription_idINTFK -> user_subscriptions.id
billing_period_startDATE账期开始
billing_period_endDATE账期结束
subtotalDECIMAL(10,2)小计
taxDECIMAL(10,2)税费
totalDECIMAL(10,2)总计
total_callsINT周期调用总量
plan_limitINT套餐周期限额
overage_callsINT超额次数
statusVARCHAR(20)draft/sent/paid/overdue/cancelled
sent_atTIMESTAMP发送时间
paid_atTIMESTAMP支付时间
invoicesRows
iduser_idbilling_period_startbilling_period_endtotalstatus
100011012026-04-012026-04-30132.40paid
100022042026-04-012026-04-30529.00sent
100033052026-04-012026-04-3099.00overdue

使用实时数据生成账单

计费的终点是账单,而账单必须能被逐笔核对。改用实时用量生成账单后,每一笔超额费用都要能回溯到具体的调用记录和发生时点,基础费用、超额费用、已付状态分开列出。金额算错不可怕,可怕的是错得查不出来,所以账单和明细都要留下可对账的依据。

设计流程

使用实时数据生成账单

  1. 步骤 1:按用户和计费周期取实时用量快照,固定本次出账的数据基准
  2. 步骤 2:分别计算套餐月费与超额部分费用,写入账单主记录
  3. 步骤 3:为每笔超额费用生成明细,关联到具体调用记录和发生时点
  4. 步骤 4:校验账单金额等于明细汇总,并标记出账状态供后续发送

关注点:账单与明细的一致性、金额可逐笔核对、状态可追溯。

用量仪表盘

本月用量

正常
已用 7,500 次限额 10,000 次
75.0%
当前套餐基础版
日限额333 次/天
剩余可用2,500 次
超额调用0 次
预估超额费用 ¥0.00
计费周期2024-03

上线前多想一步

  • 会不会重复扣费? 同一次调用、同一张账单,重试多次也只能算一次。
  • 账单算错怎么办? 要能追到每一笔超额费用来自哪次调用,而不是只给一个总数。
  • 用户申诉怎么查? 客服至少能看到用量时间线、套餐变化和扣费明细。

当前架构

技术架构
客户端

用户请求

GET https://dashboard.kuaiyizhi.cn/api/v3/weather?city=北京
负载均衡层

Nginx 负载均衡

应用服务

Nginx

Python

Public API Gateway

Admin API

Static

Admin Dashboard


MySQL

Redis 缓存 + 限流

外部服务

MySQL 计费库

消息队列

外部天气 API

发现内容有问题?

告诉我们哪里需要改进。