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

7.8 KiB
Raw Blame History

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

  1. 添加导入

    import com.fasterxml.jackson.databind.ObjectMapper;
    
  2. 添加依赖注入

    private final ObjectMapper objectMapper;
    
    @Autowired
    public WebSocketMessageBroadcaster(MessageCacheService messageCacheService, 
                                     CollisionWebSocketHandler collisionWebSocketHandler, 
                                     ObjectMapper objectMapper) {
        // ...
        this.objectMapper = objectMapper;
    }
    
  3. 修复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();
        }
    }
    

预期效果

修复后的系统应该能够:

  1. 向前端发送完整的JSON格式消息
  2. 前端可以根据消息类型进行正确路由
  3. 前端可以获取时间戳进行时序处理
  4. 前端可以使用消息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;

修复文件

  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 函数:

// 修复前
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.jsmain.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'

最终验证

已完成全部修复:

  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序列化异常处理

状态: 代码修复完成,待测试验证