本文為您詳細介紹呼叫中心SDK前端接入流程。

注意 當前版本僅做維護,后續不會繼續迭代。如有需求,請參見熱線SDK接入(新版)

引用SDK

  1. SDK cdn assets(umd module)

    https://g.alicdn.com/xspace/phone/0.5.1/sdk.js

  2. SDK Preview Sandbox

    https://g.alicdn.com/xspace/phone/0.5.1/index.html#/sample/use-sdk

依賴

  1. SIM-API:https://g.alicdn.com/crm/sipml-api/0.0.8/SIPml-api.js
  2. HTTP:https://g.alicdn.com/xspace/phone/0.5.1/http.js
啟用渲染撥號盤會自動加載以上依賴。加載后即可獲取SDK實例內容:
const { SoftPhoneSDK } = window

前提條件

初始化前需要完成以下準備工作:
  1. 必須使用Chrome瀏覽器,版本號為58以上。原因是云呼叫中心的通話是通過webRTC技術實現的,目前Chrome瀏覽器對于webRTC技術的支持是最好的。為了保證您的通話質量及安全性,所以我們做出了這樣的要求。
  2. 軟電話SDK所嵌入的自有業務系統必須使用HTTPS協議。原因是Chrome在47版本之后,禁止HTTP協議獲取系統麥克風權限,會造成無法正常通話。
  3. 如果您是在iframe標簽內使用軟電話SDK,那么需要為iframe標簽增加allow="microphone"屬性,來允許iframe標簽獲取系統麥克風權限。

接入手冊

