9.3 KiB
9.3 KiB
Web端呼叫小程序端通话问题修复指南
问题描述
Web端可以呼叫Web端,但无法呼叫小程序端。
原因分析
1. 用户ID格式不一致
- Web端医生:
doctor_{admin_id} - 小程序端患者: 需要确认格式是否为
patient_{patient_id}或其他格式
2. 小程序端未登录IM
- 小程序端必须先登录腾讯云IM才能接收通话邀请
- 需要在小程序启动时初始化 TUICallKit
3. 跨平台通话配置
- 需要确保 SDKAppID 一致
- 需要确保 UserSig 生成方式一致
- 需要确保双方都使用相同的通话协议版本
解决方案
步骤1: 确认小程序端用户ID格式
检查小程序端的用户ID生成逻辑,确保与Web端一致。
后端代码检查点:
// server/app/adminapi/logic/tcm/DiagnosisLogic.php
// 查找 createPatientTrtcAccount 方法
// 确认患者的 userId 格式
预期格式:
- 医生:
doctor_{admin_id} - 患者:
patient_{patient_id}
步骤2: 检查小程序端是否正确初始化
小程序端需要在 app.js 或页面 onLoad 时初始化 TUICallKit:
// 小程序端代码示例
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: 确保小程序端在线
小程序端必须:
- 已登录到腾讯云IM
- 保持在线状态
- 正确监听来电事件
步骤4: 检查Web端发起通话的userId
在Web端发起通话时,确保使用正确的患者userId:
// 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:
// 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:
// 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端发起通话前,可以先检查患者是否在线:
// 可以通过腾讯云IM的REST API查询用户在线状态
// 或者在发起通话时捕获 60011 错误码(用户不在线)
2. 查看控制台日志
Web端:
- 打开浏览器控制台
- 查看发起通话时的userId
- 查看是否有错误提示
小程序端:
- 打开微信开发者工具控制台
- 查看是否收到来电事件
- 查看IM登录状态
3. 常见错误码
60011: 对方不在线 - 小程序端未登录IM或已退出60010: 对方忙线中 - 小程序端正在通话中60008: 对方拒绝了通话60009: 对方未接听(超时)
完整修复代码
后端修改
// 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;
}
}
前端修改
// 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 发起通话
- 小程序端能收到来电通知
- 小程序端能正常接听
- 视频和音频正常传输
注意事项
- userId格式必须完全一致:Web端和小程序端必须使用相同的userId格式
- 小程序端必须先登录:在发起通话前,确保小程序端已登录IM
- 检查网络环境:小程序端需要在HTTPS环境或微信开发者工具中测试
- 版本兼容性:确保Web端和小程序端使用兼容的TUICallKit版本