# 轻松聘聊天实时接口 本文补充 `openapi-rc.json` 中的消息发送、事件补拉和消息字段。所有 HTTP 路径以 `/api/v1` 为前缀;下文省略该前缀。 ## 连接和发送 通过 STOMP WebSocket 连接 `/api/v1/ws`,在 **STOMP CONNECT 帧**中设置 `Authorization: Bearer `。不得放在 URL 中,refresh token 不可代替 access token。连接成功后订阅唯一用户队列 `/user/queue/chat-events`,收到 `{"type":"CHAT_READY"}` 后开始补拉。业务消息继续通过 HTTP 接口发送,STOMP SEND 不开放。 `POST /conversations/messages` 请求: ```json { "conversationId": 1, "content": "你好", "clientMessageId": "example-message-001" } ``` `clientMessageId` 为 1–64 位英文字母、数字、下划线或连字符;为兼容旧客户端可省略,但新客户端必须为一次逻辑发送生成固定标识,并在超时、断线或未收到响应时复用。同一会话、同一发送者下,相同标识和正文返回原消息,不重复生成事件;相同标识搭配不同正文返回 400。撤回后重试也不会恢复旧正文。 消息响应增加 `clientMessageId`(旧消息可为空)和 `version`(消息版本)。`MessageResponse` 在普通会话返回 `senderUserId`;人才邀请会话继续使用 `senderScope` 隐藏用户标识。撤回消息只返回“消息已撤回”占位。 ## 事件补拉 `GET /conversations/{id}/events?after=0&size=50` 必须携带普通 HTTP Bearer access token,且当前账号必须属于该会话。`after` 是非负事件游标;`size` 默认 50,合法范围 1–100。 响应的 `data`: ```json { "items": [ { "eventId": 2, "type": "MESSAGE_CREATED", "conversationId": 1, "occurredAt": "2026-10-15T10:00:00", "actorScope": "OTHER_PARTICIPANT", "payload": { "id": 1, "clientMessageId": "example-message-001", "version": 0, "conversationId": 1, "content": "你好", "status": "SENT", "isRead": false } } ], "nextCursor": 2, "hasMore": false } ``` 事件包括 `MESSAGE_CREATED`、`MESSAGE_READ`、`MESSAGE_RECALLED`。`actorScope` 为 `CURRENT_USER` 或 `OTHER_PARTICIPANT`;`payload.isRead` 表示当前接收用户的已读状态。payload 读取消息当前状态,而不是保存历史正文快照。 客户端按以下顺序恢复: 1. 连接、鉴权、订阅并等待 `CHAT_READY`。 2. 使用该账号、该会话已持久化的补拉游标调用事件接口;首次使用 0。 3. 以 `eventId` 去重,以消息 `id` 和 `version` 合并,避免旧版本覆盖新状态。 4. 本页成功合并后才持久化 `nextCursor`;`hasMore=true` 时继续请求,直到为 false。 5. 补拉期间同时合并实时事件。实时事件 ID 可能乱序,不得直接用其最大值推进补拉游标。同版本的已读状态还需按 `actorScope` 和本地已读操作处理,不能仅按版本丢弃已读事件。 断线后重新获取有效 access token、重连、订阅,再重复上述流程。账号切换必须隔离游标、消息缓存和连接。 ## 运行边界 这些接口沿用企业认证、会话成员、屏蔽和现有匹配准入规则;不会改变投递或开聊门槛。目前 WebSocket 使用单实例内存 broker,多实例部署需要共享消息分发能力。Nginx 示例文件须经部署配置和验证后才能使用。