1
狀態流轉
  • 藍色為流轉狀態觸發的事件hook,請參見事件回調枚舉。
  • 白色為坐席狀態,請參見坐席狀態枚舉。
  • 加載SDK初始坐席狀態為-1表示未注冊,需要申請InstanceId BearerToken進行簽入坐席操作。每個坐席原則意義上按1人/位,可能存在設置同一坐席公用。具體流程如下:2
  1. 軟電話類
    const { SoftPhone } = window.SoftPhoneSDK;
    const softPhone = SoftPhone.getInstance(); // softphone follow Singleton Pattern

    通過getInstance方法生成唯一實例。

    • 實例API
      API type description
      setConfig (config: Partial<SDKConfig> = {}) 注入配置,如下所示
      register (eventName: EnumEventName, cb: (eventData: { type: EnumEventName, preConfig: Partial<SDKConfig>, data: any)} => void ) => Canceler 注冊LifeCycle事件返回其注銷方法。
      getUIComponent () => React.ElementType 獲取撥號條組件實例,配合APIs.renderComponent 渲染

      使用getUIComponent獲取的組件必須使用APIs.renderComponent去掛載,這是因為SDK內部集成了React&ReactDOM,使用外部版本掛載會導致版本不通或者雙副本的問題。

    • SDKConfig Options
      option type defaultvalue description
      InstanceId string "" 實例ID,可在阿里云控制臺使用主賬號登錄實例列表獲取https://ccc.console.aliyun.com/AccInstance。
      BearerToken string "" bearerToken,通過三方賬號授權置換token中的access_token。
      env "online" | "preOnline" 'online' 配置環境。
      apiHost "scsp.aliyuncs.com" | "aiccs.aliyuncs.com" | "api.rhinokeen.com" 'scsp.aliyuncs.com' API服務器。
      Version string '2020-07-02' POP API版本號。
      • scsp.aliyun.com對應版本為'2020-07-02'
      • aiccs.aliyun.com對應版本為'2019-10-15'
      • api.rhinekeen.com無需版本
      locale EnumLocale EnumLocale['zh-CN'] 國際化。
      isPlugin boolean true 撥號條插件是否支持拖拽, 默認支持。
      needDesensitize boolean false 是否對入呼或外呼號碼脫敏展示,默認不脫敏。
      autoChangeOnLineTime number 1 自定義話后處理時間,單位為s(秒),為0代表不需要自動切狀態,默認為1s自動結束話后處理。
      canChangeStatusByHand boolean false 是否能夠手動切換狀態的能力,默認為false,不展示操作欄&不支持手動更新。
      enableVoiceToText Array<'callin' | 'callout'> [] 啟用語音轉文本,該能力需要BU配置支持。
      enableServiceSummary boolean false 啟用服務摘要。
      disableUI boolean false 是否隱藏UI,默認為false,不隱藏。
      cdnPath string //g.alicdn.com 內置資源默認引用CDN域。
    • Demo
      const config =  {
        InstanceId: 'ccc_xp_pre-cn-78v1gnp97002',   // 實例id,可在阿里云控制臺實例列表獲取
        BearerToken: '',                            // bearerToken,通過【三方賬號授權】置換token中的access_token
        env: 'online',                              // online || preOnline, 
        apiHost: 'api.rhinokeen.com',               // scsp.aliyuncs.com || aiccs.aliyuncs.com || api.rhinokeen.com
        Version: '',                                // Pop Api 版本號,默認為'2020-07-02'. scsp.aliyun.com對應版本為'2020-07-02';aiccs.aliyun.com對應版本為'2019-10-15';api.rhinekeen.com無需版本
        locale: EnumLocale['zh-CN'],
        isPlugin: true,                             // 插件是否支持拖拽,默認支持
        needDesensitize: false,                     // 是否對入呼/外呼號碼脫敏展示,默認不脫敏
        autoChangeOnLineTime: 1,                    // 自定義話后處理時間,單位為s,為0代表不需要自動切狀態,默認為1s自動結束話后處理
        canChangeStatusByHand: false,               // 是否能夠手動切換狀態
        disableUI: false                            // 是否隱藏UI, 默認為false,不隱藏
      };
      softPhone.setConfig(config);
      softPhone.register(EnumEventName.actionError, () => {
        softPhone.setConfig({
          BearerToken: 'newBearerToken'             // token失效后,此處進行事件注冊,設置新的token
        });
      });
      
      const SoftPhoneUI: React.ElementType<any> = softPhone.getUIComponent();
      
      // if use React Framework, you can render component directly
      const ExampleApp: React.FC = () => {
        return (
          <SoftPhoneUI />
        );
      }
      // if not use React Framework, you can render softphone to specified ElementNode
      APIs.renderComponent(SoftPhoneUI, document.querySelector('#phone-container'));
  2. LifeCycle事件注冊

    旨在各調度過程中觸發的鉤子,采用訂閱派發的模式進行管理。具體流轉狀態,請參見流轉狀態流程圖。

    • 事件回調枚舉
      export enum EnumEventName {
        actionError = 'actionError', // 出錯
        actionSuccess = 'actionSuccess', // 撥號盤流程回調
        onAgentCheckedin = 'onAgentCheckedin', // 上班簽入成功回調
        onAgentCheckedout = 'onAgentCheckedout', // 下班簽出成功回調
        onCallComing = 'onCallComing', // 新來電振鈴回調
        onCallDialOut = 'onCallDialOut', // 外撥請求中回調
        onCallWillAnswer = 'onCallWillAnswer', // 呼入電話執行接聽動作回調
        onCallEstablishForCallin = 'onCallEstablishForCallin', // 呼入通話建立完成回調(等同于完成接聽):
        onCallEstablishForCallout = 'onCallEstablishForCallout', // 呼出通話建立完成回調
        onCallWillHangup = 'onCallWillHangup', // 執行掛斷動作回調
        onCallDidHangup = 'onCallDidHangup', // 呼入電話掛斷完成回調
        onCallOutDidHangup = 'onCallOutDidHangup', // 呼出電話掛斷完成回調
        onCallTransferOut = 'onCallTransferOut', // 轉接回調 0.4.16版本新增
        onCallHold = 'onCallHold', // 通話保持回調 0.4.16版本新增
        onCallRecovery = 'onCallRecovery', // 通話恢復回調 0.4.16版本新增
        'actionError.tokenExpired' = 'actionError.tokenExpired',  // token失效回調
      }
    • 注冊監聽Demo
      // register action error callback
      const unRegister = softPhone.register(EnumEventName['actionError.tokenExpired'], (eventData, prevConfig, data) => {
        // here! your can update BearerToken by softPhone.setConfig({BearerToken: 'xxx'});
          const { type, data } = eventData;
        console.log('[SoftPhone actionError.tokenExpired]',type, data);
      });
      unRegister() // unRegister current actionError.tokenExpired event
      eventData字段說明:
      acid: string // 通話id channelId
      agentBasicCode: "AgentBusyForCall" // 代理狀態Code
      agentBasicDesc: "坐席通話忙" // 代理狀態描述
      agentCallCode: "AgentCallDialOut" // 撥號狀態Code
      agentCallDesc: "外撥中" // 撥號狀態描述
      aid: string // 在線id
      ani: string 
      appName: string
      cmd: string
      connId: string // 話務Id
      departmentId: string
      dnis: string
      eventTime: string // 事件時間
      holdConnId: ""
      jobId: string  // 小二職位id
      mid: string // memberId?
      name: string // 會員名稱
      status: AgentBarStatus // 坐席狀態AgentBarStatus
      subSessionId: string // 子會話id
      supportNewFunction: string
      time: string
  3. 靜態APIs列表
    const APIs = {
      renderComponent, // 渲染組件到指定節點
      doCallOut, // 執行外呼
      startWork,  // 執行開始上班, 已廢棄,可由agentCheckin替代
      agentCheckin, // 執行撥號盤簽入
      agentCheckout, // 執行撥號盤簽出
      getAgentBarStatus, // 獲取當前撥號盤狀態
      callAnswer,    // 來電接聽
      callHangup,            // 電話掛斷
      takeWebSocketNotify, // 獲取 websocket 通道實例
    }
    • 坐席簽入Demo
      登錄操作,狀態流轉到AgentBarStatus.AgentCheckout。
      const { APIs } = window.SoftPhoneSDK;
      APIs.agentCheckin(); // 是否成功可通過注冊onAgentCheckedin事件來監聽
    • 坐席簽出Demo
      登出操作,狀態流轉到 AgentBarStatus.AgentCheckin。
      const { APIs } = window.SoftPhoneSDK;
      APIs.agentCheckout(); // 是否成功可通過注冊onAgentCheckedout事件來監聽
    • 發起呼叫Demo
      調用外呼能力,外呼成功后執行AgentBarStatus.onCallDialOut,通過返回的eventData.data執行掛斷操作。
      const { APIs } = window.SoftPhoneSDK;
      
      APIs.doCallOut({
        number, // 要呼叫的號碼
        onSuccess: this.onSuccess, // 外呼成功接通后的回調,參數為通話信息對象
        param: {
          number, // 要呼叫的號碼
          memberId, // 必填, 匿名或未知用戶可傳-1
          memberName, // 必填,用戶名稱,未知可傳 '匿名會員'
          calloutNumber, // 選填, 主叫號碼,如未填則默認為配置的第一個
          fromSource: 'other_system_out', // 必填, hotlinebs_out || ticket_out || other_system_out (熱線||工單||其他系統)
          taskId: this.props.common.taskId, // 如需查動作記錄/服務記錄時, channelId || caseId必傳其中之一
          channelId: this.props.channelId, // 會話id 可選
          caseId: this.props.common.caseId, // 工單id
        }
      })
    • 掛斷電話Demo
      APIs.callHangup(connId, jobId, acid, aid);
      應用場景:呼入不接起掛斷、呼入接起主動掛斷、撥出未接/接起主動掛斷。
      // 撥入掛斷
      softPhone.register(EnumEventName.AgentRinging, (eventData, prevConfig, data) => {
          const { data: { connId, jobId, acid, aid } } = eventData;
        APIs.callHangup(connId, jobId, acid, aid);
      });
      
      // 撥出掛斷
      softPhone.register(EnumEventName.AgentCallDialOut, (eventData, prevConfig, data) => {
          const { data: { connId, jobId, acid, aid } } = eventData;
        APIs.callHangup(connId, jobId, acid, aid);
      });
    • 接聽來電Demo
      softPhone.register(EnumEventName.AgentRinging, (eventData, prevConfig, data) => {
        // 參數可從 AgentRinging 的事件回調參數里取 
          const { data: { connId, jobId, acid, aid } } = eventData;
        APIs.callAnswer(connId, jobId, acid, aid);
      });
    • 獲取坐席狀態Demo
      回坐席狀態,對應值對應的狀態如下,初始為-1未注冊。
      const { APIs } = window.SoftPhoneSDK;
      const status = await APIs.getAgentBarStatus();
      
      enum AgentBarStatus {
        AgentCheckout = 1, // 簽出
        AgentCheckin = 2, // 簽入
        AgentReady = 3, // 空閑
        AgentBreak = 4, // 小休
        AgentCallRelease = 5, // 通話結束(掛斷的時候)
        AgentAcw = 6, // 話后處理
        AgentRinging = 7, // 振鈴
        AgentCallAnswerRequest = 8, // 接聽請求中
        AgentCallDialOut = 9, // 撥號請求中
        AgentCallInboundEstablish = 10, // 呼入通話
        AgentCallOutBoundEstablish = 11, // 呼出通話
        AgentCallInternalBoundEstablish = 12, // 內部通話b打給c
        AgentCallConsultEstablish = 13, // 被動求助通話建立
        AgentCallHeld = 14, // 通話保持
        AgentCallConferenceWaitAnswer = 16, // 發起三方
        AgentCallThirdConsult = 17, // 三方通話中
        AgentThirdRetrieveRequest = 18, // 三方取回中
        AgentCallConference = 19, // 會議  三方通話狀態
      }
    • 獲取webSocket通道
      當某些場景下我們需要定制能力時(e.g.自定義語音轉文本能力),可以直接獲取websocket通道,該對象提供了兩個方法和一個websocket實例。
      type Subscribe = (eventName: string, callback: (msgData: any) => void) => void;
      type PhoneNotify = {
        listenerContainer: {
          [eventName: string]: Array<(msgObj: any) => void>;
        };
        on: Subscribe;
        off: Subscribe;
        socket: ReconnectingWebSocket;
      };
      
      const { APIs } = window.SoftPhoneSDK;
      const notify: PhoneNotify = APIs.takeWebSocketNotify();
      function listenerHotlineMsg(msgData) {
        // 當通道返回的 data.sceneType === 'hotline-message' 時執行
      }
      notify.on('hotline-message', listenerHotlineMsg); // 執行訂閱
      notify.off('hotline-message', listenerHotlineMsg); // 注銷訂閱
      當前(0.4.16)已定的 sceneType有:
      • hotline-message熱線消息,表示通話狀態和信息。
      • voice-to-text語音轉文本,表示推送實時語音同聲傳譯后的文本內容。
  4. 多語言
    • 已支持語言枚舉:
      export enum EnumLocale {
        en = 'en',
        'zh-CN' = 'zh-CN',
      }
    • 默認語言:

      zh_CN

    • 設置語言Demo:
      const { SoftPhone, EnumLocale } = window.SoftPhoneSDK;
      
      const softPhone = SoftPhone.getInstance();
      
      softPhone.setConfig({
          locale: EnumLocale.en, // default = 'zh_CN'
      });
  5. 熱線狀態
    當我們想對熱線中狀態流轉有更高的定制需求時,e.g.
    • 在RTC通道(打開或關閉)時打點。
    • 在通話恢復時同步文本語音信息等。
    可以先獲取到websocket實例,然后根據msgData.head.agentCallCode參照EnumWSEventType枚舉熱線狀態進行定制邏輯處理。具體枚舉:
    export enum EnumWSEventType {
      // 上班狀態
      AgentCheckin = 'AgentCheckin', // 簽入
      AgentCheckout = 'AgentCheckout', // 簽出
      AgentReady = 'AgentReady', // 空閑
      AgentBreak = 'AgentBreak', // 小休
      AgentAcw = 'AgentAcw', // 話后處理
      
      // 通話事件監聽
      AgentRinging = 'AgentRinging', // 來電
      AgentCallInboundEstablish = 'AgentCallInboundEstablish', // 呼入電話建立
      AgentCallOutBoundEstablish = 'AgentCallOutBoundEstablish', // 呼出通話建立
      AgentCallDialOut = 'AgentCallDialOut', // 撥號呼出
      AgentCallAnswerRequest = 'AgentCallAnswerRequest', // 呼入通話執行接聽
      AgentCallRelease = 'AgentCallRelease', // 坐席釋放
      AgentCallHeld = 'AgentCallHeld', // 通話保持
      
      // 雙步轉
      AgentCallConferenceWaitAnswer = 'AgentCallConferenceWaitAnswer', // 三方求助等待應答
      AgentCallConference = 'AgentCallConference', // 會議中
      AgentCallThirdConsult = 'AgentCallThirdConsult', // 三方求助通話中
      AgentThirdRetrieveRequest = 'AgentThirdRetrieveRequest', // 三方取回請求中
      AgentCallConsultThirdRetrieveReq = 'AgentCallConsultThirdRetrieveReq', // 三方求助三方取回請求中
      AgentCallConsultEstablish = 'AgentCallConsultEstablish', // 三方求助電話建立
      AgentCallInternalBoundEstablish = 'AgentCallInternalBoundEstablish', // 內部通話中
    
      // RTC
      AgentJoinChannel = 'AgentJoinChannel', // 創建 RTC 連接
      AgentLeaveChannel = 'AgentLeaveChannel' // 銷毀 RTC 連接
    }
    e.g.
    const { APIs, EnumWSEventType } = window.SoftPhone;
    const notify: PhoneNotify = APIs.takeWebSocketNotify;
    function listenerHotlineMsg(msgData) {
      // 當通道返回的 data.sceneType === 'hotline-message' 時執行
      if(_get(msgData, 'head.agentCallCode') === EnumWSEventType.AgentLeaveChannel) {
          // dosomething
      }
    }
    takeWebSocketNotify.on('hotline-message', listenerHotlineMsg); // 執行訂閱

