收藏
回答

原生小程序 TS 切换到后台,再切回来所有和websocket+ mqtt相关操作都失效

框架类型 问题类型 操作系统版本 手机型号 微信版本
小程序 Bug IOS 18.0.1 PROMAX 8.0.56

原生小程序 TS 现在切屏就重新连接 websocket mqtt ,设置五分钟延迟断连保活也没生效。

// mqtt-client.ts
const mqtt = require("../utils/mqtt.min.js");
import { Encrypt, Decrypt } from '../utils/crypto';


// MQTT 消息接口定义
interface MqttMessage {
  id: string;
  code: number;
  msg: string;
  type: string;
  ts: number;
  expired: number;
  data: {
    houseId?: string;    // 房屋ID
    houseName?: string;  // 房屋名称  
    role?: number;       // 用户角色
    deviceCode?: string;
    online?: boolean;
    lockStatus?: number;
    userId?: string;
    success?: boolean;
    errorCode?: string;
    errorMsg?: string;
    keyType?: string;
    hijack?: string;
    password?: string;
    imgUrl?: string;
    platform?: string;
    serialNo?: string;
    opTime?: number;
    leaveHouseId?: string;  // 被移出的房屋ID
  };
}


interface MqttClient {
  connected?: boolean;
  on(event: 'connect' | 'message' | 'error' | 'reconnect' | 'offline' | 'close', callback: (...args: any[]) => void): void;
  removeListener(event: 'connect' | 'message' | 'error' | 'reconnect' | 'offline' | 'close', callback: (...args: any[]) => void): void;
  removeAllListeners(event?: 'connect' | 'message' | 'error' | 'reconnect' | 'offline' | 'close'): void;
  subscribe(topic: string | string[], opts?: any, callback?: (err: Error, granted: any) => void): void;
  unsubscribe(topic: string | string[], opts?: any, callback?: (err: Error) => void): void;
  publish(topic: string, message: string): void;
  end(force?: boolean, opts?: any, cb?: () => void): void;
}


interface MqttClientConfig {
  userId: string;
  familyId: string;
  deviceCode: string;
}


interface IClientOptions {
  clientId: string;
  username: string;
  password: string;
  reconnectPeriod: number;
  connectTimeout: number;
  protocol: string;
  protocolVersion: number;
  clean: boolean;
  rejectUnauthorized: boolean;
  keepalive: number;
  path: string;
  will?: {
    topic: string;
    payload: string;
    qos: number;
    retain: boolean;
  };
}


interface EventEmitter {
  on(event: string, listener: Function): void;
  emit(event: string, ...args: any[]): void;
  off(event: string, listener: Function): void;
  removeAllListeners?(): void;
}


// 设备状态接口
interface DeviceStatus {
  deviceCode: string;
  online: boolean;
  lockStatus?: number;
  lastUpdateTime: number;
}


// 简单事件发射器实现
class SimpleEventEmitter implements EventEmitter {
  private listeners: { [event: string]: Function[] } = {};


  on(event: string, listener: Function): void {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(listener);
  }


  emit(event: string, ...args: any[]): void {
    if (this.listeners[event]) {
      this.listeners[event].forEach((listener) => {
        try {
          listener(...args);
        } catch (error) {
          console.error(`[EventEmitter] Error in listener for event ${event}:`, error);
        }
      });
    }
  }


  off(event: string, listener: Function): void {
    if (this.listeners[event]) {
      this.listeners[event] = this.listeners[event].filter(l => l !== listener);
    }
  }


  removeAllListeners(): void {
    this.listeners = {};
  }
}


// 全局单例实例
let globalClient: SmartDoorMQTTClient | null = null;


class SmartDoorMQTTClient {
  private client: MqttClient | null = null;
  private config: MqttClientConfig;
  private eventEmitter: EventEmitter;
  private deviceStatuses: Map<string, DeviceStatus> = new Map();
  private reconnectAttempts: number = 0;
  private readonly MAX_RECONNECT_ATTEMPTS: number = 15;
  private isConnecting: boolean = false;
  private connectionTimer: any = null;
  private reconnectTimer: any = null;
  private readonly MQTT_URL = 'wss://iot-door.lloong.com:30013';
  private readonly RECONNECT_DELAY = 1000; // 3秒重连延迟



