234 lines
10 KiB
Markdown
234 lines
10 KiB
Markdown
# 聊天室实现文档
|
||
|
||
## 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)` | 添加转录记录 |
|