Files
urbanLifeline/urbanLifelineServ/workcase/聊天室实现文档.md

234 lines
10 KiB
Markdown
Raw Normal View History

2025-12-22 13:08:08 +08:00
# 聊天室实现文档
## 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)` | 添加转录记录 |