# Data Model: 骑手与用户 IM 即时沟通账号接入

**Phase**: 1 | **Date**: 2026-06-23

## 变更表：info_user（用户信息）

现有用户表，本期**新增两列**存储 IM 平台返回的凭证。

### 新增字段

| 列名 | Java 属性 | 类型 | 可空 | 说明 |
|------|-----------|------|------|------|
| `im_api_key` | `imApiKey` | VARCHAR(64) | 是 | IM 平台 extCreate 返回的 `data.apiKey`（如 `f7a49f9745114462a3333e5698aac9ae`，32 位十六进制）。用户尚未开通 IM 时为 NULL。 |
| `im_user_id` | `imUserId` | BIGINT | 是 | IM 平台 extCreate 返回的 `data.userId`（如 `2069305587785445400`，19 位长整型）。用户尚未开通时为 NULL。 |

### 关系

- 一个 `info_user` 对应 **0 或 1** 组 IM 凭证（imApiKey 与 imUserId 同时为空，或同时有值）。
- 凭证由 IM 平台生成，平台侧不可变；写入后不再更新（幂等返回）。

### 验证规则（来自需求）

- `imApiKey` 非空 ⇔ `imUserId` 非空（成对写入，避免脏数据）。
- 写入前必须 IM 平台 `code == 200` 且 `data` 含非空 apiKey/userId，否则不写库。
- 已有凭证的用户再次调用开通接口：不写库，直接返回现有凭证（幂等）。

### Java 实体改动

`InfoUser.java`（`com.ruoyi.system.domain`）新增：

```java
/** IM 平台 API 密钥（extCreate 返回） */
@Excel(name = "IM apiKey")
private String imApiKey;

/** IM 平台用户ID（extCreate 返回，长整型） */
@Excel(name = "IM userId")
private Long imUserId;
```

> 因实体已用 Lombok `@Data`，无需手写 getter/setter；但该类仍保留大量手写 getter/setter，按现有风格在末尾补 `getImApiKey/setImApiKey`、`getImUserId/setImUserId` 保持一致（可选，@Data 已覆盖）。

### Mapper 改动

`InfoUserMapper.xml` 的 `InfoUserResult` resultMap 新增两行映射：

```xml
<result property="imApiKey"    column="im_api_key"    />
<result property="imUserId"    column="im_user_id"    />
```

> `selectInfoUserVo` 为 `select * from info_user`，自动覆盖新列，无需改 SQL。

### SQL 迁移

写入 `updatesql/sql.md`（不直接执行）：

```sql
-- 2026-06-23 IM 即时沟通账号接入：info_user 新增 IM 凭证两列
ALTER TABLE info_user ADD COLUMN im_api_key VARCHAR(64) DEFAULT NULL COMMENT 'IM平台API密钥(extCreate返回)';
ALTER TABLE info_user ADD COLUMN im_user_id BIGINT DEFAULT NULL COMMENT 'IM平台用户ID(extCreate返回)';
```

## 新增 VO：ImAccountVo

`com.ruoyi.system.domain.vo.ImAccountVo` —— 开通接口返回体。

| 字段 | 类型 | 说明 |
|------|------|------|
| `apiKey` | String | IM 平台返回的 apiKey |
| `imUserId` | String | IM 平台返回的 userId（**String 规避前端 Long 精度丢失**） |

> service 内部 `InfoUser.imUserId` 为 Long；组装 VO 时 `String.valueOf(imUserId)` 转字符串返回。
