package polling import ( "context" "fmt" "github.com/break/junhong_cmp_fiber/internal/model/dto" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // validatePollingConfig 校验轮询配置:间隔为 0(不检查)或不小于最小间隔;至少配置一种间隔; // 重置日前加密的两个字段成对配置;匹配"无套餐且已销售"或"在库"的配置卡状态检查间隔必须为正数。 // 现有 Handler 未调用校验器,校验在服务层完成。 func validatePollingConfig(cfg *model.PollingConfig) error { intervals := []struct { name string value *int min int }{ {"实名检查间隔", cfg.RealnameCheckInterval, 30}, {"流量检查间隔", cfg.CarddataCheckInterval, 60}, {"套餐检查间隔", cfg.PackageCheckInterval, 60}, {"保护期一致性检查间隔", cfg.ProtectCheckInterval, 30}, {"卡状态检查间隔", cfg.CardStatusCheckInterval, 30}, } configured := false for _, item := range intervals { if item.value == nil { continue } configured = true if *item.value < 0 || (*item.value > 0 && *item.value < item.min) { return errors.New(errors.CodeInvalidParam, fmt.Sprintf("轮询配置参数不合法:%s须为 0(不检查)或不小于 %d 秒", item.name, item.min)) } } if !configured { return errors.New(errors.CodeInvalidParam, "至少需要配置一种检查间隔") } if cfg.PackageState != nil && *cfg.PackageState != "" && !validPackageState(*cfg.PackageState) { return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:套餐状态取值不合法") } if cfg.AssetStatus != nil && (*cfg.AssetStatus < constants.AssetStatusInStock || *cfg.AssetStatus > constants.AssetStatusDeactivated) { return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:资产状态取值不合法") } if (cfg.PreResetWindow == nil) != (cfg.PreResetCarddataInterval == nil) { return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:重置日前窗口长度与窗口内流量间隔须同时配置或同时为空") } if cfg.PreResetWindow != nil && (*cfg.PreResetWindow < 60 || *cfg.PreResetCarddataInterval < 60) { return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:重置日前窗口长度与窗口内流量间隔不小于 60 秒") } if requiresCardStatusCheck(cfg) && (cfg.CardStatusCheckInterval == nil || *cfg.CardStatusCheckInterval <= 0) { return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:匹配无套餐已销售卡或在库卡的配置必须配置卡状态检查间隔(正数),用于发现上游开机后停机") } return nil } // requiresCardStatusCheck 判断配置是否匹配必须兜底查卡状态的层:在库,或无套餐且已销售。 func requiresCardStatusCheck(cfg *model.PollingConfig) bool { if cfg.AssetStatus != nil && *cfg.AssetStatus == constants.AssetStatusInStock { return true } return cfg.AssetStatus != nil && *cfg.AssetStatus == constants.AssetStatusSold && cfg.PackageState != nil && *cfg.PackageState == constants.PollingPackageStateNone } func validPackageState(state string) bool { for _, item := range constants.PollingPackageStates { if item == state { return true } } return false } // applyPollingConfigClearFields 把指定字段清空为 NULL:间隔清空即改为继承,匹配条件清空即不限。 func applyPollingConfigClearFields(cfg *model.PollingConfig, fields []dto.PollingConfigClearField) error { for _, field := range fields { switch string(field) { case "realname_check_interval": cfg.RealnameCheckInterval = nil case "carddata_check_interval": cfg.CarddataCheckInterval = nil case "package_check_interval": cfg.PackageCheckInterval = nil case "card_status_check_interval": cfg.CardStatusCheckInterval = nil case "carrier_id": cfg.CarrierID = nil case "package_state": cfg.PackageState = nil case "asset_status": cfg.AssetStatus = nil case "pre_reset_window": cfg.PreResetWindow = nil case "pre_reset_carddata_interval": cfg.PreResetCarddataInterval = nil default: return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:不支持清空字段 "+string(field)) } } return nil } // priorityWarnings 返回与其他启用配置优先级相同且匹配条件可能重叠的提示;只提示不拦截。 func (s *ConfigService) priorityWarnings(ctx context.Context, cfg *model.PollingConfig) []string { others, err := s.configStore.ListEnabled(ctx) if err != nil { return nil } var warnings []string for _, other := range others { if other.ID == cfg.ID || other.Priority != cfg.Priority || !conditionsMayOverlap(cfg, other) { continue } warnings = append(warnings, fmt.Sprintf("与配置「%s」优先级同为 %d 且匹配条件可能重叠,相同优先级按配置 ID 升序生效", other.ConfigName, cfg.Priority)) } return warnings } // conditionsMayOverlap 判断两条配置是否可能匹配同一张卡:任一维度两边都填且取值不同即不重叠。 func conditionsMayOverlap(a, b *model.PollingConfig) bool { if a.CardCondition != "" && b.CardCondition != "" && a.CardCondition != b.CardCondition { return false } if a.CardCategory != "" && b.CardCategory != "" && a.CardCategory != b.CardCategory { return false } if a.CarrierID != nil && b.CarrierID != nil && *a.CarrierID != *b.CarrierID { return false } if a.AssetStatus != nil && b.AssetStatus != nil && *a.AssetStatus != *b.AssetStatus { return false } if a.PackageState != nil && b.PackageState != nil && *a.PackageState != *b.PackageState { return false } return true }