fix: 停复机成功后上游状态滞后导致暂停轮询的卡到期或退款漏停
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m29s

- 停复机成功后保护窗口内(polling.stop_resume.status_grace_seconds,默认 300 秒)查卡状态返回操作前的状态视为上游滞后,不回写网络状态
- 套餐到期、退款场景不看库里的开停状态,资产无可用主套餐即停机,上游已停时按已是目标状态结束
- 迁移 000257 插入保护窗口配置

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
邹进
2026-10-26 10:41:45 +09:00
parent f6a740b9f3
commit d4fde99cc3
11 changed files with 109 additions and 5 deletions

View File

@@ -78,6 +78,20 @@ type Service struct {
trafficCalc *TrafficCalcPolicy
// logger 记录快照写入失败等不影响观测结果的告警;未注入时不输出。
logger *zap.Logger
// commandGrace 返回停复机成功后的状态保护窗口;未注入时不保护。
commandGrace func(ctx context.Context) time.Duration
}
// SetCommandGrace 注入停复机成功后的状态保护窗口(受控系统配置 polling.stop_resume.status_grace_seconds)。
func (s *Service) SetCommandGrace(grace func(ctx context.Context) time.Duration) {
s.commandGrace = grace
}
func (s *Service) currentCommandGrace(ctx context.Context) time.Duration {
if s == nil || s.commandGrace == nil {
return 0
}
return s.commandGrace(ctx)
}
// SetTrafficCalcPolicy 注入卡流量计量切换策略与告警日志。

View File

@@ -3,6 +3,7 @@ package cardobservation
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/pollingpause"
"go.uber.org/zap"
"strconv"
"time"
@@ -47,11 +48,17 @@ func (s *Service) ApplyNetworkObservation(ctx context.Context, observation domai
auditedCard = &card
nextDecision, decisionErr := domain.ApplyNetwork(domain.CardNetworkSnapshot{
CardID: card.ID, NetworkStatus: card.NetworkStatus, StopReason: card.StopReason,
EnablePolling: card.EnablePolling,
EnablePolling: card.EnablePolling, StoppedAt: card.StoppedAt, ResumedAt: card.ResumedAt,
CommandGrace: s.currentCommandGrace(ctx),
}, observation)
if decisionErr != nil {
return decisionErr
}
if nextDecision.StatusSuppressed {
s.warnLogger().Info("停复机成功后保护窗口内观测到相反状态,判定为上游状态滞后,不回写网络状态",
zap.Uint("card_id", card.ID), zap.Int("network_status", card.NetworkStatus),
zap.String("gateway_status", observation.GatewayStatus), zap.String("metric", "card_observation.network_suppressed"))
}
decision = nextDecision
updates := map[string]any{
"last_card_status_check_at": observation.Metadata.ObservedAt,