完整版Demo

export function APPControll(props: RouterCommonProps) {
  const [hash, refresh] = useHash();
  const { state: { config } } = useContext(Context); // 配置托管到context
  const standWrap = useRef();

  useRegisterHooks(props.history); // 注冊鉤子
  useInitalIDPToken(hash); // fetch BearerToken assign to `context state` when hash changed

  useDeepEffect(() => {
    instance.setConfig(config);
    // 檢測到過期 regenerator token
    const unRegister = instance.register(EnumEventName.actionError, refresh);
    // 渲染撥號條
    APIs.renderComponent(instance.getUIComponent(), standWrap.current);
    // 執行簽入操作
    APIs.agentCheckin();
    return () => {
      APIs.agentCheckout();
      unRegister();
    };
  }, [config]);
  return (
        <div>
        <App />
      <div ref={standWrap} className="statusbar" />
    </div>
  )
}

// e.g. use ReactHooks Synchronization status to Context
function useRegisterHooks(history: RouterCommonProps['history']) {
  const { state, dispatch } = useContext(Context);
  useEffect(() => {
    softPhone.register(EnumEventName.onAgentCheckedin, e => {
      safeConsole('成功登錄', e);
      dispatch({
        type: EnumEventName.onAgentCheckedin,
        payload: _get(e, 'data'),
      });
    });
    softPhone.register(EnumEventName.onAgentCheckedout, e => {
      safeConsole('成功登出', e);
      dispatch({
        type: EnumEventName.onAgentCheckedout,
        payload: _get(e, 'data'),
      });
    });
    softPhone.register(EnumEventName.onCallDialOut, e => {
      safeConsole('外撥請求中', e);
      // ...
    });
    softPhone.register(EnumEventName.onCallComing, e => {
      safeConsole('新來電振鈴', e);
      // navigation to callpanel page...
      // 暫不支持呼入方主動掛斷的事件,繞一下輪詢去偵測是否放開坐席
      ;(async function() {
        while (true) {
          await sleep(1000);
          const status = await APIs.getAgentBarStatus();
          if (status === agmentBarStatus.AgentReady) {
            // 處理呼入方主動掛斷邏輯...
            return false;
          }
        }
      })();
    });
    softPhone.register(EnumEventName.onCallWillAnswer, e => {
      safeConsole('呼入電話執行接聽動作', e);
    });
    softPhone.register(EnumEventName.onCallEstablishForCallin, e => {
      safeConsole('已接聽來電', e);
    });
    softPhone.register(EnumEventName.onCallEstablishForCallout, e => {
      safeConsole('呼出通話建立完成', e);
    });
    softPhone.register(EnumEventName.onCallWillHangup, e => {
      safeConsole('掛斷回調', e);
      // navigation to pre page before callin
    });
    softPhone.register(EnumEventName.onCallDidHangup, e => {
      safeConsole('呼入電話掛斷完成', e);
    });
    softPhone.register(EnumEventName.onCallOutDidHangup, e => {
      safeConsole('呼出電話掛斷完成', e);
    });
    softPhone.register(EnumEventName.onCallHold, (e) => {
      safeConsole('通話保持', e);
    });
    softPhone.register(EnumEventName.onCallRecovery, () => {
      safeConsole('通話恢復');
    });
    softPhone.register(EnumEventName.onCallTransferOut, () => {
      safeConsole('轉交');
    });
    softPhone.register(EnumEventName['actionError.tokenExpired'], e => {
      safeConsole('token失效', e);
      // refresh logic...
    });
    return () => /* hanlder cancel events... */
  }, []);
}

