3.8 KiB
3.8 KiB
CDR Minimal Billing Runbook
任务:S23 - 最小计费与余额扣减
完成时间:2026-06-21 19:55 +08:00
1. 目标
S23 在 S22 的 Redis Stream 消费基础上完成最小计费闭环:
- 成功通话 CDR 写入
raw_cdrs和rated_cdrs。 - 按周期秒数和周期费率计算费用。
- 扣减客户余额,并写入不可变客户余额流水。
- 同一
event_id或同一raw_cdr_id不重复扣费。
当前系统尚未建立客户侧费率表,因此 S23 使用落地网关的 billingCycleSec 和 cycleRate 同时作为供应商成本和临时客户费用,grossProfit 为 0.000000。正式客户费率和利润计算留给后续计费版本。
2. 计费规则
apps/worker-cdr/src/billing.ts 提供周期计费函数:
cycles = ceil(duration_sec / billing_cycle_sec)
bill_sec = cycles * billing_cycle_sec
amount = cycles * cycle_rate
约束:
duration_sec必须为非负整数。billing_cycle_sec必须为 1 到 60 的整数。- 金额统一保留 6 位小数。
sip_code为2xx且duration_sec > 0时才进入计费。
失败 CDR、零时长 CDR、缺少客户或落地网关 ID 的 CDR 只写 raw_cdrs,ratingStatus 标记为 SKIPPED,不扣余额。
3. 写库与幂等
apps/worker-cdr/src/rating.ts 的 CdrRatingService 在单个数据库事务内处理:
- 按
raw_cdrs.event_id查询是否已处理。 - 新建
raw_cdrs。 - 锁定客户余额行:
SELECT ... FOR UPDATE。 - 读取落地网关周期费率。
- 新建
rated_cdrs。 - 新建一条负数
customer_recharges作为扣费流水。 - 更新
customers.balance。 - 将
raw_cdrs.ratingStatus标记为RATED。
扣费流水约定:
| 字段 | 值 |
|---|---|
amount |
负数费用 |
idempotencyKey |
cdr:<event_id> |
remark |
CDR_CHARGE:<raw_cdr_id> |
createdBy |
worker-cdr |
数据库唯一约束共同防止重复扣费:
raw_cdrs.event_idrated_cdrs.raw_cdr_idcustomer_recharges.idempotencyKey
4. Worker 行为
apps/worker-cdr/src/main.ts 现在连接 Redis 和 MySQL:
- 从
stream:cdr_payload的billing-workersConsumer Group 读取 CDR。 - 解析成功后调用
CdrRatingService.rate(event)。 - 处理成功或重复消息后 ACK Redis 消息。
- 处理异常时沿用 S22 死信机制写入
stream:cdr_deadletter。
必需环境变量:
REDIS_URL
DATABASE_URL
可选环境变量:
CDR_CONSUMER_NAME
CDR_BLOCK_MS
CDR_BATCH_SIZE
CDR_PENDING_IDLE_MS
LISGLOSIPS_LOG_LEVEL
5. 验证结果
corepack pnpm@10.33.0 exec vitest run apps/worker-cdr/src/rating.spec.ts通过,3 条测试覆盖周期计费、成功 CDR 扣费幂等、失败 CDR 不扣费。corepack pnpm@10.33.0 --filter @lisglosips/worker-cdr build通过。corepack pnpm@10.33.0 typecheck通过。corepack pnpm@10.33.0 lint通过。corepack pnpm@10.33.0 build通过。
本次未连接 B 执行服务部署或真实库写入验证,因为 B sudo 凭据仍未通过校验。S23 仅完成本地代码闭环和构建验收。
6. 回滚
代码回滚:
- 恢复
apps/worker-cdr/src/main.ts到 S22 只记录 CDR 的版本。 - 删除或恢复
apps/worker-cdr/src/billing.ts、apps/worker-cdr/src/rating.ts、apps/worker-cdr/src/rating.spec.ts。 - 恢复
apps/worker-cdr/package.json和apps/worker-cdr/tsconfig.json。 - 重新执行
corepack pnpm@10.33.0 build。
数据回滚:
- S23 未执行真实数据库写入验证,无需清理远端数据。
- 若后续环境已运行 worker 并产生扣费数据,回滚前必须先停止 worker,记录待回滚的
event_id、raw_cdr_id、rated_cdr_id和客户流水;不得直接删除余额流水,应通过受控反向流水或备份恢复方案处理。