Files
junhong_cmp_fiber/openspec/changes/add-card-traffic-queue-and-observed-deduction/tasks.md
邹进 9c3c258f16
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m27s
feat: 卡流量读数先过采集队列再入账,扣减按采集时刻确定套餐
- 每卡 Redis 采集队列(默认 N=3、跨天清空、回退容差 1 MB),攒满且不下降才写最后一个点
- 测试环境全量切换日快照计量,下线灰度名单、切换水位与卡表基线扣费;全部卡置待建起点
- 读数区分没给值与 0;有套餐的卡读数 0 也写快照与日用量 0 行;重置日比较基准为 0 照常防旧值
- 新增采集记录(按月分区保留 90 天)、人工冲正流水与后台接口、采集告警
- 扣减按采集时刻有效套餐查找,只对生效中套餐做状态迁移,查不到套餐按成功跳过
- 换货新卡(新设备下的卡)打待建起点;采集间隔上限只校验启用中的轮询配置

Change: add-card-traffic-queue-and-observed-deduction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:18:47 +09:00

7.9 KiB
Raw Blame History

0. 前置

  • 0.1 经维护者确认,归档 rebuild-card-traffic-metering-and-stop-resume(9.2–9.5 灰度与切换水位验证作废)与 add-tiered-polling-and-pre-activation-settlement,确认 openspec/specs/card-traffic-metering/spec.md 存在且包含本 Change 修改与删除的要求。

1. 迁移与模型

  • 1.1 新增成对迁移:采集记录表(按 observed_at 按月分区,字段见方案 7.1,索引 (iot_card_id, observed_at DESC)、observation_id),预建当月与下月分区;down 删除。
  • 1.2 新增成对迁移:冲正流水表(字段见方案第 11 节);down 删除。
  • 1.3 新增成对迁移:tb_iot_card 增加待建起点标记,up 内把全部卡置位;down 删除该列。
  • 1.4 新增成对迁移:套餐日用量 (package_usage_id, date) 唯一索引,建前合并已有重复行;down 删除索引。(测试库已有唯一索引 idx_package_usage_daily_record_unique 且无重复行,无需新迁移)
  • 1.5 新增对应 model;采集记录分区的按月新建与过期整月删除任务(保留 90 天)。

2. 读数有效性

  • 2.1 internal/gateway/models.go:流量查询结果的已用流量改为零值表示"没值"的类型,区分 null/空/无法解析/缺字段与 0;其他使用 FlexFloat 的字段不变。
  • 2.2 internal/task/polling_carddata_handler.go:按方案第 3 节把结果分为有效、失败、限流、响应无效、无读数、读数无效。
  • 2.3 internal/domain/cardflow/rules.go:ValidReading 放宽为有限且 ≥ 0;isStale 去掉 ThisMonthBeforeResetMax > 0 条件;同步注释。

3. 采集队列

  • 3.1 新增队列组件:key traffic:queue:{card_id},Lua 原子完成跨天清空、过期丢弃、按采集时间插入去重、保留最后 N 个、设置过期(跨天清空开启时为次日 01:00 上海时间,关闭时为最长点龄后),返回队列与新点是否在队尾。
  • 3.2 写入判断:新点在队尾、队列满 N、相邻两点不下降(容差内算持平);返回写入或不写原因(队列未满、读数下降、乱序)。
  • 3.3 系统配置注册 traffic.queue.size(默认 3,最小 2)、traffic.queue.clear_on_new_day(默认 true)、traffic.queue.max_point_age_hours(默认 72)、traffic.queue.drop_tolerance_mb(默认 1),读法与 package.silence.enabled 相同(带短缓存)。
  • 3.4 轮询配置保存与启用时、队列长度或跨天清空开关修改时,校验启用中轮询配置的流量采集间隔上限;停用配置不校验。

4. 有套餐判断与观测写入

  • 4.1 package 服务新增"采集时刻有效套餐"查询(状态生效中/已用完/已过期、activated_at ≤ observed_at 或为空、expires_at 为空或晚于采集时间、未退款,卡没有时看当前绑定设备),供观测与扣减共用。
  • 4.2 traffic_calc_policy.go:hasActivePackageForCard 改调 4.1;删除灰度名单与计量模式;删除 pkg/config 中 TrafficCalc 的模式与名单配置及部署变量。
  • 4.3 traffic.go、traffic_flow.go:只在队列写入点进入快照;删除未切换分支、firstSwitch 与切换水位读写;卡表基线算法不再产生扣费增量,last_gateway_reading_mb 不再使用。
  • 4.4 写入范围:有套餐读数 0 写快照、没套餐读数 0 不写;Δ 有套餐发事件(带 observed_at、observation_id),没套餐只记已结算。
  • 4.5 待建起点:带标记的卡第一次写入时写快照、本月最大全部记为已结算、不发事件、清标记,采集记录判断为建起点。
  • 4.6 观测事务内当天第一次写入时,调用 package 服务为采集时刻每个有效套餐确保当天日用量 0 行。
  • 4.7 通道阈值判定只在写入点、读数 > 0 且未触发防旧值时调用;卡表本月用量对全部卡用本月最大快照覆盖;日流量对全部卡读快照。
  • 4.8 采集记录:有套餐的卡每次采集写一条(含失败、读数 0、被拦下),字段按方案 7.1。

