3. 计费核心数据模型:租户表、产品表、定价计划表、订阅表、用量记录表、账单表的设计与ER图
多租户SaaS计费系统的数据模型是计费逻辑的基石。本章将详细拆解六张核心表:租户表、产品表、定价计划表、订阅表、用量记录表和账单表。理解它们之间的关联关系与字段设计,是后续实现计费接口与鉴权拦截链路的前提。
3.1 核心表结构设计
每张表都围绕“多租户隔离”与“计费原子化”两个原则设计。租户ID(tenant_id)作为全局分片键,确保数据安全隔离;计费相关字段采用精度可控的数值类型(如 DECIMAL(18,6)),避免浮点误差。
| 表名 | 核心字段(示例) | 主键/索引 | 业务说明 |
|---|---|---|---|
| 租户表 (tenant) | tenant_id, tenant_name, status, tier, created_at | PK: tenant_id | 标识每个租户及其服务等级(免费/专业/企业) |
| 产品表 (product) | product_id, product_name, product_type, unit, tenant_id | PK: product_id; UK: (tenant_id, product_name) | 定义可计费的资源或服务(如API调用次数、存储空间) |
| 定价计划表 (pricing_plan) | plan_id, plan_name, billing_cycle, price, tier, tenant_id | PK: plan_id; UK: (tenant_id, plan_name) | 定义具体的定价策略(如按月订阅、按量计费单价) |
| 订阅表 (subscription) | sub_id, tenant_id, plan_id, start_date, end_date, status | PK: sub_id; IDX: (tenant_id, status) | 记录租户当前生效的定价计划及时间范围 |
| 用量记录表 (usage_record) | usage_id, tenant_id, product_id, quantity, recorded_at | PK: usage_id; IDX: (tenant_id, product_id, recorded_at) | 存储每次资源消耗的明细数据,用于按量计费 |
| 账单表 (invoice) | invoice_id, tenant_id, sub_id, amount, due_date, status | PK: invoice_id; IDX: (tenant_id, status, due_date) | 最终生成的计费单据,包含应缴金额与支付状态 |
设计要点: 所有涉及金额或数量的字段均使用
DECIMAL 类型,避免 FLOAT/DOUBLE 带来的精度丢失。例如 price DECIMAL(18,6) 可支持到小数点后6位,满足微计费场景。
3.2 核心实体关系(ER图)
下图展示了六张表之间的主外键关联与业务流向。租户是顶层实体,产品与定价计划独立于订阅存在,订阅关联租户与定价计划,用量记录关联租户与产品,账单最终关联订阅与租户。
3.3 关键字段与索引设计
以下列出每张表中容易忽略但至关重要的字段与索引策略,这些设计直接影响计费接口的查询性能与数据一致性。
3.3.1 租户表 (tenant)
- tier:枚举类型(
FREE,PRO,ENTERPRISE),用于鉴权拦截时判断功能可用性。 - status:
ACTIVE/SUSPENDED/CLOSED,计费接口需先校验此字段。 - 索引:
idx_tenant_status (status)用于批量查询活跃租户。
3.3.2 产品表 (product)
- product_type:
UNIT(按量计费)或SUBSCRIPTION(订阅制),决定计费引擎的分支逻辑。 - unit:计量单位(如
次、GB、小时),用于用量记录聚合。 - 唯一约束:
uk_tenant_product (tenant_id, product_name)防止同一租户下产品名重复。
3.3.3 定价计划表 (pricing_plan)
- billing_cycle:
MONTHLY/YEARLY/ONE_TIME,影响账单生成周期。 - price:
DECIMAL(18,6),支持阶梯定价时需配合min_quantity/max_quantity字段(扩展设计)。 - 索引:
idx_plan_tenant (tenant_id)加速租户内计划查询。
3.3.4 订阅表 (subscription)
- status:
ACTIVE/EXPIRED/CANCELLED,计费鉴权时需校验订阅是否在有效期内。 - start_date / end_date:
DATETIME类型,用于判断当前时间是否在订阅区间。 - 复合索引:
idx_sub_tenant_status (tenant_id, status)是计费接口最频繁的查询路径。
3.3.5 用量记录表 (usage_record)
- quantity:
DECIMAL(18,6),记录单次消耗量。 - recorded_at:
DATETIME(3),精确到毫秒,用于去重与时间窗口聚合。 - 复合索引:
idx_usage_tenant_product_time (tenant_id, product_id, recorded_at)是按量计费查询的核心索引。
3.3.6 账单表 (invoice)
- amount:
DECIMAL(18,6),最终应缴金额。 - due_date:
DATE,用于逾期判断与催收。 - status:
PENDING/PAID/OVERDUE/CANCELLED。 - 索引:
idx_invoice_tenant_due (tenant_id, due_date, status)支持账单列表分页与逾期筛选。
实战建议: 在用量记录表与账单表上,务必添加
created_at 与 updated_at 审计字段,并设置 DEFAULT CURRENT_TIMESTAMP。这有助于排查计费数据不一致问题,也便于后续对账系统使用。
3.4 数据模型与鉴权拦截的关联
计费数据模型不仅是存储结构,更是鉴权拦截链路的决策依据。以下流程图展示了在API请求中,如何利用上述数据模型进行多租户计费鉴权。
注意: 在鉴权拦截链路中,
subscription 表的 end_date 字段必须与当前时间进行比对。如果订阅已过期,即使租户状态为 ACTIVE,也应拒绝计费操作,防止产生无效账单。
3.5 数据模型扩展建议
在实际生产环境中,上述六张表通常需要配合以下扩展设计来应对复杂计费场景:
- 阶梯定价表:当定价计划支持阶梯价格时,可拆出
pricing_tier表,包含plan_id、min_quantity、max_quantity、unit_price字段。 - 折扣与优惠券表:与账单表关联,支持按比例或固定金额减免。
- 对账表:记录每次计费引擎的执行批次与结果摘要,便于财务对账与异常排查。
- 审计日志表:记录所有计费相关数据的变更历史,满足合规要求。