# 聊天室实现文档 ## 1. 业务规则 - 一个工单只能创建一个聊天室,一个聊天室可以发起多次会议 - 创建聊天室时自动添加来客和所有在线客服到成员表 - 消息发送使用分布式锁保证时间戳递增,避免并发乱序 --- ## 2. 核心文件结构 ``` workcase/ ├── src/main/java/org/xyzh/workcase/ │ ├── service/ │ │ ├── ChatRoomServiceImpl.java # 聊天室服务实现 │ │ └── MeetServiceImpl.java # 会议服务实现(伪代码) │ ├── listener/ │ │ └── ChatMessageListener.java # Redis消息监听器 │ ├── config/ │ │ ├── WebSocketConfig.java # STOMP WebSocket配置 │ │ └── RedisSubscriberConfig.java # Redis订阅配置 │ └── mapper/ │ ├── TbChatRoomMapper.java │ ├── TbChatRoomMemberMapper.java │ ├── TbChatMessageMapper.java │ └── TbCustomerServiceMapper.java apis/api-workcase/ ├── src/main/java/org/xyzh/api/workcase/ │ ├── constant/ │ │ └── WorkcaseConstant.java # 常量定义 │ ├── service/ │ │ ├── ChatRoomService.java # 聊天室服务接口 │ │ └── MeetService.java # 会议服务接口 │ ├── dto/ │ │ ├── TbChatRoomDTO.java │ │ ├── TbChatRoomMemberDTO.java │ │ └── TbChatMessageDTO.java │ └── vo/ │ ├── ChatRoomVO.java │ ├── ChatMemberVO.java │ └── ChatMessageVO.java ``` --- ## 3. Redis Key 设计 | Key | 说明 | 示例 | |-----|------|------| | `chat:room:{roomId}` | 聊天室消息Pub/Sub频道 | `chat:room:abc123` | | `chat:room:online:{roomId}` | 在线用户Set | `chat:room:online:abc123` | | `chat:room:lock:{roomId}` | 消息发送锁 | `chat:room:lock:abc123` | | `chat:room:lasttime:{roomId}` | 最后消息时间戳 | `chat:room:lasttime:abc123` | | `chat:list:update` | 聊天室列表更新通知频道 | `chat:list:update` | --- ## 4. 消息流转架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 消息发送流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 客户端 ──HTTP POST──> ChatRoomServiceImpl.sendMessage() │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ 获取分布式锁 │ │ │ │ chat:room:lock │ │ │ └────────┬────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ 递增时间戳保证 │ │ │ │ 消息顺序 │ │ │ └────────┬────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ 保存到数据库 │ │ │ └────────┬────────┘ │ │ │ │ │ ▼ │ │ redisService.publish("chat:room:{roomId}") │ │ │ │ │ ▼ │ │ redisService.publish("chat:list:update") │ │ │ │ └──────────────────────────────┼──────────────────────────────────┘ │ ┌──────────────────────────────┼──────────────────────────────────┐ │ 消息接收流程 │ ├──────────────────────────────┼──────────────────────────────────┤ │ │ │ │ RedisSubscriberConfig (订阅 chat:room:*, chat:list:update)│ │ │ │ │ ▼ │ │ ChatMessageListener.onMessage() │ │ ┌────┴────┐ │ │ │ │ │ │ ▼ ▼ │ │ /topic/chat/{roomId} /topic/chat/list-update │ │ (聊天窗口消息) (聊天室列表更新) │ │ │ │ │ │ ▼ ▼ │ │ 聊天窗口订阅者 列表页面订阅者 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 5. 用户类型常量 | 常量 | 值 | 说明 | |------|-----|------| | `USER_TYPE_GUEST` | `guest` | 来客 | | `USER_TYPE_STAFF` | `staff` | 客服 | | `USER_TYPE_AI` | `ai` | AI助手 | --- ## 6. 状态常量 ### 聊天室状态 | 常量 | 值 | 说明 | |------|-----|------| | `ROOM_STATUS_ACTIVE` | `active` | 活跃 | | `ROOM_STATUS_CLOSED` | `closed` | 已关闭 | ### 成员状态 | 常量 | 值 | 说明 | |------|-----|------| | `MEMBER_STATUS_ACTIVE` | `active` | 活跃 | | `MEMBER_STATUS_LEFT` | `left` | 已离开 | | `MEMBER_STATUS_REMOVED` | `removed` | 被移除 | ### 消息状态 | 常量 | 值 | 说明 | |------|-----|------| | `MESSAGE_STATUS_SENT` | `sent` | 已发送 | | `MESSAGE_STATUS_RECALLED` | `recalled` | 已撤回 | --- ## 7. 并发消息处理 ```java // 获取分布式锁 String lockKey = WorkcaseConstant.REDIS_CHAT_LOCK + roomId; redisService.setIfAbsent(lockKey, "1", 5); // 5秒过期 // 保证时间戳递增 String timeKey = WorkcaseConstant.REDIS_CHAT_LASTTIME + roomId; long lastTime = redisService.get(timeKey); if (currentTime <= lastTime) { currentTime = lastTime + 1; } message.setSendTime(new Date(currentTime)); redisService.set(timeKey, currentTime); // 释放锁 redisService.delete(lockKey); ``` --- ## 8. 前端WebSocket连接示例 ```javascript // 连接WebSocket const socket = new SockJS('/ws/chat'); const stompClient = Stomp.over(socket); stompClient.connect({}, () => { // 1. 订阅聊天室消息(聊天窗口使用) stompClient.subscribe('/topic/chat/' + roomId, (message) => { const chatMessage = JSON.parse(message.body); // 处理收到的消息,添加到聊天窗口 console.log('收到消息:', chatMessage); }); // 2. 订阅聊天室列表更新(列表页面使用) stompClient.subscribe('/topic/chat/list-update', (message) => { const chatMessage = JSON.parse(message.body); // 根据roomId更新对应聊天室的lastMessage和lastMessageTime updateChatRoomInList(chatMessage.roomId, { lastMessage: chatMessage.content, lastMessageTime: chatMessage.sendTime, senderName: chatMessage.senderName }); }); }); // 断开连接 stompClient.disconnect(); ``` --- ## 9. API接口 ### ChatRoomService | 方法 | 说明 | |------|------| | `createChatRoom(dto)` | 创建聊天室,自动添加来客和客服 | | `updateChatRoom(dto)` | 更新聊天室信息 | | `closeChatRoom(roomId, closedBy)` | 关闭聊天室 | | `deleteChatRoom(roomId)` | 删除聊天室 | | `getChatRoomById(roomId)` | 获取聊天室详情 | | `getChatRoomPage(pageRequest)` | 分页查询聊天室 | | `addChatRoomMember(member)` | 添加成员 | | `removeChatRoomMember(memberId)` | 移除成员 | | `sendMessage(message)` | 发送消息(含并发处理) | | `getChatMessagePage(pageRequest)` | 分页查询消息 | | `assignCustomerService(roomId)` | 分配所有客服到聊天室 | ### MeetService(伪代码) | 方法 | 说明 | |------|------| | `createMeeting(dto)` | 创建会议 | | `startMeeting(meetingId)` | 开始会议 | | `endMeeting(meetingId)` | 结束会议 | | `joinMeeting(participant)` | 参与者加入 | | `leaveMeeting(participantId)` | 参与者离开 | | `generateMeetingJoinUrl(...)` | 生成Jitsi加入链接 | | `addTranscription(dto)` | 添加转录记录 |