收藏
回答

手动授权设备,不生效

框架类型 问题类型 API/组件名称 终端类型 微信版本 基础库版本
小程序 Bug wmpf-voip 微信安卓客户端 8.0.61 2.19.4

https://developers.weixin.qq.com/miniprogram/dev/framework/device/voip/auth.html,已经手动授权设备权限了,重新授权还是提示错误:denied

device-manage.js:911 错误信息: 该设备授权已被拒绝,需要手动开启权限

device-manage.js:912 设备信息: {id: 1, deviceId: "DEV001", deviceSn: "1747101539289666", name: "A栋门禁对讲系统", type: "INTERCOM", …}

/** * 检查用户VoIP设备授权状态 * @returns {Promise<boolean>} 返回授权状态 */ async checkVoipDeviceAuthStatus() { return new Promise((resolve) => { wx.getSetting({ success: (res) => { console.log('用户授权状态:', res.authSetting); // 检查是否有VoIP相关的授权状态 // 注意:微信可能没有单独的VoIP设备授权scope,这里先检查相关权限 const hasRecordAuth = res.authSetting['scope.record'] !== false; resolve(hasRecordAuth); }, fail: () => { console.error('获取授权状态失败'); resolve(false); } }); }); } /** * 引导用户到设置页面开启VoIP授权 * @param {string} deviceName - 设备名称 * @returns {Promise<boolean>} 用户是否打开了设置页面 */ async guideUserToSettings(deviceName = '设备', deviceSn = null) { console.log('=== 开始引导用户到设置页面 ==='); console.log('设备名称:', deviceName); console.log('设备序列号:', deviceSn); return new Promise((resolve) => { console.log('显示授权引导弹窗'); wx.showModal({ title: '授权被取消', content: `您取消了对"${deviceName}"的VoIP授权。\n\n为了正常使用通话功能,需要授权微信访问该设备。\n\n是否重新进行授权?`, confirmText: '重新授权', confirmColor: '#007aff', cancelText: '暂不授权', cancelColor: '#999999', success: (res) => { console.log('用户选择结果:', res.confirm ? '重新授权' : '暂不授权'); if (res.confirm) { console.log('用户选择重新授权'); resolve(true); } else { console.log('用户选择暂不授权,显示授权说明'); // 显示授权说明 this.showAuthCancelNotice(deviceName); resolve(false); } }, fail: (err) => { console.error('显示授权引导弹窗失败:', err); resolve(false); } }); }); } /** * 显示授权取消说明 * @param {string} deviceName - 设备名称 */ showAuthCancelNotice(deviceName) { wx.showModal({ title: '授权说明', content: `未授权"${deviceName}"的VoIP权限。\n\n• 您仍可以在设备列表中查看该设备\n• 但无法进行音视频通话\n• 如需使用通话功能,请点击"授权"按钮重新授权`, confirmText: '知道了', showCancel: false }); } /** * 显示详细的授权指南 * @param {string} deviceName - 设备名称 * @param {string} deviceSn - 设备序列号 */ showDetailedAuthGuide(deviceName, deviceSn) { console.log('显示详细的授权指南:', { deviceName, deviceSn }); const content = `VoIP通话权限设置步骤:\n\n` + `1. 点击"重新设置"按钮\n` + `2. 在弹出的设置页面中,找到"${deviceName}"\n` + `3. 确保右侧的开关是"开启"状态\n` + `4. 返回小程序,点击"重新授权"\n\n` + `注意:\n` + `• 如果设置页面中没有找到该设备,请重新启动小程序\n` + `• 权限设置后可能需要等待片刻才能生效\n` + `• 如果仍然无法授权,请尝试重新启动微信`; wx.showModal({ title: '授权设置指南', content: content, confirmText: '重新设置', confirmColor: '#007aff', cancelText: '稍后再说', success: (res) => { if (res.confirm) { console.log('用户选择重新设置,再次打开设置页面'); // 用户选择重新设置,再次打开设置页面 this.openSettingsForAuth(deviceName, deviceSn); } else { console.log('用户选择稍后再说'); } } }); } /** * 打开设置页面进行授权 * @param {string} deviceName - 设备名称 * @param {string} deviceSn - 设备序列号 */ openSettingsForAuth(deviceName, deviceSn) { console.log('打开设置页面进行授权:', { deviceName, deviceSn }); wx.openSetting({ success: async (settingRes) => { console.log('设置页面返回结果:', settingRes.authSetting); // 延迟一段时间再检查权限,给微信时间更新权限状态 setTimeout(async () => { try { console.log('延迟后检查权限状态...'); const voipList = await this.getDeviceVoIPList(); console.log('权限检查结果:', voipList); if (voipList.list && voipList.list.length > 0) { const deviceAuth = voipList.list.find(item => item.sn === deviceSn); const isAuthorized = deviceAuth && deviceAuth.status === 1; console.log('延迟检查结果:', { deviceAuth, isAuthorized }); if (isAuthorized) { wx.showToast({ title: '权限设置成功', icon: 'success' }); } else { wx.showModal({ title: '权限检查', content: `${isAuthorized ? '检测到' : '未检测到'}权限开启。\n\n如果您已经开启了权限,请点击"重新授权"继续。`, confirmText: '重新授权', confirmColor: '#007aff', cancelText: '稍后再说', success: (retryRes) => { // 这里只是提示,实际的重新授权还是需要回到原来的流程 console.log('权限检查后用户选择:', retryRes.confirm ? '重新授权' : '稍后再说'); } }); } } } catch (error) { console.error('延迟检查权限失败:', error); wx.showToast({ title: '权限检查失败', icon: 'none' }); } }, 1000); // 延迟1秒 }, fail: (err) => { console.error('打开设置页面失败:', err); wx.showToast({ title: '打开设置失败', icon: 'none' }); } }); } /** * 处理用户拒绝授权的情况(已拒绝且无法再次弹窗) * @param {string} deviceName - 设备名称 * @param {string} deviceSn - 设备序列号(可选,用于检查授权状态) * @returns {Promise<boolean>} 用户是否打开了设置页面 */ async handleUserDeniedAuth(deviceName = '设备', deviceSn = null) { console.log('=== 处理用户拒绝授权(需要去设置页面) ==='); console.log('设备名称:', deviceName); console.log('设备序列号:', deviceSn); return new Promise((resolve) => { wx.showModal({ title: '需要手动开启权限', content: `您之前拒绝了对"${deviceName}"的VoIP通话权限。\n\n请点击"去设置",在打开的设置页面中找到"${deviceName}",并开启右侧的开关。`, confirmText: '去设置', confirmColor: '#007aff', cancelText: '取消', success: (res) => { console.log('用户选择结果:', res.confirm ? '去设置' : '取消'); if (res.confirm) { console.log('用户点击"去设置",准备打开设置页面'); // 用户点击"去设置",打开设置页面 wx.openSetting({ success: async (settingRes) => { console.log('设置页面打开成功,返回结果:', settingRes.authSetting); // 用户从设置页面返回,检查VoIP设备权限状态 try { console.log('检查VoIP设备权限状态...'); console.log('目标设备信息:', { deviceSn, deviceName }); // 使用wx.getDeviceVoIPList检查实际的授权状态 const voipList = await this.getDeviceVoIPList(); console.log('VoIP设备权限列表完整结果:', voipList); console.log('VoIP设备权限列表数组:', voipList.list); let isDeviceAuthorized = false; let deviceAuth = null; // 检查指定设备是否已授权 if (deviceSn && voipList.list && voipList.list.length > 0) { console.log('开始查找设备授权状态,目标设备序列号:', deviceSn); // 详细打印每个设备的信息 voipList.list.forEach((item, index) => { console.log(`设备${index + 1}:`, { sn: item.sn, model_id: item.model_id, status: item.status, statusText: item.status === 1 ? '已授权' : '未授权' }); }); deviceAuth = voipList.list.find(item => item.sn === deviceSn); isDeviceAuthorized = deviceAuth && deviceAuth.status === 1; console.log('设备授权状态检查结果:', { deviceSn, isDeviceAuthorized, deviceAuth, foundDevice: !!deviceAuth, deviceAuthStatus: deviceAuth ? deviceAuth.status : 'not found' }); } else { console.log('设备权限检查条件不满足:', { hasDeviceSn: !!deviceSn, hasVoipList: !!voipList.list, voipListLength: voipList.list ? voipList.list.length : 0 }); } if (isDeviceAuthorized) { // 设备已授权,直接返回true,让调用方重新授权 console.log('设备已授权,直接重新授权'); wx.showModal({ title: '权限设置完成', content: `检测到您已开启"${deviceName}"的VoIP通话权限。\n\n点击"重新授权"继续。`, confirmText: '重新授权', confirmColor: '#007aff', cancelText: '稍后再说', success: (retryRes) => { console.log('用户选择结果:', retryRes.confirm ? '重新授权' : '稍后再说'); resolve(retryRes.confirm); } }); } else { // 设备未授权,提供更详细的操作指导 console.log('设备未授权,提供详细的操作指导'); // 构建更详细的说明内容 let content = `您已完成权限设置。\n\n`; if (deviceAuth) { // 找到了设备但状态为未授权 content += `检测到"${deviceName}"权限状态为:${deviceAuth.status === 1 ? '已授权' : '未授权'}。\n\n`; content += `如果您已在设置页面开启了权限,可能需要等待片刻让权限生效,或者重新启动小程序。\n\n`; } else { // 没有找到设备 content += `未检测到"${deviceName}"的权限记录。\n\n`; content += `请确保您在设置页面中找到了"${deviceName}"并开启了右侧的开关。\n\n`; } content += `点击"重新授权"继续,或者"查看说明"了解详细操作步骤。`; wx.showModal({ title: '权限设置完成', content: content, confirmText: '重新授权', confirmColor: '#007aff', cancelText: '查看说明', success: (retryRes) => { console.log('用户选择结果:', retryRes.confirm ? '重新授权' : '查看说明'); if (retryRes.confirm) { // 用户选择重新授权 resolve(true); } else { // 用户选择查看说明 this.showDetailedAuthGuide(deviceName, deviceSn); resolve(false); } } }); } } catch (error) { console.error('检查VoIP设备权限状态失败:', error); // 检查失败,询问用户是否重新尝试 wx.showModal({ title: '权限设置完成', content: `您已完成权限设置。\n\n如果您已在设置页面开启了"${deviceName}"的VoIP通话权限,请点击"重新授权"继续。`, confirmText: '重新授权', confirmColor: '#007aff', cancelText: '稍后再说', success: (retryRes) => { console.log('用户选择结果:', retryRes.confirm ? '重新授权' : '稍后再说'); resolve(retryRes.confirm); } }); } }, fail: (err) => { console.error('打开设置页面失败:', err); wx.showToast({ title: '打开设置失败', icon: 'none' }); resolve(false); } }); } else { console.log('用户选择取消,不去设置页面'); resolve(false); } }, fail: (err) => { console.error('显示授权引导弹窗失败:', err); resolve(false); } }); }); } /** * 请求设备VoIP权限(支持完整设备信息) * @param {Object|string} deviceInfo - 设备信息对象或设备序列号 * @param {string} snTicket - 设备票据(可选) * @param {Object} options - 额外参数(可选) * @returns {Promise} 返回授权结果 */ async requestDeviceVoIP(deviceInfo, snTicket = null, options = {}) { return new Promise((resolve, reject) => { console.log('=== 开始VoIP设备授权 ==='); // 兼容性处理:支持传入字符串(deviceSn)或对象 let device = {}; if (typeof deviceInfo === 'string') { // 向后兼容:如果传入的是字符串,认为是deviceSn device = { deviceSn: deviceInfo, modelId: options.modelId || 'default_model', deviceName: options.deviceName || '未知设备' }; } else if (typeof deviceInfo === 'object' && deviceInfo !== null) { // 推荐方式:传入完整的设备信息对象 device = { deviceSn: deviceInfo.deviceSn, modelId: deviceInfo.modelId || deviceInfo.model_id || 'default_model', deviceName: deviceInfo.deviceName || deviceInfo.name || deviceInfo.device_name || '未知设备' }; } else { const error = new Error('设备信息参数格式错误'); console.error('参数错误:', error.message); reject(error); return; } console.log('设备信息:', device); console.log('设备票据:', snTicket ? 'snTicket已提供' : '无snTicket'); // 检查微信版本是否支持VoIP功能 if (!wx.canIUse('requestDeviceVoIP')) { const error = new Error('当前微信版本不支持VoIP功能,请升级微信到最新版本'); console.error('VoIP功能不支持:', error.message); reject(error); return; } // 检查必需参数 if (!device.deviceSn) { const error = new Error('设备序列号不能为空'); console.error('参数错误:', error.message); reject(error); return; } // 构建请求参数(按照微信官方文档要求) const requestParams = { sn: device.deviceSn, modelId: device.modelId, deviceName: device.deviceName }; // 如果提供了snTicket,则添加到请求参数中 if (snTicket) { requestParams.snTicket = snTicket; } console.log('wx.requestDeviceVoIP 请求参数:', requestParams); wx.requestDeviceVoIP({ ...requestParams, success: (res) => { console.log('=== VoIP设备授权成功 ==='); console.log('授权响应:', res); resolve(res); }, fail: (err) => { console.error('=== VoIP设备授权失败 ==='); console.error('错误信息:', err); // 根据错误码和错误信息提供更详细的错误信息 let errorMessage = '设备授权失败'; let errorType = 'error'; // 错误类型:error, cancel, denied, network, parameter // 优先检查具体的错误消息内容 if (err.errMsg) { // 优先检查拒绝相关错误(这些是最常见的) if (err.errMsg.includes('The sn has been subscribed') || err.errMsg.includes('The sn has been rejected') || err.errMsg.includes('auth deny') || err.errMsg.includes('permission denied') || err.errMsg.includes('unauthorized') || err.errMsg.includes('已拒绝') || err.errMsg.includes('deny') || err.errMsg.includes('rejected')) { // 设备已被订阅/拒绝或用户之前拒绝过授权,现在再次调用时直接被拒绝 errorMessage = '该设备授权已被拒绝,需要手动开启权限'; errorType = 'denied'; } else if (err.errMsg.includes('user cancel operation') || err.errMsg.includes('cancel')) { // 用户主动取消授权 errorMessage = '用户取消授权'; errorType = 'cancel'; } else if (err.errMsg.includes('network') || err.errMsg.includes('timeout')) { // 网络相关错误 errorMessage = '网络错误,请检查网络连接'; errorType = 'network'; } else if (err.errMsg.includes('modelId') || err.errMsg.includes('deviceName') || (err.errMsg.includes('sn') && !err.errMsg.includes('rejected') && !err.errMsg.includes('subscribed')) || err.errMsg.includes('parameter') || err.errMsg.includes('invalid')) { // 参数错误(但排除包含rejected/subscribed的sn错误) errorType = 'parameter'; if (err.errMsg.includes('modelId')) { errorMessage = '设备型号ID参数错误,请检查设备信息'; } else if (err.errMsg.includes('deviceName')) { errorMessage = '设备名称参数错误,请检查设备信息'; } else if (err.errMsg.includes('sn') && !err.errMsg.includes('rejected') && !err.errMsg.includes('subscribed')) { errorMessage = '设备序列号参数错误,请检查设备信息'; } else { errorMessage = '设备参数错误:' + err.errMsg; } } } // 如果上面的错误消息检查没有匹配到,再检查错误码 if (errorType === 'error' && err.errCode) { switch (err.errCode) { case -1: errorMessage = '系统错误,请稍后重试'; errorType = 'error'; break; case 10001: // 根据错误消息进一步判断 if (err.errMsg && err.errMsg.includes('The sn has been subscribed')) { errorMessage = '该设备授权已被拒绝,需要手动开启权限'; errorType = 'denied'; } else { errorMessage = '用户取消授权'; errorType = 'cancel'; } break; case 10002: errorMessage = '网络错误,请检查网络连接'; errorType = 'network'; break; case 10003: errorMessage = '设备票据无效或已过期'; errorType = 'parameter'; break; case 10004: errorMessage = '设备序列号无效'; errorType = 'parameter'; break; case 10021: // 用户已拒绝该设备的授权,微信系统记录了拒绝状态 errorMessage = '该设备授权已被拒绝,需要手动开启权限'; errorType = 'denied'; break; default: errorMessage = err.errMsg || '设备授权失败'; errorType = 'error'; } } // 如果仍然是默认错误类型,使用错误消息作为默认值 if (errorType === 'error' && err.errMsg) { errorMessage = err.errMsg; } console.log('错误分类结果:', { errorType, errorMessage, originalError: err }); // 创建包含错误类型的VoIP授权错误对象 const authError = this.createVoipAuthError(errorMessage, errorType, err); reject(authError); } }); }); }
回答关注问题邀请回答
收藏

1 个回答

  • Joker_Woov
    Joker_Woov
    2025-07-07

    反复卡在手动授权和授权设备之间

    2025-07-07
    有用
    回复
登录 后发表内容