From 6717e40659df427f74563381648a104ffc685eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A5=BF=E8=A1=97=E9=95=BF=E5=AE=89?= Date: Sun, 12 Oct 2025 20:36:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ER图.drawio | 9 + IM 系统消息存储与推送策略文档.md | 141 ++++++++++++++ IM 系统鉴权与 Token 安全规范文档.md | 115 +++++++++++ 📘 WebSocket 通讯协议设计文档.md | 285 ++++++++++++++++++++++++++++ 📘 接口响应 Code 设计文档.md | 144 ++++++++++++++ 5 files changed, 694 insertions(+) create mode 100644 IM 系统消息存储与推送策略文档.md create mode 100644 IM 系统鉴权与 Token 安全规范文档.md create mode 100644 📘 WebSocket 通讯协议设计文档.md create mode 100644 📘 接口响应 Code 设计文档.md diff --git a/ER图.drawio b/ER图.drawio index 5ca828b..4a22d5a 100644 --- a/ER图.drawio +++ b/ER图.drawio @@ -1079,6 +1079,15 @@ + + + + + + + + + diff --git a/IM 系统消息存储与推送策略文档.md b/IM 系统消息存储与推送策略文档.md new file mode 100644 index 0000000..b76cd49 --- /dev/null +++ b/IM 系统消息存储与推送策略文档.md @@ -0,0 +1,141 @@ +# IM 系统消息存储与推送策略文档 + +## 1. 概述 + +本策略文档定义了 **消息在系统中的存储、读取和推送流程**,目标是: + +- 保证 **消息实时性** +- 支持 **离线消息存储与同步** +- 支持 **多端登录同步** +- 支持 **单聊、群聊及系统消息** + +------ + +## 2. 消息存储策略 + +### 2.1 消息表设计 + +表结构参考前期设计: + +| 表名 | 作用 | +| ------------ | ------------------------------------------------------------ | +| Messages | 存储所有聊天消息(单聊/群聊) | +| Conversation | 缓存用户最近会话信息(last_message_id, target_id, unread_count) | +| Files | 附件 / 图片 / 语音存储URL | + +------ + +### 2.2 消息存储规则 + +1. **单聊消息** + - 写入 `Messages` 表 + - 更新发送者和接收者 `Conversation` 表 + - 更新 `UnreadCount` +2. **群聊消息** + - 写入 `Messages` 表 + - 更新群成员对应的 `Conversation` 表(except 发送者) + - 更新每个成员的 `UnreadCount` +3. **文件消息** + - 文件存储到对象存储(OSS/S3/MinIO) + - `Messages.Content` 存文件 URL + metadata +4. **消息撤回** + - 消息允许撤回时,修改 `message.status = 1 + - 更新 `Conversation.LastMessageId`(如撤回的是最后一条消息) + +------ + +## 3. 消息推送策略 + +### 3.1 推送原则 + +- **实时性**:在线用户立即通过 WebSocket 推送 +- **可靠性**:离线用户存储消息,登录时同步 +- **顺序保证**:消息按 `timestamp` 或 `messageId` 顺序发送 +- **幂等性**:客户端可根据 `messageId` 去重 + +------ + +### 3.2 单聊推送流程 + +1. 发送者通过 WebSocket 或 HTTP API 发送消息 +2. 服务端写入 `Messages` 表 +3. 查询接收者是否在线 + - **在线**:通过 WebSocket 推送 + - **离线**:存储到 Redis 或 `Conversation.UnreadCount` +4. 接收者收到消息后发送 `MESSAGE_ACK` +5. //暂不要求:更新消息状态(已送达 / 已读) + +------ + +### 3.3 群聊推送流程 + +1. 发送者发送群消息 +2. 服务端写入 `Message`s 表 +3. 查询群成员列表(`GroupMember` 表) +4. 遍历成员: + - **在线成员**:WebSocket 推送 + - **离线成员**:增加 `UnreadCount`,保存在 Redis/数据库 +5. //暂不要求:接收者回 ACK 后更新 `message_receipt`(已读) + +------ + +### 3.4 离线消息处理 + +- 离线消息存储位置: + 1. 数据库 `Messages` 表(长期保存) + 2. Redis 缓存(短期加速推送) +- 客户端上线时: + 1. 请求 `/syncMessages` 接口 + 2. 返回未读消息 + 未读计数 +- 消息同步完成后清除缓存或更新状态 + +------ + +### 3.5 多端同步策略 + +- 每个设备维护独立的 `deviceId` +- WebSocket 推送时: + - 排除发送设备 + - 推送给同账号其他设备 +- //暂不要求:消息回执: + - 每端发送 ACK + - 服务端更新 `Voncers` 和 `message_receipt` + +------ + +## 4. 消息可靠性保障 + +| 场景 | 解决方案 | +| ------------------ | ---------------------------------- | +| 消息丢失 | 发送端生成 `requestId`,服务端去重 | +| 消息顺序错乱 | 按 `messageId` 或 `timestamp` 排序 | +| WebSocket 异常断开 | 客户端重连后同步离线消息 | +| 群聊大消息量 | 异步推送 + 批量 ACK | + +------ + +## 5. //暂不要求:高性能优化策略 + +1. **消息表索引**:`(chat_type, to_id, created_at)` +2. **会话表缓存**:`conversation` 表避免全表查询 +3. **Redis 缓存**:用户在线状态、未读消息数 +4. **分表/分库**:按月或按用户分表 +5. **异步推送队列**:消息通过 MQ(Kafka/RabbitMQ)推送,保证高并发 + +------ + +## 6. 消息撤回与删除策略 + +1. **撤回条件**:超时限制( 2 分钟内可撤回) +2. **撤回操作**: + - 更新 `message.status = 1` + - 更新 `Conversation.LastMessageId` + - 推送撤回事件到在线用户 + +------ + +## 7. 系统消息与通知策略 + +- 系统消息(好友申请、群邀请、公告)走 **同样的消息推送流程** +- 保留在 `Notification` 表 +- 支持离线同步 \ No newline at end of file diff --git a/IM 系统鉴权与 Token 安全规范文档.md b/IM 系统鉴权与 Token 安全规范文档.md new file mode 100644 index 0000000..92aa5b3 --- /dev/null +++ b/IM 系统鉴权与 Token 安全规范文档.md @@ -0,0 +1,115 @@ +# IM 系统鉴权与 Token 安全规范文档 + +## 1. 概述 + +本规范用于确保系统用户身份验证、消息安全和多端同步安全。 + 鉴权体系采用 **Token(JWT 或自定义) + HTTPS/WebSocket** 方式。 + +------ + +## 2. 鉴权方式选择 + +| 方法 | +| -------------------------------------- | +| JWT(JSON Web Token)+ Redis黑名单机制 | + +------ + +## 3. Token 生成规则 + +### 3.1 Token 内容结构(JWT 示例) + +``` +{ + "userId": 1001, // 用户ID + "iat": 1700000000, // 签发时间(Unix时间戳) + "exp": 1700003600, // 过期时间 + "deviceId": "uuid-xxxx", // 设备ID,用于多端区分 + "role": "user" // 角色 +} +``` + +- **签名算法**:HMAC-SHA256 或 RSA +- **签名秘钥**:服务端统一管理,不暴露给客户端 + +------ + +### 3.2 Token 生成流程 + +1. 用户登录(用户名/密码) +2. 验证用户名与密码正确 +3. 生成 Token,写入 Redis(可选) +4. 返回 Token 给客户端 + +**响应示例**: + +``` +{ + "code": 0, + "message": "登录成功", + "data": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } +} +``` + +------ + +## 4. Token 使用 + +### 4.1 HTTP 接口鉴权 + +- 客户端请求带上 Header: + +``` +Authorization: Bearer +``` + +- 后端解析 Token: + 1. 校验签名 + 2. 校验 exp 是否过期 + 3. 校验 Redis 黑名单(可选) +- 不通过返回 401 / code 1006 + +------ + +### 4.2 WebSocket 鉴权 + +- 建立连接时通过 Query 或 Header 传 Token: + +``` +ws://example.com/ws?token=xxxx&deviceId=uuid-001 +``` + +- 握手阶段: + 1. 服务器验证 Token + 2. 成功返回 AUTH_SUCCESS + 3. 失败返回 AUTH_FAIL 并关闭连接 + +------ + +## 5. Token 过期策略 + +| 类型 | 建议值 | 说明 | +| ------------------ | ---------------------- | ---------------------------------------- | +| 短期 Token | 30 分钟 ~ 1 小时 | 防止长时间泄露 | +| 长期 Refresh Token | 7 ~ 30 天 | 用于获取新 Token,安全性高 | +| WebSocket 长连接 | Token 与短期有效期一致 | 客户端定期刷新 Token(心跳或重连时验证) | + +### 5.1 Token 刷新流程 + +1. 客户端 Token 快过期时,调用刷新接口 +2. 服务端验证 Refresh Token +3. 返回新 Token,更新 Redis / 黑名单 + +------ + +## 6. 多端登录处理 + +- **每个设备对应一个 deviceId** +- Token 中绑定 deviceId +- 多端策略: + 1. **允许多端同时登录**:每端单独维护 Token + 2. **限制单端登录**:新登录覆盖旧设备 Token + 3. **设备列表管理**:可查看在线设备并强制下线 + diff --git a/📘 WebSocket 通讯协议设计文档.md b/📘 WebSocket 通讯协议设计文档.md new file mode 100644 index 0000000..d7e3946 --- /dev/null +++ b/📘 WebSocket 通讯协议设计文档.md @@ -0,0 +1,285 @@ +# 📘 WebSocket 通讯协议设计文档 + +## 1. 概述 + +本协议用于实现 **即时聊天(IM)系统的实时消息传输**。 + 客户端通过 WebSocket 连接后与服务器保持长连接,实现消息推送、状态同步、群聊与单聊。 + +**支持功能**: + +- 用户登录鉴权 +- 单聊消息发送/接收 +- 群聊消息发送/接收 +- 消息撤回、已读回执 +- 心跳保活与断线重连 +- 系统通知(好友请求、群邀请) + +------ + +## 2. 消息传输格式 + +### 2.1 基础消息结构(JSON) + +``` +{ + "type": "MESSAGE_TYPE", + "requestId": "string", // 客户端生成的请求ID,便于幂等 + "from": 1001, // 发送者ID + "to": 1002, // 接收者ID(单聊)或群ID(群聊) + "chatType": "single", // "single" | "group" + "contentType": "text", // "text" | "image" | "file" | "voice" | "system" + "content": "消息内容或文件URL", + "timestamp": 1700000000 // Unix时间戳 +} +``` + +------ + +### 2.2 常用 type 枚举 + +| type | 说明 | +| -------------- | ------------------------- | +| AUTH | 握手鉴权 | +| HEARTBEAT | 心跳保活 | +| MESSAGE | 普通聊天消息(单聊/群聊) | +| MESSAGE_ACK | 消息已读/送达回执 | +| MESSAGE_RECALL | 消息撤回 | +| FRIEND_REQUEST | 好友申请通知 | +| GROUP_INVITE | 群邀请通知 | +| SYSTEM_NOTICE | 系统公告/通知 | +| ERROR | 错误消息 | + +------ + +## 3. 握手与鉴权 + +### 3.1 客户端连接 + +``` +ws://example.com/ws?token=xxxx +``` + +- 客户端通过 Token 鉴权 +- 服务器验证 Token 后,返回 AUTH_SUCCESS 或 AUTH_FAIL + +### 3.2 服务端响应示例 + +**成功:** + +``` +{ + "type": "AUTH", + "status": "success", + "userId": 1001, + "timestamp": 1700000000 +} +``` + +**失败:** + +``` +{ + "type": "AUTH", + "status": "fail", + "code": 1006, + "message": "Token无效或过期" +} +``` + +------ + +## 4. 心跳机制 + +### 4.1 客户端发送 + +``` +{ + "type": "HEARTBEAT", + "timestamp": 1700000000 +} +``` + +### 4.2 服务器响应 + +``` +{ + "type": "HEARTBEAT", + "timestamp": 1700000000 +} +``` + +- **客户端**:每隔 30 秒发送一次心跳 +- **服务器**:若 2 倍心跳时间未收到消息,则断开连接 + +------ + +## 5. 消息传输 + +### 5.1 单聊消息 + +客户端发送: + +``` +{ + "type": "MESSAGE", + "requestId": "uuid-001", + "from": 1001, + "to": 1002, + "chatType": "single", + "contentType": "text", + "content": "你好", + "timestamp": 1700000000 +} +``` + +服务器推送给接收者: + +``` +{ + "type": "MESSAGE", + "messageId": 50001, + "from": 1001, + "to": 1002, + "chatType": "single", + "contentType": "text", + "content": "你好", + "timestamp": 1700000000 +} +``` + +------ + +### 5.2 群聊消息 + +``` +{ + "type": "MESSAGE", + "requestId": "uuid-002", + "from": 1001, + "to": 3001, // 群ID + "chatType": "group", + "contentType": "image", + "content": "http://img.example.com/xxx.jpg", + "timestamp": 1700000000 +} +``` + +服务器会 **推送到群成员列表(除了自己)**。 + +------ + +### 5.3 消息回执(MESSAGE_ACK) + +客户端收到消息后发送: + +``` +{ + "type": "MESSAGE_ACK", + "messageId": 50001, + "from": 1002, + "to": 1001, + "chatType": "single", + "status": "read", + "timestamp": 1700000000 +} +``` + +服务器更新消息状态,并可推送给发送方。 + +------ + +### 5.4 消息撤回(MESSAGE_RECALL) + +客户端请求撤回消息: + +``` +{ + "type": "MESSAGE_RECALL", + "messageId": 50001, + "from": 1001, + "to": 1002, + "chatType": "single", + "timestamp": 1700000010 +} +``` + +服务器验证是否允许撤回(时间限制、权限等),允许则推送给接收方: + +``` +{ + "type": "MESSAGE_RECALL", + "messageId": 50001, + "from": 1001, + "chatType": "single", + "status": "success", + "timestamp": 1700000010 +} +``` + +------ + +## 6. 好友 / 群邀请通知 + +### 6.1 好友申请(FRIEND_REQUEST) + +``` +{ + "type": "FRIEND_REQUEST", + "requestId": "uuid-003", + "from": 1001, + "to": 1002, + "content": "加个好友吧", + "timestamp": 1700000020 +} +``` + +### 6.2 群邀请(GROUP_INVITE) + +``` +{ + "type": "GROUP_INVITE", + "inviteId": "uuid-004", + "groupId": 3001, + "inviter": 1001, + "invitee": 1003, + "content": "邀请你加入群聊", + "timestamp": 1700000030 +} +``` + +------ + +## 7. 错误处理(ERROR) + +``` +{ + "type": "ERROR", + "code": 2300, + "message": "消息发送失败", + "requestId": "uuid-001", + "timestamp": 1700000040 +} +``` + +- **code** 对应响应 Code 规范 +- **requestId** 可帮助客户端确认失败的具体请求 + +------ + +## 8. 断线重连 + +- 客户端断线后,尝试每隔 5 秒重连一次 +- 重连成功后,重新发送 AUTH 消息进行鉴权 +- 重连后可请求 **未读消息同步**(message 表或 Redis 缓存) + +------ + +## 9. 附录:contentType 示例 + +| contentType | content 示例 | 描述 | +| ----------- | ---------------------------------- | ----------------- | +| text | "你好" | 文本消息 | +| image | "http://img.example.com/xxx.jpg" | 图片 URL | +| file | "http://file.example.com/xxx.pdf" | 文件 URL + 文件名 | +| voice | "http://audio.example.com/xxx.mp3" | 语音 URL + 时长 | +| system | "用户xxx加入群" | 系统消息 | \ No newline at end of file diff --git a/📘 接口响应 Code 设计文档.md b/📘 接口响应 Code 设计文档.md new file mode 100644 index 0000000..aea81dd --- /dev/null +++ b/📘 接口响应 Code 设计文档.md @@ -0,0 +1,144 @@ +# 📘 接口响应 Code 设计文档 + +## 1. 响应数据结构 + +统一使用 JSON 格式: + +``` +{ + "code": 0, + "message": "请求成功", + "data": {} +} +``` + +- **code**:数字型,业务状态码 +- **message**:字符串,错误或提示信息 +- **data**:对象/数组,返回的数据内容(可选) + +------ + +## 2. Code 设计原则 + +1. **统一性**:所有接口返回结构一致。 +2. **分级设计**:分为系统级错误(1xxx)、业务错误(2xxx+)。 +3. **可扩展性**:预留范围,避免混乱。 + +------ + +## 3. Code 约定规范 + +### 3.1 成功类 + +| code | message | 说明 | +| ---- | ------- | ------------ | +| 0 | 成功 | 通用成功响应 | + +------ + +### 3.2 系统级错误(1000 ~ 1999) + +| code | message | 说明 | +| ---- | ---------- | ------------------ | +| 1000 | 系统错误 | 未知异常 | +| 1001 | 服务不可用 | 服务器维护中或宕机 | +| 1002 | 请求超时 | 后端超时 | +| 1003 | 参数错误 | 缺少或参数不合法 | +| 1004 | 数据库错误 | 数据库读写失败 | +| 1005 | 权限不足 | 无权限访问 | +| 1006 | 认证失败 | Token 无效/过期 | + +------ + +### 3.3 用户相关错误(2000 ~ 2099) + +| code | message | 说明 | +| ---- | ---------- | ---------------- | +| 2000 | 用户不存在 | 查询不到用户 | +| 2001 | 用户已存在 | 注册时用户已存在 | +| 2002 | 密码错误 | 登录密码错误 | +| 2003 | 用户被禁用 | 被管理员封禁 | +| 2004 | 登录过期 | 需重新登录 | + +------ + +### 3.4 好友相关错误(2100 ~ 2199) + +| code | message | 说明 | +| ---- | -------------- | ---------- | +| 2100 | 好友申请已存在 | 重复申请 | +| 2101 | 好友关系不存在 | 不是好友 | +| 2102 | 已经是好友 | 重复添加 | +| 2103 | 好友请求被拒绝 | 被对方拒绝 | + +------ + +### 3.5 群聊相关错误(2200 ~ 2299) + +| code | message | 说明 | +| ---- | ------------ | ------------- | +| 2200 | 群不存在 | 查询不到群 | +| 2201 | 已在群中 | 不能重复加入 | +| 2202 | 群成员已满 | 超出限制 | +| 2203 | 无加群权限 | 需要邀请/验证 | +| 2204 | 群邀请已过期 | 邀请链接过期 | + +------ + +### 3.6 消息相关错误(2300 ~ 2399) + +| code | message | 说明 | +| ---- | ---------------- | ------------------- | +| 2300 | 消息发送失败 | 发送时异常 | +| 2301 | 消息不存在 | 查询不到 | +| 2302 | 消息撤回失败 | 超过时间限制 | +| 2303 | 不支持的消息类型 | message_type 不合法 | + +------ + +### 3.7 文件相关错误(2400 ~ 2499) + +| code | message | 说明 | +| ---- | -------------- | ------------ | +| 2400 | 文件上传失败 | 存储服务错误 | +| 2401 | 文件不存在 | 下载时未找到 | +| 2402 | 文件大小超限 | 超过配置限制 | +| 2403 | 文件类型不支持 | 格式不允许 | + +------ + +### 3.8 管理后台相关错误(3000 ~ 3099) + +| code | message | 说明 | +| ---- | ------------ | ---------------- | +| 3000 | 管理员不存在 | 账号错误 | +| 3001 | 密码错误 | 后台登录失败 | +| 3002 | 角色不存在 | 角色未找到 | +| 3003 | 权限不足 | 无操作权限 | +| 3004 | 操作记录失败 | 后台日志写入失败 | + +------ + +## 4. 响应示例 + +### 成功示例 + +``` +{ + "code": 0, + "message": "好友申请成功", + "data": { + "requestId": 12345 + } +} +``` + +### 失败示例 + +``` +{ + "code": 2100, + "message": "好友申请已存在", + "data": null +} +``` \ No newline at end of file