  // 新增:记录后台切换前的连接状态
  private wasConnectedBeforeBackground: boolean = false;
  // 新增:消息队列,存储待发送的消息
  private messageQueue: Array<{ topic: string, type: string, data: any }> = [];
  // 新增:连接状态
  private _connectingPromise: Promise<void> | null = null;
  // 新增:重连提示弹窗
  private reconnectModal: any = null;


  private constructor(config: MqttClientConfig) {


    this.config = config;
    this.eventEmitter = new SimpleEventEmitter();
    this.initDeviceStatus();


    // 新增:注册小程序前后台切换事件
    this.registerAppEvents();
  }


  // 单例获取方法
  public static getInstance(config?: MqttClientConfig): SmartDoorMQTTClient {
    if (!globalClient && config) {
      globalClient = new SmartDoorMQTTClient(config);
    } else if (globalClient && config) {
      // 更新现有实例的配置
      globalClient.updateConfig(config);
    }
    return globalClient as SmartDoorMQTTClient;
  }


  private updateConfig(config: MqttClientConfig): void {
    this.config = config;


  }


  // 新增:注册小程序前后台切换事件
  private registerAppEvents(): void {
    let backgroundTimer: number;


    wx.onAppHide(() => {
      const app = getApp<IAppOption>();
      // 优先检查全局导航标记
      if (app.globalData.__pageNavigating) {
        console.log('[MQTT] 页面导航中,保持连接');
        return;
      }
      // 检查全局标记,判断是否是页面导航
      // @ts-ignore - 访问自定义属性
      if (getApp() && getApp().__pageNavigating === true) {
        console.log('[MQTT] 检测到页面导航,不断开连接');
        return;
      }


      this.wasConnectedBeforeBackground = this.isConnected();
      this.closeConnection(1000, "App entering background");


      backgroundTimer = setTimeout(() => {
        this.cleanup();
        this.messageQueue = [];
      }, 5000) as unknown as number;
    });


    wx.onAppShow(() => {
      const app = getApp<IAppOption>();
      // 清除导航标记
      if (app.globalData.__pageNavigating) {
        console.log('[MQTT] 页面导航返回,保持连接');
        app.globalData.__pageNavigating = false;
        return;
      }
      // 检查全局标记,判断是否是页面导航
      // @ts-ignore - 访问自定义属性
      if (getApp() && getApp().__pageNavigating === true) {
        console.log('[MQTT] 检测到页面导航返回,不触发重连');
        // @ts-ignore
        getApp().__pageNavigating = false;
        return;
      }


      clearTimeout(backgroundTimer);
      if (this.reconnectTimer) clearTimeout(this.reconnectTimer);


      this.reconnectTimer = setTimeout(() => {
        this.handleForegroundReconnect();
      }, 300) as unknown as number;
    });
  }
  // 新增前台重连处理方法
  private handleForegroundReconnect(): void {
    if (!this.wasConnectedBeforeBackground) return;


    this.showReconnectModal();
    this.connect()
      .then(() => {
        this.hideReconnectModal();
        this.processQueuedMessages();
      })
      .catch(error => {
        console.error('[MQTT][AppShow] Failed to reconnect:', error);
        this.hideReconnectModal();
        if (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
          this.scheduleReconnect(); // 继续按策略重连
        }
      });
  }
  // 新增:显示重连提示弹窗
  private showReconnectModal(): void {
    if (this.reconnectModal) {
      return; // 已存在弹窗,不重复显示
    }


    try {
      this.reconnectModal = wx.showLoading({
        title: '正在重新连接...',
        mask: true
      });
    } catch (error) {
      console.error('[MQTT][ShowReconnectModal] Failed to show modal:', error);
    }
  }


  // 新增:隐藏重连提示弹窗
  private hideReconnectModal(): void {
    if (this.reconnectModal) {
      try {
        wx.hideLoading();
        this.reconnectModal = null;
      } catch (error) {
        console.error('[MQTT][HideReconnectModal] Failed to hide modal:', error);
      }
    }
  }