5. 其他读数来源

  • 5.1 人工刷新过队列,接口返回本次读数、是否写入与原因;更新路由文档说明。
  • 5.2 激活前结算过队列;未写入时用已有本月最大快照结算、不新写快照,采集记录判断为"激活前结算用已有快照";删除"未切换卡不做结算"的分支。

6. 换卡

  • 6.1 internal/service/exchange:换卡完成事务内给新卡置待建起点标记;设备换货时给新设备当前绑定的每张卡置位;不调用运营商接口。

7. 扣减

  • 7.1 internal/infrastructure/cardobservation/event.go:把 observed_at、observation_id 传给扣减;日流量缓冲 key 改用 cardflow.ShanghaiDate(observed_at)。
  • 7.2 DeductDataUsage 按采集时刻调用 4.1 查套餐;排序、容量、超出承接与小数累积不变。
  • 7.3 状态迁移只作用于当前生效中套餐;已用完、已过期只累加不改状态;停机判断仍按当前状态。
  • 7.4 查不到套餐时写告警与跳过审计,事件按成功处理不重试。(没有套餐可挂审计资源,跳过只写带 metric 的告警日志)
  • 7.5 采集早于套餐 last_reset_at 的照扣在重置后周期,审计记录 observed_at 与 last_reset_at。
  • 7.6 updateDailyRecord 改为 upsert(当天用量累加、累计用量覆盖),日期取采集时间上海日期;提供"确保当天日用量行"方法。

8. 冲正与后台接口

  • 8.1 冲正用例:事务内 SELECT ... FOR UPDATE 锁套餐行,按当前值加减,结果 < 0 拒绝;写流水、更新已用量、按阈值重算状态(已过期/已失效只改已用量);写审计 package_usage.adjust_traffic;提交后调用 evaluator.EvaluateAndAct。
  • 8.2 后台接口:冲正(必填原因、可选关联采集与事件)、冲正流水查询、采集记录按卡与时间范围查询;权限与现有套餐管理一致。(原"修改资产套餐已用量"接口改为按差额写冲正流水)
  • 8.3 同步 cmd/api/docs.go、cmd/gendocs/main.go,运行 go run cmd/gendocs/main.go。

9. 告警

  • 9.1 开启轮询且有套餐的卡一整天没有已写入;开启轮询的卡待建起点超过 1 天;单卡读数下降占比过高或全局拦截率突增;乱序突增;重置日防旧值命中数统计。不轮询的卡不告警。

10. 构建与测试环境验证

  • 10.1 对变更 Go 文件执行 gofmt -w,运行 go build ./cmd/api ./cmd/worker、openspec validate add-card-traffic-queue-and-observed-deduction --strict。
  • 10.2 按 ENG-TEST-001 在测试环境执行迁移 up/down/up,核对新表、分区、唯一索引与全部卡待建起点标记。
  • 10.3 用构造的测试卡(参照 cmd/zz_tmp_cardflow_verify)逐项核对:正常累加、中间点暴涨被拦、清零前后不混写、容差内回退写入、乱序不写、跨天清空、读数 0 有/没套餐的写入范围、null 与缺字段不入队。
  • 10.4 重置日:配置重置日 1 与 27 各一张卡,核对重置日全天防旧值、比较基准为 0 时当天按 0 计且次日如实写入、重置日早上的旧数不更新前一天快照。
  • 10.5 全量切换与换卡:切换后第一次写入只建起点不扣费;换卡新卡写入 55 建起点、58 扣 3;起点读数异常不被当起点。
  • 10.6 扣减:已用完在有效期内的套餐承接超出;采集后到期照扣、状态不被改回已用完;采集后退款跳过不重试;套餐日用量按采集日期 upsert、没扣写 0。
  • 10.7 冲正:退回后状态恢复并交给停复机判断;结果为负被拒;与扣减并发不覆盖;因未实名停机的卡冲正后不复机。
  • 10.8 人工刷新返回写入结果;激活前结算未写入时用已有快照结算;采集记录可从扣费审计经采集 ID 追到;轮询配置间隔校验只拦启用中配置。