版發布記錄

0.5.0

語音轉文本提供呼入呼出可選自開。

0.4.17

  • 優化代碼邏輯,移除非必要打印信息(sipml除外)
  • 新增拋出三個鉤子(保持、恢復、轉交)
0.4.16
  • 新增語音轉文本插件。
  • 新增服務摘要插件。
  • 新增對外暴露websocket以支持定制功能。
  • 在線Demo更新。

0.4.15

新增本地化歸屬

0.4.12

  • 新增對外的接聽和掛斷API。
  • 新增隱藏UI的配置參數。

0.4.10

  • 歸屬地外化多tab不顯示問題修復
  • 增加初始化配置參數-autoChangeOnLineTime用來配置話后處理時間(默認為0,不觸發)
  • 增加初始化配置參數-canChangeStatusByHand用來配置是否隱藏手動操作狀態欄的能力(默認為false,不需要隱藏)

0.4.9

  • 號碼列表支持“號碼組外呼”
  • 浮層層級提升至9999
  • 撥打電話,上下班等操作提供“同步api”
  • 增加“獲取撥號盤狀態”的前端api
  • fuyun api token 過期事件修復
  • 入呼、外呼歸屬地外化

0.4.7

  • 撥號盤內部事件通信優化,棄用msg-bus
  • iconfont更新

0.4.6

Demo頁增加cdnPath的可配置化

0.4.5

  • 增加對后端接口外呼的支持
  • 支持在已加載SIPml-api的環境下依舊能夠正常運行
  • 修改默認外呼來源為other_system_out

0.4.4

  • 增加雙步轉UI調整
  • 測試環境變量修改

0.4.3

  • 交互體驗優化
  • 增加系統外呼透露
  • 增加是否脫敏展現號碼的配置項
  • 增加是否可拖動的配置項
  • Fuyun和Pop空入參優化
  • 轉交邏輯優化,加入咨詢和會議邏輯(暫時隱藏)
  • 部分已知bug修復

附:運行正常時,圖例

123456