Files
lisglosips/docs/CDR_MINIMAL_BILLING_RUNBOOK.md
T

3.8 KiB
Raw Blame History

CDR Minimal Billing Runbook

任务:S23 - 最小计费与余额扣减
完成时间:2026-06-21 19:55 +08:00

1. 目标

S23 在 S22 的 Redis Stream 消费基础上完成最小计费闭环:

  • 成功通话 CDR 写入 raw_cdrsrated_cdrs
  • 按周期秒数和周期费率计算费用。
  • 扣减客户余额,并写入不可变客户余额流水。
  • 同一 event_id 或同一 raw_cdr_id 不重复扣费。

当前系统尚未建立客户侧费率表,因此 S23 使用落地网关的 billingCycleSeccycleRate 同时作为供应商成本和临时客户费用,grossProfit0.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_code2xxduration_sec > 0 时才进入计费。

失败 CDR、零时长 CDR、缺少客户或落地网关 ID 的 CDR 只写 raw_cdrsratingStatus 标记为 SKIPPED,不扣余额。

3. 写库与幂等

apps/worker-cdr/src/rating.tsCdrRatingService 在单个数据库事务内处理:

  1. raw_cdrs.event_id 查询是否已处理。
  2. 新建 raw_cdrs
  3. 锁定客户余额行:SELECT ... FOR UPDATE
  4. 读取落地网关周期费率。
  5. 新建 rated_cdrs
  6. 新建一条负数 customer_recharges 作为扣费流水。
  7. 更新 customers.balance
  8. raw_cdrs.ratingStatus 标记为 RATED

扣费流水约定:

字段
amount 负数费用
idempotencyKey cdr:<event_id>
remark CDR_CHARGE:<raw_cdr_id>
createdBy worker-cdr

数据库唯一约束共同防止重复扣费:

  • raw_cdrs.event_id
  • rated_cdrs.raw_cdr_id
  • customer_recharges.idempotencyKey

4. Worker 行为

apps/worker-cdr/src/main.ts 现在连接 Redis 和 MySQL

  1. stream:cdr_payloadbilling-workers Consumer Group 读取 CDR。
  2. 解析成功后调用 CdrRatingService.rate(event)
  3. 处理成功或重复消息后 ACK Redis 消息。
  4. 处理异常时沿用 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.tsapps/worker-cdr/src/rating.tsapps/worker-cdr/src/rating.spec.ts
  • 恢复 apps/worker-cdr/package.jsonapps/worker-cdr/tsconfig.json
  • 重新执行 corepack pnpm@10.33.0 build

数据回滚:

  • S23 未执行真实数据库写入验证,无需清理远端数据。
  • 若后续环境已运行 worker 并产生扣费数据,回滚前必须先停止 worker,记录待回滚的 event_idraw_cdr_idrated_cdr_id 和客户流水;不得直接删除余额流水,应通过受控反向流水或备份恢复方案处理。