feat:单聊页面bug修复+后台管理端好友页面bug修复+项目进度、记忆文件更新
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
| [frontend/auth.md](frontend/auth.md) | 用户认证 | ✅ Phase 1 | 注册、登录、Token 刷新、个人信息管理 |
|
||||
| [frontend/contact.md](frontend/contact.md) | 联系人 | ✅ Phase 2a | 17 个 API:好友申请/管理、好友分组、黑名单、搜索/推荐、在线状态 |
|
||||
| [frontend/websocket.md](frontend/websocket.md) | WebSocket | ✅ Phase 2a | 前端 WebSocket 连接管理、事件协议、心跳、重连 |
|
||||
| [frontend/im.md](frontend/im.md) | 即时通讯 | 📋 Phase 2b | 会话列表、消息历史、群聊创建与管理 |
|
||||
| [frontend/im.md](frontend/im.md) | 即时通讯 | ✅ Phase 2b | 7 个 API:会话列表/置顶/删除/清空、历史消息、全局搜索、未读数 |
|
||||
| [frontend/meeting.md](frontend/meeting.md) | 会议 | 📋 后续 | 即时会议、预约会议、加入/离开、会议列表 |
|
||||
| [frontend/notify.md](frontend/notify.md) | 通知 | 📋 后续 | 通知列表、标记已读 |
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
| 文档 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| [websocket.md](websocket.md) | ✅ Phase 2a | WebSocket 实时事件协议(联系人通知、在线状态、心跳) |
|
||||
| [websocket.md](websocket.md) | ✅ Phase 2a/2b | WebSocket 实时事件协议(IM 消息收发/撤回/已读/输入、联系人通知、在线状态、心跳) |
|
||||
|
||||
---
|
||||
|
||||
@@ -194,7 +194,7 @@ docs/api/
|
||||
│ ├── auth.md # 用户认证 ✅ Phase 1
|
||||
│ ├── contact.md # 联系人管理(17 个 API) ✅ Phase 2a
|
||||
│ ├── websocket.md # WebSocket 事件协议 ✅ Phase 2a
|
||||
│ ├── im.md # 即时通讯 📋 Phase 2b
|
||||
│ ├── im.md # 即时通讯(7 个 API) ✅ Phase 2b
|
||||
│ ├── meeting.md # 会议 📋 后续
|
||||
│ └── notify.md # 通知 📋 后续
|
||||
├── admin/ # 后台管理端 API
|
||||
@@ -204,5 +204,5 @@ docs/api/
|
||||
│ ├── contact.md # 好友关系管理 ✅ Phase 2a
|
||||
│ ├── meeting.md # 会议管理 📋 后续
|
||||
│ └── system.md # 系统管理 📋 待定
|
||||
└── websocket.md # WebSocket 全量事件协议 ✅ Phase 2a
|
||||
└── websocket.md # WebSocket 全量事件协议 ✅ Phase 2a/2b
|
||||
```
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# 即时通讯模块 API (IM)
|
||||
# 即时通讯模块 REST API (IM)
|
||||
|
||||
> 通用规范(认证方式、响应格式、错误码)见 [README.md](../README.md)
|
||||
> 消息的实时收发通过 WebSocket 完成,见 [websocket.md](../websocket.md)
|
||||
> 消息的实时收发(发送/撤回/标记已读/正在输入)通过 WebSocket 完成,见 [websocket.md](../websocket.md)
|
||||
> 本文档中的接口用于会话管理和消息历史查询等非实时操作。
|
||||
> **最后更新:** 2026-03-03(代码审查修复后同步)
|
||||
|
||||
---
|
||||
|
||||
@@ -10,166 +11,84 @@
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/v1/conversations | 需认证 | 获取会话列表 |
|
||||
| POST | /api/v1/conversations | 需认证 | 创建群聊 |
|
||||
| GET | /api/v1/conversations/:id | 需认证 | 获取会话详情 |
|
||||
| GET | /api/v1/conversations/:id/messages | 需认证 | 获取消息历史 |
|
||||
| POST | /api/v1/conversations/:id/members | 需认证 | 邀请成员加入群聊 |
|
||||
| DELETE | /api/v1/conversations/:id/members/:uid | 需认证 | 移除群聊成员 |
|
||||
| GET | /api/v1/im/conversations | 需认证 | 获取会话列表 |
|
||||
| GET | /api/v1/im/messages | 需认证 | 获取历史消息(游标分页) |
|
||||
| PUT | /api/v1/im/conversations/:id/pin | 需认证 | 置顶/取消置顶 |
|
||||
| DELETE | /api/v1/im/conversations/:id | 需认证 | 删除会话(软删除) |
|
||||
| DELETE | /api/v1/im/conversations/:id/messages | 需认证 | 清空聊天记录(个人视图) |
|
||||
| GET | /api/v1/im/messages/search | 需认证 | 全局消息搜索 |
|
||||
| GET | /api/v1/im/unread | 需认证 | 获取全局未读消息总数 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 获取会话列表
|
||||
|
||||
`GET /api/v1/conversations`
|
||||
`GET /api/v1/im/conversations`
|
||||
|
||||
**权限:** 需认证
|
||||
|
||||
**说明:** 返回当前用户的所有会话,按最后消息时间倒序排列。单聊会话的 `name`/`avatar` 为空,前端应使用 `target_user` 的信息展示。
|
||||
**说明:** 返回当前用户的所有会话,排序:置顶优先 → 最后消息时间降序。已软删除的会话不返回。通过 LEFT JOIN 一次获取单聊对方用户 ID,避免 N+1 查询。
|
||||
|
||||
**成功响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": 1,
|
||||
"name": "",
|
||||
"avatar": "",
|
||||
"target_user": {
|
||||
"id": 2,
|
||||
"nickname": "李四",
|
||||
"avatar": "https://cdn.echochat.com/avatar/2.jpg",
|
||||
"online": true
|
||||
},
|
||||
"last_message": {
|
||||
"id": 100,
|
||||
"type": 1,
|
||||
"content": "你好",
|
||||
"sender_id": 2,
|
||||
"created_at": "2026-02-27 10:30:00"
|
||||
},
|
||||
"unread_count": 3,
|
||||
"is_pinned": false
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": 2,
|
||||
"name": "产品讨论组",
|
||||
"avatar": "https://cdn.echochat.com/group/5.jpg",
|
||||
"target_user": null,
|
||||
"last_message": {
|
||||
"id": 205,
|
||||
"type": 1,
|
||||
"content": "明天开会",
|
||||
"sender_id": 3,
|
||||
"created_at": "2026-02-27 11:00:00"
|
||||
},
|
||||
"unread_count": 0,
|
||||
"is_pinned": true,
|
||||
"member_count": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 创建群聊
|
||||
|
||||
`POST /api/v1/conversations`
|
||||
|
||||
**权限:** 需认证
|
||||
|
||||
**请求参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| name | string | 是 | 群聊名称 |
|
||||
| member_ids | int[] | 是 | 初始成员用户 ID 列表(不含自己,至少 2 人) |
|
||||
|
||||
**说明:** 创建者自动成为群主(role=2),被邀请的成员为普通成员(role=0)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 获取会话详情
|
||||
|
||||
`GET /api/v1/conversations/:id`
|
||||
|
||||
**权限:** 需认证,且为该会话成员
|
||||
|
||||
**成功响应(群聊示例):**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": 5,
|
||||
"type": 2,
|
||||
"name": "产品讨论组",
|
||||
"avatar": "https://cdn.echochat.com/group/5.jpg",
|
||||
"owner_id": 1,
|
||||
"max_members": 200,
|
||||
"member_count": 8,
|
||||
"members": [
|
||||
{ "user_id": 1, "nickname": "张三", "role": 2, "online": true },
|
||||
{ "user_id": 2, "nickname": "李四", "role": 0, "online": false }
|
||||
],
|
||||
"created_at": "2026-02-20 09:00:00"
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": 1,
|
||||
"peer_user_id": 2,
|
||||
"peer_nickname": "李四",
|
||||
"peer_avatar": "https://...",
|
||||
"last_msg_content": "你好",
|
||||
"last_msg_time": "2026-03-03 10:30:00",
|
||||
"last_msg_sender_id": 2,
|
||||
"is_pinned": false,
|
||||
"unread_count": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 获取消息历史
|
||||
## 2. 获取历史消息
|
||||
|
||||
`GET /api/v1/conversations/:id/messages`
|
||||
`GET /api/v1/im/messages`
|
||||
|
||||
**权限:** 需认证,且为该会话成员
|
||||
|
||||
**查询参数:**
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| before_id | int | 无 | 获取此消息 ID 之前的消息(用于向上翻页加载历史) |
|
||||
| limit | int | 30 | 每次获取数量,最大 50 |
|
||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| conversation_id | int | 是 | - | 会话 ID |
|
||||
| before_id | int | 否 | 0 | 游标:查询 ID 小于此值的消息,0=最新 |
|
||||
| limit | int | 否 | 30 | 每次获取数量,最大 100 |
|
||||
|
||||
**说明:** 支持 `clear_before_msg_id` 个人视图过滤(清空聊天记录后,仅过滤当前用户视图,不影响对方)。
|
||||
|
||||
**成功响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"message": "success",
|
||||
"data": {
|
||||
"messages": [
|
||||
{
|
||||
"id": 98,
|
||||
"sender_id": 1,
|
||||
"sender_name": "张三",
|
||||
"sender_avatar": "https://...",
|
||||
"type": 1,
|
||||
"content": "明天几点开会?",
|
||||
"extra": {},
|
||||
"status": 1,
|
||||
"created_at": "2026-02-27 10:28:00"
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"id": 99,
|
||||
"conversation_id": 1,
|
||||
"sender_id": 2,
|
||||
"sender_name": "李四",
|
||||
"sender_avatar": "https://...",
|
||||
"type": 2,
|
||||
"content": "",
|
||||
"extra": {
|
||||
"url": "https://cdn.echochat.com/img/xxx.jpg",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"thumbnail": "https://cdn.echochat.com/img/xxx_thumb.jpg"
|
||||
},
|
||||
"type": 1,
|
||||
"content": "你好",
|
||||
"status": 1,
|
||||
"created_at": "2026-02-27 10:29:00"
|
||||
"client_msg_id": "",
|
||||
"created_at": "2026-03-03 10:29:00"
|
||||
}
|
||||
],
|
||||
"has_more": true
|
||||
@@ -179,30 +98,97 @@
|
||||
|
||||
---
|
||||
|
||||
## 5. 邀请成员加入群聊
|
||||
## 3. 置顶/取消置顶会话
|
||||
|
||||
`POST /api/v1/conversations/:id/members`
|
||||
`PUT /api/v1/im/conversations/:id/pin`
|
||||
|
||||
**权限:** 需认证,且为该群聊成员
|
||||
**权限:** 需认证,且为该会话成员
|
||||
|
||||
**请求参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| user_ids | int[] | 是 | 要邀请的用户 ID 列表 |
|
||||
|
||||
**可能的错误码:** 3001(会话不存在),3002(非会话成员),4003(超出人数上限)
|
||||
| is_pinned | bool | 是 | true=置顶, false=取消 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 移除群聊成员
|
||||
## 4. 删除会话
|
||||
|
||||
`DELETE /api/v1/conversations/:id/members/:uid`
|
||||
`DELETE /api/v1/im/conversations/:id`
|
||||
|
||||
**权限:** 需认证,且为群主或管理员
|
||||
**权限:** 需认证,且为该会话成员
|
||||
|
||||
**路径参数:**
|
||||
- `id` — 会话 ID
|
||||
- `uid` — 被移除的用户 ID
|
||||
**说明:** 软删除,仅影响当前用户视图,不影响对方。同时清零未读数并更新 Redis 全局未读。
|
||||
|
||||
**可能的错误码:** 3001, 3002, 1003(非群主/管理员无权操作)
|
||||
---
|
||||
|
||||
## 5. 清空聊天记录
|
||||
|
||||
`DELETE /api/v1/im/conversations/:id/messages`
|
||||
|
||||
**权限:** 需认证,且为该会话成员
|
||||
|
||||
**说明:** 个人视图操作,不影响对方的消息。实现方式:记录清空时的最后消息 ID(`clear_before_msg_id`),后续查询历史消息时过滤。同时清零该会话未读数。
|
||||
|
||||
---
|
||||
|
||||
## 6. 全局消息搜索
|
||||
|
||||
`GET /api/v1/im/messages/search`
|
||||
|
||||
**权限:** 需认证
|
||||
|
||||
**查询参数:**
|
||||
|
||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| keyword | string | 是 | - | 搜索关键词 |
|
||||
| limit | int | 否 | 50 | 返回条数上限 |
|
||||
|
||||
**说明:** 使用 PostgreSQL GIN 全文索引(`to_tsvector('simple', content) @@ plainto_tsquery('simple', ?)`),仅搜索用户所在会话的消息。
|
||||
|
||||
**成功响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"id": 99,
|
||||
"conversation_id": 1,
|
||||
"sender_id": 2,
|
||||
"type": 1,
|
||||
"content": "你好世界",
|
||||
"status": 1,
|
||||
"created_at": "2026-03-03 10:29:00",
|
||||
"sender_nickname": "李四",
|
||||
"sender_avatar": "https://..."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 获取全局未读消息总数
|
||||
|
||||
`GET /api/v1/im/unread`
|
||||
|
||||
**权限:** 需认证
|
||||
|
||||
**说明:** 从 Redis STRING 读取全局未读总数,用于 TabBar badge 显示。
|
||||
|
||||
**成功响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"total_unread": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -84,24 +84,38 @@
|
||||
|
||||
**方向:** 客户端 → 服务端
|
||||
|
||||
**说明:** 发送消息到会话
|
||||
**说明:** 发送消息到会话。`conversation_id` 和 `target_user_id` 二选一:首次发消息使用 `target_user_id`(自动创建会话),后续使用 `conversation_id`。
|
||||
|
||||
**data 参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| conversation_id | int | 是 | 目标会话 ID |
|
||||
| type | int | 是 | 消息类型:1=文本,2=图片,3=文件,4=语音 |
|
||||
| content | string | 否 | 文本内容 |
|
||||
| extra | object | 否 | 附加数据(图片/文件信息) |
|
||||
| conversation_id | int | 否 | 已有会话 ID(与 target_user_id 二选一) |
|
||||
| target_user_id | int | 否 | 对方用户 ID(首次发消息时使用) |
|
||||
| type | int | 是 | 消息类型:1=文本 |
|
||||
| content | string | 是 | 文本内容 |
|
||||
| client_msg_id | string | 否 | 客户端消息唯一 ID,用于幂等去重 |
|
||||
|
||||
**ACK 响应 data:** `{ "msg_id": 10086 }`
|
||||
**ACK 响应 data:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 10086,
|
||||
"conversation_id": 1,
|
||||
"sender_id": 1,
|
||||
"type": 1,
|
||||
"content": "你好",
|
||||
"status": 1,
|
||||
"client_msg_id": "xxxx-xxxx",
|
||||
"created_at": "2026-03-03 10:30:00"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### im.message.new
|
||||
|
||||
**方向:** 服务端 → 客户端
|
||||
**方向:** 服务端 → 客户端(推送)
|
||||
|
||||
**说明:** 收到新消息推送
|
||||
|
||||
@@ -116,50 +130,76 @@
|
||||
"sender_avatar": "https://...",
|
||||
"type": 1,
|
||||
"content": "你好",
|
||||
"extra": {},
|
||||
"created_at": "2026-02-27 10:30:00"
|
||||
"client_msg_id": "",
|
||||
"created_at": "2026-03-03 10:30:00"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### im.message.revoke
|
||||
### im.message.recall
|
||||
|
||||
**方向:** 客户端 → 服务端
|
||||
|
||||
**说明:** 撤回消息(发送后 2 分钟内)
|
||||
**说明:** 撤回消息(发送后 2 分钟内)。撤回成功后若该消息是会话最后一条,会同步更新会话预览为"XX 撤回了一条消息"。
|
||||
|
||||
**data 参数:** `{ "message_id": 10086 }`
|
||||
|
||||
---
|
||||
|
||||
### im.message.read
|
||||
### im.message.recalled
|
||||
|
||||
**方向:** 客户端 → 服务端
|
||||
**方向:** 服务端 → 客户端(推送)
|
||||
|
||||
**说明:** 消息已读回执
|
||||
**说明:** 消息被撤回通知
|
||||
|
||||
**data 参数:** `{ "conversation_id": 1, "message_id": 10086 }`
|
||||
**data 内容:** `{ "message_id": 10086, "conversation_id": 1, "sender_id": 2 }`
|
||||
|
||||
---
|
||||
|
||||
### im.typing.start
|
||||
### im.conversation.read
|
||||
|
||||
**方向:** 客户端 → 服务端
|
||||
|
||||
**说明:** 通知对方"正在输入"
|
||||
**说明:** 标记会话已读(清零未读数 + 更新 Redis 全局未读数)
|
||||
|
||||
**data 参数:** `{ "conversation_id": 1 }`
|
||||
|
||||
---
|
||||
|
||||
### im.typing.stop
|
||||
### im.typing
|
||||
|
||||
**方向:** 客户端 → 服务端
|
||||
**方向:** 双向(客户端发送 → 服务端转发给对方)
|
||||
|
||||
**说明:** 停止输入
|
||||
**说明:** 正在输入通知。客户端发送后服务端转发给对方,前端收到后设置 3 秒超时自动清除。
|
||||
|
||||
**data 参数:** `{ "conversation_id": 1 }`
|
||||
**data 参数(客户端发送):** `{ "conversation_id": 1 }`
|
||||
|
||||
**data 内容(服务端推送):** `{ "conversation_id": 1, "user_id": 2 }`
|
||||
|
||||
---
|
||||
|
||||
### im.offline.sync
|
||||
|
||||
**方向:** 服务端 → 客户端(推送)
|
||||
|
||||
**说明:** WebSocket 连接成功后服务端主动推送离线未读摘要
|
||||
|
||||
**data 内容:**
|
||||
|
||||
```json
|
||||
{
|
||||
"total_unread": 5,
|
||||
"conversations": [
|
||||
{
|
||||
"conversation_id": 1,
|
||||
"unread_count": 3,
|
||||
"last_msg_content": "你好",
|
||||
"last_msg_time": "2026-03-03 10:30:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user