升鲜宝 供应链管理系统 SaaS 自动计费引擎详细算法说明书(Algorithm Spec)
升鲜宝 生鲜配送供应链管理系统SaaS 自动计费引擎详细算法说明书(Algorithm Spec)
适用架构:阿里云 RDS + ECS + Nginx;SaaS 平台库 + 每租户独立业务库(可分片)
目标:订阅计费 + 用量计费 + 账单生成 + 支付对账 + 到期冻结 + 授权快照,一套闭环
关键原则:可重算、可审计、幂等、安全、可扩展
1. 术语与对象
-
Tenant(租户):购买升鲜宝 SaaS 服务的企业实体
-
Plan(套餐):订阅基础(包含模块、基础价、周期)
-
Component(计费组件):收费项(订阅/用量/一次性)
-
Meter(用量指标):用量计费的统计口径(门店数、订单数、API 次数等)
-
Invoice(账单):一个计费周期内的应收单
-
Entitlement Snapshot(授权快照):业务系统只读的最终模块+配额+到期信息
-
Proration(按比例补差):周期中途升级产生的补差价
2. 计费引擎职责边界
2.1 输入
-
租户订阅:
tenant_subscription -
套餐/价格:
saas_plan/saas_price_component/saas_price_tier -
用量快照:
tenant_usage_daily(由采集器写入) -
变更记录:
subscription_change -
支付结果:
tenant_payment回调
2.2 输出
-
账单:
tenant_invoice/tenant_invoice_item -
审计:
invoice_calculation_log -
授权快照:
tenant_entitlement_snapshot -
状态变更:
tenant.status、tenant_account_state_log
3. 数据一致性与幂等规范(必须)
3.1 幂等键规范(建议)
-
账单幂等键:
invoice_biz_key = tenantId + ':' + periodStart + ':' + periodEnd + ':' + subscriptionId -
账单号:
INV-YYYYMM-<tenantId>-<seq> -
支付单幂等键:
pay_biz_key = invoiceId + ':' + channel
3.2 账单生成幂等策略
-
插入 invoice 前先查:同
invoice_biz_key是否存在 -
存在则:
-
若状态=已支付 → 返回已生成
-
若状态=待支付/逾期 → 可“重算并覆盖 item”(需带版本号与审计记录)
-
3.3 审计要求
每次计算必须写:invoice_calculation_log
-
输入快照:订阅信息、组件信息、用量数据、折扣、税率、proration
-
输出快照:明细、汇总金额、四舍五入规则
4. 计费类型与价格模型
4.1 计费类型(charge_type)
-
1 = 订阅(Subscription)
-
2 = 用量(Usage)
-
3 = 一次性(One-time)
4.2 价格模型(pricing_model)
-
1 = 固定价(Fixed)
-
2 = 阶梯价(Tiered)
-
3 = 按量单价(PerUnit)
5. 用量计费:数据采集与口径
5.1 推荐采集方式:日聚合快照
业务系统每日写入平台库(或平台拉取):
-
tenant_usage_daily(tenant_id, meter_code, stat_date, used_value)
计费只读 usage 表,不要扫描业务大表(订单、流水会非常大)。
5.2 用量统计口径建议
-
SHOP_COUNT:门店数量(当天快照)
-
USER_COUNT:用户数量(当天快照)
-
ORDER_COUNT:订单量(当日新增量)
-
API_CALL:网关计数(当日新增量)
-
STORAGE_GB:存储量(当日快照/最大值)
6. 计费周期与结算规则
6.1 周期定义
-
月付:
periodStart ~ periodEnd(自然月或按订阅起始日滚动,推荐自然月) -
年付:自然年或按起始日滚动(推荐按起始日滚动,避免跨年复杂)
6.2 结算窗口
-
账单生成时间:到期前
T-7天(可配置) -
宽限期:到期后
grace_days(例如 7 天)
6.3 取数策略(用量)
-
累计型(如 ORDER_COUNT):对
stat_date在周期内求和 -
快照型(如 SHOP_COUNT):取周期内最大值(或最后一天值),推荐 最大值 防止规避
7. 核心算法:账单生成(Invoice Build)
7.1 总流程(伪代码)
8. 组件计算算法(Component Calc)
8.1 订阅组件(charge_type=1)
固定价(pricing_model=1)
-
quantity = 1
-
unit_price = plan.base_price 或 component 固定价
-
amount = unit_price
阶梯(订阅一般不用阶梯)
-
不建议:订阅类保持固定价,阶梯放到用量
8.2 用量组件(charge_type=2)
获取用量(usageValue)
meter_policy 建议:
-
快照型:max(daily.used_value)
-
累计型:sum(daily.used_value)
价格模型 1:固定价(Fixed)
-
amount = fixed_price
-
不依赖 usageValue(用于“包含多少量的包月”也可以配合阶梯做)
价格模型 3:按量单价(PerUnit)
-
quantity = usageValue
-
amount = roundMoney(quantity * unit_price)
价格模型 2:阶梯价(Tiered)
阶梯表:tier_start, tier_end, unit_price, fixed_price
两种阶梯模式(强烈建议选一种并固化):
-
分段累进(推荐,类似电费)
-
每段按照该段单价计算
-
整段匹配(不推荐)
-
落在哪一段,全部按该段单价计算(容易引争议)
分段累进伪代码:
9. Proration(升级补差)算法
9.1 触发条件
订阅周期内发生:
-
升级(new_plan_price > old_plan_price)
-
或增购模块(等价升级)
9.2 计算原则(推荐)
按剩余天数补差(按日计)
9.3 输出为账单明细
生成 tenant_invoice_item:
-
component_code =
PRORATION -
amount = proration
-
meta_json 记录 old/new plan 与天数
降级一般下期生效,不退费(除非你要做更复杂的退款策略)。
10. 折扣与优惠算法(可选但建议预留)
折扣来源:
-
优惠券(一次性抵扣)
-
年付折扣(比例)
-
渠道代理折扣(比例/固定)
计算顺序建议:
-
subtotal(所有 item)
-
discount(可按 item 分类:订阅折扣、用量折扣)
-
tax(对折后金额计税)
-
total
折扣必须写入
invoice_calculation_log,并可复算。
11. 税费算法(可选)
如果你在美国也可能涉及税(州税),建议税费做成策略:
-
税率来源:tenant.billing_address / tax_region
-
tax_amount = roundMoney((subtotal - discount) * taxRate)
12. 金额精度与舍入规范(升鲜宝统一规则)
建议全平台统一:
-
金额:
DECIMAL(18,2),四舍五入 -
单价/成本/用量单价:
DECIMAL(18,6) -
运算中间值用 BigDecimal,最后落库时 setScale(2, HALF_UP)
13. 自动计费调度(Jobs)详细规则
13.1 DailyInvoiceGenerateJob(每日生成账单)
运行频率:每天 02:00(可配置)
筛选条件:
-
tenant_subscription.status=有效 -
end_time - now <= invoice_generate_days(例如 7 天) -
当前周期账单不存在(幂等键)
输出:
-
invoice + items
-
通知(短信/邮件/站内信)
13.2 AutoRenewJob(自动续费)
筛选:
-
auto_renew=1
-
invoice.status=待支付
-
到期前 X 天或到期当天
动作:
-
创建支付单(tenant_payment)
-
拉起扣款(Stripe/支付宝/微信或线下)
13.3 ExpireEnforcementJob(到期冻结)
频率:每小时
规则:
-
now > subscription.end_time
-
且 invoice 未支付
-
且 now > grace_end_time
动作:
-
tenant.status = 欠费停用
-
tenant_entitlement_snapshot.status=冻结
-
业务系统 AOP 拦截(返回“租户已过期/欠费”)
13.4 UsageAggregateJob(用量汇总)
频率:每日 01:00
-
将业务系统上报的用量落
tenant_usage_daily -
或平台从业务库采集(不推荐扫大表)
14. 支付回调与对账算法
14.1 支付回调幂等
-
按
pay_no或第三方交易号做唯一 -
重复回调只更新一次
14.2 回调成功后的状态机
14.3 账单部分支付(可选)
如支持对公转账分笔:
-
invoice.status = 部分支付
-
paid_amount 记录累计
-
paid_amount >= total → 已支付
15. 授权快照(Entitlement Snapshot)刷新算法
15.1 输出内容
-
module_json:模块开关
-
quota_json:配额上限
-
expire_time:订阅到期时间
-
status:有效/冻结/过期
15.2 计算优先级
-
当前订阅 plan 的 module 列表
-
tenant_module_override(运营后台强制开/关)
-
配额:plan 默认 quota + tenant_quota_override(可选)
-
到期:取 subscription.end_time(已支付后的)
15.3 刷新时机
-
支付成功
-
订阅升级/降级生效
-
运营调整模块或配额
-
到期冻结/解冻
16. 异常与重试策略(必须)
16.1 可重试的任务类型
-
账单生成失败:DB 临时不可用、锁等待超时、网络抖动
-
用量汇总失败:上报延迟、聚合任务异常
-
授权快照刷新失败:平台库写入失败
-
支付回调处理失败:验签失败/数据库异常/幂等冲突
-
自动续费扣款失败:支付渠道超时/失败
16.2 重试机制(推荐 Outbox + Job)
建议计费引擎所有异步动作统一走 billing_outbox_event(可选新增表):
-
事件写入平台库(与业务状态同事务)
-
定时 Job 扫描未成功事件进行重试
-
指数退避:1m / 5m / 15m / 1h / 6h
-
最大重试次数达到阈值 → 标记 DEAD 并告警
事件示例
-
INVOICE_CREATED
-
PAYMENT_CREATED
-
ENTITLEMENT_REFRESH
-
SUBSCRIPTION_EXTEND
-
TENANT_FREEZE
17. 并发控制与锁策略(关键)
17.1 账单生成并发(同租户同周期只能生成一次)
推荐两种方式:
方式 A:数据库唯一约束(强烈推荐)
-
tenant_invoice增加唯一键:uk_invoice_biz_key(invoice_biz_key) -
多线程并发插入时,只有一个成功,其它捕获重复键即可返回已存在账单
方式 B:分布式锁(可选)
-
Redis 锁 key:
LOCK:INVOICE:{tenantId}:{periodStart} -
超时 30s,防止死锁
-
仍建议保留数据库唯一约束做最终兜底
17.2 支付回调并发(同 pay_no 只处理一次)
-
tenant_payment.pay_no唯一键 -
payment_callback_log.pay_no唯一键(可选) -
回调处理逻辑:先落日志再处理,保证可追溯
18. 可重算与对账机制(审计级要求)
18.1 为什么必须可重算
-
用量口径调整
-
价格策略调整(tier 变更)
-
税率变更
-
折扣策略变更
-
Bug 修复后要能“回放”历史账单计算
18.2 重算策略(两种)
策略 1:只对未支付账单重算
-
invoice.status in (待支付, 逾期)才允许重算 -
重算会:删除旧 items → 写新 items → 写 calculation_log 新版本
策略 2:支付后不改账单,只做“差异单”
-
已支付账单不允许改动
-
差异通过下一期账单增加
ADJUSTMENT明细体现(更符合财务审计)
18.3 建议增加字段(便于审计)
在 tenant_invoice 增加:
-
invoice_biz_key(幂等键) -
calc_version(计算版本号,如 2026.02) -
recalc_count(重算次数) -
locked(支付后锁定)
19. 用量口径:最大值/累计值/去重(核心争议点)
19.1 快照型用量(门店数/用户数/SKU 数)
建议取 周期最大值:
-
避免用户在结算前临时删减规避计费
-
更贴近资源占用事实
19.2 累计型用量(订单数/API 次数)
建议取 周期内累计和:
19.3 去重口径(如活跃用户数)
如果要统计“独立活跃用户”,不要用简单 sum,应改为:
-
日去重集合 → 月去重(成本较高)
-
或用数据仓库/日志系统(后续扩展)
20. 账单项目分类建议(升鲜宝标准)
20.1 component_code 命名规范
-
订阅类:
BASE_SUBSCRIPTION -
用量类:
SHOP_COUNT/USER_COUNT/ORDER_COUNT/API_CALL/STORAGE_GB -
调整类:
PRORATION/ADJUSTMENT -
税费类:
TAX -
折扣类:
DISCOUNT
20.2 invoice_item.meta_json 建议内容
用于可追溯与解释账单:
21. 欠费与宽限期算法(Enforcement Spec)
21.1 状态机建议
-
tenant.status:
正常/试用/冻结/欠费停用 -
subscription.status:
有效/过期/取消/冻结
21.2 冻结触发规则(推荐)
21.3 宽限期策略(建议给配置)
-
宽限期只读:允许查询、不允许新增单据/审核
-
宽限期限制写:只允许支付/续费相关操作
-
无宽限期:到期立刻停用(不推荐)
22. 自动续费算法(Auto Renew)
22.1 触发条件
-
subscription.auto_renew=1 -
当前周期账单存在且
status=待支付 -
当前时间进入扣款窗口(到期前 1 天/到期当天)
22.2 扣款重试策略
-
第一次失败:5 分钟后重试
-
连续失败:改为每日一次
-
达到重试阈值:通知租户管理员 + 运营
22.3 成功后续期算法
月付:
-
newEnd = addMonths(end_time, 1)(按订阅规则:自然月或滚动月)
年付: -
newEnd = addYears(end_time, 1)
注意:一定以“原 end_time”做加法,避免多次回调导致延长过多。
23. 价格变更与历史账单的关系(Pricing Governance)
23.1 价格版本化(强烈建议)
当套餐/组件/阶梯调整时,不要覆盖旧记录,建议:
-
新增
price_version或直接用created_at做版本 -
订阅绑定“价格版本”(或在 calculation_log 里冻结一份价格快照)
23.2 账单计算时冻结输入
每次生成账单时,把以下快照写入 invoice_calculation_log.input_snapshot_json:
-
plan/base_price
-
component 定义
-
tiers 列表
-
税率/折扣策略
这样未来重算时能选择: -
用旧快照重算(对账一致)
-
用新策略重算(纠错)
24. 性能与索引要求(平台库)
24.1 高查询表索引建议
-
tenant_subscription(end_time):到期扫描用 -
tenant_invoice(tenant_id, status, due_time):账单列表/逾期扫描 -
tenant_usage_daily(tenant_id, meter_code, stat_date):周期汇总 -
tenant_entitlement_snapshot(tenant_id):业务系统高频读
24.2 扫描任务的分页策略
-
所有 Job 扫描必须分页(按主键/时间范围)
-
避免一次扫描全表导致平台库抖动
25. 典型示例:一张账单如何计算(可对外解释)
25.1 场景
-
月付套餐:基础订阅价 199 元
-
用量:订单数 ORDER_COUNT 阶梯累进
-
0~1000:0.02 元/单
-
1001+:0.015 元/单
-
-
本周期订单数:5231 单
-
税率:0(示例)
25.2 计算
-
BASE_SUBSCRIPTION:199
-
ORDER_COUNT:
-
1000 * 0.02 = 20
-
4231 * 0.015 = 63.465 → 四舍五入 63.47
-
合计 83.47
-
-
subtotal = 199 + 83.47 = 282.47
-
total = 282.47
26. 与业务系统的集成点(升鲜宝落地)
26.1 业务系统需要做什么
-
业务库:无需关心计费表
-
每次请求:
-
读取
tenant_entitlement_snapshot(缓存) -
做模块授权 AOP + 配额校验(门店上限/用户上限等)
-
-
用量采集:
-
每日上报平台:
tenant_usage_daily
-
26.2 平台系统需要做什么
-
套餐/价格配置后台
-
订阅管理后台(续费、升级、冻结)
-
账单与支付后台
-
用量报表与对账
27. 最小可上线版本(MVP 但可商业化)
必做:
-
plan / component / tier
-
subscription
-
invoice + items
-
payment 回调幂等
-
entitlement snapshot
-
到期冻结 job
可延期:
-
用量计费(先只做订阅固定价)
-
proration(先升级下期生效)
-
税费/折扣/渠道分账
28. 你接下来落地时的“工程清单”(建议顺序)
-
平台库建表(Tenant/Catalog/Subscription/Billing/Payment/Entitlement)
-
定义模块枚举 + 配额枚举(升鲜宝标准)
-
用量采集器(daily)上线
-
InvoiceGenerateJob(幂等 + 审计)上线
-
Payment 回调处理(幂等 + 对账)上线
-
EntitlementSnapshot 刷新机制上线
-
ExpireEnforcementJob 上线(宽限期策略)
-
后台页面(运营/租户)逐步补齐

浙公网安备 33010602011771号