Files
zyt/WEB_TO_MINIPROGRAM_CALL_FIX.md
T
2026-03-04 15:32:30 +08:00

9.3 KiB
Raw Blame History

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: 确保小程序端在线

小程序端必须:

  1. 已登录到腾讯云IM
  2. 保持在线状态
  3. 正确监听来电事件

步骤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 发起通话
  • 小程序端能收到来电通知
  • 小程序端能正常接听
  • 视频和音频正常传输

注意事项

  1. userId格式必须完全一致:Web端和小程序端必须使用相同的userId格式
  2. 小程序端必须先登录:在发起通话前,确保小程序端已登录IM
  3. 检查网络环境:小程序端需要在HTTPS环境或微信开发者工具中测试
  4. 版本兼容性:确保Web端和小程序端使用兼容的TUICallKit版本

相关文档