All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m26s
统一停复机规则转正的第一次发布:总开关仍关闭,现有停复机行为不变。 - 新增停复机规则演练:GET /stop-resume-rule/dry-run 按新规则对开机与停机卡只判断不执行,返回运营商账户 × 停机原因 × 判断结果汇总与分页明细,可假设通道实名管控配置;POST /stop-resume-rule/dry-run/export 导出明细(场景 stop_resume_rule_dry_run);仅超级管理员与平台账号 - 风险停机、已销户关闭轮询不再限于独立卡,只关该卡自身轮询,不级联设备 - 错误分类规则增加 auto_disable_polling,仅终态类别可勾选;停复机(含设备停复机)命中该规则时不重试、关闭该卡轮询、回写网关扩展状态并写审计 iot_card.terminal_disable_polling - 迁移 000250 加列与约束,并为"已销户""仅支持风险复机"置位
91 lines
3.3 KiB
Go
91 lines
3.3 KiB
Go
package cardobservation
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
)
|
|
|
|
// NetworkObservation 是领域层接收的标准 Gateway 网络观测。
|
|
type NetworkObservation struct {
|
|
CardID uint
|
|
GatewayStatus string
|
|
GatewayExtend string
|
|
GatewayIMEI string
|
|
Metadata ObservationMetadata
|
|
}
|
|
|
|
// CardNetworkSnapshot 是网络规则需要的最小卡快照。
|
|
type CardNetworkSnapshot struct {
|
|
CardID uint
|
|
NetworkStatus int
|
|
StopReason string
|
|
EnablePolling bool
|
|
}
|
|
|
|
// NetworkDecision 描述一次网络观测可安全持久化的最终值。
|
|
type NetworkDecision struct {
|
|
StatusKnown bool
|
|
StatusChanged bool
|
|
BeforeStatus int
|
|
AfterStatus int
|
|
GatewayExtend string
|
|
GatewayIMEI string
|
|
UpdateIMEI bool
|
|
StopReason string
|
|
StopReasonChanged bool
|
|
StopPolling bool
|
|
}
|
|
|
|
// ApplyNetwork 应用 Gateway 状态映射、运营商停机原因和风险终止卡关闭轮询规则。
|
|
func ApplyNetwork(snapshot CardNetworkSnapshot, observation NetworkObservation) (NetworkDecision, error) {
|
|
if err := validateObservationMetadata(observation.CardID, observation.Metadata); err != nil {
|
|
return NetworkDecision{}, err
|
|
}
|
|
if snapshot.NetworkStatus != constants.NetworkStatusOffline && snapshot.NetworkStatus != constants.NetworkStatusOnline {
|
|
return NetworkDecision{}, errors.New(errors.CodeInvalidStatus, "卡网络状态无效")
|
|
}
|
|
status, known := MapGatewayNetworkStatus(observation.GatewayStatus, observation.GatewayExtend)
|
|
extend := strings.TrimSpace(observation.GatewayExtend)
|
|
imei := strings.TrimSpace(observation.GatewayIMEI)
|
|
decision := NetworkDecision{
|
|
StatusKnown: known, BeforeStatus: snapshot.NetworkStatus, AfterStatus: snapshot.NetworkStatus,
|
|
GatewayExtend: extend, GatewayIMEI: imei, UpdateIMEI: imei != "", StopReason: snapshot.StopReason,
|
|
StopPolling: snapshot.EnablePolling && ShouldStopPollingForRisk(extend),
|
|
}
|
|
if !known {
|
|
return decision, nil
|
|
}
|
|
decision.AfterStatus = status
|
|
decision.StatusChanged = status != snapshot.NetworkStatus
|
|
if decision.StatusChanged && status == constants.NetworkStatusOffline && snapshot.StopReason == "" &&
|
|
strings.TrimSpace(observation.GatewayStatus) == constants.GatewayCardStatusStopped {
|
|
decision.StopReason = constants.StopReasonCarrierStopped
|
|
decision.StopReasonChanged = true
|
|
}
|
|
return decision, nil
|
|
}
|
|
|
|
// MapGatewayNetworkStatus 将 Gateway 状态稳定映射为本地网络状态。
|
|
func MapGatewayNetworkStatus(cardStatus, extend string) (int, bool) {
|
|
status := strings.TrimSpace(cardStatus)
|
|
ext := strings.TrimSpace(extend)
|
|
switch status {
|
|
case constants.GatewayCardStatusNormal:
|
|
return constants.NetworkStatusOnline, true
|
|
case constants.GatewayCardStatusStopped, constants.GatewayCardStatusReady:
|
|
return constants.NetworkStatusOffline, true
|
|
}
|
|
if ext == constants.GatewayCardExtendPendingActivation {
|
|
return constants.NetworkStatusOffline, true
|
|
}
|
|
return constants.NetworkStatusOffline, false
|
|
}
|
|
|
|
// ShouldStopPollingForRisk 判断卡是否命中运营商风险终止状态(风险停机、已销户)。
|
|
// 不论是否绑定设备都关闭该卡自身轮询,不级联设备与设备下其他卡。
|
|
func ShouldStopPollingForRisk(extend string) bool {
|
|
return extend == constants.GatewayCardExtendRiskStop || extend == constants.GatewayCardExtendCancelled
|
|
}
|