- 小程序用户隐私保护指引审核因用户信息授权描述不明确、不清晰,审核很多次不通过,是哪里有问题呢?怎么改
[图片]你好,你的小程序“双龙护家”用户隐私保护指引审核因用户信息授权描述不明确、不清晰,本次审核不通过,建议修改后重新提交。 平台提示:接口用途内容说明需要填写合理使用内容,避免无关内容,小程序隐私保护指引内容(含补充文件内容)审核通过后将公开展示在《用户隐私保护指引》内容页面。 点击查看隐私保护指引内容介绍
06-29 - 清除缓存后关闭微信开发者工具,再导入项目,第一次启动的时候编译失败,重新进入小程序才可以?
原生小程序 TS :清除缓存后关闭微信开发者工具,再导入项目,第一次启动的时候编译失败,要用户重新进入小程序才行,这是机制还是bug?冷启动:如果用户首次打开,或小程序销毁后被用户再次打开,此时小程序需要重新加载启动,即冷启动。 // app.ts import { StorageService } from './utils/storage'; import SmartDoorMQTTClient from './utils/mqtt-client'; App<IAppOption>({ globalData: { userInfo: undefined, isLoggedIn: false, __pageNavigating: false // 导航标记 }, onLaunch() { // 初始化登录状态 const isLoggedIn = StorageService.isLoggedIn(); this.globalData.isLoggedIn = isLoggedIn; // 获取用户信息 if (isLoggedIn) { const userProfile = StorageService.getUserProfile(); if (userProfile) { this.globalData.userInfo = userProfile; } } else { // 未登录,直接跳转到登录页 console.log('用户未登录,跳转到登录页'); wx.redirectTo({ url: '/pages/login/login', fail: function () { // 如果在tabBar页面,则使用reLaunch wx.reLaunch({ url: '/pages/login/login' }); } }); } }, onShow() { // 每次显示时检查登录状态 const isLoggedIn = StorageService.isLoggedIn(); if (this.globalData.isLoggedIn !== isLoggedIn) { this.globalData.isLoggedIn = isLoggedIn; } }, // 添加 onHide 方法处理小程序进入后台的情况 onHide() { // 检查是否登录 if (!this.globalData.isLoggedIn) { return; } try { // 获取 MQTT 客户端实例并发送离线消息 const mqttClient = SmartDoorMQTTClient.getInstance(); if (mqttClient && mqttClient.isConnected()) { mqttClient.publishServerOffline(); } } catch (error) { console.error('[App] 发送离线消息时出错:', error); } } });
03-20 - wx.chooseMedia 选择视频时如何限定时间在15s之内?有什么办法选择视频时限定时间?
wx.chooseMedia 选择视频时如何限定时间在15s之内?有什么办法选择视频时限定时间?
03-19 - 原生小程序 TS 切换到后台,再切回来所有和websocket+ mqtt相关操作都失效
原生小程序 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;
02-23