7.8 KiB
7.8 KiB
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. 修复消息发送逻辑
将:
// 错误的方式 - 只发送payload
this.collisionWebSocketHandler.broadcastMessage(message.getPayload().toString());
修改为:
// 正确的方式 - 发送完整的JSON消息
String jsonMessage = objectMapper.writeValueAsString(message);
this.collisionWebSocketHandler.broadcastMessage(jsonMessage);
3. 前端接收格式
修复后,前端将接收到如下格式的完整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
-
添加导入:
import com.fasterxml.jackson.databind.ObjectMapper; -
添加依赖注入:
private final ObjectMapper objectMapper; @Autowired public WebSocketMessageBroadcaster(MessageCacheService messageCacheService, CollisionWebSocketHandler collisionWebSocketHandler, ObjectMapper objectMapper) { // ... this.objectMapper = objectMapper; } -
修复broadcastMessage方法:
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(); } }
预期效果
修复后的系统应该能够:
- ✅ 向前端发送完整的JSON格式消息
- ✅ 前端可以根据消息类型进行正确路由
- ✅ 前端可以获取时间戳进行时序处理
- ✅ 前端可以使用消息ID进行去重等操作
🔧 额外发现和修复:时间戳一致性问题
问题诊断
用户反馈发现外层时间戳和payload中的时间戳不一致:
- 外层时间戳:721736352917(相对时间)
- payload时间戳:1751939868396000(绝对时间)
根本原因
不同的WebSocket事件类使用了不同的时间戳生成方式:
PositionUpdateEvent: 使用System.nanoTime() / 1000→ 相对时间(从系统启动开始)CollisionWarningEvent: 使用Instant.now().toEpochMilli() * 1000→ 绝对时间(Unix时间戳)
修复方案
统一所有WebSocket事件和UniversalMessage便捷方法使用绝对时间戳:
// 修改前(相对时间)
this.timestamp = System.nanoTime() / 1000;
// 修改后(绝对时间)
this.timestamp = java.time.Instant.now().toEpochMilli() * 1000;
修复文件
PositionUpdateEvent.java- 两个构造函数VehicleCommandEvent.java- 构造函数RuleViolationWebSocketEvent.java- builder方法RuleStateChangeWebSocketEvent.java- builder方法RuleExecutionStatusWebSocketEvent.java- builder方法UniversalMessage.java- 所有9个便捷方法
测试验证
已完成:
- ✅ 统一JSON序列化使用ObjectMapper
- ✅ 修复时间戳一致性问题
- ✅ 确保所有WebSocket事件使用相同的时间戳格式
✅ 前端页面修复:解决"undefined"显示问题
问题诊断
用户测试发现前端显示 "位置更新: undefined",分析原因:
- 前端代码使用
message.payload.vehicleId访问车辆ID - 但
PositionUpdatePayload实际字段名是object_id - 字段名不匹配导致返回
undefined
修复方案
更新 test_websocket.html 中的 handleCollisionMessage 函数:
// 修复前
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- 红绿灯状态- 添加了安全的空值检查(
?.操作符)
测试验证
已完成:
- ✅ 统一JSON序列化使用ObjectMapper
- ✅ 修复时间戳一致性问题
- ✅ 确保所有WebSocket事件使用相同的时间戳格式
- ✅ 修复前端页面字段名不匹配导致的"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 中的函数:
export {
parseTime,
resetForm,
addDateRange,
selectDictLabel,
selectDictLabels,
handleTree,
sprintf,
parseStrEmpty,
mergeRecursive,
tansParams,
getNormalPath,
blobValidate
} from './ruoyi'
最终验证
已完成全部修复:
- ✅ 统一JSON序列化使用ObjectMapper
- ✅ 修复时间戳一致性问题
- ✅ 确保所有WebSocket事件使用相同的时间戳格式
- ✅ 修复前端页面字段名不匹配导致的"undefined"问题
- ✅ 修复前端编译错误,创建缺失的qaup.js模块
最终测试:
- 重新启动WebSocket服务
- 前端编译成功
- 连接前端客户端测试
- 验证完整JSON消息和前端正确显示
技术要点
- JSON序列化: 使用Jackson ObjectMapper确保正确的JSON格式输出
- 消息完整性: 保持UniversalMessage的完整结构,包含type、timestamp、messageId、payload
- 兼容性: 与前端JSON消息格式保持一致
- 错误处理: 添加JSON序列化异常处理
状态: 代码修复完成,待测试验证