# IM即时通讯 - 接入文档

## 一、概述

IM即时通讯是一个全自研多端即时通讯开放平台，支持第三方平台通过密钥授权接入，实现文字、表情、图片、语音、消息撤回、已读未读、离线消息等全套 IM 功能。全端兼容 H5、微信小程序、APP、PC 端。

---

## 二、接入总流程

1. 在 IM即时通讯开放平台注册开发者账号
2. 登录后创建应用，获取 **AppID** 与 **AppSecret**
3. 在开发者服务端，使用 AppSecret 生成 **UserSig** 用户签名
4. 端侧引入 SDK，传入参数初始化
5. 调用 API 发起聊天，实现即时通讯

---

## 三、获取密钥

### 3.1 注册账号
访问平台首页，点击注册，填写用户名、密码、手机号完成注册。

### 3.2 创建应用
登录后进入「应用管理」，点击「创建应用」，填写应用名称即可获得 AppID 和 AppSecret。

> **重要：AppSecret 是核心密钥，仅保存在您的服务端，严禁暴露到前端代码中！**

### 3.3 体验与购买套餐
- 免费体验：每个用户的第一个应用自动获得免费体验，默认有效期 7 天
- 付费套餐：支持月卡、季卡、年卡，按需购买

---

## 四、生成 UserSig

UserSig 是用户登录签名，由您的后端使用 AppSecret 通过 HMAC-SHA256 算法生成。

### 签名算法

- **算法**：HMAC-SHA256
- **签名内容**：`appId + userId + expireTime`
- **有效期**：默认 7 天（604800 秒），可配置

### 生成方式

**方式一：调用平台 API 生成（推荐）**

```bash
POST /api/app/generate-sig
Content-Type: application/json

{
  "appId": "你的AppID",
  "appSecret": "你的AppSecret",
  "userId": "用户唯一标识",
  "expire": 604800    // 可选，有效期秒数，默认7天
}
```

**响应：**
```json
{
  "code": 200,
  "data": {
    "userSig": "生成的签名字符串",
    "appId": "你的AppID",
    "userId": "用户ID",
    "expireAt": 1718600000
  }
}
```

**方式二：自行生成（Node.js 示例）**

```javascript
const crypto = require('crypto');

function generateUserSig(appId, appSecret, userId, expireSeconds = 604800) {
  const expireTime = Math.floor(Date.now() / 1000) + expireSeconds;
  const signContent = `${appId}${userId}${expireTime}`;
  
  const hmac = crypto.createHmac('sha256', appSecret);
  hmac.update(signContent);
  const signature = hmac.digest('base64');
  
  return `${signature}.${expireTime}`;
}
```

---

## 五、Web/H5 端接入

### 5.1 引入 SDK

```html
<script src="https://你的域名/sdk/im-sdk.js"></script>
```

### 5.2 初始化与使用

```html
<!-- 放置聊天容器 -->
<div id="im-container" style="width: 380px; height: 650px;"></div>

<script>
  // 创建IM实例（3行代码即可使用）
  const im = new OpenIM({
    appId: "你的AppID",
    userId: "当前平台用户ID",
    userSig: "后端生成的UserSig",
    nickName: "用户昵称",
    avatar: "用户头像URL（可选）",
    serverUrl: "https://你的域名"  // 可选，默认当前域名
  });

  // 挂载到页面
  im.mount("#im-container");

  // 发起与指定用户的聊天
  im.startChat("对方用户ID", {
    nickName: "对方昵称",
    avatar: "对方头像URL"
  });
</script>
```

### 5.3 API 参考

| 方法 | 说明 | 参数 |
|------|------|------|
| `new OpenIM(config)` | 初始化SDK | config: {appId, userId, userSig, nickName, avatar?, serverUrl?, primaryColor?} |
| `im.mount(selector)` | 挂载UI到DOM | selector: CSS选择器，如 `"#im-container"` |
| `im.startChat(userId, info?)` | 打开聊天窗口 | userId: 对方ID, info: {nickName, avatar} |
| `im.sendMessage(to, type, content)` | 发送消息 | to: 接收方ID, type: text/image/audio, content: 内容 |
| `im.on(event, callback)` | 监听事件 | event: 事件名, callback: 回调函数 |
| `im.logout()` | 退出登录 | 无 |

### 5.4 事件列表

| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| `connect` | 连接成功 | {code, message} |
| `disconnect` | 连接断开 | {code, reason} |
| `private_message` | 收到私聊消息 | {msgId, from, to, type, content, timestamp} |
| `message_ack` | 消息送达回执 | {msgId, status} |
| `message_read` | 消息已读 | {from, to, timestamp} |
| `message_recall` | 消息撤回 | {msgId, from, timestamp} |
| `user_typing` | 输入中 | {from, timestamp} |
| `auth_expired` | 授权过期 | {code: 403, message} |
| `error` | 错误 | {code, message} |

---

## 六、微信小程序端接入

### 6.1 引入 SDK

将 `im-sdk-mp.js` 放入小程序项目的 `utils` 目录。

```javascript
import OpenIM from '../../utils/im-sdk-mp.js';
```

### 6.2 初始化与使用

```javascript
Page({
  onLoad() {
    // 初始化
    this.im = new OpenIM({
      appId: "你的AppID",
      userId: "当前用户ID",
      userSig: "后端生成的签名",
      nickName: "用户昵称",
      avatar: "头像URL",
      serverUrl: "wss://你的域名"
    });

    // 建立连接
    this.im.connect();

    // 监听消息
    this.im.on('private_message', (msg) => {
      console.log('收到消息:', msg);
    });

    // 监听授权过期
    this.im.on('auth_expired', () => {
      wx.showModal({
        title: '提示',
        content: '授权已过期，请续费后使用',
        showCancel: false
      });
    });
  },

  // 发送文本消息
  sendText(toUserId, text) {
    this.im.sendMessage(toUserId, 'text', text);
  },

  // 发送图片消息
  async sendImage(toUserId, filePath) {
    await this.im.sendImage(toUserId, filePath);
  },

  // 发送语音消息
  async sendAudio(toUserId, filePath, duration) {
    await this.im.sendAudio(toUserId, filePath, duration);
  },

  onUnload() {
    this.im.logout();
  }
});
```

---

## 七、APP/其他端接入

APP 和其他端可以通过标准 WebSocket 协议对接，无需特定 SDK。

### 连接地址

```
ws://你的域名/socket.io/?appId={appId}&userId={userId}&userSig={userSig}&EIO=4&transport=websocket
```

### 协议说明

使用 Socket.io v4 协议（Engine.IO v4），消息格式为：

**发送消息：**
```
42["private_message", {"to": "对方ID", "type": "text", "content": "消息内容"}]
```

**接收消息：**
```
42["private_message", {"msgId": "...", "from": "发送方ID", "to": "接收方ID", "type": "text", "content": "消息内容", "timestamp": 1718000000000}]
```

### 消息体格式

```json
{
  "msgId": "全局唯一消息ID",
  "from": "发送方用户ID",
  "to": "接收方用户ID",
  "type": "text/image/audio",
  "content": "消息内容/资源地址",
  "timestamp": 1718000000000,
  "status": 1
}
```

---

## 八、错误码说明

| 错误码 | 说明 | 处理建议 |
|--------|------|----------|
| 200 | 成功 | - |
| 400 | 参数错误 | 检查请求参数是否完整 |
| 401 | UserSig 无效或已过期 | 重新生成 UserSig |
| 403 | 应用已禁用/套餐已过期 | 检查套餐状态，续费后使用 |
| 404 | 应用不存在 | 检查 AppID 是否正确 |
| 429 | 请求过于频繁 | 降低请求频率 |
| 500 | 服务器内部错误 | 联系技术支持 |

---

## 九、常见问题

**Q: UserSig 在哪里生成？**
A: 在您的后端服务中生成，可以使用我们提供的 API 接口，也可以自行实现 HMAC-SHA256 签名。

**Q: 免费体验套餐能用多久？**
A: 默认 7 天，管理员可在后台配置。每个用户仅第一个应用享受免费体验。

**Q: 消息量上限是多少？**
A: 根据套餐不同有不同限额，具体可在用户中心查看套餐详情。

**Q: 支持哪些消息类型？**
A: 文字、Emoji 表情、图片、语音消息，以及消息撤回、已读/未读状态。

**Q: 离线消息如何处理？**
A: 用户在线时消息实时推送；离线时消息存储在服务端，上线后自动补发，确保不丢失。

**Q: 如何自定义聊天UI？**
A: Web SDK 内置完整聊天 UI，支持自定义主题色。如需完全自定义 UI，可使用小程序 SDK 或 WebSocket 协议自行开发。SDK 提供 `on(event, callback)` 接口监听所有消息事件。