QDAirPortBackend0122/doc/work/WebSocket消息广播修复_20250116.md
2026-01-22 13:19:47 +08:00

242 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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序列化异常处理
---
**状态:** 代码修复完成,待测试验证