计费与账单
1. 实时计费流程
每次通话结束时,Wholesale 自动:
通话结束
│
▼
计算话费 (卖价)
│
├── 写入 wholesale_cdrs 话单
├── 扣减租户余额 (SQL: balance = balance - price)
├── 报告熔断器 (成功/失败)
├── 记录滑动窗口事件 (ASR/ACD)
└── 上报 Prometheus 指标
计费精度规则(0.5+):
- 向上取整到秒:计费时长在计价前统一向上取整到整秒。
- 账期日期校验:账期/账单日期设置在保存时校验——过去会造成静默错账的错误日期配置现在会被直接拒绝。
- 允许负数金额:贷记与话后调整走与计费相同的账本,争议修正只是一条账目,不再需要外部表格。
- CDR 携带命中路由 id:每条 wholesale CDR 都带有计费上下文中的路由 id,每通被计费的呼叫都能追溯到具体计价的费率条目。
- 批量话单写入:呼叫记录钩子按批处理,高话务量下每秒数千条 CDR 不再逐行挤占呼叫路径。
1.1 CDR 字段
| 字段 | 说明 |
|---|---|
| call_id | SIP Call-ID |
| tenant_id | 租户 ID |
| trunk_id | 使用的中继 |
| caller / callee | 主叫/被叫 |
| call_start / call_end | 起止时间 |
| ring_duration | 振铃时长 |
| talk_duration | 通话时长 |
| billable_duration | 计费时长 |
| sell_rate / sell_price | 卖费率/卖价 |
| buy_rate / buy_price | 买费率/买价 |
| profit | 利润 |
| sip_code | SIP 最终状态码 |
| answered | 是否应答 |
2. 话单查询
Wholesale → 话单管理:
支持筛选条件:
- 时间范围
- 租户
- 中继
- 主叫/被叫
- 应答/未应答
- SIP 状态码
2.1 CDR 导出
| 方式 | 说明 |
|---|---|
| 同步导出 | 即时下载 CSV(小数据量) |
| 异步导出 | 后台任务,生成 gzip CSV(大数据量) |
异步导出流程:
- 创建导出任务(
wholesale_export_tasks表) - 后台 Worker 流式生成 gzip CSV
- 完成后在 导出任务 列表下载
2.2 话单重算(Rebill)
话单管理 → 重算:修改费率后对历史话单重新计费。
- 按时间范围和租户筛选
- 使用当前费率表重新计算
- 更新 CDR 和租户余额
3. 账单周期
3.1 配置
租户详情 → 账单设置:
| 字段 | 说明 |
|---|---|
| 周期类型 | Weekly / Monthly |
| 结算日 | 每月几号 / 每周几 |
| 自动归档 | 是否自动归档已结算 CDR |
3.2 账单生成
后台任务每小时检查:
- 查找到期的账单周期
- 汇总该周期内的 CDR
- 生成
wholesale_bills记录:- 总金额、通话数、ASR、总时长
- 账单状态:draft → confirmed → paid
3.3 账单操作
| 操作 | 说明 |
|---|---|
| 查看 | 账单明细(按中继/日期汇总) |
| 打印 | 生成 HTML 账单页面 |
| 下载 | 导出 CSV |
| 确认 | 标记为已确认 |
| 删除 | 删除草稿账单 |
4. 充值与余额管理
租户详情 → 充值:
- 金额
- 凭证图片(上传到服务器本地或 S3)
- 系统记录到
rustpbx_recharge_logs
租户详情 → 生成账单:
- 手动触发当期账单生成
5. 数据库要求
Wholesale 计费涉及大量并发写入,数据库要求:
| 项目 | MySQL | PostgreSQL |
|---|---|---|
| 事务隔离 | READ COMMITTED | READ COMMITTED |
| 连接池 | 每节点 10-20 连接 | 同左 |
| 索引 | migration 自动创建 | 同左 |
| CDR 分区 | 建议按月分区(手动) | 建议按月分区(手动) |
CDR 数据增长估算:
- 每通电话 1 条 CDR ≈ 1KB
- 10 万通/天 ≈ 100MB/天 ≈ 3GB/月
- 建议对
wholesale_cdrs表按月分区或定期归档