  // 新增:手动关闭WebSocket连接方法
  private closeConnection(code: number = 1000, reason: string = "Normal closure"): void {


    try {
      // 发送静默下线通知(不触发重连)
      const silentClose = code === 1000 && reason.includes("background");
      if (!silentClose) {
        this.publishServerOffline();
      }


      if (this.client?.connected) {
        // 发送下线通知
        this.publishServerOffline();


        // 取消订阅
        if (this.config.userId && this.config.familyId) {
          const userTopic = `mquser-to-${this.config.userId}`;
          const familyTopic = `mquser-to-${this.config.familyId}`;


          this.client.unsubscribe([userTopic, familyTopic], {}, () => {
            this.cleanup();
          });
        } else {
          this.cleanup();
        }
      } else {
        this.cleanup();
      }
    } catch (error) {
      console.error('[MQTT][CloseConnection] Error closing connection:', error);
      this.cleanup();
    }
  }


  // 新增:处理队列中的消息
  private processQueuedMessages(): void {


    if (this.messageQueue.length === 0) return;


    if (this.isConnected()) {
      // 处理队列中的所有消息
      const messages = [...this.messageQueue];
      this.messageQueue = [];


      messages.forEach(msg => {
        try {
          this.publishMessage(msg.topic, msg.type, msg.data);
        } catch (error) {
          console.error('[MQTT][ProcessQueue] Failed to publish queued message:', error);
        }
      });
    } else {
      console.warn('[MQTT][ProcessQueue] Client not connected, messages remain queued');
    }
  }


  // 初始化设备状态
  private initDeviceStatus(): void {
    try {
      const storedStatuses = wx.getStorageSync('deviceStatuses');
      if (storedStatuses) {
        this.deviceStatuses = new Map(JSON.parse(storedStatuses));
        console.log('[MQTT][InitDeviceStatus] Loaded stored statuses:',
          Array.from(this.deviceStatuses.entries()));
      }
    } catch (error) {
      console.error('[MQTT][InitDeviceStatus] Error loading stored statuses:', error);
    }
  }
  // 保存设备状态
  private saveDeviceStatuses(): void {
    try {
      const statusesArray = Array.from(this.deviceStatuses.entries());
      wx.setStorageSync('deviceStatuses', JSON.stringify(statusesArray));
    } catch (error) {
      console.error('[MQTT][SaveDeviceStatuses] Error saving statuses:', error);
    }
  }


  // 连接状态检查
  public isConnected(): boolean {
    return this.client?.connected || false;
  }


  // 连接方法,修改为返回 Promise
  public connect(): Promise<void> {
    // 如果已有连接进程在进行,返回现有 Promise
    // 增加状态检查
    if (this.isConnected()) {
      return Promise.resolve();
    }
    // 增加网络状态检查
    wx.getNetworkType({
      success: res => {
        if (res.networkType === 'none') {
          throw new Error('No network connection');
        }
      }
    });
    if (this._connectingPromise) {
      return this._connectingPromise;
    }


    if (this.isConnecting) {
      this._connectingPromise = new Promise((resolve, reject) => {
        const checkInterval = setInterval(() => {
          if (this.isConnected()) {
            clearInterval(checkInterval);
            resolve();
          }
        }, 500);


        // 设置超时
        setTimeout(() => {
          clearInterval(checkInterval);
          reject(new Error('Connection timeout while waiting for existing connection'));
        }, 2000);
      });
      return this._connectingPromise;
    }


    // 创建新的连接 Promise
    this._connectingPromise = new Promise((resolve, reject) => {
      try {
        this.isConnecting = true;


        // 清理现有连接
        if (this.client) {
          this.cleanup();
        }


        const clientId = `mquser-xcx-cid-${this.config.userId}-${Date.now()}`;
        const willMessage: MqttMessage = {
          id: Math.random().toString(16).substr(2, 16),
          code: 0,
          msg: "success",
          type: "server/offline",
          ts: Date.now(),
          expired: Date.now() + 86400000,
          data: {
            userId: this.config.userId,
            platform: "wechat"
          }
        };


        const willTopic = "mqsvr-to-iot-door-user";
        const encryptedWillMessage = Encrypt(JSON.stringify(willMessage));


        const options: IClientOptions = {
          clientId: clientId,
          username: "xcxtest",
          password: "xcxtest",
          reconnectPeriod: 1000,
          connectTimeout: 1000,
          protocol: 'wxs',
          protocolVersion: 4,
          clean: true,
          rejectUnauthorized: false,
          keepalive: 10,
          path: '/mqtt',
          will: {
            topic: willTopic,
            payload: encryptedWillMessage,
            qos: 1,
            retain: false
          }
        };


        // 设置连接超时处理
        this.connectionTimer = setTimeout(() => {
          if (this.isConnecting) {
            this.cleanup();
            this.scheduleReconnect();
            this.eventEmitter.emit('connectionTimeout');
            reject(new Error('Connection timeout'));
            this._connectingPromise = null;
          }
        }, options.connectTimeout);


        this.client = mqtt.connect(this.MQTT_URL, options);


        if (!this.client) {
          throw new Error('Failed to create MQTT client instance');
        }


        // 修改事件监听器,添加Promise处理
        this.client.on('connect', () => {
          this.isConnecting = false;
          this.reconnectAttempts = 0;
          if (this.connectionTimer) {
            clearTimeout(this.connectionTimer);
            this.connectionTimer = null;
          }
          this.handleConnect();
          resolve(); // 解决Promise
          this._connectingPromise = null;
        });


        this.setupEventListeners(() => {
          // 在出错时reject Promise
          reject(new Error('Connection failed or was closed'));
          this._connectingPromise = null;
        });



      } catch (error) {
        console.error('[MQTT][Connect] Setup failed:', error);
        this.cleanup();
        this.scheduleReconnect();
        reject(error);
        this._connectingPromise = null;
      }
    });


    return this._connectingPromise;
  }


