From 64c1c84ed3df1b71ac2f9189442a115741d3fa35 Mon Sep 17 00:00:00 2001 From: dfdg <2710245601@qq.com> Date: Tue, 4 Nov 2025 09:46:40 +0800 Subject: [PATCH] =?UTF-8?q?websocket=E4=BC=98=E5=8C=96=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E5=88=9D=E5=A7=8B=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/bigscreen/config/RedisConfig.java | 1 + .../manager/RedisSubscribeManager.java | 188 ++++++++++-------- .../manager/RedisSubscribeStartupRunner.java | 28 +++ .../service/ProjectBigScreenService.java | 1 + .../impl/AsyncMessageHandlerServiceImpl.java | 12 +- .../impl/ProjectBigScreenServiceImpl.java | 106 ++++++++++ .../drone/mapper/DroProjectDroneMapper.java | 3 + .../service/IDroProjectDroneService.java | 3 + .../impl/DroProjectDroneServiceImpl.java | 5 + .../gps/domain/vo/GpsEquipmentSonVo.java | 4 + .../gps/mapper/GpsEquipmentSonMapper.java | 39 ++++ .../gps/service/IGpsEquipmentService.java | 4 + .../gps/service/IGpsEquipmentSonService.java | 4 + .../service/impl/GpsEquipmentServiceImpl.java | 10 + .../impl/GpsEquipmentSonServiceImpl.java | 10 + .../service/InitOnStartWebSocketServer.java | 36 ++++ .../mapper/drone/DroProjectDroneMapper.xml | 7 + 17 files changed, 376 insertions(+), 85 deletions(-) create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeStartupRunner.java diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/config/RedisConfig.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/config/RedisConfig.java index f4db93da..2283dc4e 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/config/RedisConfig.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/config/RedisConfig.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeManager.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeManager.java index 35e71ebe..b01379d1 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeManager.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeManager.java @@ -1,23 +1,20 @@ package org.dromara.bigscreen.manager; -import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; - +import org.dromara.websocket.websocket.service.InitOnStartWebSocketServer; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.drone.domain.DroProjectDrone; import org.dromara.drone.service.IDroProjectDroneService; -import org.dromara.websocket.websocket.service.InitOnStartWebSocketServer; import org.springframework.context.annotation.Lazy; -import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -44,13 +41,20 @@ public class RedisSubscribeManager { private IDroProjectDroneService droProjectDroneService; // 3. 维护已订阅的主题关系:key=完整主题(如wrj:8UUXN4P00A06NK),value=对应的PatternTopic - private final Map subscribedTopics = new ConcurrentHashMap<>(); + private final Map subscribedTopics = new ConcurrentHashMap<>(); + + // 定义锁对象 + private final Object lock = new Object(); + + // 注入 RedisConnectionFactory + @Resource + private RedisConnectionFactory redisConnectionFactory; /** * 项目启动后立即执行一次订阅(避免等待定时任务首次执行) */ - @PostConstruct +// @PostConstruct public void initSubscribe() { log.info("项目启动,初始化Redis订阅..."); // 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix) @@ -133,56 +137,58 @@ public class RedisSubscribeManager { } - @Scheduled(cron = "0/10 * * * * ?") + @Scheduled(cron = "0/10 * * * * ?") // @JobExecutor(name = "ueWsConnect") public void ueWsConnect() { - try { - int onlineCount = InitOnStartWebSocketServer.getOnlineCount(); - Object object = RedisUtils.getCacheObject("xmjdap:ws"); - log.info("开始执行Redis订阅更新定时任务..."); - if (onlineCount == 0 && object == null) { - cancelAllSubscribes(); - return; - } - if (object != null) { - long oldTime = Long.parseLong(String.valueOf(object)); - long now = System.currentTimeMillis(); - if (now-oldTime > 300000) { - RedisUtils.deleteObject("xmjdap:ws"); + synchronized (lock) { // 确保同一时间只有一个线程修改 + try { + int onlineCount = InitOnStartWebSocketServer.getOnlineCount(); + Object object = RedisUtils.getCacheObject("xmjdap:ws"); + log.info("开始执行Redis订阅更新定时任务..."); + if (onlineCount == 0 && object == null) { cancelAllSubscribes(); return; } - log.info("Redis时间缓存更新定时任务"); - } - // 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix) - List latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper().groupBy(DroProjectDrone::getDroneSn)); - if (latestKeys == null || latestKeys.isEmpty()) { - log.warn("定时任务未获取到任何主题,将取消所有现有订阅"); - cancelAllSubscribes(); - return; - } - - // 步骤2:构建最新的完整主题(格式:wrj:key) - Set latestFullTopics = new HashSet<>(); - for (DroProjectDrone key : latestKeys) { - latestFullTopics.add("wrj:osd1:" + key.getDroneSn()); - latestFullTopics.add("wrj:osd2:" + key.getDroneSn()); - latestFullTopics.add("wrj:osd3:" + key.getDroneSn()); - if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) { - latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn()); + if (object != null) { + long oldTime = Long.parseLong(String.valueOf(object)); + long now = System.currentTimeMillis(); + if (now - oldTime > 300000) { + RedisUtils.deleteObject("xmjdap:ws"); + cancelAllSubscribes(); + return; + } + log.info("Redis时间缓存更新定时任务"); } + // 步骤1:从数据库获取最新的主题列表(原逻辑:getTopicsByKeyPrefix) + List latestKeys = droProjectDroneService.getBaseMapper().selectList(new LambdaQueryWrapper().groupBy(DroProjectDrone::getDroneSn)); + if (latestKeys == null || latestKeys.isEmpty()) { + log.warn("定时任务未获取到任何主题,将取消所有现有订阅"); + cancelAllSubscribes(); + return; + } + + // 步骤2:构建最新的完整主题(格式:wrj:key) + Set latestFullTopics = new HashSet<>(); + for (DroProjectDrone key : latestKeys) { + latestFullTopics.add("wrj:osd1:" + key.getDroneSn()); + latestFullTopics.add("wrj:osd2:" + key.getDroneSn()); + latestFullTopics.add("wrj:osd3:" + key.getDroneSn()); + if (key.getAirplaneSn() != null && !key.getAirplaneSn().isEmpty()) { + latestFullTopics.add("wrj:osd4:" + key.getAirplaneSn()); + } + } + + // 步骤3:对比现有订阅,删除过期主题 + cancelExpiredSubscribes(latestFullTopics); + + // 步骤4:对比现有订阅,新增未订阅的主题 + addNewSubscribes(latestFullTopics); + + log.info("Redis订阅更新完成,当前已订阅主题数:{}", subscribedTopics.size()); + } catch (Exception e) { + log.error("Redis订阅更新定时任务执行失败", e); + // 异常时不修改现有订阅,避免误删 } - - // 步骤3:对比现有订阅,删除过期主题 - cancelExpiredSubscribes(latestFullTopics); - - // 步骤4:对比现有订阅,新增未订阅的主题 - addNewSubscribes(latestFullTopics); - - log.info("Redis订阅更新完成,当前已订阅主题数:{}", subscribedTopics.size()); - } catch (Exception e) { - log.error("Redis订阅更新定时任务执行失败", e); - // 异常时不修改现有订阅,避免误删 } } @@ -190,24 +196,30 @@ public class RedisSubscribeManager { * 取消过期的订阅(现有订阅不在最新列表中的主题) */ public void cancelExpiredSubscribes(Set latestFullTopics) { - // 遍历现有订阅,找出需要删除的主题 - Set expiredTopics = subscribedTopics.keySet().stream() + synchronized (lock) { // 确保同一时间只有一个线程修改 + if (!redisMessageListenerContainer.isRunning()) { + log.error("RedisMessageListenerContainer 未运行,无法取消订阅"); + return; + } + // 遍历现有订阅,找出需要删除的主题 + Set expiredTopics = subscribedTopics.keySet().stream() .filter(topic -> !latestFullTopics.contains(topic)) .collect(Collectors.toSet()); - if (expiredTopics.isEmpty()) { - log.info("无过期订阅主题,无需删除"); - return; - } + if (expiredTopics.isEmpty()) { + log.info("无过期订阅主题,无需删除"); + return; + } - // 取消每个过期主题的订阅 - for (String expiredTopic : expiredTopics) { - PatternTopic topic = subscribedTopics.get(expiredTopic); - if (topic != null) { - // 从容器中移除监听器(取消订阅) - redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, topic); - subscribedTopics.remove(expiredTopic); - log.info("已取消过期订阅:{}", expiredTopic); + // 取消每个过期主题的订阅 + for (String expiredTopic : expiredTopics) { + ChannelTopic topic = subscribedTopics.get(expiredTopic); + if (topic != null) { + // 从容器中移除监听器(取消订阅) + redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, topic); + subscribedTopics.remove(expiredTopic); + log.info("已取消过期订阅:{}", expiredTopic); + } } } } @@ -216,23 +228,41 @@ public class RedisSubscribeManager { * 新增未订阅的主题(最新列表中有但现有订阅没有的主题) */ public void addNewSubscribes(Set latestFullTopics) { - // 遍历最新主题,找出需要新增的主题 - Set newTopics = latestFullTopics.stream() + synchronized (lock) { // 确保同一时间只有一个线程修改 + if (!redisMessageListenerContainer.isRunning()) { + log.error("RedisMessageListenerContainer 未运行,无法添加订阅"); + return; + } +// // 测试 Redis 连接 +// try (RedisConnection conn = redisConnectionFactory.getConnection()) { +// conn.ping(); // 发送 ping 命令,验证连接 +// log.info("Redis 连接正常,可执行命令"); +// } catch (Exception e) { +// log.error("Redis 连接失败,无法订阅主题", e); +// return; // 连接失败,终止订阅 +// } + // 遍历最新主题,找出需要新增的主题 + Set newTopics = latestFullTopics.stream() .filter(topic -> !subscribedTopics.containsKey(topic)) .collect(Collectors.toSet()); - if (newTopics.isEmpty()) { - log.info("无新增订阅主题,无需添加"); - return; - } + if (newTopics.isEmpty()) { + log.info("无新增订阅主题,无需添加"); + return; + } +// Object delegate = redisMessageListenerAdapter.getDelegate(); +// log.info("适配器绑定的监听器实例:{}", +// delegate.getClass().getName()); - // 为每个新主题添加订阅 - for (String newTopic : newTopics) { - PatternTopic topic = new PatternTopic(newTopic); - // 向容器中添加监听器(新增订阅) - redisMessageListenerContainer.addMessageListener(redisMessageListenerAdapter, topic); - subscribedTopics.put(newTopic, topic); - log.info("已新增订阅:{}", newTopic); + // 为每个新主题添加订阅 + for (String newTopic : newTopics) { + ChannelTopic topic = new ChannelTopic(newTopic); + // 向容器中添加监听器(新增订阅) +// log.info("添加订阅前,容器状态:{}", redisMessageListenerContainer.isRunning() ? "运行中" : "已停止"); + redisMessageListenerContainer.addMessageListener(redisMessageListenerAdapter, topic); + subscribedTopics.put(newTopic, topic); + log.info("已新增订阅:{}", newTopic); + } } } @@ -244,7 +274,7 @@ public class RedisSubscribeManager { return; } // 遍历所有已订阅主题,取消订阅 - for (Map.Entry entry : subscribedTopics.entrySet()) { + for (Map.Entry entry : subscribedTopics.entrySet()) { redisMessageListenerContainer.removeMessageListener(redisMessageListenerAdapter, entry.getValue()); log.info("已取消订阅:{}", entry.getKey()); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeStartupRunner.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeStartupRunner.java new file mode 100644 index 00000000..c77ab2ed --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/manager/RedisSubscribeStartupRunner.java @@ -0,0 +1,28 @@ +package org.dromara.bigscreen.manager; + +import jakarta.annotation.Resource; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + + + +/** + * 项目启动完成后执行 Redis 订阅初始化 + */ +@Component +public class RedisSubscribeStartupRunner implements ApplicationRunner { + + // 注入 Redis 订阅管理器 + @Resource + private RedisSubscribeManager redisSubscribeManager; + + /** + * 项目完全启动后执行(替代 @PostConstruct) + */ + @Override + public void run(ApplicationArguments args) throws Exception { + // 此时 Spring 容器已完全启动,所有组件就绪 + redisSubscribeManager.initSubscribe(); + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/ProjectBigScreenService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/ProjectBigScreenService.java index 5e84081c..55661df9 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/ProjectBigScreenService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/ProjectBigScreenService.java @@ -89,6 +89,7 @@ public interface ProjectBigScreenService { void setList(GpsEquipmentBo bo); List> getClientList(Long projectId); + List getUeClientList(); /** * 更新无人机缓存 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/AsyncMessageHandlerServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/AsyncMessageHandlerServiceImpl.java index 026108cb..4b32d9a6 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/AsyncMessageHandlerServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/AsyncMessageHandlerServiceImpl.java @@ -92,7 +92,7 @@ public class AsyncMessageHandlerServiceImpl implements IAsyncMessageHandlerServi new LambdaQueryWrapper() .eq(DroProjectDrone::getDroneSn, gateway) ); - log.info("【异步消息处理】osd3类型消息,gateway:{}", gateway); +// log.info("【异步消息处理】osd3类型消息,gateway:{}", gateway); // 调用setWs方法(非osd4类型) setWs(messageContent, gateway, droProjectDrone, false); } else { @@ -101,7 +101,7 @@ public class AsyncMessageHandlerServiceImpl implements IAsyncMessageHandlerServi new LambdaQueryWrapper() .eq(DroProjectDrone::getAirplaneSn, gateway) ); - log.info("【异步消息处理】osd4类型消息,gateway:{}", gateway); +// log.info("【异步消息处理】osd4类型消息,gateway:{}", gateway); isOsd4 = true; // 调用setWs方法(osd4类型) setWs(messageContent, gateway, droProjectDrone, true); @@ -109,7 +109,7 @@ public class AsyncMessageHandlerServiceImpl implements IAsyncMessageHandlerServi // 6. 存储消息到Redis(使用Objects.requireNonNull避免空指针) stringRedisTemplate.opsForValue().set(key, Objects.requireNonNull(messageContent)); - log.info("【异步消息处理】Redis存储成功,key:{}", key); +// log.info("【异步消息处理】Redis存储成功,key:{}", key); } catch (Exception e) { // 捕获所有异常,避免线程终止 @@ -168,9 +168,9 @@ public class AsyncMessageHandlerServiceImpl implements IAsyncMessageHandlerServi // 位置信息 JSONObject position = new JSONObject(); - position.put("lat", JSONUtil.parseObj(message).getJSONObject("data").get("latitude").toString()); // 纬度 - position.put("lng", JSONUtil.parseObj(message).getJSONObject("data").get("longitude").toString()); // 经度 - position.put("alt", JSONUtil.parseObj(message).getJSONObject("data").get("height").toString()); // 海拔 + position.put("lat", JSONUtil.parseObj(message).getJSONObject("data").get("latitude")); // 纬度 + position.put("lng", JSONUtil.parseObj(message).getJSONObject("data").get("longitude")); // 经度 + position.put("alt", JSONUtil.parseObj(message).getJSONObject("data").get("height")); // 海拔 data.put("position", position); messageObj.put("data", data); // 设备唯一标识 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/ProjectBigScreenServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/ProjectBigScreenServiceImpl.java index 9179bd0f..b12baeed 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/ProjectBigScreenServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/service/impl/ProjectBigScreenServiceImpl.java @@ -16,6 +16,7 @@ import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.utils.BigDecimalUtil; import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.drone.domain.vo.DroProjectDroneVo; import org.dromara.drone.service.IDroProjectDroneService; import org.dromara.gps.domain.bo.GpsEquipmentBo; import org.dromara.gps.domain.vo.GpsEquipmentSonVo; @@ -702,6 +703,111 @@ public class ProjectBigScreenServiceImpl implements ProjectBigScreenService { return maps; } + @Override + public List getUeClientList() { + // 获取当天的开始时间(00:00:00) + LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIN); + + // 获取当前时间 + LocalDateTime now = LocalDateTime.now(); + List voList = gpsEquipmentService.getUeClientList(startOfDay, now); + List appList = gpsEquipmentService.getUeUserListByProjectId( startOfDay, now); +// List anqmList = deviceService.getUeUserListByProjectId( startOfDay, now); +// List othYs7DeviceList = othYs7DeviceService.lambdaQuery() +// .list(); + List wrjKeys = droProjectDroneService.getUeTopicsByProjectId(); + List maps = new ArrayList<>(); + if (voList != null && !voList.isEmpty()) { + for (GpsEquipmentSonVo item : voList) { + JSONObject messageObj = new JSONObject(); + messageObj.put("type", "location"); // 消息类型 + + JSONObject data = new JSONObject(); + data.put("id", item.getModelId()); + + // 位置信息 + JSONObject position = new JSONObject(); + position.put("lat", item.getLocLatitude()); // 纬度 + position.put("lng", item.getLocLongitude()); // 经度 + position.put("alt", item.getLocAltitude()); // 海拔 + + data.put("position", position); + messageObj.put("data", data); // 设备唯一标识 + maps.add(messageObj.toString()); + } + } + if (appList != null && !appList.isEmpty()) { + for (GpsEquipmentSonVo item : appList) { + JSONObject messageObj = new JSONObject(); + messageObj.put("type", "location"); // 消息类型 + + JSONObject data = new JSONObject(); + data.put("id", item.getModelId()); + + // 位置信息 + JSONObject position = new JSONObject(); + position.put("lat", item.getLocLatitude()); // 纬度 + position.put("lng", item.getLocLongitude()); // 经度 + position.put("alt", item.getLocAltitude()); // 海拔 + + data.put("position", position); + messageObj.put("data", data); // 设备唯一标识 + maps.add(messageObj.toString()); + } + } + if (wrjKeys != null && !wrjKeys.isEmpty()) { + for (DroProjectDroneVo key : wrjKeys) { + Object object = stringRedisTemplate.opsForValue().get("wrj:osd4:" + key.getAirplaneSn()); + Object object6 = stringRedisTemplate.opsForValue().get("wrj:osd2:" + key.getDroneSn()); + String status = ""; + if (object6 != null) { + JSONObject object1 = JSONUtil.parseObj(object6); + status = object1.getJSONObject("data").get("flighttask_step_code").toString(); + } + if (object != null) { + JSONObject object1 = JSONUtil.parseObj(object); + JSONObject messageObj = new JSONObject(); + messageObj.put("type", "location"); // 消息类型 + + JSONObject data = new JSONObject(); + data.put("id", key.getAirplaneModleId()); + + // 位置信息 + JSONObject position = new JSONObject(); + position.put("lat", object1.getJSONObject("data").get("latitude")); // 纬度 + position.put("lng", object1.getJSONObject("data").get("longitude")); // 经度 + position.put("alt", object1.getJSONObject("data").get("height")); // 海拔 + + data.put("position", position); + messageObj.put("data", data); // 设备唯一标识 + maps.add(messageObj.toString()); + } else { + Object object2 = stringRedisTemplate.opsForValue().get("wrj:osd3:" + key); + if (object2 != null) { + JSONObject object3 = JSONUtil.parseObj(object2); + + JSONObject messageObj = new JSONObject(); + messageObj.put("type", "location"); // 消息类型 + + JSONObject data = new JSONObject(); + data.put("id", key.getDroneModleId()); + + // 位置信息 + JSONObject position = new JSONObject(); + position.put("lat", object3.getJSONObject("data").get("latitude")); // 纬度 + position.put("lng", object3.getJSONObject("data").get("longitude")); // 经度 + position.put("alt", object3.getJSONObject("data").get("height")); // 海拔 + + data.put("position", position); + messageObj.put("data", data); // 设备唯一标识 + maps.add(messageObj.toString()); + } + } + } + } + return maps; + } + @Override public void setWrjHc() { RedisUtils.setCacheObject("xmjdap:ws", System.currentTimeMillis()); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/mapper/DroProjectDroneMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/mapper/DroProjectDroneMapper.java index 0518dbc6..b593fbdc 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/mapper/DroProjectDroneMapper.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/mapper/DroProjectDroneMapper.java @@ -18,4 +18,7 @@ public interface DroProjectDroneMapper extends BaseMapperPlus getTopicsByKeyPrefix(); List getTopicsByProjectId(@Param("projectId") Long projectId); + + List getUeTopicsByProjectId(); + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/IDroProjectDroneService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/IDroProjectDroneService.java index 31ef54e6..881a7d42 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/IDroProjectDroneService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/IDroProjectDroneService.java @@ -83,4 +83,7 @@ public interface IDroProjectDroneService extends IService { List getTopicsByKeyPrefix(); List getTopicsByProjectId(Long projectId); + + List getUeTopicsByProjectId(); + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/impl/DroProjectDroneServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/impl/DroProjectDroneServiceImpl.java index 01dd4f8a..37322aab 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/impl/DroProjectDroneServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/drone/service/impl/DroProjectDroneServiceImpl.java @@ -167,4 +167,9 @@ public class DroProjectDroneServiceImpl extends ServiceImpl getTopicsByProjectId(Long projectId) { return baseMapper.getTopicsByProjectId(projectId); } + + @Override + public List getUeTopicsByProjectId() { + return baseMapper.getUeTopicsByProjectId(); + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/domain/vo/GpsEquipmentSonVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/domain/vo/GpsEquipmentSonVo.java index d3697806..293b1e30 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/domain/vo/GpsEquipmentSonVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/domain/vo/GpsEquipmentSonVo.java @@ -149,6 +149,10 @@ public class GpsEquipmentSonVo implements Serializable { */ @ExcelProperty(value = "备注") private String remark; + /** + * 模型id + */ + private String modelId; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/mapper/GpsEquipmentSonMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/mapper/GpsEquipmentSonMapper.java index 475e75e2..7b261120 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/mapper/GpsEquipmentSonMapper.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/mapper/GpsEquipmentSonMapper.java @@ -92,4 +92,43 @@ public interface GpsEquipmentSonMapper extends BaseMapperPlus getRlList(@Param("bo") GpsEquipmentSonBo bo); + + @Select("WITH RankedData AS (\n" + + " SELECT\n" + + " *,\n" + + " ROW_NUMBER() OVER (PARTITION BY client_id ORDER BY create_time DESC) AS rn\n" + + " FROM\n" + + " gps_equipment_son \n" + + "WHERE \n" + + "create_time BETWEEN #{startTime} AND #{endTime}\n" + + ")\n" + + "SELECT\n" + + " r.*,su.nick_name as userName,ge.model_id as modelId\n" + + "FROM\n" + + " RankedData r\n" + + "LEFT JOIN sys_user su ON r.user_id=su.user_id\n" + + "LEFT JOIN gps_equipment ge ON r.client_id = ge.client_id \n" + + "WHERE\n" + + " rn = 1;") + List getUeClientList(@Param("startTime") LocalDateTime startOfDay, @Param("endTime") LocalDateTime now); + + @Select("WITH RankedData AS (\n" + + " SELECT\n" + + " *,\n" + + " ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY create_time DESC) AS rn\n" + + " FROM\n" + + " gps_equipment_son \n " + + "WHERE \n" + + "client_id IS NULL \n" + + "AND create_time BETWEEN #{startTime} AND #{endTime} \n" + + ")\n" + + "SELECT\n" + + " r.*,su.nick_name as userName,ge.model_id as modelId\n" + + "FROM\n" + + " RankedData r " + + "LEFT JOIN sys_user su ON r.user_id=su.user_id\n" + + "LEFT JOIN gps_equipment ge ON r.user_id = ge.user_id \n" + + "WHERE\n" + + " rn = 1;") + List getUeUserListByProjectId(@Param("startTime") LocalDateTime startOfDay, @Param("endTime") LocalDateTime now); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentService.java index dcbc6302..c38283c7 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentService.java @@ -102,4 +102,8 @@ public interface IGpsEquipmentService extends IService{ void setData(String jsonData); List getUserListByProjectId(Long projectId, LocalDateTime startOfDay, LocalDateTime now); + + List getUeClientList(LocalDateTime startOfDay, LocalDateTime now); + + List getUeUserListByProjectId(LocalDateTime startOfDay, LocalDateTime now); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentSonService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentSonService.java index 4584790b..7773c0f7 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentSonService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/IGpsEquipmentSonService.java @@ -81,4 +81,8 @@ public interface IGpsEquipmentSonService extends IService{ List getUserListByProjectId(Long projectId, LocalDateTime startOfDay, LocalDateTime now); List getRlList(GpsEquipmentSonBo bo); + + List getUeClientList(LocalDateTime startOfDay, LocalDateTime now); + + List getUeUserListByProjectId(LocalDateTime startOfDay, LocalDateTime now); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentServiceImpl.java index 7e7546ec..502cd5bc 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentServiceImpl.java @@ -529,5 +529,15 @@ public class GpsEquipmentServiceImpl extends ServiceImpl getUeClientList(LocalDateTime startOfDay, LocalDateTime now) { + return gpsEquipmentSonService.getUeClientList(startOfDay,now); + } + + @Override + public List getUeUserListByProjectId(LocalDateTime startOfDay, LocalDateTime now) { + return gpsEquipmentSonService.getUeUserListByProjectId(startOfDay,now); + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentSonServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentSonServiceImpl.java index 75a7295a..a0143aab 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentSonServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/gps/service/impl/GpsEquipmentSonServiceImpl.java @@ -211,4 +211,14 @@ public class GpsEquipmentSonServiceImpl extends ServiceImpl getUeClientList(LocalDateTime startOfDay, LocalDateTime now) { + return baseMapper.getUeClientList(startOfDay,now); + } + + @Override + public List getUeUserListByProjectId(LocalDateTime startOfDay, LocalDateTime now) { + return baseMapper.getUeUserListByProjectId(startOfDay,now); + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/websocket/websocket/service/InitOnStartWebSocketServer.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/websocket/websocket/service/InitOnStartWebSocketServer.java index c11f2e85..50cab2aa 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/websocket/websocket/service/InitOnStartWebSocketServer.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/websocket/websocket/service/InitOnStartWebSocketServer.java @@ -2,10 +2,14 @@ package org.dromara.websocket.websocket.service;// 路径:com.ruoyi.web.websoc import jakarta.websocket.*; import jakarta.websocket.server.ServerEndpoint; import lombok.extern.slf4j.Slf4j; +import org.dromara.bigscreen.service.ProjectBigScreenService; +import org.dromara.common.core.utils.SpringUtils; import org.springframework.stereotype.Component; import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; /** @@ -20,6 +24,9 @@ public class InitOnStartWebSocketServer { // 2. 静态会话存储(线程安全,项目启动时即初始化) private static final Map ONLINE_SESSIONS = new ConcurrentHashMap<>(); +/* @Autowired + private ProjectBigScreenService projectBigScreenService;*/ + // 3. 静态代码块:项目启动时执行(初始化资源、打印启动日志) static { // 此处可添加启动时的初始化逻辑(如加载配置、连接外部资源等) @@ -35,6 +42,35 @@ public class InitOnStartWebSocketServer { ONLINE_SESSIONS.put(session.getId(), session); // RedisUtils.setCacheObject("xmjdap:ws",System.currentTimeMillis() ); log.info("📌 客户端连接成功!会话ID:{},当前在线数:{}", session.getId(), ONLINE_SESSIONS.size()); + // 2. 异步获取并推送初始化数据(避免阻塞连接) + CompletableFuture.runAsync(() -> { + try { + // 2.1 获取数据 + ProjectBigScreenService service = SpringUtils.getBean(ProjectBigScreenService.class); + List ueClientList = service.getUeClientList(); + if (ueClientList == null || ueClientList.isEmpty()) { + session.getBasicRemote().sendText("初始化数据为空"); + log.warn("会话[{}]未获取到初始化数据", session.getId()); + return; + } + for (String ueClient : ueClientList) { + session.getBasicRemote().sendText(ueClient); + log.info("📤 已向会话[{}]推送初始化数据,长度:{}字节", session.getId(), ueClient.length()); + } + // 2.3 推送数据 + + } catch (Exception e) { + log.error("会话[{}]初始化数据处理失败", session.getId(), e); + // 推送错误信息 + try { + if (session.isOpen()) { // 检查会话是否仍打开 + session.getBasicRemote().sendText("初始化失败:" + e.getMessage()); + } + } catch (IOException ex) { + log.error("会话[{}]推送错误信息失败", session.getId(), ex); + } + } + }); } /** diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/drone/DroProjectDroneMapper.xml b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/drone/DroProjectDroneMapper.xml index 7c5a2b6a..26619c8f 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/drone/DroProjectDroneMapper.xml +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/drone/DroProjectDroneMapper.xml @@ -19,4 +19,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" WHERE project_id = #{projectId} GROUP BY drone_sn +