1. 使用说明
欢迎使用“AICC”的 TiCloudRTC 外呼功能。我们提供在安卓、iOS原生开发中对接外呼(不包含uniapp框架对接),支持企微、微信平台跳转小程序外呼,可以在您的App及小程序中快速对接并实现拨打电话的能力。
2. Android开发文档
2.2. 集成SDK
目前支持的最低安卓系统及平台架构: Android 5.0+ (arm64, armv7, x86, x86-64)
2.2.1. 添加自定义仓库地址
如果您使用的是 Gradle kotlin 脚本, 则在项目根目录的 build.gradle.kts 或 setting.gradle.kts 的 repositories 配置块中添加如下仓库配置:
maven { url=uri("https://www.jitpack.io") }
maven { url = uri("https://mvnrepo.jiagouyun.com/repository/maven-releases") }
如果您使用的是 Gradle Groovy 脚本, 则在项目根目录的 build.gradle 或 setting.gradle 的 repositories 配置块中添加如下仓库配置:
maven { url 'https://www.jitpack.io'}
maven { url "https://mvnrepo.jiagouyun.com/repository/maven-releases"}
2.2.2. 导入SDK及依赖
在使用到 SDK 的模块中的 build.gradle 或 build.gradle.kts 的 dependencies 配置块添加 SDK 依赖
// TiCloudRtc SDK
implementation("com.github.ti-net:TiCloud-RTC-Android:"版本号"@aar")
// 依赖 1.0.0 版本 TiCloudRtc SDK 样例
// implementation("com.github.ti-net:TiCloud-RTC-Android:1.0.0@aar")
SDK 涉及的第三方依赖项
注: 1.当通过 Gradle 引入 SDK 时,不需要手动添加下面这些依赖项,这些依赖项会跟随 SDK 引入. 2.如果需要通过观测云上传日志,则需要主动添加观测云的依赖. 否则 CreateClientOption 中的 isStartFTMobile 设置无法生效.
// kotlin 协程
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
// kotlin 反射
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.24")
// retrofit2 gson 转换库
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
// json 解析库
api("com.google.code.gson:gson:2.11.0")
// 网络请求库
api("com.squareup.okhttp3:okhttp:4.12.0")
api("com.squareup.retrofit2:retrofit:2.11.0")
// 观测云(可选, 不添加则无法上报日志)
implementation("com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:1.5.0")
implementation("com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:1.1.0")
2.3. 设置权限
注: 当通过 Gradle 引入 SDK 时, 不需要手动声明下面这些权限,这些权限会跟随 SDK 引入.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 对于 Android 12 及以上设备,还需要添加如下权限: -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!--android 9.0上使用前台服务,需要添加权限-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
需要动态申请的权限
Manifest.permission.RECORD_AUDIO
// 对于 Android 12 及以上设备,还需要添加如下权限
Manifest.permission.BLUETOOTH_CONNECT
2.4. SDK接口说明
2.4.1. 初始化
接口声明
@JvmStatic
fun createClient(
context: Context,
createClientOption:CreateClientOption?,
resultCallback:CreateResultCallback?,
){}
参数说明
参数名称 |
参数类型 |
说明 |
context |
Context |
application context |
createClientOption |
CreateClientOption |
创建客户端所需的参数配置 |
resultCallback |
CreateResultCallback |
创建结果回调 |
CreateClientOption 说明
/** 创建客户端所需的参数配置 */
data class CreateClientOption(
/** 平台地址 */
var rtcEndpoint: String? = "",
/** 企业 ID*/
var enterpriseId: String? = "",
/** 用户 ID */
var userId: String? = "",
/**
* 外部私有服务端登录接口返回的 accessToken.
*
* 注: 生成 accessToken 时,有效时间至少设置为 10 分钟以上, 设置极端的 10 分钟有可能因为
* 网络延迟导致 SDK 认为 accessToken 有效期小于 10 分钟而导致无法初始化. 建议设置 15 分钟以上.
*/
var accessToken: String? = ""
) {
/** 预留高级参数配置 */
var advancedParams = mutableMapOf<String, String>()
/** 调试标识,true 为输出调试信息,false 为不输出调试信息 */
var isDebug = false
/** 默认的主叫真实号码,用于接听回呼的电话 */
var callerNumber: String = ""
/** 输出日志到文件标识, true 为输出日志到文件里, false 为不输出日志到文件 */
var isLogToFile = true
/**
* 启用观测云上传日志标识
*
* true: 启用观测云上传日志
* false: 不启用观测云上传日志
*
* 注:
* 1. 当设置该标识为 false 时, 当前SDK 不影响 APP 的观测云 SDK 的初始化, 且不会上传日志到观测云.
* 2. 当设置该标识为 true 时, 如果 APP 先于当前 SDK 初始化观测云 SDK,则当前 SDK 不再初始化观测云 SDK. 当前 SDK 的日志将上传到 APP 的观测云 app_id 下, 日志携带 log_tag 属性, 属性值为 TiCloudRTC.
* 3. 当设置该标识为 true 时, 如果 APP 后于当前 SDK 初始化观测云, 则观测云 SDK 的配置最终为 APP 初始化观测云的配置. 当前 SDK 的日志将上传到 APP 的观测云 app_id 下, 日志携带 log_tag 属性, 属性值为 TiCloudRTC.
* 4. 只有当前标识设置为 true, 且 APP 未使用观测云 SDK 时,当前 SDK 的日志才会上传到当前 SDK 初始化的观测云 app_id 下, 日志携带 log_tag 属性, 属性值为 TiCloudRTC.
*/
var isStartFTMobile = true
/**
* 是否显示通话中的通知栏
*
* true: 显示通话中的通知栏(默认)
* false: 不显示通话中的通知栏
*/
var isShowCallingNotification = true
/**
* 是否在对端挂断时播放预置挂机音频(注:通话错误导致的挂机不会播放音频)
*
* true: 播放音频(默认)
* false: 不播放音频
*/
var isPlayHangupAudio = true
}
CreateResultCallback 说明
/**
* 创建 RTC 客户端的结果回调
*/
interface CreateResultCallback {
/**
* 成功创建 rtc 客户端,返回实例 [rtcClient]
*/
fun onSuccess(rtcClient: TiCloudRTC)
/**
* 创建 rtc 客户端失败,返回错误码 [errorCode] 和错误描述 [errorMessage]
*
* errorCode 可能的值为:
* 10xxx 类错误码
* 11xxx 类错误码
* 19999
* 4xxxx 类错误码
*/
fun onFailed(errorCode: Int, errorMessage: String)
}
使用样例
kotlin
TiCloudRTC.createClient(
context = context,
createClientOption = CreateClientOption(
rtcEndpoint = rtcEndpoint,
enterpriseId = enterpriseId,
userId = userId,
accessToken = accessToken,
),
resultCallback = object : CreateResultCallback {
override fun onSuccess(rtcClient: TiCloudRTC) {
// 创建成功,保存客户端
this@MainActivityViewModel.rtcClient = rtcClient
// 设置事件监听
rtcClient.setEventListener(CustomEventListener())
}
override fun onFailed(errorCode: Int, errorMessage: String) {
// 创建失败
}
}
)
java
CreateClientOption option = new CreateClientOption(
rtcEndPoint,
enterpriseId,
userId,
accessToken
);
TiCloudRTC.createClient(context, option, new CreateResultCallback() {
@Override
public void onSuccess(@NonNull TiCloudRTC rtcClient) {
// 创建成功,保存客户端
myViewModel.rtcClient = rtcClient;
// 设置事件监听
rtcClient.setEventListener(new CustomEventListener());
}
@Override
public void onFailed(int errorCode, @NonNull String errorMessage) {
// 创建失败
}
});
2.4.2. 设置事件监听
接口声明
abstract fun setEventListener(eventListener: TiCloudRTCEventListener?)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
eventListener |
必填 |
TiCloudRTCEventListener |
引擎事件监听 |
TiCloudRTCEventListener 说明
/**
* TiCloudRTC 事件监听
*/
abstract class TiCloudRTCEventListener {
/**
* 引擎内部错误信息事件,返回错误码 [errorCode] 和错误描述 [errorMessage]
*
* errorCode 可能的值为:
* 10xxx 类错误码
* 11xxx 类错误码
* 12005
* 12006
* 19999
* 4xxxx 类错误码
*/
open fun onError(errorCode:Int, errorMessage:String){}
/** 开始通话,[requestUniqueId] 为当前呼叫的唯一标识 */
open fun onCallingStart(requestUniqueId:String){}
/** 播放铃声中 */
open fun onRinging(){}
/** 通话在会话邀请阶段被取消 */
open fun onCallCancelled(){}
/** 呼叫被拒绝 */
open fun onCallRefused(){}
/** 通话中 */
open fun onCalling(){}
/** 通话结束,[isPeerHangup] 为 true 表示对方挂断,false 表示己方挂断*/
@Deprecated(
message = "当前接口返回的 isPeerHangup 参数并不能准确描述是否是真正的被叫挂断,因此废弃,请使用新的 onCallingEnd 接口替代,当前回调将在 5.0.0 版本移除",
replaceWith = ReplaceWith("onCallingEnd(errorCode,errorMessage,sipCode)"),
level = DeprecationLevel.WARNING
)
open fun onCallingEnd(isPeerHangup:Boolean){}
/**
* 通话结束
*
* [errorCode] 为通话结束的错误码
* [errorMessage] 为通话结束的错误信息
* [sipCode] 为 sip 错误码, 默认为 0 , 表示无返回
*
* errorCode 可能的值为:
* 10009
* 11001
* 14xxx 类错误码
* 19999
* 2xxxx 类错误码
* 3xxxx 类错误码
*
* 注:
* 1. SDK 对 sipCode 无定义
* 2. 只有当 errorCode 为 14003 和 39999 时,需要关注 sipCode 值,
* 此时技术支持人员同时需要 errorCode 和 sipCode 排查具体问题
*/
open fun onCallingEnd(
errorCode: Int,
errorMessage: String,
sipCode: Int
){}
/**
* SDK 未正常发起通话,返回错误码 [errorCode] 和失败信息 [failureMessage]
*
* errorCode 可能的值为:
* 10000
* 10001
* 10002
* 10003
* 10009
* 11001
* 12xxx 类错误码
* 19999
*/
open fun onCallFailure(errorCode:Int,failureMessage:String){}
/**
* 调用 SDK hangup 接口引发挂断
*
* 注: 对应原 ErrorCode.SELF_HANGUP(14001) 错误码
*/
open fun onLocalHangup(){}
/**
* 对方接起后挂断
*
* 注: 对应原 ErrorCode.REMOTE_HANGUP(14002) 错误码
*/
open fun onRemoteHangup(){}
/**
* 接收到远端的会话邀请及高级字段 [fields]
*
* 目前 fields 包含如下字段信息:
*
* requestUniqueId: 通话唯一标识
* customerNumber: 呼入的号码
*/
open fun onRemoteInviteReceived(fields: HashMap<String, String>) {}
/**
* 远端会话邀请被本地拒绝, [fields] 为 SDK 内部返回的高级字段
*
* 目前 fields 包含如下字段信息:
*
* isCalling: 标识当前邀请是否由于本地处于通话中而被自动拒绝.
* true: 由于本地处于通话中而被自动拒绝会话邀请
* false:本地主动拒绝会话邀请
* requestUniqueId: 通话唯一标识
* customerNumber: 呼入的号码
*/
open fun onInviteRefuseByLocal(fields: HashMap<String, String>) {}
/**
* 远端取消会话邀请, [fields] 为 SDK 内部返回的高级字段
*
* 目前 fields 包含如下字段信息:
*
* requestUniqueId: 通话唯一标识
* customerNumber: 呼入的号码
*/
open fun onRemoteInvitationCancel(fields: HashMap<String, String>) {}
/** 远端邀请会话失败,返回错误码 [errorCode] 和失败信息 [failureMessage] */
open fun onRemoteInviteFailure(errorCode: Int, failureMessage: String) {}
/** [accessToken] 将在 10 分钟后过期 */
open fun onAccessTokenWillExpire(accessToken:String){
// 当前回调返回的 accessToken 不可用于 renewAccessToken 接口,
// 此处的 accessToken 一般用于日志打印
// 当此事件回调执行时, app 应该调用部署了 accessToken 生成工具的自建服务端提供的接口
// 生成新的 accessToken, 并使用新的 accessToken 作为参数调用 SDK 的 renewAccessToken 接口
}
/** accessToken 已过期 */
open fun onAccessTokenHasExpired(){}
/**
* 回调当前 userId 的通话网络上行质量 [txQuality]
*
* txQuality 取值及含义
* 0: 质量未知
* 1: 网络质量较好
* 2: 网络质量一般
* 3: 网络质量较差
*
* 注: 当前回调只有在 txQuality 改变时触发
* */
open fun onNetworkQuality(txQuality: Int) {}
/**
* 返回通话中所有 userId 的通话上下行质量 [txQuality] 和 [rxQuality]
*
* txQuality ---- 该用户的上行网络质量,基于上行视频的发送码率、上行丢包率、平均往返时延和网络抖动计算
* 0:质量未知
* 1:质量极好
* 2:用户主观感觉和极好差不多,但码率可能略低于极好
* 3:用户主观感受有瑕疵但不影响沟通
* 4:勉强能沟通但不顺畅
* 5:网络质量非常差,基本不能沟通
* 6:网络连接断开,完全无法沟通
* 8:SDK 正在探测网络质量
*
* rxQuality ---- 该用户的下行网络质量,基于下行网络的丢包率、平均往返延时和网络抖动计算
* 0:质量未知
* 1:质量极好
* 2:用户主观感觉和极好差不多,但码率可能略低于极好
* 3:用户主观感受有瑕疵但不影响沟通
* 4:勉强能沟通但不顺畅
* 5:网络质量非常差,基本不能沟通
* 6:网络连接断开,完全无法沟通
* 8:SDK 正在探测网络质量
*
* 注:
* 1. 当 uid == 0 时为当前 userId
* 2. 该回调每 2 秒会被触发一次, 当通话中有多个用户时, 会多次触发
* 3. 当相应 uid 不发流时,txQuality 为 0; 当相应 uid 不收流时,rxQuality 为 0
*
*/
open fun onNetworkQuality(uid: Int, txQuality: Int, rxQuality: Int) {}
/** 回调对端音频数据二进制数据流 [data] 及数据流大小 [bufSize] */
open fun onReceiveStreamData(data: ByteArray, bufSize : Int) {}
/** 回调特定频道 [channels] 的对端音频采样率信息 [sampleRate] */
open fun onReceiveStreamSample(sampleRate: Int, channels : Int) {}
/** 当前 userId 在其他端登录,当前引擎已销毁 */
open fun onRemoteLogin() {}
/** 会话时检测到长达 5 秒时间未成功发送本地语音流 */
open fun onLocalNoVoiceStreamSent() {}
/**
* SDK 根据平台配置对 userField 外呼参数里的特殊字符进行了移除处理
*
* @param removedCharList 被移除的特殊字符列表
* @param srcUserField 原始的 userField
* @param finalUserField 处理后的 userField
* */
open fun onUserFieldModifiedByConfig(
removedCharList: List<String>,
srcUserField: String,
finalUserField: String
) {}
/**
* 通话中本地音频流的统计信息回调
*
* 该回调描述本地设备发送音频流的统计信息. SDK 每 2 秒触发该回调一次.
*
* stats 成员变量说明:
* stats?.numChannels: 声道数
* stats?.sentSampleRate: 发送的采样率,单位为 Hz
* stats?.sentBitrate: 发送码率的 2 秒平均值,单位为 Kbps
* stats?.txPacketLossRate: 音频丢包率
*/
open fun onLocalAudioStats(stats: IRtcEngineEventHandler.LocalAudioStats?) {}
/**
* 通话中远端音频流的统计信息回调
*
* 该回调描述远端用户在通话中端到端的音频流统计信息. SDK 每 2 秒触发该回调一次.
*
* stats 成员变量说明:
* stats?.uid: 用户 ID
* stats?.quality: 质量 (0:质量未知; 1:质量极好; 2:用户主观感觉和极好差不多 ,但码率可能略低于极好; 3:用户主观感受有瑕疵,但不影响沟通; 4:勉强能沟通但不顺畅; 5:网络质量非常差,基本不能沟通; 6:网络连接已断开,完全无法沟通; 8:网络质量探测中;)
* stats?.networkTransportDelay: 音频发送端到接收端的网络延迟((ms)
* stats?.jitterBufferDelay: 接收端到网络抖动缓冲的网络延迟(ms)
* stats?.audioLossRate: 统计周期内的远端音频流的丢帧率
* stats?.numChannels: 声道数
* stats?.receivedSampleRate: 统计周期内接收到的远端音频采样率(Hz)
* stats?.receivedBitrate: 接收流在统计周期内的平均码率(Kbps)
* stats?.totalFrozenTime: 远端用户在加入频道后发生音频卡顿的累计时长 (一个统计周期内,音频丢帧率达到 4% 即记为一次音频卡顿,单位: ms)
* stats?.frozenRate: 远端用户在加入频道后发生音频卡顿的累计时长占音频总有效时长的百分比
* stats?.totalActiveTime: 远端用户在音频通话开始到本次回调之间的有效时长(ms)
* stats?.publishDuration: 远端音频流的累计发布时长(ms)
* stats?.qoeQuality: 接收远端音频时,本地用户的主观体验质量 (0:好; 1:坏)
* stats?.qualityChangedReason: 接收远端音频时,本地用户主观体验质量较差的原因 (0:无原因,说明主观体验质量较好; 1:远端用户的网络较差; 2:本地用户的网络较差; 4:本地用户的 Wi-Fi 或者移动数据网络信号弱; 8:本地用户同时开启 Wi-Fi 和蓝牙,二者信号互相干扰,导致音频传输质量下降;)
* stats?.mosValue: MOS (平均主观意见分)
*
* MOS 评分参考:
* 大于 4 分: 音频质量佳,清晰流畅
* 3.5-4 分: 音频质量较好,偶有音质损伤,但依然清晰
* 3-3.5 分: 音频质量一般,偶有卡顿,不是非常流畅,需要一点注意力才能听清
* 2.5-3 分: 音频质量较差,卡顿频繁,需要集中精力才能听清
* 2-2.5 分: 音频质量很差,偶有杂音,部分语义丢失,难以交流
* 小于 2 分: 音频质量非常差,杂音频现,大量语义丢失,完全无法交流
*/
open fun onRemoteAudioStats(stats: IRtcEngineEventHandler.RemoteAudioStats?) {}
/**
* 本地音频状态发生改变回调
*
* 本地音频的状态发生改变时(包括本地麦克风采集状态和音频编码状态),SDK 会触发该回调报告当前的本地音频状态。 在本地音频出现故障时,该回调可以帮助你了解当前音频的状态以及出现故障的原因,方便你排查问题
*
* state ---- 当前的本地音频状态
* 0:本地音频默认初始状态
* 1:本地音频采集设备启动成功
* 2:本地音频首帧编码成功
* 3:本地音频启动失败
*
* error ---- 本地音频出错原因
* 0:本地音频状态正常
* 1:本地音频出错原因不明确
* 2:没有权限启动本地音频采集设备
* 3:本地音频采集设备已经在使用中
* 4:本地音频采集失败
* 5:本地音频编码失败
* 8:本地音频采集被系统来电、闹钟中断
*/
open fun onLocalAudioStateChanged(state: Int, error: Int) {}
/**
* 远端音频状态发生改变回调
*
* 远端用户音频状态发生改变时,SDK 会触发该回调向本地用户报告当前的远端音频流状态
*
* uid ---- 发生音频状态改变的远端用户 ID
*
* state ---- 远端音频流状
* 0:远端音频流默认初始状态. 在 reason 为 3、5、7 的情况下,会报告该状态
* 1:本地用户已接收远端音频首包
* 2:远端音频流正在解码,正常播放. 在 reason 为 2、4、6 的情况下,会报告该状态
* 3:远端音频流卡顿. 在 reason 为 1 的情况下,会报告该状态
* 4:远端音频流播放失败. 在 reason 为 0 的情况下,会报告该状态
*
* reason ---- 远端音频流状态改变的具体原因
* 0:音频状态发生改变时,会报告该原因
* 1:远端用户的网络阻塞
* 2:远端用户的网络恢复正常
* 3:本地用户停止接收远端音频流或本地用户禁用音频模块
* 4:本地用户恢复接收远端音频流或本地用户启用音频模块
* 5:远端用户停止发送音频流或远端用户禁用音频模块
* 6:远端用户恢复发送音频流或远端用户启用音频模块
* 7:远端用户离开频道
*
* elapsed ---- 从本地用户调用 joinChannel 方法直至发生音频状态改变的延迟(毫秒)
*/
open fun onRemoteAudioStateChanged(uid: Int, state: Int, reason: Int, elapsed: Int) {}
}
使用样例
kotlin
class CustomEventListener: TiCloudRTCEventListener() {
// 按需重写回调事件方法
}
rtcClient?.setEventListener(CustomEventListener())
java
public class CustomEventListener extends TiCloudRTCEventListener{
// 按需重写回调事件方法
}
rtcClient.setEventListener(new CustomEventListener());
2.4.3. 外呼
接口声明
abstract fun call(callOption:CallOption?)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
callOption |
必填 |
CallOption |
外呼所需的参数配置 |
CallOption 说明
/** 外呼所需的参数配置 */
data class CallOption(
/** 被呼叫的号码, 注:在客服场景 tel 传空字符串 */
var tel: String? = "",
/** 外显号码*/
var clid: String? = "",
/** 通话唯一标识, 不填则由 SDK 内部自动生成*/
var requestUniqueId: String? = "",
/**
* 自定义字段
*
* 注:
* 1. userField 有字符长度限制,默认 userField 字符总数最大为 512B
* 2. userField 中的所有空格将被清除
*/
var userField: String? = "",
/** 外呼场景,1:客服场景,6:外呼场景*/
var type: Int = 6
){
/** 主叫真实号码,用于接听回呼的电话,不传则使用创建客户端时所设置的默认 callerNumber */
var callerNumber: String = ""
/** 绑定的座席号 */
var cno: String = ""
/** 外显号码区号 */
var obClidAreaCode: String = ""
/** 外显号码组 */
var obClidGroup: String = ""
/**
* 参数签名
*
* 注: 在加密模式下用到,不使用加密则不需要传入
*
* 签名规则:
* 1. 将参数 tel、callerNumber、clid、obClidAreaCode、obClidGroup、userField、type、timestamp 按照字典顺序排序,
* 然后按照 key=value&key2=value2 的规则进行拼接(注意:如果没有传对应字段则不用参与拼接,requestUniqueId 不参与签名)
* 2. 将企业 ID 拼接到拼接串开头,将企业 Token 拼接到拼接串结尾
* 3. 将拼接串进行 SHA256 加密,最终得到的值就是签名(sign)
*
* 签名示例:
* 假设企业 ID 为 7000002 , 企业 Token 为 token123456
* 请求参数为
* tel: 13000000000
* callerNumber: 14000000000
* obClidGroup: 号码池1
* type: 6
* timestamp: 1735192796
*
*
* 按照步骤开始计算签名
*
* 经过第一步计算之后得到 callerNumber=14000000000&obClidGroup=号码池1&tel=13000000000×tamp=1735192796&type=6
*
* 经过第二步计算之后得到 7000002callerNumber=14000000000&obClidGroup=号码池1&tel=13000000000×tamp=1735192796&type=6token123456
*
* 经过第三步计算之后得到 398db99bf641dbc489a61204257ea7e91ce2dd490a3f37862dc6c4992168c5dd
*
*/
var sign: String = ""
/**
* 签名时间戳
*
* 注: 在加密模式下用到,不使用加密则不需要传入
*/
var timestamp: Int? = null
}
userField 传值样例(假设要传递 3 个数据: id、workNum、depId)
kotlin 样例
val userField = """
{
"id":"90007573",
"workNum":"1026658",
"depId":"340179",
}
""".trimIndent()
rtcClient.call(CallOption("156xxxx1200","","",userField,6))
java 样例
String userField = "{"+
\"id\",:\"90007573\","+
\"workNum\":\"1026658\","+
\"depId\":\"340179\""+
"}";
rtcClient.call(new CallOption("156xxxx1200","","",userField,6));
2.4.4. 接受会话邀请
接口声明
abstract fun acceptCall()
kotlin 样例
rtcClient?.acceptCall()
java 样例
rtcClient.acceptCall();
2.4.5. 拒绝会话邀请
接口声明
abstract fun refuseCall()
kotlin 样例
rtcClient?.refuseCall()
java 样例
rtcClient.refuseCall();
2.4.6. 发送按键
接口声明
abstract fun dtmf(digits:String?)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
digits |
必填 |
String |
按键字符,取值范围: 0,1,2,3,4,5,6,7,8,9,*,# |
|
使用样例
kotlin
rtcClient.dtmf("8")
java
rtcClient.dtmf("8");
2.4.7. 更新令牌
通过新的令牌 [accessToken](以下均以accessToken为示例) 刷新 SDK 的使用期限
|
接口声明
abstract fun renewAccessToken(accessToken:String?)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
accessToken |
必填 |
String |
使用样例
rtcClient.renewAccessToken("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
2.4.9. 销毁
接口声明
abstract fun destroyClient(resultCallback: DestroyResultCallback?)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
resultCallback |
必填 |
DestroyResultCallback |
销毁结果回调 |
DestroyResultCallback 说明
/**
* 销毁 RTC 客户端的结果回调
*/
interface DestroyResultCallback {
/** 成功销毁 rtc 客户端 */
fun onSuccess()
/**
* 销毁客户端失败,返回错错误码 [errorCode] 和错误描述 [errorMessage]
*
* errorCode 可能的值为:
* 10000
*/
fun onFailed(errorCode:Int,errorMessage:String)
}
使用样例
kotlin
rtcClient?.destroyClient(object :DestroyResultCallback {
override fun onSuccess() {
// 销毁成功
rtcClient = null
}
override fun onFailed(errorCode: Int, errorMessage: String) {
// 销毁失败
}
})
java
rtcClient.destroyClient(new DestroyResultCallback() {
@Override
public void onSuccess() {
// 销毁成功
rtcClient = null;
}
@Override
public void onFailed(int errorCode, @NonNull String errorMessage) {
// 销毁失败
}
});
2.4.10. 扬声器控制
接口声明
abstract fun setEnableSpeakerphone(isEnable:Boolean)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
isEnable |
必填 |
Boolean |
true: 打开扬声器,false: 关闭扬声器 |
|
使用样例
kotlin
rtcClient.setEnableSpeakerphone(true)
java
rtcClient.setEnableSpeakerphone(true);
2.4.11. 是否开启扬声器
接口声明
abstract fun isSpeakerphoneEnabled():Boolean
返回值说明
true:扬声器已开启,语音会输出到扬声器 false:扬声器未开启,语音会输出到非扬声器(听筒,耳机等)
|
使用样例
kotlin
rtcClient.isSpeakerphoneEnabled()
java
rtcClient.isSpeakerphoneEnabled();
2.4.12. 音频采集控制
接口声明
abstract fun setEnableLocalAudio(isEnable: Boolean)
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
isEnable |
必填 |
Boolean |
true:开启本地音频采集(取消静音),false:关闭本地音频采集(静音) |
|
使用样例
kotlin
rtcClient.setEnableLocalAudio(true)
java
rtcClient.setEnableLocalAudio(true);
2.4.13. 获取版本号
接口声明
@JvmStatic
fun getVersion():String
使用样例
kotlin
TiCloudRTC.getVersion()
java
TiCloudRTC.getVersion();
2.5. 错误码
2.5.1. 错误码分类
code |
说明 |
1xxxx |
SDK自身内部运行所遇到的错误,19999为SDK未知错误码 |
2xxxx |
网关类错误码 |
3xxxx |
Media-Server类错误码,39999为Media-Server未知错误码 |
4xxxx |
RTC API接口返回的配置错误类错误码,49999为RTC API接口的默认错误码,由SDK返回 |
2.5.2. 错误码具体定义
code |
message |
说明 |
启用版本 |
废弃版本 |
10000 |
SDK未初始化 |
在SDK未初始化时,调用任意除初始化接口之外的接口触发 |
4.0.0 |
- |
10001 |
网络异常 |
- |
4.0.0 |
- |
10002 |
解析HTTP响应失败 |
- |
4.0.0 |
- |
10003 |
SDK正在初始化或SDK正在销毁 |
- |
4.0.0 |
- |
10004 |
RTC平台地址格式不正确.正确格式:^https://[a-zA-Z0-9\.\-_]+$ |
在初始化SDK的回调中返回 |
4.0.0 |
- |
10005 |
企业ID格式不正确.正确格式: ^\d{7}$ |
在初始化SDK的回调中返回 |
4.0.0 |
- |
10006 |
用户ID格式不正确.正确格式: ^[a-zA-Z0-9@\-\.:]{1,512}$ |
在初始化SDK的回调中返回 |
4.0.0 |
- |
10007 |
accessToken格式不正确.正确格式:^[a-zA-Z0-9%/=]$ |
在初始化SDK的回调中返回 |
4.0.0 |
- |
10008 |
RTM初始化失败 |
1.RTM的login接口调用失败 2.在初始化SDK接口中返回 |
4.0.0 |
- |
10009 |
RTM未初始化 |
- |
4.0.0 |
- |
11000 |
HTTP鉴权失败 |
- |
4.0.0 |
- |
11001 |
AccessToken已过期 |
- |
4.0.0 |
- |
11002 |
AccessToken有效期短于10分钟 |
在初始化SDK或renewAccessToken接口传入的AccessToken的有效期至少大于600秒(十分钟) |
4.0.0 |
- |
11003 |
AccessToken版本错误 |
- |
4.0.0 |
- |
11004 |
HTTP未授权 |
- |
4.0.0 |
- |
11005 |
资源未定义(404异常) |
- |
4.0.0 |
- |
11006 |
请求方式错误 |
移动端的http请求方式不会出错,所以不支持 |
4.0.0 |
- |
11007 |
AccessToken 与 userId 不匹配 |
- |
4.0.0 |
- |
12000 |
不支持的呼叫场景 |
- |
4.0.0 |
- |
12001 |
呼叫参数错误 |
- |
4.0.0 |
- |
12002 |
呼叫重复 |
1.在调用call后,未收到onCallingEnd(对应web端Hangup事件)或onCallingFailue(对应web端Failure事件)回调就再次调用call时触发 2.在收到网关的会话邀请并接受后,未收到onCallingEnd(对应web端Hangup事件)或onCallingFailue(对应web端Failure事件)回调就再次调用call时触发 |
4.0.0 |
- |
12003 |
当前有其他客户端正在通话中 |
移动端无法像Web一样能知道其他客户端正在通话,所以不支持 |
4.0.0 |
- |
12004 |
发送本地会话邀请失败 |
SDK在RTM的sendLocalInvitation接口回调中判断 |
4.0.0 |
- |
12005 |
外呼场景不支持DTMF(已废弃,SDK 不会再返回此错误码,无论外呼场景还是客服场景都支持传 DTMF,当前错误码将在 5.0.0 版本移除) |
- |
4.0.0 |
4.1.0 |
12006 |
请输入正确的DTMF字符 |
- |
4.0.0 |
- |
12007 |
无音频采集权限 |
- |
4.0.0 |
- |
13000 |
因意外情况无法接受网关的远端会话邀请 |
移动端的实现为:收到网关的会话邀请后,存储远端会话邀请对象,如果此时app正在销毁SDK并把远端会话邀请的对象清除为null,此时app再调用接受会话邀请接口(acceptCall),将返回此错误码 |
4.0.0 |
- |
13001 |
解析网关远端会话邀请内容失败 |
- |
4.0.0 |
- |
13002 |
接受网关远端会话邀请失败 |
调用RTM的acceptRemoteInvitation接口失败 |
4.0.0 |
- |
14001 |
调用SDK hangup接口引发挂断(已废弃,请用 onLocalHangup 回调,SDK 不会再返回此错误码,当前错误码将在 5.0.0 版本移除) |
1.对应旧sdk onCallingEnd回调的isPeerHangup==false结果 2.通过onCallingEnd回调返回 |
4.0.0 |
4.1.0 |
14002 |
对方接起后挂断(已废弃,请用 onRemoteHangup 回调,SDK 不会再返回此错误码,当前错误码将在 5.0.0 版本移除) |
1.此时RTM Hangup信令中的sipCode==""且errorCode==0 2.对应旧sdk onCallingEnd回调的isPeerHangup==true结果 3.通过onCallingEnd回调返回 |
4.0.0 |
4.1.0 |
14003 |
对方未接起挂断,请检查sipCode |
1.此时RTM Hangup信令中的sipCode!=""且errorCode==0 2.对应旧sdk onCallingEnd回调的isPeerHangup==true结果 3.需要返回sipCode 4.通过onCallingEnd回调返回 |
4.0.0 |
- |
14004 |
通话过程中SDK因网络问题断线 |
1.在收到声网RTC SDK的onConnectionStateChanged回调后开始计时20秒,如果超时后网络仍未链接则返回次错误码 2.通过onCallingEnd回调返回 |
4.0.0 |
- |
14005 |
通话过程中同一userId在其他端登录导致断线 |
1.通过onCallingEnd回调返回 2.onCallingEnd先于onRemoteLogin回调 |
4.0.0 |
- |
14006 |
通话过程中调用销毁SDK导致断线 |
通过onCallingEnd回调返回 |
4.0.0 |
- |
19999 |
未知SDK内部错误 |
- |
4.0.0 |
- |
20001 |
本地会话邀请参数错误 |
HangUp事件返回的,AgoraGateway侧返回的,还没到平台 |
4.0.0 |
- |
20002 |
主被叫错误 |
HangUp事件返回的,AgoraGateway侧返回的,还没到平台 |
4.0.0 |
- |
20003 |
网关内部错误 |
HangUp事件返回的,AgoraGateway侧返回的,还没到平台 |
4.0.0 |
- |
20004 |
SDK侧RTP超时 |
HangUp事件返回的,AgoraGateway侧返回的,还没到平台 |
4.0.0 |
- |
20005 |
SIP侧RTP超时 |
HangUp事件返回的,AgoraGateway侧返回的,还没到平台 |
4.0.0 |
- |
20006 |
呼叫Media-Server超时 |
HangUp事件返回的,AgoraGateway侧返回的,呼叫平台,但是没有收到4xx 5xx 或者200Ok |
4.0.0 |
- |
21000 |
通话过程中网关掉线 |
1.SDK收到声网RTC的onUserOffline且reason为USER_OFFLINE_DROPPED时触发 2.SDK收到声网RTC的onUserOffline事件且reason为USER_OFFLINE_QUIT后,1秒后未收到gateway的HANGUP信令时触发 3.通过onCallingEnd回调返回 |
4.0.0 |
- |
21001 |
网关离线无法接受本地会话邀请 |
SDK在RTM的onLocalInvitationFailure回调中判断 |
4.0.0 |
- |
21002 |
网关对本地会话邀请无响应 |
SDK在RTM的onLocalInvitationFailure回调中判断 |
4.0.0 |
- |
21003 |
网关接受邀请超时 |
SDK在RTM的onLocalInvitationFailure回调中判断 |
4.0.0 |
- |
30001 |
企业状态异常 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30002 |
没有外呼权限 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30003 |
并发超限 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30004 |
外呼号码格式错误 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30005 |
未获取到外显号码 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30006 |
风控黑名单拦截 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30007 |
风控频次拦截 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30008 |
风控时间限制拦截 |
HangUp事件返回的,Media-Server返回的 |
4.0.0 |
- |
30009 |
座席策略限制 |
HangUp事件返回的,Media-Server返回的 |
4.4.0 |
- |
30010 |
检测到特殊违规词 |
HangUp事件返回的,Media-Server返回的 |
4.4.0 |
- |
30011 |
tel 解密失败 |
HangUp事件返回的,Media-Server返回的 |
4.4.0 |
- |
30012 |
tel 应该为密文 |
HangUp事件返回的,Media-Server返回的 |
4.4.0 |
- |
39999 |
其他异常 |
HangUp事件返回的,Media-Server返回的,此时需要透传信令中的sipCode给SDK调用方 |
4.0.0 |
- |
40000 |
系统异常(500异常) |
- |
4.0.0 |
- |
40001 |
热线号码未配置 |
- |
4.0.0 |
- |
40002 |
接口频次超限 |
- |
4.0.0 |
- |
40003 |
注册并发超限 |
- |
4.0.0 |
- |
40004 |
呼叫并发超限 |
- |
4.0.0 |
- |
40005 |
账户状态异常:停机 |
- |
4.0.0 |
- |
40006 |
账户状态异常:注销 |
- |
4.0.0 |
- |
40007 |
企业不存在 |
- |
4.0.0 |
- |
40008 |
无可用 Gateway |
- |
4.0.0 |
- |
40009 |
tel 应该为密文 |
- |
4.4.0 |
- |
40010 |
tel 应该为密文 |
- |
4.4.0 |
- |
40011 |
tel 应该为密文 |
- |
4.4.0 |
- |
49912 |
未知RTC HTTP API错误 |
由SDK在无法判断RTC API的具体错误时返回 |
4.0.0 |
- |
2.6. 常见问题
-
主要接口调用流程是怎样的?
接口主要调用流程如下:

在调用 `destroyClient` 之前,可重复调用 `call` 进行外呼. |
在频繁使用 SDK 的情景下,建议在应用初始化时初始化 SDK, 并将实例化后的对象放到安卓服务中使用; 与此同时 `setEventListener` 接口也仅设置一次,并再次手动实现一次 SDK 事件(`setEventListener` 中的回调)的分发,以满足业务需要. `destroyClient` 仅需要在应用被关闭时调用. 在不频繁使用 SDK 的情景下(例如在整个应用生命周期中可能只需要外呼几次甚至不外呼),建议在外呼功能的实现界面按需初始化 SDK, 并调用 `setEventListener` 接口. 外呼结束后,可按需调用 `destroyClient` 以让 SDK 内部释放资源. |
-
SDK各阶段可调用的接口及返回的事件是什么?
阶段名称 |
可调用接口 |
可能返回事件 |
未初始化 |
createClient、getVersion |
无 |
已初始化 |
setEventListener、call、renewAccessToken、destroyClient、isSpeakerphoneEnabled、setEnableLocalAudio、getVersion |
onError、onCallingStart、onAccessTokenWillExpire、onAccessTokenHasExpired、onRemoteInviteReceived、onInviteRefuseByLocal、onRemoteInvitationCancel、onRemoteInviteFailure、onRemoteLogin |
呼叫中 |
dtmf、setEnableSpeakerphone、hangup、renewAccessToken、destroyClient、isSpeakerphoneEnabled、setEnableLocalAudio、getVersion |
onError、onRinging、onCallCancelled、onCallRefused、onCalling、onCallingEnd、onCallFailure、onAccessTokenWillExpire、onAccessTokenHasExpired、onNetworkQuality、onReceiveStreamData、onReceiveStreamSample、onInviteRefuseByLocal、onRemoteInvitationCancel、onLocalNoVoiceStreamSent、onUserFieldModifiedByConfig、onLocalAudioStats、onRemoteAudioStats、onLocalAudioStateChanged、onRemoteAudioStateChanged |
收到会话邀请 |
acceptCall、refuseCall |
onError、onRemoteInviteReceived、onInviteRefuseByLocal、onRemoteInvitationCancel、onRemoteInviteFailure、onRemoteLogin |
-
如何获取本地日志?
建议将日志文件发送给研发人员. 默认日志文件路径为: `/Android/data/<package name>/files/agorasdk.log` 其中 `<package name>` 为 app 包名.
-
是否支持断线重连,重连失败后如何通知?
支持断线重连. 目前重连失败后暂无通知, 但会在外呼时收到 `onCallFailure` 事件, errorCode 为 `ERR_CALL_FAILED_RTM_ERROR`
-
accessToken过期后,如果未调用renewAccessToken将新accessToken更新到 SDK 中,SDK是否仍可用?
不可用,SDK内部在accessToken过期后将自动调用 `destoryClient` 接口.
-
回呼号码的有效期为多长时间?(A呼叫B后,B在多长时间内,仍能通过呼叫B的号码回呼到A)
有效期取决于平台的主叫记忆时长设置
3. iOS开发文档
3.2. 集成SDK
目前支持的最低iOS系统及平台架构: iOS9.0+ (arm64, x86_64, armv7(3.6.2版本不再支持))
3.3. 设置权限
(必填)麦克风权限:NSMicrophoneUsageDescription
(选填)后台通话权限:Required background modes:App plays audio or streams audio/video using AirPlay
3.4. SDK接口说明
3.4.1. 初始化
+ (TiCloudRTCEngine * _Nonnull)createClient:(TiCloudRTCEngineConfig *_Nonnull)config success:(void (^)( NSDictionary *data))successBlock error:(void (^)(TiCloudRtcErrCode nErrorCode, NSString *errorDes))errorBlock;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
config |
必填 |
[TiCloudRTCEngineConfig](#ticloudrtcengineconfig) |
创建客户端所需的参数配置 |
successBlock |
选填 |
block |
成功结果回调 |
errorBlock |
选填 |
block |
失败结果回调 |
TiCloudRTCEngineConfig
@interface TiCloudRTCEngineConfig : NSObject
// 平台地址
@property(nonatomic, copy, nonnull) NSString *rtcEndpoint;
// 企业ID
@property(nonatomic, assign) NSInteger enterpriseId;
// 用户ID
@property(nonatomic, copy, nonnull) NSString *userId;
// 登录接口返回的 accessToken
@property(nonatomic, copy, nonnull) NSString *accessToken;
// 预留扩展参数
@property(nonatomic, copy, nullable) NSDictionary<NSString *, NSString *> *advancedConnectConfig;
// 调试标识,true 为输出调试信息,false 为不输出调试信息
@property(nonatomic, assign) BOOL isDebug;
/**
* 启动SDK端观测云,true 为启动观测云,false 为不启动观测云
*
* 注:
* 1. 当设置该标识为 false 时, 关闭 SDK 的观测云初始化.
*
* 2. 当设置该标识为 true 时, 开启 SDK 的观测云初始化.
*
* 3. 如果 APP 端使用观测云, 则 isStartFTMobile 需要设置为 false.
*/
@property(nonatomic, assign) BOOL isStartFTMobile;
// 主叫号码(用于回呼)
@property(nonatomic, copy, nullable) NSString *callerNumber;
/**
* 是否在对端挂断时播放预置挂机音频(注:通话错误导致的挂机不会播放音频)
*
* true: 播放音频(默认)
* false: 不播放音频
*/
@property(nonatomic, assign) BOOL isPlayHangupAudio;
+ (instancetype _Nonnull)defaultConfig;
@end
3.4.2. 设置事件监听
-(void)setEventListener:(nullable id<TiCloudRTCEventDelegate>)delegate;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
delegate |
必填 |
TiCloudRTCEventDelegate |
事件监听 |
TiCloudRTCEventDelegate
@protocol TiCloudRTCEventDelegate <NSObject>
/**
* 引擎全局内错误信息事件回调
*
* @param errorCode 错误码
* @param errorMessage 错误描述
*/
- (void)onError:(TiCloudRtcErrCode)errorCode errorMessage:(nonnull NSString *)errorMessage;
/**
* 开始外呼
*
* @param requestUniqueId 为当前呼叫的唯一标识
*/
- (void)onCallingStart:(nonnull NSString *)requestUniqueId;
/**
* 播放铃声中
*
*/
- (void)onRinging;
/**
* 外呼已取消
*
*/
- (void)onCallCancelled;
/**
* 呼叫被拒绝
*
*/
- (void)onCallRefused;
/**
* 呼叫中
*
*/
- (void)onCalling;
/**
* 调用 SDK hangup 接口引发挂断
*
*/
- (void)onLocalHangup;
/**
* 对方接起后挂断
*
*/
- (void)onRemoteHangup;
/**
* 外呼结束
* @param errorCode 错误码
* @param errorMessage 错误描述
* @param sipCode sip错误码
*
*/
- (void)onCallingEnd:(TiCloudRtcErrCode)errorCode errorMessage:(nonnull NSString *)errorMessage sipCode:(NSInteger)sipCode;
/**
* 外呼失败
*
* @param errorCode 错误码
* @param errorMessage 错误描述
*/
- (void)onCallFailure:(TiCloudRtcErrCode)errorCode errorMessage:(nonnull NSString *)errorMessage;
/**
* token即将过期提醒
*
* @param accessToken 将在 10 分钟后过期
*
* 注:
* 当前回调返回的 accessToken 不可用于 renewAccessToken 方法
* 此处的 accessToken 一般用于日志打印。当此事件回调执行时,
* app 应该调用部署了 accessToken 生成工具的自建服务端提供的接口,生成新的 accessToken,
* 并使用新的 accessToken 作为参数调用 SDK 的 renewAccessToken 方法
*
*/
- (void)onAccessTokenWillExpire:(nonnull NSString *)accessToken;
/**
* token已过期
*
*/
- (void)onAccessTokenHasExpired;
/**
* 拉取到的对方的数据流
*
* @param data 对方原始数据
* @param size 数据大小
*/
- (void)receiveStreamDataFromOther:(void *_Nonnull)data size:(int)size;
/**
* 拉取的对端的音频格式
*
* @param samples 采样率
* @param channels 声道数
*/
- (void)receiveStreamSample:(int)samples channels:(int)channels;
/**
* 检测本端网络质量
*/
- (void)networkQuality:(TiCloudRtcNetwotkQuality)netwotkQuality;
/**
* 接收到远端呼叫
*
* @param fields 包含如下数据:
*
* customerNumber:主叫号码
* requestUniqueId:通话唯一标识
*
*/
- (void)onRemoteInvitationReceived:(nonnull NSDictionary *)fields;
/**
* 远端呼叫已拒绝
*
* @param fields 包含如下数据:
*
* customerNumber:主叫号码
* requestUniqueId:通话唯一标识
* isCalling:标识本次邀请是否因正处于通话中而自动拒绝 YES:是,NO:否
*
*/
- (void)onInvitationRefusedByLocal:(nonnull NSDictionary *)fields;
/**
* 远端呼叫已取消
*
* @param fields 包含如下数据:
*
* customerNumber:主叫号码
* requestUniqueId:通话唯一标识
*
*/
- (void)onRemoteInvitationCanceled:(nonnull NSDictionary *)fields;
/**
* 接收回呼失败
*
* errorCode:错误码
* errorMessage:通话唯一标识
*
*/
- (void)onRemoteInviteFailure:(TiCloudRtcErrCode)errorCode errorMessage:(nonnull NSString *)errorMessage
/**
* 当前 userId 在其他设备登录,此时引擎已销毁
*/
- (void)onRemoteLogin;
/**
* 本地监测无发送语音流时间间隔5秒时回调
*/
- (void)onLocalNoVoiceStreamSent;
/**
* SDK 根据平台配置对 userField 外呼参数里的特殊字符进行了移除处理
*
* @param removedCharList 被移除的特殊字符列表
* @param srcUserField 原始的 userField
* @param finalUserField 处理后的 userField
* */
- (void)onUserFieldModifiedByConfig:(nonnull NSArray *)removedCharList srcUserField:(nonnull NSString *)srcUserField finalUserField:(nonnull NSString *)finalUserField;
/// 远程音频状态改变
- (void)onRemoteAudioStateChangedOfUid:(NSUInteger)uid state:(AgoraAudioRemoteState)state reason:(AgoraAudioRemoteReason)reason elapsed:(NSInteger)elapsed;
/// 远程音频质量统计
- (void)onRemoteAudioStats:(AgoraRtcRemoteAudioStats * _Nonnull)stats;
/// 本地音频状态改变
- (void)onLocalAudioStateChanged:(AgoraAudioLocalState)state error:(AgoraAudioLocalError)error;
/// 本地音频质量统计
- (void)onLocalAudioStats:(AgoraRtcLocalAudioStats *_Nullable)stats;
/**
* 检测全部用户网络质量
*
* uid:用户id
*
* txQuality:传输质量
* rxQuality:接受质量
*
*/
- (void)networkQuality:(NSUInteger)uid txQuality:(AgoraNetworkQuality)txQuality rxQuality:(AgoraNetworkQuality)rxQuality;
@end
3.4.3. 外呼
- (void)call:(TiCloudRTCCallConfig *_Nonnull)config success:(void (^)(void))successBlock error:(void (^)(TiCloudRtcErrCode nErrorCode, NSString *errorDes))errorBlock;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
config |
必填 |
TiCloudRTCCallConfig |
外呼所需的参数配置 |
successBlock |
选填 |
block |
成功结果回调 |
errorBlock |
选填 |
block |
失败结果回调 |
TiCloudRTCCallConfig
@interface TiCloudRTCCallConfig : NSObject
// 外呼号码
@property(nonatomic, copy, nonnull) NSString *tel;
// 外显号码
@property(nonatomic, copy, nullable) NSString *clid;
// 指定外显区号
@property(nonatomic, copy, nullable) NSString *obClidAreaCode;
// 指定外显号码池,使用此参数时obClid参数无效
@property (nonatomic, copy, nullable) NSString *obClidGroup;
// 坐席号
@property(nonatomic, copy, nullable) NSString *cno;
// 通话唯一标识 不填则有sdk内部生成
@property(nonatomic, copy, nullable) NSString *requestUniqueId;
// 自定义字段
@property(nonatomic, copy, nullable) NSString *userField;
// 呼叫场景 1:客服场景 6:外呼场景
@property(nonatomic, assign) TiCloudRtcScence type;
// 主叫号码(用于回呼,若不为空可覆盖初始化时传入的值)
@property(nonatomic, copy, nullable) NSString *callerNumber;
/**
* 签名时间戳
*
* 注: 在加密模式下用到,不使用加密则不需要传入
*/
@property(nonatomic, assign) NSInteger timestamp;
/**
* 参数签名
*
* 注: 在加密模式下用到,不使用加密则不需要传入
*
* 签名规则:
* 1. 将参数 tel、callerNumber、clid、obClidAreaCode、obClidGroup、userField、type、timestamp 按照字典顺序排序,
* 然后按照 key=value&key2=value2 的规则进行拼接(注意:如果没有传对应字段则不用参与拼接,requestUniqueId 不参与签名)
* 2. 将企业 ID 拼接到拼接串开头,将企业 Token 拼接到拼接串结尾
* 3. 将拼接串进行 SHA256 加密,最终得到的值就是签名(sign)
*
* 签名示例:
* 假设企业 ID 为 7000002 , 企业 Token 为 token123456
* 请求参数为
* tel: 13000000000
* callerNumber: 14000000000
* obClidGroup: 号码池1
* type: 6
* timestamp: 1735192796
*
*
* 按照步骤开始计算签名
*
* 经过第一步计算之后得到 callerNumber=14000000000&obClidGroup=号码池1&tel=13000000000×tamp=1735192796&type=6
*
* 经过第二步计算之后得到 7000002callerNumber=14000000000&obClidGroup=号码池1&tel=13000000000×tamp=1735192796&type=6token123456
*
* 经过第三步计算之后得到 398db99bf641dbc489a61204257ea7e91ce2dd490a3f37862dc6c4992168c5dd
*
*/
@property(nonatomic, copy, nullable) NSString *sign;
// 预留扩展参数
@property(nonatomic, copy, nullable) NSDictionary<NSString *, NSString *> *advancedConnectConfig;
+ (instancetype _Nonnull)defaultConfig;
@end
呼叫场景
typedef NS_ENUM(NSInteger, TiCloudRtcScence)
{
TiCloudRtcScence_AGENTSCENCE = 1, // 客服场景
TiCloudRtcScence_OUTCALLSCENCE = 6, // 外呼场景
};
网络状态
typedef NS_ENUM(NSInteger, TiCloudRtcNetwotkQuality)
{
TiCloudRtcNetwotkQuality_Unknown, // 网络质量未知
TiCloudRtcNetwotkQuality_Good , // 网络质量较好
TiCloudRtcNetwotkQuality_General , // 网络质量一般
TiCloudRtcNetwotkQuality_Bad , // 网络质量较差
};
userField传值示例 假设要传递 3 个数据 id、workNum、depId
NSString *userField = "["
"{\"name\":\"id\",\"value\":\"90007573\",\"type\":1},"
"{\"name\":\"workNum\",\"value\":\"1026658\",\"type\":1},"
"{\"name\":\"depId\",\"value\":\"340179\",\"type\":1}"
"]";
TiCloudRTCCallConfig * callConf = [[TiCloudRTCCallConfig alloc] init];
callConf.type = TiCloudRtcScence_AGENTSCENCE;
callConf.userField = userField;
[self.SDKEngine.tiCloudEngine call:callConf success:^{
} error:^(TiCloudRtcErrCode nErrorCode, NSString * _Nonnull errorDes) {
}];
3.4.6. 挂断
- (void)hangup;
3.4.7. 销毁
- (void)destroyClient:(void (^)(void))onSuccess error:(void (^)(TiCloudRtcErrCode errorCode, NSString *errorMessage))onFailed;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
onSuccess |
选填 |
block |
成功销毁回调 |
onFailed |
选填 |
Block |
销毁失败回调 |
3.4.8. 更新令牌
通过新的令牌 [accessToken](以下均以accessToken为示例) 刷新 SDK 的使用期限
- (void)renewAccessToken:(NSString *)accessToken;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
accessToken |
必填 |
String |
新的accessToken |
3.4.9. 发送按键
- (void)dtmf:(NSString *)digits;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
digits |
必填 |
String |
按键字符,取值范围: 0,1,2,3,4,5,6,7,8,9,*,# |
|
3.4.10. 扬声器控制
- (void)setEnableSpeakerphone:(BOOL)isEnable;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
isEnable |
必填 |
BOOL |
YES: 打开扬声器 NO: 关闭扬声器 |
|
3.4.11. 是否开启扬声器
BOOL isEnable = [rtcClient isSpeakerphoneEnabled];
返回值说明
YES: 扬声器已开启,语音会输出到扬声器
NO: 扬声器未开启,语音会输出到非扬声器(听筒,耳机等)
3.4.12. 本地音频控制
-(void)setMicrophoneMute:(BOOL)muted;
参数说明
参数名称 |
要求 |
参数类型 |
说明 |
muted |
必填 |
BOOL |
YES: 关闭本地音频 (静音)NO: 打开本地音频(取消静音)) |
|
3.4.13. 获取版本号
NSString *String = [rtcClient getVersion];
3.6. 常见问题
-
主要接口调用流程是怎样的?
接口主要调用流程如下:

|
在频繁使用 SDK 的情景下,建议在应用初始化时初始化 SDK, 并将实例化后的对象放到iOS服务中使用; 与此同时 `setEventListener` 方法也仅设置一次,并再次手动实现一次 SDK 事件(setEventListener 中的回调)的分发,以满足业务需要. `destroyClient` 仅需要在应用被关闭时调用. 在不频繁使用 SDK 的情景下(例如在整个应用生命周期中可能只需要外呼几次甚至不外呼),建议在外呼功能的实现界面按需初始化 SDK, 并调用 `setEventListener` 方法. 外呼结束后,可按需调用 `destroyClient` 以让 SDK 内部释放资源. |
-
调用依赖顺序
本SDK中,部分方法需要注意先后顺序: (1)初始化[TiCloudRTC](#初始化-ticloudrtc-客户端)方法在所有方法之前 (2)方法[通话中发送按键信息](#通话中发送按键信息)必须在通话中才有效 (3)方法[打开/关闭扬声器](#打开关闭扬声器)只在播放铃声中和通话中有效
-
如何获取本地日志?
如需查看本地日志,iOS日志路径为:App Sandbox/Library/Caches/agorasdk.log
-
是否支持断线重连,重连失败后如何通知?
支持断线重连. 目前重连失败后暂无通知, 但会在外呼时收到 `onCallFailure` 事件, errorCode 为 `ERR_CALL_FAILED_RTM_ERROR`
-
accessToken过期后,如果未调用renewAccessToken将新accessToken更新到SDK 中,SDK是否仍可用?
不可用,SDK内部在accessToken过期后将自动调用 `destoryClient` 方法。
4. HarmonyOS开发文档
4.2. 环境要求
-
HarmonyOS API 12 及以上
-
DevEco Studio 5.0 及以上
-
Node.js 18.0.0 及以上
4.3. 集成SDK
4.3.2. 权限配置
在 module.json5 文件中添加以下权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "需要使用麦克风进行通话",//此处建议引用$string资源文件内容
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "为了在后台使用语音通话",//此处建议引用$string资源文件内容
"usedScene": {
"abilities": [
"EntryAbility"
],
"when":"inuse"
}
},
{
"name": "ohos.permission.INTERNET"
}
]
}
}
4.4. SDK接口说明
4.4.1. 初始化
TiCloudRTC.shared.CreateEngine(createClientOption, resultCallback);
参数说明:
CreateClientOption 参数说明:
参数名称 | 参数类型 | 说明 |
---|---|---|
rtcEndpoint |
string |
RTC 服务地址,必填项(请联系技术支持) |
enterpriseId |
string |
企业 ID,必填项,由系统分配的企业唯一标识 |
userId |
string |
用户 ID,必填项,用于标识用户身份,建议使用系统中唯一的用户标识 |
accessToken |
string |
访问令牌,必填项,用于鉴权,可通过服务端接口获取。注:生成 accessToken 时,有效时间至少设置为 10 分钟以上,设置极端的 10 分钟有可能因为网络延迟导致 SDK 认为 accessToken 有效期小于 10 分钟而导致无法初始化。建议设置 15 分钟以上。 |
advancedConnectConfig |
Map<string, string> |
高级配置,可选项,用于设置特殊参数,一般情况下无需设置 |
debug |
boolean |
是否开启调试模式,默认为 true,开启后会输出详细日志 |
CreateResultCallback 回调说明:
回调方法 | 说明 |
---|---|
onSuccess(rtcClient: TiCloudRTC) |
初始化成功回调,返回 TiCloudRTC 实例,可以进行后续操作 |
onFailed(errorCode: number, errorMessage: string) |
初始化失败回调,返回错误码和错误信息,可根据错误码进行相应处理。errorCode 可能的值为:10xxx 类错误码、11xxx 类错误码、19999、4xxxx 类错误码 |
调用示例:
// 创建配置选项
let createClientOption = new CreateClientOption()
createClientOption.rtcEndpoint = "YOUR_RTC_ENDPOINT" // RTC服务地址
createClientOption.enterpriseId = "YOUR_ENTERPRISE_ID" // 企业ID
createClientOption.userId = "YOUR_USER_ID" // 用户ID
createClientOption.accessToken = "YOUR_ACCESS_TOKEN" // 访问令牌
createClientOption.advancedConnectConfig = new Map() // 高级配置
createClientOption.debug = true // 是否开启调试模式
// 初始化引擎
TiCloudRTC.shared.CreateEngine(createClientOption, {
onSuccess: () => {
console.info('引擎初始化成功');
},
onFailed: (errorCode: number, errorMessage: string) => {
console.error('引擎初始化失败:', errorMessage);
}
});
4.4.2. 设置事件监听器
TiCloudRTC.shared.setEventListener(listener);
参数说明:
-
listener
: TiCloudRTCEventListener 类型,事件监听器实例
调用示例:
// 实现 TiCloudRTCEventListener 接口
class MyEventHandler implements TiCloudRTCEventListener {
onError?(errorCode: number, errorMessage: string): void {
console.error('发生错误:', errorMessage);
}
onCallingStart?(requestUniqueId: string): void {
console.info('开始呼叫:', requestUniqueId);
}
onRinging?(): void {
console.info('正在振铃');
}
onCalling?(): void {
console.info('通话进行中');
}
// ... 实现其他需要的回调方法
}
// 设置事件监听
TiCloudRTC.shared.setEventListener(new MyEventHandler());
4.4.3. 外呼
TiCloudRTC.shared.call(callOption);
参数说明:
CallOption 参数说明:
参数名称 | 参数类型 | 说明 |
---|---|---|
tel |
string |
被叫号码,必填项,例如:138xxxx8000,呼叫用户场景下为用户手机号 |
type |
number |
呼叫类型,必填项,6:外呼场景;1:客服场景 |
clid |
string |
外显号码,可选项,用于设置主叫方显示的号码,若不设置则使用系统配置的默认外显号码 |
requestUniqueId |
string |
请求唯一标识,可选项,用于标识本次呼叫,若不设置则系统自动生成 |
userField |
string |
用户自定义字段,可选项,最大长度 128 字节,可用于传递业务相关信息。注:userField 中的所有空格将被清除。 |
调用示例:
let callOption = new CallOption();
callOption.tel = "PHONE_NUMBER" // 被叫号码
callOption.type = 6 // 呼叫类型
callOption.clid = "DISPLAY_NUMBER" // 外显号码
TiCloudRTC.shared.call(callOption);
4.4.6. 发送按键
TiCloudRTC.shared.dtmf(digits);
参数说明:
-
digits
: string 类型,DTMF 按键信号,支持 0-9、*、#
|
调用示例:
// 发送 DTMF 按键信号
TiCloudRTC.shared.dtmf("1");
4.4.7. 更新令牌
TiCloudRTC.shared.renewAccessToken(newAccessToken);
参数说明:
-
newAccessToken
: string 类型,新的访问令牌
调用示例:
// 更新 AccessToken
TiCloudRTC.shared.renewAccessToken("NEW_ACCESS_TOKEN");
4.4.9. 销毁
TiCloudRTC.shared.destroyClient(callback);
4.4.10. 扬声器控制
TiCloudRTC.shared.setEnableSpeakerphone(enable);
参数说明:
-
enable
: boolean 类型,true 表示开启扬声器,false 表示关闭扬声器
调用示例:
// 控制扬声器
TiCloudRTC.shared.setEnableSpeakerphone(true); // 开启扬声器
TiCloudRTC.shared.setEnableSpeakerphone(false); // 关闭扬声器
当前方法只在播放铃声中和通话中有效 |
4.4.11. 是否开启扬声器
const enabled = TiCloudRTC.shared.isSpeakerphoneEnabled();
返回值:
-
enabled
: boolean 类型,true 表示扬声器已开启,false 表示扬声器已关闭
当前方法无论是否在通话中都能调用 |
4.4.12. 音频采集控制
TiCloudRTC.shared.setMicrophoneMute(mute);
参数说明:
-
mute
: boolean 类型,true 表示静音麦克风,false 表示取消静音
|
调用示例:
// 控制麦克风
TiCloudRTC.shared.setMicrophoneMute(true); // 静音
TiCloudRTC.shared.setMicrophoneMute(false); // 取消静音
4.4.13. 获取版本号
const version = await TiCloudRTC.shared.getVersion();
返回值:
-
version
: string 类型,SDK 版本号
调用示例:
// 获取版本号
const version = await TiCloudRTC.shared.getVersion();
4.6. 注意事项
|
5. 小程序开发文档
5.1. 小程序版本
3.6.0
5.2. 使用流程
1.联系客户经理开通CTI-Cloud小程序呼叫服务
2.在用户管理页给员工授权(rtc外呼配置项开启)
3.在系统对接的接口密钥生成密钥对(获取token时使用)
|
5.3. 接入方式
5.3.1. 企微H5应用外呼
唤起小程序外呼
企微H5应用将其余参数拼接到CTI-Cloud启动path后,并通过企微JS-SDK调用wx.invoke接口唤起小程序. 接口说明
以上截图来自腾讯官方文档, 实际使用时, 需要替换为CTI-Cloud小程序的appid及实际的path数据。 |
小程序页面路径: "pages/calling/calling"
参数说明:
参数名称 |
是否必填 |
类型 |
说明 |
token |
是 |
String |
获取到的token(每次外呼都需要重新获取) |
enterpriseId |
是 |
Integer |
企业ID |
cno |
是 |
String |
座席工号,4-11位数字 |
called_number |
是 |
String |
被叫电话号码,明文(注意需要进行 encodeURIComponent ) |
clid |
否 |
String |
外显号码 |
requestUniqueId |
否 |
String |
通话唯一标识(可通过调用相应接口/cc/list_cdr_obs获取通话记录唯一标识mainUniqueId) |
userField |
否 |
String |
自定义参数,为经过 URL 编码之后的 JSON 字符串, JSON 内容具体格式见下面例子 |
callerNumber |
否 |
String |
主叫真实号码(回呼时会呼到当前号码) |
obClidAreaCode |
否 |
String |
外显号码区号 |
obClidGroup |
否 |
String |
外显号码组 |
platform |
是 |
String |
自动登录的平台: 北京平台: 0 ; 上海平台: 1 ; |
type |
是 |
String |
小程序自动登录的类型: 1:H5应用免登录 2.小程序免登录 |
enableSpeaker |
否 |
Boolean |
控制是否打开扬声器. true: 打开扬声器; false: 不打开扬声器. 默认为 false |
useTRtc |
否 |
Boolean |
标识是否使用 TRTC. true: 使用; false: 不使用. 默认为 false (TRTC 费用更高质量更好) |
isPlayHangupAudio |
否 |
Boolean |
标识是否在对端挂断时播放预置挂机音频. true: 播放; false: 不播放. 默认为 true |
示例代码:
/**
* 假设通过 userField 传递 3 个数据: id、workNum、depId,
* 值分别为: 90007573、1026658、340179
*/
let userField = encodeURIComponent(JSON.stringify({
"id":"90007573",
"workNum":"1026658",
"depId":"340179",
}))
wx.invoke(
'launchMiniprogram',
{
"appid" : 'wxae54af9eaf3f3982', // CTI-Cloud 小程序 appid
"path" : `pages/calling/calling?token=${token}&enterpriseId=${enterpriseId}&cno=${cno}&called_number=${calledNumber}&clid=${clid}&requestUniqueId=${requestUniqueId}&userField=${userField}&callerNumber=${callerNumber}&platform=${platform}&type=${type}&enableSpeaker=${enableSpeaker}`, // 所需跳转的小程序内页面路径及参数
},
function(res) {
if(res.err_msg == "launchMiniprogram:ok") {
// launchMiniprogram 调用正常
} else {
// launchMiniprogram 调用错误
}
}
)
|
5.3.2. 小程序外呼
小程序跳转小程序接口 wx.navigateToMiniProgram 接口说明
小程序页面路径: "pages/calling/calling"
参数说明:
参数名称 |
是否必填 |
类型 |
说明 |
token |
是 |
String |
接口1中获取到的token |
enterpriseId |
是 |
Integer |
企业ID |
cno |
是 |
String |
座席工号,4-11位数字 |
called_number |
是 |
String |
被叫电话号码,明文 |
clid |
否 |
String |
外显号码 |
requestUniqueId |
否 |
String |
通话唯一标识(可通过调用相应接口/cc/list_cdr_obs获取通话记录唯一标识mainUniqueId) |
userField |
否 |
String |
自定义参数,为经过 URL 编码之后的 JSON 字符串, JSON 内容具体格式见下面例子 |
callerNumber |
否 |
String |
主叫真实号码(回呼时会呼到当前号码) |
obClidAreaCode |
否 |
String |
外显号码区号 |
obClidGroup |
否 |
String |
外显号码组 |
platform |
是 |
String |
自动登录的平台: 北京平台: 0 ; 上海平台: 1 ; |
type |
是 |
String |
小程序自动登录的类型: 1:H5应用免登录 2.小程序免登录 |
enableSpeaker |
否 |
Boolean |
控制是否打开扬声器. true: 打开扬声器; false: 不打开扬声器. 默认为 false |
useTRtc |
否 |
Boolean |
标识是否使用 TRTC. true: 使用; false: 不使用. 默认为 false (TRTC 费用更高质量更好) |
isPlayHangupAudio |
否 |
Boolean |
标识是否在对端挂断时播放预置挂机音频. true: 播放; false: 不播放. 默认为 true |
示例代码:
/**
* 假设通过 userField 传递 3 个数据: id、workNum、depId,
* 值分别为: 90007573、1026658、340179
*/
let userField = encodeURIComponent(JSON.stringify({
"id":"90007573",
"workNum":"1026658",
"depId":"340179",
}))
wx.navigateToMiniProgram({
appId: 'wxae54af9eaf3f3982', // CTI-Cloud 小程序 appid
path: `pages/calling/calling?token=${token}&enterpriseId=${enterpriseId}&cno=${cno}&called_number=${calledNumber}&clid=${clid}&requestUniqueId=${requestUniqueId}&userField=${userField}&callerNumber=${callerNumber}&platform=${platform}&type=${type}&enableSpeaker=${enableSpeaker}`,
success(res) {
// 打开成功
}
})
5.4. 错误码
报错提示汇总,详细说明参见2.5安卓错误码
提示类别 | 提示内容 | |||
---|---|---|---|---|
1xxxx |
[10000]初始化错误,请联系企业管理员 |
[10001]当前网络异常,请在网络较好时使用 |
[10002]企业配置有误,请联系企业管理员 |
[10003]初始化中,请稍后使用 |
[10004]企业配置有误,请联系企业管理员 |
[10005]企业配置有误,请联系企业管理员 |
[10006]企业配置有误,请联系企业管理员 |
[10007]企业配置有误,请联系企业管理员 |
|
[10008]当前网络较差,小程序重连超限,请稍后几分钟再试 |
[10009]企业配置有误,请联系企业管理员 |
[11000]企业鉴权失败,请联系企业管理员 |
[11001]企业配置有误,请联系企业管理员 |
|
[11002]企业配置有误,请联系企业管理员 |
[11003]企业配置有误,请联系企业管理员 |
[11004]企业未授权,请联系企业管理员 |
[11005]企业配置有误,请联系企业管理员 |
|
[11006]企业配置有误,请联系企业管理员 |
[11007]企业配置有误,请联系企业管理员 |
[12000]企业配置有误,请联系企业管理员 |
[12001]企业配置有误,请联系企业管理员 |
|
[12002]上一通电话尚未结束,请稍后重试 |
[12003]已有其他设备通话中,请稍后再试或结束当前通话再呼叫 |
[12004]建立通话失败,请稍后再试 |
[12006]按键错误,请挂机后重试 |
|
[12007]未授权麦克风权限,请在手机设置中授权后使用 |
[13000]网络故障,接听失败 |
[13001]网络故障,接听失败 |
[13002]网络故障,接听失败 |
|
[14001]已挂断,通话结束 |
[14002]对方挂断,通话结束 |
[14003]呼叫失败(信令码:4XX) |
[14004]网络超时,请重试 |
|
[14005]账号已在其他设备登录,通话结束 |
[14006]企业配置有误,请联系企业管理员 |
[19999]系统错误,请联系企业管理员 |
- |
|
2xxxx |
[20001]系统错误,请重试 |
[20002]系统错误,请重试 |
[20003]系统错误,请重试 |
[20004]系统错误,请重试 |
[20005]系统错误,请重试 |
[20006]呼叫超时,请重试 |
[21000]系统错误,请重试 |
[21001]系统错误,请重试 |
|
[21002]系统错误,请重试 |
[21003]系统错误,请重试 |
- |
- |
|
3xxxx |
[30001]企业账户异常,请联系企业管理员 |
[30002]未开通外呼权限,请联系企业管理员 |
[30003]外呼并发超限 |
[30004]外呼号码格式错误 |
[30005]企业未配置外显号码,请联系企业管理员 |
[30006]风控黑名单拦截 |
[30007]风控频次拦截 |
[30008]风控时间限制拦截 |
|
[30009]座席策略限制 |
[30010]检测到特殊违规词 |
[30011]未按照加密规范传输,请联系企业管理员 |
[30012]未按照加密规范传输,请联系企业管理员风控时间限制拦截 |
|
[39999]未知错误,请联系企业管理员 |
- |
- |
- |
4xxxx |
[40000]系统异常(500异常) |
[40001]热线号码未配置,请联系企业管理员 |
[40002]企业使用频次超限,请稍后重试 |
[40003]登录并发超限,请稍后重试 |
|
[40004]RTC呼叫并发超限,请稍后重试 |
[40005]企业已停机,请联系企业管理员 |
[40006]企业已注销,请联系企业管理员 |
[40007]企业不存在,请联系企业管理员 |
|
[40008]系统错误,请联系企业管理员 |
[40009]参数校验过期,请稍后重试 |
[40010]参数校验未通过,请稍后重试 |
[40011]重复呼叫,请稍后重试 |
[49999]未知错误,请联系企业管理员 |