Files
junhong_cmp_fiber/internal/service/polling/traffic_interval.go
邹进 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

65 lines
2.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package polling
import (
"context"
"fmt"
"time"
"gorm.io/gorm"
"github.com/break/junhong_cmp_fiber/internal/domain/cardflow"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// TrafficQueueLimits 是校验流量采集间隔需要的采集队列配置。
type TrafficQueueLimits struct {
Size int
ClearOnNewDay bool
MaxPointAgeHours int
}
// SetTrafficQueueLimits 注入当前采集队列配置;未注入时不校验流量采集间隔。
func (s *ConfigService) SetTrafficQueueLimits(limits func(ctx context.Context) TrafficQueueLimits) {
s.trafficQueueLimits = limits
}
// validateTrafficInterval 只对启用中的轮询配置校验流量采集间隔:不超过 24h ÷ (N+2)(跨天清空开启)
// 或最长点龄 ÷ (N+2)(关闭);停用的配置不校验。
func (s *ConfigService) validateTrafficInterval(ctx context.Context, config *model.PollingConfig) error {
if s.trafficQueueLimits == nil || config.Status != 1 {
return nil
}
return checkTrafficInterval(config, s.trafficQueueLimits(ctx))
}
func checkTrafficInterval(config *model.PollingConfig, limits TrafficQueueLimits) error {
limit := cardflow.MaxCollectionInterval(limits.Size, limits.ClearOnNewDay, limits.MaxPointAgeHours, constants.TrafficQueueIntervalSpare)
if limit <= 0 || config.CarddataCheckInterval == nil || *config.CarddataCheckInterval <= 0 {
return nil
}
interval := time.Duration(*config.CarddataCheckInterval) * time.Second
if interval <= limit {
return nil
}
return errors.New(errors.CodeInvalidParam, fmt.Sprintf(
"轮询配置「%s」流量检查间隔 %d 秒超过上限 %d 秒(采集队列长度 %d,留 %d 次冗余),请调小间隔",
config.ConfigName, *config.CarddataCheckInterval, int64(limit/time.Second), limits.Size, constants.TrafficQueueIntervalSpare))
}
// CheckEnabledTrafficIntervals 校验全部启用中的轮询配置在给定队列配置下是否都满足流量采集间隔上限,
// 用于修改队列长度、跨天清空、最长点龄前的拦截。
func CheckEnabledTrafficIntervals(ctx context.Context, db *gorm.DB, limits TrafficQueueLimits) error {
var configs []*model.PollingConfig
if err := db.WithContext(ctx).Where("status = ? AND carddata_check_interval > 0", 1).Find(&configs).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询启用中的轮询配置失败")
}
for _, config := range configs {
if err := checkTrafficInterval(config, limits); err != nil {
return err
}
}
return nil
}