> For the complete documentation index, see [llms.txt](https://doc1.antgst.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc1.antgst.com/ant-api/voice/zuo-xi-duan-sdk.md).

# 坐席端SDK

欢迎使用 ANT-SDK。本文档面向接入方开发人员，介绍如何在 Web 页面中集成软电话能力，包括外呼、来电接听、坐席状态管理等功能。

***

## 概述

ANT-SDK 是一款浏览器端软电话 SDK，通过一行 `<script>` 引入即可在网页中实现：

* 呼出电话
* 接听 / 拒接来电
* 通话保持与切换（等待通话管理）
* 坐席状态切换（空闲 / 休息）
* 页面刷新自动恢复

SDK 版本：v1.0.0

***

## 接入准备

### 部署环境

| 项目   | 要求                                  |
| ---- | ----------------------------------- |
| 页面协议 | 必须 HTTPS(浏览器仅在 HTTPS下授予麦克风权限)       |
| 浏览器  | Chrome 90+ / Edge 90+ / Firefox 88+ |
| 网络   | 开放 WSS(7443)与 HTTPS(443)端口          |

### 1. 获取 API Key

接入前需向平台获取 `X-API-Key`，用于调用 Token 接口鉴权。

### 2. 引入 SDK

在页面 `<head>` 中添加：

```html
<script src="https://wssip.antgst.com/sdk/ant-sip-sdk-1.0.js"></script>
```

SDK 加载后会自动注册全局对象 `window.antSdk` 与 Web Component `<ant-sdk>`。

***

## 快速开始

以下示例展示最小可用接入：注册 → 接听来电 → 挂断。

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="https://wssip.antgst.com/sdk/ant-sip-sdk-1.0.js"></script>
</head>
<body>
  <ant-sdk id="sdk"></ant-sdk>

  <button id="btnAnswer">接听</button>
  <button id="btnHangup">挂断</button>

  <script>
    var sdk = document.getElementById('sdk');

    // 1. 注册 SDK
    antSdk.register({
      token: '通过 POST /sdk/token/create 获取,为了鉴权安全，确保不要将此接口暴露在前端',
      showInfo: true
    });

    // 2. 监听关键事件
    sdk.addEventListener('onLoginStatus', function (e) {
      console.log('登录状态:', e.detail[0] === 1 ? '在线' : '离线');
    });

    sdk.addEventListener('onIncoming', function (e) {
      var data = e.detail[0];
      console.log('来电:', data.callerNumber);
    });

    sdk.addEventListener('onCall', function (e) {
      console.log('通话状态:', e.detail[0].callStatus);
    });

    // 3. 操作按钮
    document.getElementById('btnAnswer').onclick = function () {
      sdk.answerCall();
    };
    document.getElementById('btnHangup').onclick = function () {
      sdk.hangupCall();
    };
  </script>
</body>
</html>
```

> **提示**：实际接入时，请将 token 替换为后端接口返回的真实值。

***

## 鉴权

### 获取 Token

接入方后端调用以下接口换取 SDK token：

```
POST https://wssip.antgst.com/v1/sdk/token/create?account={分机号}
Header: X-API-Key: {Your API Key}
```

**响应示例**：

```json
{
  "code": 200,
  "data": "eyJhbGciOiJIUzI1NiJ9...",
  "message": "success"
}
```

| 参数              | 说明                                         |
| --------------- | ------------------------------------------ |
| `account`       | 分机号（坐席绑定的分机）                               |
| `expireSeconds` | 可选，token 有效期（秒），默认 10800（3 小时,60-25200(秒）） |

### Token 失效处理

Token 过期或被吊销时，SDK 会：

1. 派发 `onHttp` 事件，错误码 `100102`
2. 派发 `onLoginStatus(0)` 离线状态
3. 停止重连

接入方需监听 `100102` 错误码，提示用户并重新获取 token 后调用 `antSdk.register()`。

> **注意**：token无续签机制，并且同一坐席创建多个token，只保证最新创建的有效，坐席通话中状态（token 过期时）不会强制下线，通话结束才会失效。

***

## API 参考

### 全局方法 `antSdk`

| 方法/属性                      | 说明          |
| -------------------------- | ----------- |
| `antSdk.register(options)` | 注册并初始化 SDK  |
| `antSdk.unregister()`      | 注销 SDK，清理会话 |
| `antSdk.getState()`        | 获取当前完整状态    |

### `register(options)` 参数

| 参数         | 类型      | 必填 | 默认值     | 说明            |
| ---------- | ------- | -- | ------- | ------------- |
| `token`    | string  | 是  | —       | JWT token     |
| `showInfo` | boolean | 否  | `false` | 来电时是否拉取被叫业务信息 |

**返回值**：`{ code, message }`。`code=0` 表示注册请求已发起，最终状态通过事件通知。

#### `getState()` 状态对象

```js
{
  state: 'READY',              // SDK 状态
  seatStatus: 'idle',          // 坐席状态文本
  seatStatusCode: 1,           // 坐席状态码
  callStatus: null,            // 当前通话状态(null 表示无通话)
  webrtcStatus: 1,             // WebRTC 连接状态
  loginStatus: 1,              // 登录状态
  gatewayConnected: true,      // 网关是否已连接
  sipRegistered: true,         // SIP 是否已注册
  authFailed: false,           // token 是否失效
  lastCallNumber: '13800000000',
  waitingCalls: []             // 等待通话列表
}
```

### `<ant-sdk>` 元素方法

#### callPhone(option) — 外呼

```js
sdk.callPhone({ phone: '13800000000' });
```

| 参数      | 类型     | 必填 | 说明                       |
| ------- | ------ | -- | ------------------------ |
| `phone` | string | 是  | 被叫号码                     |
| `info`  | object | 否  | 业务自定义信息，会随 `onCall` 事件回传 |

#### **answerCall(callId) - 接听来电**

收到 `onIncoming` 事件后调用。所有来电统一进入来电队列,通过 `callId` 指定接听哪一通。

```js
// 接听指定来电(推荐,从 onIncoming 事件中获取 callId) 
sdk.answerCall(callId);
// 不传 callId:接听队列中最早的一通来电 
sdk.answerCall(); 
```

| 参数       | 类型     | 必填 | 说明                    |
| -------- | ------ | -- | --------------------- |
| `callId` | string | 否  | 来电唯一 ID。不传时接听队列中最早的一通 |

> **通话中接听新来电**:若当前已有通话(通话中),调用 `answerCall(callId)` 会自动挂断当前通话并接听新来电,无需先手动挂断。

#### **rejectCall(callId) - 拒接来电**

```js
// 拒接指定来电
sdk.rejectCall(callId);
// 不传 callId:拒接队列中最早的一通来电
sdk.rejectCall();
```

| 参数       | 类型     | 必填 | 说明                    |
| -------- | ------ | -- | --------------------- |
| `callId` | string | 否  | 来电唯一 ID。不传时拒接队列中最早的一通 |

#### hangupCall(callback) — 挂断当前通话

```js
sdk.hangupCall(function (result) {
  console.log(result.code, result.message);
});
```

#### getWaitingCalls() — 获取等待通话列表

通话中又来新通话时，新通话进入等待队列。调用此方法查询等待列表。

```js
var list = sdk.getWaitingCalls();
// [{ callId: 'xxx', callerNumber: '13800000000' }, ...]
```

#### idle() / rest() — 切换坐席状态

```js
sdk.idle().then(function (result) { /* 切换到空闲 */ });
sdk.rest().then(function (result) { /* 切换到休息 */ });
```

通话中不可切换状态。

#### reConnectWebrtc() — 重连 WebRTC

媒体通道异常时手动触发重连（最多 5 次）。

```js
sdk.reConnectWebrtc();
```

***

## 事件

通过 `addEventListener` 监听。事件参数在 `event.detail` 数组中，取首元素使用。

### onLoginStatus — 登录状态变更

```js
sdk.addEventListener('onLoginStatus', function (e) {
  var status = e.detail[0];  // 0=离线, 1=在线
});
```

### onSeatStatus — 坐席状态变更

```js
sdk.addEventListener('onSeatStatus', function (e) {
  var statusText = e.detail[0];  // 'idle' | 'rest' | 'busy' | 'offline' | 'assigned' 
});
```

### onWebrtcStatus — WebRTC 连接状态变更

```js
sdk.addEventListener('onWebrtcStatus', function (e) {
  var status = e.detail[0];  // 0=未连接, 1=已连接
});
```

### onIncoming — 来电通知

```js
sdk.addEventListener('onIncoming', function (e) {
  var data = e.detail[0];
  // data = { callId, callerNumber, callInfo }
});
```

| 字段             | 类型             | 说明                                 |
| -------------- | -------------- | ---------------------------------- |
| `callId`       | string         | 来电唯一 ID                            |
| `callerNumber` | string         | 主叫号码                               |
| `callInfo`     | object \| null | 业务信息（仅 `showInfo=true` 且配置额外信息时有值） |

> **提示**：已有通话时收到新来电,SDK 自动将其放入来电队列。`onIncoming` 与 `onCall(INCOMING)` 均会为每一通来电派发(每通必发),接入方据此将新来电追加到来电列表。接听使用 `answerCall(callId)`。

### onCall — 通话状态变更

```js
sdk.addEventListener('onCall', function (e) {
  var data = e.detail[0];
  // data = { callStatus, calledNumber, calledInfo, callId }
});
```

| 字段             | 类型             | 说明           |
| -------------- | -------------- | ------------ |
| `callStatus`   | number         | 通话状态码（见状态码表） |
| `calledNumber` | string         | 对方号码         |
| `calledInfo`   | object \| null | 业务信息         |
| `callId`       | string         | 通话唯一 ID      |

### onHttp — 错误通知

```js
sdk.addEventListener('onHttp', function (e) {
  var err = e.detail[0];
  // err = { code, message }
});
```

> **重要**：务必监听此事件并处理 `100102`（token 失效）与 `100103`（多页面被踢）。

***

## 状态码

### 坐席状态

| 文本         | 说明  |
| ---------- | --- |
| `idle`     | 空闲  |
| `assigned` | 已分配 |
| `rest`     | 休息  |
| `busy`     | 忙碌  |
| `offline`  | 离线  |

### 通话状态

| 码值 | 说明       |
| -- | -------- |
| 1  | 外呼中      |
| 2  | 振铃中      |
| 3  | 通话中      |
| 4  | 正常结束     |
| 5  | 未接通 / 被拒 |
| 6  | 来电接入中    |

### 登录状态

| 码值 | 说明 |
| -- | -- |
| 0  | 离线 |
| 1  | 在线 |

### WebRTC 状态

| 码值 | 说明  |
| -- | --- |
| 0  | 未连接 |
| 1  | 已连接 |

***

## 错误码

| 错误码    | 说明          | 接入方处理建议        |
| ------ | ----------- | -------------- |
| 0      | 成功          | —              |
| 100001 | 参数错误        | 检查入参           |
| 100101 | 网关断开（重连上限）  | 提示用户，必要时重新注册   |
| 100102 | Token 无效或过期 | 重新获取 token 并注册 |
| 100200 | SIP 连接失败    | 检查网络后重试        |
| 100201 | SIP 注册失败    | 检查分机配置         |
| 100202 | SIP 未注册     | 等待注册完成后再操作     |
| 100300 | 麦克风权限被拒     | 引导用户授权麦克风      |
| 100400 | 呼叫失败        | 提示用户           |
| 100401 | 接听失败        | 提示用户           |
| 100402 | 挂断失败        | 提示用户           |
| 100500 | ICE 连接失败    | 检查网络环境         |
| 100103 | 登录多页面被踢     | 检查登录情况         |
| 999001 | SDK 未初始化    | 先调用 `register` |
| 999002 | 当前无来电       | 检查调用时机         |
| 999003 | 当前状态不允许操作   | 检查状态机          |
| 999004 | 网关未连接       | 等待连接恢复         |

***

## 对接场景

### 场景 1：标准外呼

```js
// 1. 确认 SDK 就绪
if (!antSdk.getState().sipRegistered) {
  return alert('软电话未就绪');
}

// 2. 发起呼叫
sdk.callPhone({
  phone: '13800000000'
});

// 3. 监听通话状态(在 onCall 事件中处理)
// callStatus: 1(外呼中) -> 2(振铃) -> 3(通话中) -> 4(结束)
```

### 场景 2：来电接听

```js
sdk.addEventListener('onIncoming', function (e) {
  var data = e.detail[0];
  // 弹窗提示用户接听或拒接
  if (confirm('来电:' + data.callerNumber + ',是否接听?')) {
    sdk.answerCall();
  } else {
    sdk.rejectCall();
  }
});
```

### 场景 3：通话中接听第二通来电

所有来电统一进入队列,通话中收到新来电时,`onIncoming` 与 `onCall(INCOMING)` 都会派发。直接调用 `answerCall(callId)` 即可自动挂断当前通话并切换到新来电。

```js
 sdk.addEventListener('onIncoming', function (e) {
    var data = e.detail[0];
    // 当前已有通话时,确认是否切换
    var confirmSwitch = confirm('新来电:' + data.callerNumber + ',是否切换?');
    if (confirmSwitch) {
      sdk.answerCall(data.callId);  // 自动挂断当前通话,接听新来电
    } else {
      sdk.rejectCall(data.callId);
    }
});

```

### 场景 4：坐席状态管理

```js
// 上班签到:切换到空闲
sdk.idle();

// 离开休息:切换到休息
sdk.rest();
```

### 场景 5：Token 失效自动重登

```js
sdk.addEventListener('onHttp', function (e) {
  var err = e.detail[0];
  if (err.code === 100102) {
    // 调用接入方后端重新获取 token
    refreshToken().then(function (newToken) {
      antSdk.register({ token: newToken });
    });
  }
});
```

***

## 页面刷新与多页面

### 页面刷新

SDK 自动保存会话信息，页面刷新后无需重新登录，SDK 会自动恢复连接与状态。

> **提示**：调用 `antSdk.unregister()` 后会清除会话，刷新后不会自动恢复。

### 多页面登录

同一坐席在多个标签页登录时，后登录的页面会挤掉先登录的页面。被挤掉的页面会收到错误码 `100103`，接入方应监听此错误并提示用户。

***

## 常见问题

<details>

<summary>Q: 注册后一直显示未连接？</summary>

检查 token 是否有效、网络是否正常。查看浏览器控制台 `ANT-SDK:` 开头的日志定位问题。

</details>

<details>

<summary>Q: 来电有提示但接听后听不到声音？</summary>

检查浏览器麦克风权限是否已授予（地址栏锁图标），确认音频设备正常。

</details>

<details>

<summary>Q: 如何处理 Token 失效？</summary>

监听 `onHttp` 事件中的 `100102` 错误码，重新获取 token 后调用 `register()`。

</details>

<details>

<summary>Q: 通话中网络抖动导致掉线？</summary>

SDK 会自动重连。网络恢复后检查 `getState().sipRegistered`，必要时调用 `reConnectWebrtc()`。

</details>

<details>

<summary>Q: 如何获取当前完整状态？</summary>

调用 `antSdk.getState()` 可获取登录、坐席、通话、WebRTC 等所有状态。

</details>

<details>

<summary>Q: 刷新后状态会丢失吗？</summary>

不会。SDK 通过 `sessionStorage` 自动恢复，关闭标签页才会清除。

</details>

***

## 技术支持

接入过程中如遇到问题，请提供以下信息联系平台方技术支持：

1. 浏览器控制台 `ANT-SDK:` 开头的完整日志
2. 出现问题的具体操作步骤
3. `antSdk.getState()` 返回的状态快照
4. 出现时间点（精确到分钟）


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://doc1.antgst.com/ant-api/voice/zuo-xi-duan-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
