# 聊天室实现文档

## 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)` | 添加转录记录 |
