package task import ( "context" "encoding/json" "time" "go.uber.org/zap" "github.com/break/junhong_cmp_fiber/internal/polling/carrierqueue" ) // 设备判断的推后时长:旧路径持设备锁时、判断失败时。 const ( deviceEvalLegacyLockDelay = 5 * time.Second deviceEvalFailureDelay = 30 * time.Second deviceEvalTimeout = 30 * time.Second ) // DeviceEvalExecutor 设备维度判断执行器:抢设备判断锁,读取最新数据为设备下各卡生成指令;锁内不调网关。 type DeviceEvalExecutor struct { store *carrierqueue.Store service StopResumeCommandService instanceID string logger *zap.Logger } // NewDeviceEvalExecutor 创建设备判断执行器。 func NewDeviceEvalExecutor(store *carrierqueue.Store, service StopResumeCommandService, instanceID string, logger *zap.Logger) *DeviceEvalExecutor { if logger == nil { logger = zap.NewNop() } return &DeviceEvalExecutor{store: store, service: service, instanceID: instanceID, logger: logger} } // Handle 消费一条设备判断消息。收尾只移除本次已处理的场景;版本变大时用最新数据再判断一次。 func (e *DeviceEvalExecutor) Handle(ctx context.Context, body []byte, _ func()) { var msg carrierqueue.DeviceEvalMessage if err := json.Unmarshal(body, &msg); err != nil || msg.DeviceID == 0 || msg.Token == "" { e.logger.Warn("设备判断消息无效,丢弃", zap.ByteString("body", body)) return } got, err := e.store.AcquireDeviceEvalLock(ctx, msg.DeviceID, msg.Token, msg.Token+"|"+e.instanceID, 2*deviceEvalTimeout) if err != nil || got != 1 { return } claimed := carrierqueue.DeviceEvalClaimed{DeviceID: msg.DeviceID, Token: msg.Token, Version: msg.Version, Scenes: msg.Scenes} deferFor := time.Duration(0) if e.service.LegacyDeviceOpLocked(ctx, msg.DeviceID) { // 旧路径正在对该设备执行停复机:推后,不并行判断。 deferFor = deviceEvalLegacyLockDelay } else { evalCtx, cancel := context.WithTimeout(ctx, deviceEvalTimeout) if evalErr := e.service.EvaluateDeviceCommands(evalCtx, msg.DeviceID, msg.Scenes); evalErr != nil { e.logger.Warn("设备判断失败,稍后再判断", zap.Uint("device_id", msg.DeviceID), zap.Strings("scenes", msg.Scenes), zap.Error(evalErr)) deferFor = deviceEvalFailureDelay } cancel() } finishCtx, finishCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) defer finishCancel() result, err := e.store.FinishDeviceEval(finishCtx, claimed, deferFor) if err != nil { e.logger.Warn("设备判断收尾失败,等待租约到期后重新领取", zap.Uint("device_id", msg.DeviceID), zap.Error(err)) return } if result == 2 { e.logger.Info("设备判断期间有新请求,按最新数据再判断", zap.Uint("device_id", msg.DeviceID)) } }