  // 修改 scheduleReconnect 方法
  private scheduleReconnect(): void {
    if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
      this.eventEmitter.emit('maxReconnectAttemptsReached');
      this.showReconnectFailedToast();
      return;
    }


    // 使用固定延迟,不再使用指数退避策略
    const delay = this.RECONNECT_DELAY;



    this.reconnectTimer = setTimeout(() => {
      this.connect().catch(console.error);
    }, delay);


    this.reconnectAttempts++;
  }


  // 新增重连失败提示
  private showReconnectFailedToast(): void {
    wx.showToast({
      title: '连接断开,请检查网络',
      icon: 'none',
      duration: 2000
    });
  }


  private cleanup(): void {
    this.reconnectAttempts = 0; // 新增
    // 清除所有定时器
    [this.connectionTimer, this.reconnectTimer].forEach(timer => {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
    });


    // 强制关闭连接
    if (this.client) {
      try {
        this.client.removeAllListeners();
        this.client.end(true);
      } catch (error) {
        console.error('[MQTT][Cleanup] Error during cleanup:', error);
      }
      this.client = null;
    }


    // 重置状态
    this.isConnecting = false;
    this._connectingPromise = null;
    this.reconnectAttempts = 0; // 重置重试次数
  }


  private setupEventListeners(onError?: () => void): void {
    if (!this.client) {
      if (onError) onError();
      return;
    }


    this.client.on('message', (topic: string, payload: string | ArrayBuffer) => {
      this.handleMessage(topic, payload);
    });


    this.client.on('error', (error: Error) => {
      this.handleError(error);
      if (onError) onError();
    });


    this.client.on('offline', () => {
      this.handleOffline();
    });


    this.client.on('close', () => {
      this.scheduleReconnect();
      if (onError) onError();
    });


    this.client.on('reconnect', () => {
      this.reconnectAttempts++;



      if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
        console.error('[MQTT][Event:Reconnect] Max reconnection attempts reached');
        this.cleanup();
        this.eventEmitter.emit('maxReconnectAttemptsReached');
        if (onError) onError();
      }
    });
  }


  private handleConnect(): void {
    if (!this.client) {
      console.error('[MQTT][HandleConnect] Cannot handle connect - client is null');
      return;
    }


    const userTopic = `mquser-to-${this.config.userId}`;
    const familyTopic = `mquser-to-${this.config.familyId}`;




    this.client.subscribe([userTopic, familyTopic], { qos: 1 }, (err, granted) => {
      if (err) {
        console.error('[MQTT][HandleConnect] Subscribe error:', err);
        return;
      }
      console.log('[MQTT][HandleConnect] Subscribe success:', granted);
      this.publishUserOnline();
    });
  }


  private decodeMessage(payload: string | ArrayBuffer): string {
    if (typeof payload === 'string') {
      return payload;
    }
    return typeof TextDecoder !== 'undefined'
      ? new TextDecoder().decode(payload)
      : this.arrayBufferToString(payload);
  }


  private arrayBufferToString(buffer: ArrayBuffer): string {
    const uint8Array = new Uint8Array(buffer);
    let result = '';
    for (let i = 0; i < uint8Array.length; i++) {
      result += String.fromCharCode(uint8Array[i]);
    }
    return result;
  }
  private handleUserJoinHouse(message: MqttMessage): void {
    try {
      wx.setStorageSync('has_new_house', true);
    } catch (e) {
      console.error('[MQTT] Failed to set refresh flag:', e);
    }


    // 仍然触发事件以保持兼容性
    this.eventEmitter.emit('userJoinHouse');
  }
  private handleMessage(topic: string, payload: string | ArrayBuffer): void {
    try {
      console.log('[MQTT][HandleMessage] 收到消息 - 主题:', topic);
      const message = this.decodeMessage(payload);
      const parsedMessage = JSON.parse(Decrypt(message)) as MqttMessage;
      console.log('[MQTT][HandleMessage] 解密消息 - 类型:', parsedMessage.type, 'ID:', parsedMessage.id);


      switch (parsedMessage.type) {
        case 'family/user_online':
          console.log('[MQTT][HandleMessage] 处理家庭用户上线消息');
          this.handleFamilyUserOnline(parsedMessage);
          break;
        case 'server/offline':
          console.log('[MQTT][HandleMessage] 处理服务器离线消息');
          this.handleServerOffline(parsedMessage);
          break;
        case 'user/door_status':
          console.log('[MQTT][HandleMessage] 处理用户门锁状态消息');
          this.handleUserDoorStatus(parsedMessage);
          break;
        case 'family/door_status':
          console.log('[MQTT][HandleMessage] 处理家庭门锁状态消息');
          this.handleDoorStatus(parsedMessage);
          break;
        case 'user/online_status':
          console.log('[MQTT][HandleMessage] 处理用户在线状态消息');
          this.handleUserOnlineStatus(parsedMessage);
          break;
        case 'user/add_key_success':
          console.log('[MQTT][HandleMessage] 处理添加密钥成功消息');
          this.handleAddKeySuccess(parsedMessage);
          break;
        case 'door/lock_status':
          console.log('[MQTT][HandleMessage] 处理门锁状态消息');
          this.handleDoorLockStatus(parsedMessage);
          break;
        case 'door/open_door_result':
          console.log('[MQTT][HandleMessage] 处理开门结果消息');
          this.handleOpenDoorResult(parsedMessage);
          break;
        case 'door/delete_key_result':
          console.log('[MQTT][HandleMessage] 处理删除密钥结果消息');
          this.handleDeleteKeyResult(parsedMessage);
          break;
        case 'door/add_key_result':
          console.log('[MQTT][HandleMessage] 处理添加密钥结果消息');
          this.handleAddKeyResult(parsedMessage);
          break;
        case 'door/wallpaper_result':
          console.log('[MQTT][HandleMessage] 处理壁纸结果消息');
          this.handleWallpaperResult(parsedMessage);
          break;
        case 'user/reset_device':
          console.log('[MQTT][HandleMessage] 处理重置设备消息');
          this.handleResetDevice(parsedMessage);
          break;
        case 'user/join_house':
          console.log('[MQTT][HandleMessage] 处理加入房屋消息');
          this.handleUserJoinHouse(parsedMessage);
          break;
        case 'user/leave_house':
          console.log('[MQTT][HandleMessage] 处理离开房屋消息');
          this.handleUserLeaveHouse(parsedMessage);
          break;
        case 'user/house_user_role':
          console.log('[MQTT][HandleMessage] 处理房屋用户角色消息');
          this.handleHouseUserRole(parsedMessage);
          break;
        default:
          console.log('[MQTT][HandleMessage] 未知消息类型:', parsedMessage.type);
          break;
      }
    } catch (error) {
      console.error('[MQTT][HandleMessage] Processing error:', error);
    }
  }
  private handleHouseUserRole(message: MqttMessage): void {
    const { houseId } = message.data;
    if (!houseId) return;


    // 触发权限变更事件,传递房屋ID
    this.eventEmitter.emit('houseUserRole', { houseId });
  }
  private handleUserLeaveHouse(message: MqttMessage): void {
    // 触发事件,由页面处理刷新逻辑
    this.eventEmitter.emit('userLeaveHouse');
  }
  private handleResetDevice(message: MqttMessage): void {
    const { serialNo, opTime } = message.data;
    if (!serialNo) return;


    this.eventEmitter.emit('deviceReset', {
      serialNo,
      opTime
    });


    Array.from(this.deviceStatuses.keys()).forEach(deviceCode => {
      this.deviceStatuses.delete(deviceCode);
    });
    this.saveDeviceStatuses();
  }


  private handleFamilyUserOnline(message: MqttMessage): void {
    const { userId } = message.data;
    if (!userId) return;
    this.eventEmitter.emit('familyUserOnline', { userId });
  }
  private handleUserDoorStatus(message: MqttMessage): void {
    const { deviceCode, online, lockStatus } = message.data;
    if (!deviceCode) return;


    this.updateDeviceStatus(deviceCode, {
      online: online ?? true,
      lockStatus
    });


    this.eventEmitter.emit('doorLockStatusChanged', {
      deviceCode,
      online,
      lockStatus
    });
  }


  private handleServerOffline(message: MqttMessage): void {
    const { userId, platform } = message.data;
    if (!userId || !platform) return;
    this.eventEmitter.emit('serverOffline', { userId, platform });
  }


  private handleUserOnlineStatus(message: MqttMessage): void {
    const { deviceCode, online } = message.data;
    if (!deviceCode || online === undefined) return;
    this.updateDeviceStatus(deviceCode, { online });
  }


  private handleDoorStatus(message: MqttMessage): void {
    const { deviceCode, online, lockStatus } = message.data;
    if (!deviceCode) return;
    this.updateDeviceStatus(deviceCode, {
      online,
      lockStatus
    });


    if (online) {
      this.publishDoorOnlineStatus(deviceCode);
    }
  }


  private handleDoorLockStatus(message: MqttMessage): void {
    const { deviceCode, lockStatus } = message.data;
    console.log('[MQTT][HandleDoorLockStatus] 收到门锁状态更新 - 设备码:', deviceCode, '状态:', lockStatus);


    if (!deviceCode || lockStatus === undefined) {
      console.warn('[MQTT][HandleDoorLockStatus] 收到无效的门锁状态消息');
      return;
    }


    // 更新设备状态并触发事件
    this.updateDeviceStatus(deviceCode, { lockStatus });
    console.log('[MQTT][HandleDoorLockStatus] 更新设备状态并触发事件');
    this.eventEmitter.emit('doorLockStatusChanged', {
      deviceCode,
      lockStatus
    });
  }


  private handleAddKeySuccess(message: MqttMessage): void {
    this.eventEmitter.emit('addKeySuccess', message);
  }


  private handleOpenDoorResult(message: MqttMessage): void {
    const { deviceCode, success, errorCode, errorMsg } = message.data;
    console.log('[MQTT][HandleOpenDoorResult] 收到开门结果 - 设备码:', deviceCode,
      '成功:', success,
      '错误码:', errorCode || 'N/A',
      '错误信息:', errorMsg || 'N/A');


    if (!deviceCode || success === undefined) {
      console.warn('[MQTT][HandleOpenDoorResult] 收到无效的开门结果消息');
      return;
    }


    this.eventEmitter.emit('openDoorResult', {
      deviceCode,
      success,
      errorCode,
      errorMsg
    });


    if (!success) {
      // 只有在开门失败时才立即更新状态
      console.log('[MQTT][HandleOpenDoorResult] 开门失败,更新锁状态为已上锁');
      this.updateDeviceStatus(deviceCode, { lockStatus: 2 });
      this.eventEmitter.emit('openDoorFailed', {
        deviceCode,
        errorCode,
        errorMsg
      });
    }
  }


  private handleDeleteKeyResult(message: MqttMessage): void {
    const { deviceCode, success, errorCode, errorMsg } = message.data;
    if (!deviceCode || success === undefined) return;
    this.eventEmitter.emit('deleteKeyResult', {
      deviceCode,
      success,
      errorCode,
      errorMsg
    });
  }


  private handleAddKeyResult(message: MqttMessage): void {
    const { deviceCode, success, errorCode, errorMsg } = message.data;
    if (!deviceCode || success === undefined) return;
    this.eventEmitter.emit('addKeyResult', {
      deviceCode,
      success,
      errorCode,
      errorMsg
    });
  }


  private handleWallpaperResult(message: MqttMessage): void {
    const { deviceCode, success, errorCode, errorMsg } = message.data;
    if (!deviceCode || success === undefined) return;
    this.eventEmitter.emit('wallpaperResult', {
      deviceCode,
      success,
      errorCode,
      errorMsg
    });
  }


  private handleError(error: Error): void {
    this.eventEmitter.emit('error', error);
  }


  private handleOffline(): void {
    this.eventEmitter.emit('offline');
  }


  private updateDeviceStatus(deviceCode: string, updates: Partial<DeviceStatus>): void {
    const currentStatus = this.deviceStatuses.get(deviceCode) || {
      deviceCode,
      online: false,
      lastUpdateTime: Date.now()
    };


    const newStatus = {
      ...currentStatus,
      ...updates,
      lastUpdateTime: Date.now()
    };


    this.deviceStatuses.set(deviceCode, newStatus);
    this.saveDeviceStatuses();


    this.eventEmitter.emit('deviceStatusChanged', newStatus);


    if (updates.online !== undefined) {
      this.eventEmitter.emit('deviceOnlineStatusChanged', {
        deviceCode,
        online: updates.online
      });
    }
  }


  // 公共方法
  public getDeviceStatus(deviceCode: string): DeviceStatus | null {
    return this.deviceStatuses.get(deviceCode) || null;
  }


  public getLockStatusText(status?: number): string {
    switch (status) {
      case 0: return '开锁中';
      case 1: return '已开门';
      case 2: return '已上锁';
      default: return '未知状态';
    }
  }


  public updateDeviceCode(deviceCode: string): void {


    this.config.deviceCode = deviceCode;
  }


  public on(event: string, listener: Function): void {
    this.eventEmitter.on(event, listener);
  }


  public off(event: string, listener: Function): void {
    this.eventEmitter.off(event, listener);
  }


  public removeAllListeners(): void {
    if (this.eventEmitter.removeAllListeners) {
      this.eventEmitter.removeAllListeners();
    }
  }


  // 修改消息发布方法,添加连接状态检查和消息队列
  private publishMessage(topic: string, type: string, data: any): void {
    console.log('[MQTT][PublishMessage] 准备发布消息 - 主题:', topic, '类型:', type, '数据:', JSON.stringify(data));


    if (!this.isConnected()) {
      console.log('[MQTT][PublishMessage] MQTT未连接,将消息加入队列');


      // 将消息添加到队列
      this.messageQueue.push({ topic, type, data });


      // 如果没有连接,尝试重新连接
      if (!this.isConnecting && !this._connectingPromise) {
        console.log('[MQTT][PublishMessage] 尝试重新连接');
        this.showReconnectModal();
        this.connect().then(() => {
          this.hideReconnectModal();
          console.log('[MQTT][PublishMessage] 重连成功,处理队列消息');
          this.processQueuedMessages();
        }).catch(error => {
          console.error('[MQTT][PublishMessage] Failed to reconnect:', error);
          this.hideReconnectModal();
          // 显示发送失败提示
          wx.showToast({
            title: '消息发送失败,请重试',
            icon: 'none',
            duration: 2000
          });
        });
      }
      return;
    }


    if (!this.client) {
      console.error('[MQTT][PublishMessage] 客户端为空,无法发送消息');
      throw new Error('Cannot publish - client is null');
    }


    try {
      const message: MqttMessage = {
        id: Math.random().toString(16).substr(2, 16),
        code: 0,
        msg: "success",
        type,
        ts: Date.now(),
        expired: Date.now() + 86400000,
        data
      };


      console.log('[MQTT][PublishMessage] 发送消息ID:', message.id);
      const messageString = JSON.stringify(message);
      const encryptedMessage = Encrypt(messageString);


      this.client.publish(topic, encryptedMessage);
      console.log('[MQTT][PublishMessage] 消息发送成功 - 主题:', topic);
    } catch (error) {
      console.error('[MQTT][PublishMessage] Failed to publish:', error);
      throw error;
    }
  }


  public publishUserOnline(): void {
    const message = {
      userId: this.config.userId,
      platform: "wechat"
    };
    this.publishMessage(`mqdev-to-${this.config.familyId}`, 'family/user_online', message);
  }


  // 修改后的 publishServerOffline 方法
  public publishServerOffline(): void {


    try {
      // 检查客户端连接状态
      if (!this.client || !this.client.connected) {
        return;
      }


      const message = {
        userId: this.config.userId,
        platform: "wechat"
      };


      // 直接使用 MQTT 客户端发送消息,确保消息能立即发送
      try {
        const mqttMessage: MqttMessage = {
          id: Math.random().toString(16).substr(2, 16),
          code: 0,
          msg: "success",
          type: "server/offline",
          ts: Date.now(),
          expired: Date.now() + 86400000,
          data: message
        };


        const messageString = JSON.stringify(mqttMessage);
        const encryptedMessage = Encrypt(messageString);


        // 修改:使用新的 topic
        const topic = "mqsvr-to-iot-door-user";


        // 修正:只传递两个参数 - 主题和消息内容
        this.client.publish(topic, encryptedMessage);
      } catch (error) {
        console.error('[MQTT][PublishServerOffline] 直接发送消息失败:', error);


        // 如果直接发送失败,尝试使用队列方式发送(也使用新的 topic)
        this.publishMessage("mqsvr-to-iot-door-user", 'server/offline', message);
      }
    } catch (error) {
      console.error('[MQTT][PublishServerOffline] 发送离线消息失败:', error);
    }
  }


  // 修改后的 handleLogout 方法
  public handleLogout(): void {
    try {
      // 先发送离线消息,确保消息发送
      if (this.client && this.client.connected) {
        try {
          // 直接使用 MQTT 客户端发送消息
          const message = {
            userId: this.config.userId,
            platform: "wechat",
            online: false
          };
          const mqttMessage: MqttMessage = {
            id: Math.random().toString(16).substr(2, 16),
            code: 0,
            msg: "success",
            type: "server/offline",
            ts: Date.now(),
            expired: Date.now() + 86400000,
            data: message
          };
          const messageString = JSON.stringify(mqttMessage);
          const encryptedMessage = Encrypt(messageString);
          // 修改:使用新的 topic
          const topic = "mqsvr-to-iot-door-user";


          // 修正:只传递两个参数 - 主题和消息内容
          this.client.publish(topic, encryptedMessage);
        } catch (error) {
        }
        // 延迟一点时间确保消息能发送出去
        setTimeout(() => {
          this.cleanup();
          globalClient = null;
        }, 300);
      } else {
        this.cleanup();
        globalClient = null;
      }
      // 清理设备状态和消息队列
      this.deviceStatuses.clear();
      this.saveDeviceStatuses();
      this.messageQueue = [];
    } catch (error) {
      this.cleanup();
      globalClient = null;
    }
  }



  public publishDoorOnlineStatus(deviceCode: string): void {
    const message = {
      deviceCode: deviceCode,
      online: true
    };
    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/online_status', message);
  }


  // 修改开门方法,添加连接状态检查
  public publishOpenDoor(deviceCode: string): void {
    console.log('[MQTT][PublishOpenDoor] 准备发送开门指令 - 设备码:', deviceCode);


    if (!this.isConnected()) {
      console.log('[MQTT][PublishOpenDoor] MQTT未连接,取消操作');
      wx.showToast({
        title: '正在连接,请稍后重试',
        icon: 'none',
        duration: 2000
      });
      return;
    }


    const message = {
      userId: this.config.userId
    };
    console.log('[MQTT][PublishOpenDoor] 发送开门消息 - 用户ID:', this.config.userId);
    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/open_door', message);
  }


  public publishAddKey(deviceCode: string, keyType: string, password?: string, hijack: string = 'false'): void {
    const data: MqttMessage['data'] = {
      userId: this.config.userId,
      keyType,
      hijack
    };


    if (password) {
      data.password = password;
    }


    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/start_add_key', data);
  }


  public publishDeleteKey(deviceCode: string, key: any): void {
    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/delete_key', key);
  }


  public publishResetDevice(deviceCode: string): void {
    const message = {
      deviceCode,
      userId: this.config.userId
    };
    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/reset_device', message);
  }


  public publishAddHouseUser(deviceCode: string): void {
    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/house_user_add', {});
  }


  public publishUpdateWallpaper(deviceCode: string, imgUrl: string): void {
    const message = {
      imgUrl
    };
    this.publishMessage(`mqdev-to-${deviceCode}`, 'door/wallpaper', message);
  }



}
export default SmartDoorMQTTClient;
最后一次编辑于  03-04
回答关注问题邀请回答
收藏

1 个回答

  • 陈宇明
    陈宇明
    02-26

    这个代码片段格式没有人能看得懂呀

    02-26
    有用
    回复 1
    • Bliss
      Bliss
      03-04
      您好 格式改好啦
      03-04
      回复
登录 后发表内容