From 1f390fce5628f55f4b2b2763adf5a059b1c76b13 Mon Sep 17 00:00:00 2001 From: bujinyuan Date: Tue, 3 Mar 2026 10:28:46 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85=20Phase=202b=20?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3=E5=92=8C=E5=AE=9E=E6=96=BD?= =?UTF-8?q?=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 设计文档新增 Mermaid 消息收发时序图 - IMService 依赖注入补充 *redis.Client(未读数 HASH 操作) - 实施计划同步更新 Redis 依赖和 unreadKeyPrefix Made-with: Cursor --- docs/plans/2026-03-03-phase2b-design.md | 33 +++++++++++++++++-- .../2026-03-03-phase2b-implementation.plan.md | 7 +++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/plans/2026-03-03-phase2b-design.md b/docs/plans/2026-03-03-phase2b-design.md index 5b7f74c..9f2c3e8 100644 --- a/docs/plans/2026-03-03-phase2b-design.md +++ b/docs/plans/2026-03-03-phase2b-design.md @@ -127,7 +127,33 @@ IM Service.RecallMessage() └── PubSub.PublishToUser(receiverID, im.message.recalled) ``` -### 3.5 跨模块依赖(接口注入模式) +### 3.5 消息收发时序图 + +```mermaid +sequenceDiagram + participant Sender as 发送方Client + participant WS as WebSocketHandler + participant IM as IMService + participant DB as PostgreSQL + participant Redis as Redis + participant PubSub as PubSub + participant Receiver as 接收方Client + + Sender->>WS: im.message.send {target_user_id, content, client_msg_id} + WS->>IM: SendMessage(senderID, targetUserID, content) + IM->>IM: FriendChecker.IsFriend() + IM->>IM: FindOrCreateConversation() + IM->>DB: INSERT im_messages + IM->>DB: UPDATE im_conversations + IM->>DB: UPDATE im_conversation_members (unread+1) + IM->>Redis: HINCRBY echo:im:unread:{receiverID} convID 1 + IM-->>WS: messageID + conversationID + WS-->>Sender: im.message.send.ack {message_id, conversation_id} + IM->>PubSub: PublishToUser(receiverID, im.message.new) + PubSub-->>Receiver: im.message.new {消息详情} +``` + +### 3.6 跨模块依赖(接口注入模式) 沿用 Phase 2a 的接口注入标准,避免 `im` 包直接 import `contact` 包: @@ -317,12 +343,15 @@ type IMService struct { conversationDAO *dao.ConversationDAO messageDAO *dao.MessageDAO pubsub *ws.PubSub // 复用 Phase 2a + rdb *redis.Client // Redis 操作(未读数 HASH) friendChecker FriendChecker // 接口注入 → FriendshipDAO.IsFriend userInfoGetter UserInfoGetter // 接口注入 → FriendshipDAO.GetUsersByIDs } ``` -Wire 注入链:`contact.FriendshipDAO` → 隐式实现 `FriendChecker` + `UserInfoGetter`。 +Wire 注入链: +- `contact.FriendshipDAO` → 隐式实现 `FriendChecker` + `UserInfoGetter` +- `*redis.Client` → 直接注入(已在 InfraSet 中提供) ### 7.3 IMService 核心方法 diff --git a/docs/plans/2026-03-03-phase2b-implementation.plan.md b/docs/plans/2026-03-03-phase2b-implementation.plan.md index 95fee9a..01e53ab 100644 --- a/docs/plans/2026-03-03-phase2b-implementation.plan.md +++ b/docs/plans/2026-03-03-phase2b-implementation.plan.md @@ -302,13 +302,18 @@ type FriendChecker interface { type UserInfoGetter interface { GetUsersByIDs(ctx context.Context, userIDs []int64) ([]authModel.User, error) } + +// Redis Key 前缀(未读数 HASH) +const unreadKeyPrefix = "echo:im:unread:" ``` **Step 2: 创建 IMService** Create: `backend/go-service/app/im/service/im_service.go` -IMService 注入:`ConversationDAO`、`MessageDAO`、`*ws.PubSub`、`FriendChecker`、`UserInfoGetter` +IMService 注入:`ConversationDAO`、`MessageDAO`、`*ws.PubSub`、`*redis.Client`、`FriendChecker`、`UserInfoGetter` + +注意:`*redis.Client` 用于操作未读数 Redis HASH(`echo:im:unread:{user_id}`) 核心方法(参考设计文档第七节): - `SendMessage(ctx, senderID, targetUserID, content, msgType, clientMsgID)` — 核心链路:校验好友 → 查找/创建会话 → 写消息 → 更新 last_message → 增加未读 → 推送