All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m30s
- 后台设备停复机不再设置 1 小时保护期,也不再因保护期拒绝相反操作;单卡手动停复机去掉保护期检查 - 删除保护期一致性轮询(polling:protect)及其强制停复机方法;调度、初始化、监控、匹配预览不再包含保护期;已入队的旧任务由空处理函数直接确认,下一次发布移除 - 轮询配置接口不再接受与返回 protect_check_interval;并发配置不再返回 protect,查询、更新、重置该类型时拒绝 - 资产详情 device_protect_status 固定返回 none 并标记废弃,兼容老版本客户端 - 删除保护期 Redis key 与时长常量;停机原因 protect_period 保留供历史数据与开放接口映射 - 迁移 000249 只把 protect_check_interval、last_protect_check_at 列注释标记为已废弃,不删列不改数据
140 lines
5.6 KiB
Go
140 lines
5.6 KiB
Go
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.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
|
||
}
|