242 lines
7.8 KiB
Markdown
242 lines
7.8 KiB
Markdown
# WebSocket消息广播修复工作记录
|
||
|
||
**日期:** 2025年1月16日
|
||
**任务:** 修复WebSocketMessageBroadcaster.java发送JSON格式消息的问题
|
||
**目标:** 实现向前端发送完整的JSON格式消息,包含消息类型、时间戳、消息ID等元数据
|
||
|
||
## 问题分析
|
||
|
||
### 新版本 vs 旧版本差异对比
|
||
|
||
| 方面 | 新版本(当前QAUP项目) | 旧版本(备份项目) |
|
||
|------|----------------------|------------------|
|
||
| **WebSocket实现** | 原生WebSocket + CollisionWebSocketHandler | Spring WebSocket STOMP + SimpMessagingTemplate |
|
||
| **消息发送方式** | `collisionWebSocketHandler.broadcastMessage()` | `messagingTemplate.convertAndSend()` |
|
||
| **发送内容** | ❌ 只发送 `message.getPayload().toString()` | ✅ 发送完整的 `UniversalMessage` 对象 |
|
||
| **连接管理** | ✅ 有 `getOnlineCount()` 方法 | ❌ 没有连接数管理 |
|
||
| **主题管理** | 使用原生WebSocket,无主题概念 | 使用STOMP主题 `/topic/realtime` |
|
||
|
||
### 核心问题
|
||
新版本的 `WebSocketMessageBroadcaster.java` 只发送了 `message.getPayload().toString()`,导致前端接收到的消息**缺少重要信息**:
|
||
- ❌ 消息类型 (`type`)
|
||
- ❌ 时间戳 (`timestamp`)
|
||
- ❌ 消息ID (`messageId`)
|
||
|
||
前端无法根据消息类型进行正确的路由和处理。
|
||
|
||
## 解决方案
|
||
|
||
### 1. 添加JSON序列化支持
|
||
在 `WebSocketMessageBroadcaster.java` 中:
|
||
- 添加 `ObjectMapper` 依赖注入
|
||
- 导入 Jackson JSON 处理库
|
||
|
||
### 2. 修复消息发送逻辑
|
||
将:
|
||
```java
|
||
// 错误的方式 - 只发送payload
|
||
this.collisionWebSocketHandler.broadcastMessage(message.getPayload().toString());
|
||
```
|
||
|
||
修改为:
|
||
```java
|
||
// 正确的方式 - 发送完整的JSON消息
|
||
String jsonMessage = objectMapper.writeValueAsString(message);
|
||
this.collisionWebSocketHandler.broadcastMessage(jsonMessage);
|
||
```
|
||
|
||
### 3. 前端接收格式
|
||
修复后,前端将接收到如下格式的完整JSON消息:
|
||
```json
|
||
{
|
||
"type": "position_update",
|
||
"timestamp": 1705401234567890,
|
||
"messageId": "uuid-string",
|
||
"payload": {
|
||
"object_id": "vehicle_001",
|
||
"object_type": "vehicle",
|
||
"position": {"x": 100.5, "y": 200.3},
|
||
"heading": 45.0,
|
||
"speed": 15.2
|
||
}
|
||
}
|
||
```
|
||
|
||
## 代码修改记录
|
||
|
||
### 文件:`qaup-collision/src/main/java/com/qaup/collision/websocket/broadcaster/WebSocketMessageBroadcaster.java`
|
||
|
||
1. **添加导入**:
|
||
```java
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
```
|
||
|
||
2. **添加依赖注入**:
|
||
```java
|
||
private final ObjectMapper objectMapper;
|
||
|
||
@Autowired
|
||
public WebSocketMessageBroadcaster(MessageCacheService messageCacheService,
|
||
CollisionWebSocketHandler collisionWebSocketHandler,
|
||
ObjectMapper objectMapper) {
|
||
// ...
|
||
this.objectMapper = objectMapper;
|
||
}
|
||
```
|
||
|
||
3. **修复broadcastMessage方法**:
|
||
```java
|
||
private void broadcastMessage(UniversalMessage<?> message) {
|
||
try {
|
||
// 使用Jackson ObjectMapper将UniversalMessage序列化为JSON字符串
|
||
String jsonMessage = objectMapper.writeValueAsString(message);
|
||
this.collisionWebSocketHandler.broadcastMessage(jsonMessage);
|
||
|
||
messageCacheService.cacheMessage(message);
|
||
} catch (Exception e) {
|
||
System.err.println("Failed to broadcast message via native WebSocket: " + e.getMessage());
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
```
|
||
|
||
## 预期效果
|
||
|
||
修复后的系统应该能够:
|
||
1. ✅ 向前端发送完整的JSON格式消息
|
||
2. ✅ 前端可以根据消息类型进行正确路由
|
||
3. ✅ 前端可以获取时间戳进行时序处理
|
||
4. ✅ 前端可以使用消息ID进行去重等操作
|
||
|
||
## 🔧 **额外发现和修复:时间戳一致性问题**
|
||
|
||
### 问题诊断
|
||
用户反馈发现外层时间戳和payload中的时间戳不一致:
|
||
- 外层时间戳:721736352917(相对时间)
|
||
- payload时间戳:1751939868396000(绝对时间)
|
||
|
||
### 根本原因
|
||
不同的WebSocket事件类使用了不同的时间戳生成方式:
|
||
- `PositionUpdateEvent`: 使用 `System.nanoTime() / 1000` → **相对时间**(从系统启动开始)
|
||
- `CollisionWarningEvent`: 使用 `Instant.now().toEpochMilli() * 1000` → **绝对时间**(Unix时间戳)
|
||
|
||
### 修复方案
|
||
统一所有WebSocket事件和UniversalMessage便捷方法使用绝对时间戳:
|
||
```java
|
||
// 修改前(相对时间)
|
||
this.timestamp = System.nanoTime() / 1000;
|
||
|
||
// 修改后(绝对时间)
|
||
this.timestamp = java.time.Instant.now().toEpochMilli() * 1000;
|
||
```
|
||
|
||
### 修复文件
|
||
1. `PositionUpdateEvent.java` - 两个构造函数
|
||
2. `VehicleCommandEvent.java` - 构造函数
|
||
3. `RuleViolationWebSocketEvent.java` - builder方法
|
||
4. `RuleStateChangeWebSocketEvent.java` - builder方法
|
||
5. `RuleExecutionStatusWebSocketEvent.java` - builder方法
|
||
6. `UniversalMessage.java` - 所有9个便捷方法
|
||
|
||
## 测试验证
|
||
|
||
已完成:
|
||
1. ✅ 统一JSON序列化使用ObjectMapper
|
||
2. ✅ 修复时间戳一致性问题
|
||
3. ✅ 确保所有WebSocket事件使用相同的时间戳格式
|
||
|
||
## ✅ **前端页面修复:解决"undefined"显示问题**
|
||
|
||
### 问题诊断
|
||
用户测试发现前端显示 "位置更新: undefined",分析原因:
|
||
- 前端代码使用 `message.payload.vehicleId` 访问车辆ID
|
||
- 但 `PositionUpdatePayload` 实际字段名是 `object_id`
|
||
- 字段名不匹配导致返回 `undefined`
|
||
|
||
### 修复方案
|
||
更新 `test_websocket.html` 中的 `handleCollisionMessage` 函数:
|
||
```javascript
|
||
// 修复前
|
||
log('collisionLog', `位置更新: ${message.payload.vehicleId}`, 'info');
|
||
|
||
// 修复后
|
||
const objectId = message.payload?.object_id || '未知';
|
||
const objectType = message.payload?.object_type || '';
|
||
log('collisionLog', `位置更新: ${objectId} (${objectType})`, 'info');
|
||
```
|
||
|
||
### 增强功能
|
||
同时完善了对其他消息类型的处理:
|
||
- `rule_violation` - 规则违规事件
|
||
- `rule_execution_status` - 规则执行状态
|
||
- `rule_state_change` - 规则状态变更
|
||
- `vehicle_command` - 车辆指令
|
||
- `traffic_light_status` - 红绿灯状态
|
||
- 添加了安全的空值检查(`?.` 操作符)
|
||
|
||
## 测试验证
|
||
|
||
已完成:
|
||
1. ✅ 统一JSON序列化使用ObjectMapper
|
||
2. ✅ 修复时间戳一致性问题
|
||
3. ✅ 确保所有WebSocket事件使用相同的时间戳格式
|
||
4. ✅ 修复前端页面字段名不匹配导致的"undefined"问题
|
||
|
||
## 🔧 **前端编译修复:解决缺失模块问题**
|
||
|
||
### 问题诊断
|
||
前端编译失败,错误信息:
|
||
```
|
||
Failed to compile.
|
||
./src/utils/index.js
|
||
Module not found: Error: Can't resolve './qaup' in '/Users/tianjianyong/apps/Company/QAUP-Management/qaup-ui/src/utils'
|
||
```
|
||
|
||
### 根本原因
|
||
- `utils/index.js` 和 `main.js` 都尝试从 `./qaup` 导入函数
|
||
- 但 `qaup.js` 文件不存在
|
||
- 实际函数定义在 `ruoyi.js` 中
|
||
|
||
### 解决方案
|
||
创建 `qaup-ui/src/utils/qaup.js` 文件,重新导出 `ruoyi.js` 中的函数:
|
||
```javascript
|
||
export {
|
||
parseTime,
|
||
resetForm,
|
||
addDateRange,
|
||
selectDictLabel,
|
||
selectDictLabels,
|
||
handleTree,
|
||
sprintf,
|
||
parseStrEmpty,
|
||
mergeRecursive,
|
||
tansParams,
|
||
getNormalPath,
|
||
blobValidate
|
||
} from './ruoyi'
|
||
```
|
||
|
||
## 最终验证
|
||
|
||
已完成全部修复:
|
||
1. ✅ 统一JSON序列化使用ObjectMapper
|
||
2. ✅ 修复时间戳一致性问题
|
||
3. ✅ 确保所有WebSocket事件使用相同的时间戳格式
|
||
4. ✅ 修复前端页面字段名不匹配导致的"undefined"问题
|
||
5. ✅ 修复前端编译错误,创建缺失的qaup.js模块
|
||
|
||
最终测试:
|
||
1. 重新启动WebSocket服务
|
||
2. 前端编译成功
|
||
3. 连接前端客户端测试
|
||
4. 验证完整JSON消息和前端正确显示
|
||
|
||
## 技术要点
|
||
|
||
- **JSON序列化**: 使用Jackson ObjectMapper确保正确的JSON格式输出
|
||
- **消息完整性**: 保持UniversalMessage的完整结构,包含type、timestamp、messageId、payload
|
||
- **兼容性**: 与前端JSON消息格式保持一致
|
||
- **错误处理**: 添加JSON序列化异常处理
|
||
|
||
---
|
||
**状态:** 代码修复完成,待测试验证 |