# Web端呼叫小程序端通话问题修复指南 ## 问题描述 Web端可以呼叫Web端,但无法呼叫小程序端。 ## 原因分析 ### 1. 用户ID格式不一致 - Web端医生: `doctor_{admin_id}` - 小程序端患者: 需要确认格式是否为 `patient_{patient_id}` 或其他格式 ### 2. 小程序端未登录IM - 小程序端必须先登录腾讯云IM才能接收通话邀请 - 需要在小程序启动时初始化 TUICallKit ### 3. 跨平台通话配置 - 需要确保 SDKAppID 一致 - 需要确保 UserSig 生成方式一致 - 需要确保双方都使用相同的通话协议版本 ## 解决方案 ### 步骤1: 确认小程序端用户ID格式 检查小程序端的用户ID生成逻辑,确保与Web端一致。 **后端代码检查点:** ```php // server/app/adminapi/logic/tcm/DiagnosisLogic.php // 查找 createPatientTrtcAccount 方法 // 确认患者的 userId 格式 ``` **预期格式:** - 医生: `doctor_{admin_id}` - 患者: `patient_{patient_id}` ### 步骤2: 检查小程序端是否正确初始化 小程序端需要在 `app.js` 或页面 `onLoad` 时初始化 TUICallKit: ```javascript // 小程序端代码示例 import { TUICallKit } from '@tencentcloud/call-uikit-wechat' // 初始化 await TUICallKit.init({ SDKAppID: YOUR_SDKAPPID, userID: 'patient_123', // 患者ID userSig: 'YOUR_USERSIG' // 从后端获取 }) // 监听来电 TUICallKit.on('onCallReceived', (event) => { console.log('收到来电', event) }) ``` ### 步骤3: 确保小程序端在线 小程序端必须: 1. 已登录到腾讯云IM 2. 保持在线状态 3. 正确监听来电事件 ### 步骤4: 检查Web端发起通话的userId 在Web端发起通话时,确保使用正确的患者userId: ```typescript // admin/src/components/video-call/index.vue // 检查 startCall 方法中的 userId // 应该使用患者的 IM userId,而不是患者的数据库ID await TUICallKitServer.call({ userID: 'patient_123', // 必须是小程序端登录IM时使用的userId type: TUICallType.VIDEO_CALL }) ``` ### 步骤5: 后端返回正确的患者userId 修改后端逻辑,在获取签名时同时返回患者的userId: ```php // server/app/adminapi/logic/tcm/DiagnosisLogic.php public static function getCallSignature(array $params) { // ... 现有代码 ... // 获取患者信息 $patientId = $params['patient_id'] ?? 0; $patientUserId = 'patient_' . $patientId; return [ 'sdkAppId' => (int)$config['sdkAppId'], 'userId' => $userId, // 医生的userId 'userSig' => $userSig, 'patientUserId' => $patientUserId, // 添加患者的userId 'expireTime' => 86400 ]; } ``` ### 步骤6: 前端使用患者userId发起通话 修改前端代码,使用后端返回的患者userId: ```typescript // admin/src/components/video-call/index.vue const startCall = async () => { try { // ... 获取签名 ... const res = await getCallSignature({ diagnosis_id: callInfo.value.diagnosisId, patient_id: callInfo.value.patientId }) // ... 初始化 ... // 发起通话时使用后端返回的 patientUserId await TUICallKitServer.call({ userID: res.patientUserId, // 使用后端返回的患者userId type: TUICallType.VIDEO_CALL }) } catch (error) { // ... 错误处理 ... } } ``` ## 调试步骤 ### 1. 检查患者是否在线 在Web端发起通话前,可以先检查患者是否在线: ```typescript // 可以通过腾讯云IM的REST API查询用户在线状态 // 或者在发起通话时捕获 60011 错误码(用户不在线) ``` ### 2. 查看控制台日志 **Web端:** - 打开浏览器控制台 - 查看发起通话时的userId - 查看是否有错误提示 **小程序端:** - 打开微信开发者工具控制台 - 查看是否收到来电事件 - 查看IM登录状态 ### 3. 常见错误码 - `60011`: 对方不在线 - 小程序端未登录IM或已退出 - `60010`: 对方忙线中 - 小程序端正在通话中 - `60008`: 对方拒绝了通话 - `60009`: 对方未接听(超时) ## 完整修复代码 ### 后端修改 ```php // server/app/adminapi/logic/tcm/DiagnosisLogic.php public static function getCallSignature(array $params) { try { $config = self::getTrtcConfig(); if (!$config) { self::setError('请先配置腾讯云TRTC参数'); return false; } $adminId = $params['admin_id'] ?? 0; $patientId = $params['patient_id'] ?? 0; if (!$adminId) { self::setError('获取管理员信息失败'); return false; } if (!$patientId) { self::setError('获取患者信息失败'); return false; } // 医生userId $doctorUserId = 'doctor_' . $adminId; // 患者userId(必须与小程序端一致) $patientUserId = 'patient_' . $patientId; // 生成医生的 UserSig $userSig = self::generateUserSig( $config['sdkAppId'], $config['secretKey'], $doctorUserId ); if (!$userSig) { self::setError('生成签名失败'); return false; } // 导入医生账号到IM self::importDoctorAccountToIm($adminId, $doctorUserId); // 确保患者账号也已导入IM self::ensurePatientImAccount($patientId, $patientUserId); return [ 'sdkAppId' => (int)$config['sdkAppId'], 'userId' => $doctorUserId, 'userSig' => $userSig, 'patientUserId' => $patientUserId, // 返回患者userId 'expireTime' => 86400 ]; } catch (\Exception $e) { self::setError($e->getMessage()); return false; } } /** * @notes 确保患者IM账号存在 */ private static function ensurePatientImAccount(int $patientId, string $userId): bool { try { // 获取患者信息 $diagnosis = Diagnosis::where('patient_id', $patientId)->find(); $nick = $diagnosis ? $diagnosis->patient_name : '患者' . $patientId; // 调用IM服务导入账号 $imService = new \app\common\service\TencentImService(); $result = $imService->importAccount($userId, $nick); return $result['success']; } catch (\Exception $e) { \think\facade\Log::error('导入患者IM账号失败: ' . $e->getMessage()); return false; } } ``` ### 前端修改 ```typescript // admin/src/components/video-call/index.vue const startCall = async () => { try { initializing.value = true callRejected.value = false statusText.value = '正在获取签名...' // 获取腾讯云签名 const res = await getCallSignature({ diagnosis_id: callInfo.value.diagnosisId, patient_id: callInfo.value.patientId }) if (!res || !res.userSig || !res.patientUserId) { throw new Error('获取签名失败,返回数据不完整') } console.log('签名获取成功:', { doctorUserId: res.userId, patientUserId: res.patientUserId, sdkAppId: res.sdkAppId }) // ... 初始化代码 ... // 发起通话 - 使用后端返回的 patientUserId console.log('准备发起通话,目标用户:', res.patientUserId) await TUICallKitServer.call({ userID: res.patientUserId, // 使用后端返回的患者userId type: TUICallType.VIDEO_CALL }) console.log('通话已发起') statusText.value = '等待对方接听...' feedback.msgSuccess('通话已发起,等待对方接听...') } catch (error: any) { console.error('启动通话失败:', error) // 特殊处理用户不在线的情况 if (error.code === 60011) { feedback.msgError('患者不在线,请确认患者已打开小程序') } else { const errorMessage = error.message || '启动通话失败,请稍后重试' feedback.msgError(errorMessage) } // ... 错误处理 ... } finally { initializing.value = false } } ``` ## 测试清单 - [ ] 后端返回正确的 patientUserId - [ ] 小程序端使用相同的 userId 格式登录IM - [ ] 小程序端保持在线状态 - [ ] Web端使用 patientUserId 发起通话 - [ ] 小程序端能收到来电通知 - [ ] 小程序端能正常接听 - [ ] 视频和音频正常传输 ## 注意事项 1. **userId格式必须完全一致**:Web端和小程序端必须使用相同的userId格式 2. **小程序端必须先登录**:在发起通话前,确保小程序端已登录IM 3. **检查网络环境**:小程序端需要在HTTPS环境或微信开发者工具中测试 4. **版本兼容性**:确保Web端和小程序端使用兼容的TUICallKit版本 ## 相关文档 - [TUICallKit Web端文档](https://www.tencentcloud.com/document/product/647/50993) - [TUICallKit 小程序端文档](https://www.tencentcloud.com/document/product/647/50994) - [腾讯云IM跨平台通话](https://www.tencentcloud.com/document/product/1047/45907)