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