# Feature Specification: 骑手与用户 IM 即时沟通账号接入 **Feature Branch**: `012-im-user-integration` **Created**: 2026-06-23 **Status**: Draft **Input**: User description: "骑手和用户沟通要使用 im 功能,我们需要调用 im 的功能,让 im 给我们创建用户;用户表绑定 im 创建用户返回的信息;创建用户调用方法 POST https://test-im.abtim-my.com/bot/extCreate,请求头带 extToken;返回 apiKey 与 userId;im 域名和 extToken 放到配置文件。用户创建过程不改动,APP 在用户创建完成后自己触发接口,后端再去调用 im 方法创建 im 账号。" ## User Scenarios & Testing *(mandatory)* ### User Story 1 - APP 触发开通 IM 账号 (Priority: P1) 用户在平台注册完成后,APP 主动调用平台提供的「开通 IM 账号」接口。平台后端识别当前登录用户,向 IM 平台发起创建请求,将 IM 平台返回的 apiKey 与 userId 绑定保存到该用户记录中,并把这两个凭证返回给 APP,供 APP 初始化 IM SDK 进行即时沟通。 **Why this priority**: 这是整个 IM 沟通能力的根基——没有 IM 账号就无法收发任何消息。它是 MVP 的核心切片,其他故事都依赖它。 **Independent Test**: 可通过"以某用户身份调用开通接口 → 校验用户表 imApiKey / imUserId 已写入 → 校验接口返回了这两个凭证 → IM 平台确实存在该账号"独立验证,交付价值为"用户已具备 IM 身份并可直接聊天"。 **Acceptance Scenarios**: 1. **Given** 一个已登录、尚未开通 IM 账号的用户,**When** APP 调用「开通 IM 账号」接口,**Then** 平台成功调用 IM 平台创建账号,用户表 imApiKey / imUserId 被写入非空值,接口返回这两个凭证。 2. **Given** IM 平台返回成功(code=200),**When** 平台处理响应,**Then** 仅从 data 节点取出 apiKey 与 userId 落库并返回。 3. **Given** IM 平台临时不可用或返回失败,**When** APP 调用开通接口,**Then** 接口返回明确的失败信息(不写库),APP 可稍后重试。 --- ### User Story 2 - 重复开通幂等返回 (Priority: P2) 同一个用户多次调用「开通 IM 账号」接口时,平台不重复在 IM 平台创建账号,而是直接返回该用户已有的 IM 凭证,保证幂等,避免产生重复 IM 账号。 **Why this priority**: 幂等是接口健壮性的核心保障,避免 APP 重试或网络抖动导致一个用户绑定多个 IM 账号;优先级仅次于首次开通。 **Independent Test**: 可通过"对同一用户连续调用两次开通接口 → 确认 IM 平台只创建一次、返回的凭证一致"独立验证。 **Acceptance Scenarios**: 1. **Given** 某用户 imApiKey 已存在,**When** APP 再次调用开通接口,**Then** 平台直接返回现有凭证,不再次调用 IM 平台创建。 2. **Given** APP 因网络问题重复提交,**When** 并发或连续到达,**Then** 该用户最终只拥有一组 IM 凭证。 --- ### User Story 3 - IM 配置集中管理 (Priority: P3) IM 平台的访问域名与鉴权令牌(extToken)集中存放于系统配置文件中,由配置统一管理,代码中不硬编码任何 IM 域名或令牌,便于测试环境与正式环境切换。 **Why this priority**: 支撑性需求,是 P1/P2 正确、安全运行的前提,但属于工程化而非用户可见功能。 **Independent Test**: 可通过"修改配置文件中的域名/令牌 → 重启 → 确认调用指向新地址"独立验证。 **Acceptance Scenarios**: 1. **Given** 配置文件已配置 IM 域名与 extToken,**When** 系统启动,**Then** 平台读取并使用配置值调用 IM 平台,代码内不出现硬编码域名或令牌。 2. **Given** 需要从测试环境切换到正式环境,**When** 仅修改配置文件,**Then** 无需改动代码即可完成切换。 --- ### Edge Cases - IM 平台返回 userId 超出普通整数范围(如 19 位长整型)时,存储字段类型如何避免精度丢失? - APP 短时间内多次请求开通接口时,系统如何保证幂等(不重复创建、不覆盖已有有效账号)? - IM 平台返回 code 非 200(鉴权失败 / 限流 / 服务异常)时,系统如何明确返回失败,便于 APP 重试? - IM 平台调用超时(如 > 5 秒)时,是否阻塞接口响应?超时阈值与重试策略是什么? - extToken 泄露或过期,系统如何感知与处理? - 未登录或 token 失效调用开通接口,系统应拒绝(不产生账号)。 - 用户删除后,IM 账号是否需要同步注销?(本期范围外,仅记录) ## Requirements *(mandatory)* ### Functional Requirements - **FR-001**: 系统 MUST 提供一个独立的「开通 IM 账号」接口,供 APP 在用户注册完成后(或首次需要沟通时)主动调用;用户注册主流程保持不变,不在注册环节内嵌 IM 开通。 - **FR-002**: 系统 MUST 通过当前登录 token 解析出 userId 来识别要开通的用户,不接受由 APP 显式传入 userId(防止越权为他人开通)。 - **FR-003**: 系统 MUST 调用 IM 平台创建账号(POST {IM域名}/bot/extCreate,请求头带 extToken),将 IM 平台返回的 apiKey 与 userId 绑定保存到用户表对应字段。 - **FR-004**: 系统 MUST 通过请求头 `extToken` 携带鉴权令牌,令牌值与 IM 域名均从配置文件读取,代码中不硬编码。 - **FR-005**: 系统 MUST 正确解析 IM 平台返回结构(外层 code/message,内层 data 含 apiKey/userId),仅取 data 节点落库。 - **FR-006**: 系统 MUST 在开通成功后将 apiKey 与 imUserId 返回给 APP,供 APP 初始化 IM SDK。 - **FR-007**: 接口 MUST 对所有用户类型开放(普通用户 0 / 商家 1 / 骑手 2 / 夜市 3),由 APP 决定调用方;后端不按用户类型限制。 - **FR-008**: 系统 MUST 保证幂等——当目标用户已存在 IM 凭证(imApiKey 非空)时,直接返回现有凭证,不再调用 IM 平台创建。 - **FR-009**: 当 IM 平台不可用、返回失败或调用超时时,系统 MUST 返回明确的失败结果,且不在用户表写入无效凭证。 ### Key Entities *(include if feature involves data)* - **InfoUser(用户信息)**: 平台已有用户表。本期新增两个 IM 凭证字段:imApiKey(IM 平台返回的 API 密钥,字符串)与 imUserId(IM 平台返回的用户ID,长整型 BIGINT)。一个用户对应一组 IM 凭证。 - **IM 平台外部账号**: 由 IM 平台管理,通过 extCreate 接口创建,对平台而言是不可变的对外凭证,以 apiKey+userId 形式回传绑定。 ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: 100% 的「开通 IM 账号」接口调用(IM 平台可用时)成功为用户写入并返回 IM 凭证。 - **SC-002**: 对同一用户重复调用开通接口,IM 平台账号创建次数为 1,重复请求 100% 命中幂等返回。 - **SC-003**: 未登录或 token 失效调用开通接口时,100% 被拒绝,不产生任何 IM 账号或写库。 - **SC-004**: IM 创建请求平均耗时控制在可接受范围,接口响应不被显著拖慢(如额外开销 < 1 秒)。 - **SC-005**: 测试环境与正式环境的 IM 域名/令牌切换,仅需修改配置文件,零代码改动。 ## Assumptions - 用户注册主流程(骑手注册、商家注册、普通用户注册/登录即注册)保持原样,本期完全不动注册相关代码,仅在注册完成后由 APP 自行调用开通接口。 - IM 平台创建账号接口(extCreate)对同一外部用户具备可接受的重复请求处理;系统侧额外通过「凭证已存在则直接返回」做幂等保护。 - IM 平台返回的 userId 为长整型,存储字段按 BIGINT 设计以避免精度丢失。 - extToken 为平台级共享令牌(非单用户级),所有创建请求共用同一令牌。 - 接口通过项目现有登录鉴权机制识别当前用户(解析 token → userId),不新增鉴权方式。 - 配置文件采用项目现有 application.yml 风格(参考 ezpay / newebpay 段),新增独立 im 配置段。 - HTTP 客户端沿用项目现有 org.apache.http 用法(参考 NewebPay 工具类)。