Files
junhong_cmp_fiber/pkg/constants/stop_resume_queue.go
邹进 934a3a27a1
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat: 停复机指令队列与设备维度判断
自动停复机统一经停复机指令队列执行(全局开关 polling.stop_resume.queue_enabled,默认关闭):
- 统一判断函数 decideCard,新旧路径共用;暂停轮询的卡在套餐到期、退款场景照常停机
- 同卡一条指令、版本号覆盖;领取校验单卡最小重试间隔;先于卡任务领取,不受 scheduler_mode 限制
- 执行器只调用一次网关,按错误分类退避与告警,结果未知在队列内确认
- 设备维度判断合并请求与场景,版本变化再判断,旧路径持设备锁时推后
- 套餐激活、重置、订单、流量扣减、套餐到期、退款、实名管控修改改为请求评估
- 监控增加停复机队列与近 1 小时执行计数,单卡轮询状态返回当前指令
- 迁移 000255 插入开关与告警、退避参数

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:36:26 +09:00

175 lines
7.0 KiB
Go
Raw 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 constants
import (
"fmt"
"strings"
)
// 停复机指令队列(add-stop-resume-command-queue)。
// 停复机请求评估场景:随请求评估与指令保存,执行前重新判断沿用同一场景。
const (
StopResumeScenePolling = "polling" // 套餐步骤(周期评估)
StopResumeSceneFlowObserved = "flow_observed" // 流量增加观测
StopResumeSceneNetworkObserved = "network_observed" // 网络状态变化观测
StopResumeSceneRealnameObserved = "realname_observed" // 实名变化观测(含人工实名)
StopResumeSceneActivation = "activation" // 套餐激活
StopResumeSceneReset = "reset" // 流量重置
StopResumeSceneOrder = "order" // 订单支付后套餐生效
StopResumeSceneRefund = "refund" // 退款后资产无可用主套餐
StopResumeScenePackageExpired = "package_expired" // 套餐到期且无其他有效主套餐
StopResumeSceneRealnameControl = "realname_control" // 运营商账户实名管控修改
StopResumeSceneUsage = "usage" // 流量扣减后
)
// StopResumeSceneAllowsSuspendedStop 判断场景集合中是否含套餐失效类场景:暂停轮询的卡在这些场景下照常停机。
func StopResumeSceneAllowsSuspendedStop(scenes []string) bool {
for _, scene := range scenes {
if scene == StopResumeScenePackageExpired || scene == StopResumeSceneRefund {
return true
}
}
return false
}
// JoinStopResumeScenes 把场景集合编码为逗号分隔串(去重、去空)。
func JoinStopResumeScenes(scenes []string) string {
seen := map[string]bool{}
parts := make([]string, 0, len(scenes))
for _, scene := range scenes {
scene = strings.TrimSpace(scene)
if scene == "" || seen[scene] {
continue
}
seen[scene] = true
parts = append(parts, scene)
}
return strings.Join(parts, ",")
}
// SplitStopResumeScenes 解析逗号分隔的场景串。
func SplitStopResumeScenes(raw string) []string {
if strings.TrimSpace(raw) == "" {
return nil
}
parts := strings.Split(raw, ",")
scenes := make([]string, 0, len(parts))
for _, part := range parts {
if part = strings.TrimSpace(part); part != "" {
scenes = append(scenes, part)
}
}
return scenes
}
// CheckTypeStopResume 是失败告警表中停复机方向的检查类型。
const CheckTypeStopResume = "stop_resume"
// 停复机指令失败类别(不来自上游错误分类的部分)。
const (
StopResumeFailureUnknown = "unknown" // 结果未知(超时、连接失败、可能已受理)
)
// 停复机指令默认参数(账户未配置时使用)。
const (
DefaultStopResumeMinIntervalSeconds = 600 // 单卡最小重试间隔:上游卡状态变更每十分钟只允许调用一次
DefaultStopResumeConfirmDelaySeconds = 60 // 结果未知后的确认延迟
DefaultStopResumeInflightLimit = 10 // 账户停复机执行上限
DefaultStopResumeMaxInFlight = 10 // 每进程停复机消费者 max_in_flight
)
// 停复机指令队列受控系统配置键(module = polling)。
const (
SystemConfigStopResumeQueueEnabled = "polling.stop_resume.queue_enabled"
SystemConfigStopResumeUnknownAlert = "polling.stop_resume.unknown_alert_threshold"
SystemConfigStopResumeBackoff = "polling.stop_resume.backoff_seconds"
SystemConfigStopResumeAlertFailCount = "polling.stop_resume.alert_fail_count"
)
// ========================================
// 停复机指令队列 Redis Key
// ========================================
// RedisCarrierStopResumeQueueKey 账户停复机指令队列(ZSET,member=card_id,score=可执行时间毫秒)。
func RedisCarrierStopResumeQueueKey(carrierID uint) string {
return fmt.Sprintf("polling:carrier:%d:stop_resume", carrierID)
}
// RedisCarrierStopResumeRunningKey 账户停复机执行中计数(ZSET,member=token,score=租约到期毫秒)。
func RedisCarrierStopResumeRunningKey(carrierID uint) string {
return fmt.Sprintf("polling:carrier:%d:sr_running", carrierID)
}
// RedisCardStopResumeCmdKey 卡停复机指令(Hash:action、reason、scenes、source、carrier、ver、fail、ffail、err、pending、unknown、at)。
func RedisCardStopResumeCmdKey(cardID uint) string {
return fmt.Sprintf("polling:card:%d:stop_resume_cmd", cardID)
}
// RedisCardStopResumeClaimKey 停复机指令处理中标记,值=派发 token,TTL=租约。
func RedisCardStopResumeClaimKey(cardID uint) string {
return fmt.Sprintf("polling:card:%d:sr_claim", cardID)
}
// RedisCardStopResumeExecKey 停复机指令执行权,值=token + 实例。
func RedisCardStopResumeExecKey(cardID uint) string {
return fmt.Sprintf("polling:card:%d:sr_exec", cardID)
}
// RedisCardStopResumeLastCallKey 本卡最近一次停复机网关调用时间(毫秒,停机与复机合计)。
func RedisCardStopResumeLastCallKey(cardID uint) string {
return fmt.Sprintf("polling:card:%d:sr_last_call", cardID)
}
// RedisDeviceEvalQueueKey 设备判断队列(ZSET,member=device_id,score=可执行时间毫秒)。
const RedisDeviceEvalQueueKey = "polling:device_eval:queue"
// RedisDeviceEvalRunningKey 设备判断执行中计数(ZSET,member=token)。
const RedisDeviceEvalRunningKey = "polling:device_eval:running"
// RedisDeviceEvalDispatchLockKey 设备判断分发锁。
const RedisDeviceEvalDispatchLockKey = "polling:device_eval:dispatch_lock"
// RedisDeviceEvalVersionKey 设备判断版本号(每次请求 INCR)。
func RedisDeviceEvalVersionKey(deviceID uint) string {
return fmt.Sprintf("polling:device:%d:eval_ver", deviceID)
}
// RedisDeviceEvalScenesKey 设备待判断场景集合(Set)。
func RedisDeviceEvalScenesKey(deviceID uint) string {
return fmt.Sprintf("polling:device:%d:eval_scenes", deviceID)
}
// RedisDeviceEvalClaimKey 设备判断处理中标记,值=token。
func RedisDeviceEvalClaimKey(deviceID uint) string {
return fmt.Sprintf("polling:device:%d:eval_claim", deviceID)
}
// RedisDeviceEvalLockKey 设备判断锁(执行权),同一设备同一时刻只有一个判断在执行。
func RedisDeviceEvalLockKey(deviceID uint) string {
return fmt.Sprintf("polling:device:%d:eval_lock", deviceID)
}
// NSQStopResumeTopic 返回账户停复机指令 topic。
func NSQStopResumeTopic(carrierID uint) string {
return fmt.Sprintf("polling.stop_resume.%d", carrierID)
}
// NSQDeviceEvalTopic 设备判断 topic。
const NSQDeviceEvalTopic = "polling.device_eval"
// 停复机指令执行计数字段(按 10 分钟分桶,供监控汇总近 1 小时)。
const (
StopResumeStatSuccess = "success"
StopResumeStatFailed = "failed"
StopResumeStatRetry = "retry"
StopResumeStatUnknown = "unknown"
)
// StopResumeStatBucketSeconds 是停复机执行计数的分桶时长。
const StopResumeStatBucketSeconds = 600
// RedisCarrierStopResumeStatKey 账户停复机执行计数(Hash,field=计数字段),bucket = unix 秒 / 分桶时长。
func RedisCarrierStopResumeStatKey(carrierID uint, bucket int64) string {
return fmt.Sprintf("polling:carrier:%d:sr_stats:%d", carrierID, bucket)
}