feat: 卡流量读数先过采集队列再入账,扣减按采集时刻确定套餐
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m27s

- 每卡 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>
This commit is contained in:
邹进
2026-10-29 12:18:47 +09:00
parent c33cc6f6c3
commit 9c3c258f16
55 changed files with 3142 additions and 881 deletions

View File

@@ -0,0 +1,56 @@
package constants
import "fmt"
// 卡流量采集队列系统配置(add-card-traffic-queue-and-observed-deduction)。
const (
// SystemConfigModuleTraffic 是卡流量采集配置模块。
SystemConfigModuleTraffic = "traffic"
// SystemConfigTrafficQueueSize 是采集队列长度 N。
SystemConfigTrafficQueueSize = "traffic.queue.size"
// SystemConfigTrafficQueueClearOnNewDay 是跨天是否清空队列。
SystemConfigTrafficQueueClearOnNewDay = "traffic.queue.clear_on_new_day"
// SystemConfigTrafficQueueMaxPointAgeHours 是队列里点的最长保留时长(小时),0 表示不限。
SystemConfigTrafficQueueMaxPointAgeHours = "traffic.queue.max_point_age_hours"
// SystemConfigTrafficQueueDropToleranceMB 是相邻两点回退不超过该值算持平(MB)。
SystemConfigTrafficQueueDropToleranceMB = "traffic.queue.drop_tolerance_mb"
// TrafficQueueDefaultSize 等是采集队列配置默认值。
TrafficQueueDefaultSize = 3
TrafficQueueMinSize = 2
TrafficQueueMaxSize = 10
TrafficQueueDefaultMaxPointAgeHours = 72
TrafficQueueDefaultDropToleranceMB = 1.0
// TrafficQueueIntervalSpare 是采集间隔上限留的冗余次数:间隔 ≤ 窗口 ÷ (N + 冗余)。
TrafficQueueIntervalSpare = 2
// TrafficQueueUnlimitedTTLHours 是最长点龄为 0(不限)且跨天清空关闭时队列 key 的过期时长。
TrafficQueueUnlimitedTTLHours = 24 * 7
)
// 采集记录的读数结果。
const (
TrafficCollectionResultOK = "ok"
TrafficCollectionResultFailed = "failed"
TrafficCollectionResultRateLimited = "rate_limited"
TrafficCollectionResultInvalidPayload = "invalid_payload"
TrafficCollectionResultNoReading = "no_reading"
TrafficCollectionResultInvalidReading = "invalid_reading"
)
// 采集记录的写入判断。
const (
TrafficDecisionWritten = "written"
TrafficDecisionBaseline = "baseline"
TrafficDecisionQueueNotFull = "queue_not_full"
TrafficDecisionSlopeNegative = "slope_negative"
TrafficDecisionOutOfOrder = "out_of_order"
TrafficDecisionSettledExisting = "settled_existing"
)
// TrafficCollectionRetentionDays 是采集记录保留天数,过期按整月分区删除。
const TrafficCollectionRetentionDays = 90
// RedisCardTrafficQueueKey 返回卡流量采集队列 key。
func RedisCardTrafficQueueKey(cardID uint) string {
return fmt.Sprintf("traffic:queue:%d", cardID)
}