# Contract: 开通 IM 账号接口（平台对外）

**方向**: APP → 平台后端（入站接口）
**用途**: APP 在用户注册完成后（或首聊前）调用，为当前登录用户开通/获取 IM 账号凭证

## 请求

```
POST /infouser/user/im/open
Header: token: <登录token>
```

- 鉴权：通过 `token` 请求头解析当前 userId（复用 `JwtUtil.getusid`），**不接受 body 传 userId**（防越权）。
- 请求体：无（或空 JSON）。
- 适用对象：所有已登录用户类型（普通0/商家1/骑手2/夜市3）。

## 响应

### 成功（首次开通 或 幂等返回）

```json
{
  "code": 200,
  "msg": "成功",
  "data": {
    "apiKey": "f7a49f9745114462a3333e5698aac9ae",
    "imUserId": "2069305587785445400"
  }
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| `data.apiKey` | String | IM API 密钥 |
| `data.imUserId` | String | IM 用户ID（**字符串**，规避前端 Long 精度丢失） |

### 失败

| 场景 | 返回 |
|------|------|
| 未登录 / token 失效 | 错误（未登录提示），不开通 |
| IM 平台不可用 / 超时 / 返回失败 | 错误（IM 开通失败，可重试），不写库 |

> 失败沿用项目全局异常 / `AjaxResult.error(...)` 约定。

## 行为契约

1. **幂等**：当前用户已有 `imApiKey` → 直接返回现有凭证，不重复调用 IM 平台。
2. **成对写入**：仅当 IM 返回 apiKey 与 userId 均有效时才同时写 `im_api_key`、`im_user_id`。
3. **不阻塞注册**：本接口独立于注册流程；其失败不影响用户已注册状态。
4. **不接收 userId 参数**：用户身份仅来自 token。
