Browse Source

灵嗅-大气巡检业务代码整体提交

dev
时子升 1 month ago
parent
commit
16f2fd8b43
  1. 24
      dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/cloudapi/wayline/GetWaylineListRequest.java
  2. 5
      dk-common/common-redis/src/main/java/org/dromara/common/redis/config/RedisConst.java
  3. 2
      dk-common/common-websocket/src/main/java/org/dromara/common/websocket/dto/BizCodeEnum.java
  4. 140
      dk-modules/sample/src/main/java/org/dromara/sample/configuration/LingxiuMqttMessageProcessorImpl.java
  5. 14
      dk-modules/sample/src/main/java/org/dromara/sample/configuration/LingxiuMqttStartupRunner.java
  6. 5
      dk-modules/sample/src/main/java/org/dromara/sample/configuration/MqttLingxiuSubscriber.java
  7. 63
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceController.java
  8. 13
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceRedisController.java
  9. 7
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/DeviceDTO.java
  10. 12
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/IDeviceRedisService.java
  11. 12
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceProServiceImpl.java
  12. 140
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceRedisServiceImpl.java
  13. 24
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceServiceImpl.java
  14. 42
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/WaylineJobAtmosphereController.java
  15. 37
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/WaylineJobController.java
  16. 18
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/WaylineJobAtmosphere.java
  17. 16
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/bo/WaylineJobAtmosphereBo.java
  18. 14
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/vo/WaylineJobAtmosphereVo.java
  19. 6
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/mapper/WaylineJobAtmosphereMapper.java
  20. 3
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/model/dto/WaylineJobDTO.java
  21. 7
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IWaylineJobAtmosphereService.java
  22. 5
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IWaylineJobService.java
  23. 12
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineFileServiceImpl.java
  24. 29
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineJobAtmosphereServiceImpl.java
  25. 107
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineJobServiceImpl.java
  26. 5
      dk-modules/sample/src/main/java/org/dromara/sample/websocket/config/MyWebSocketFactory.java
  27. 81
      dk-modules/sample/src/main/java/org/dromara/sample/websocket/config/MyWebSocketHandler.java
  28. 2
      dk-modules/sample/src/main/java/org/dromara/sample/websocket/service/IWebSocketMessageService.java
  29. 148
      dk-modules/sample/src/main/java/org/dromara/sample/websocket/service/impl/WebSocketMessageServiceImpl.java
  30. 15
      dk-modules/sample/src/main/resources/mapper/WaylineJobAtmosphereMapper.xml
  31. 15
      pom.xml

24
dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/cloudapi/wayline/GetWaylineListRequest.java

@ -92,6 +92,14 @@ public class GetWaylineListRequest {
@Schema(name = "key", description = "wayline 文件名", example = "waypoint")
private String key;
@JsonProperty("spirit_smell_flag")
@Schema(name = "spirit_smell_flag", description = "灵嗅负载")
private Boolean spiritSmellFlag;
private List<String> spiritSmellWaylineFileIds;
public GetWaylineListRequest() {
}
@ -200,4 +208,20 @@ public class GetWaylineListRequest {
this.deviceSn = deviceSn;
return this;
}
public Boolean getSpiritSmellFlag() {
return spiritSmellFlag;
}
public void setSpiritSmellFlag(Boolean spiritSmellFlag) {
this.spiritSmellFlag = spiritSmellFlag;
}
public List<String> getSpiritSmellWaylineFileIds() {
return spiritSmellWaylineFileIds;
}
public void setSpiritSmellWaylineFileIds(List<String> spiritSmellWaylineFileIds) {
this.spiritSmellWaylineFileIds = spiritSmellWaylineFileIds;
}
}

5
dk-common/common-redis/src/main/java/org/dromara/common/redis/config/RedisConst.java

@ -18,10 +18,15 @@ public final class RedisConst {
public static final Integer DEVICE_ALIVE_SECOND = 60;
public static final Integer WEBSOCKET_ALIVE_SECOND = 60 * 60 * 24;
public static final Integer ONLINE_REDIS_USER_ALIVE_SECOND = 60 * 5;
public static final Integer DEVICE_VIDEO_STATUE_SECOND = 60 * 50;
public static final String DEVICE_ONLINE_PREFIX = "online" + DELIMITER;
public static final String DEVICE_ONLINE_USER_PREFIX = "onlineuser" + DELIMITER;
public static final String DEVICE_SPIRITSMELL_PREFIX = "spiritsmell" + DELIMITER; //灵嗅设备状态
public static final String DEVICE_SPIRITSMELL_LIST_PREFIX = "spiritsmell_list" + DELIMITER; //灵嗅设备状态
public static final String WEBSOCKET_PREFIX = "webSocket" + DELIMITER;

2
dk-common/common-websocket/src/main/java/org/dromara/common/websocket/dto/BizCodeEnum.java

@ -79,6 +79,8 @@ public enum BizCodeEnum {
FLIGHT_AREAS_UPDATE("flight_areas_update"),
SPIRIT_SMELL("spirit_smell"),
;
private String code;

140
dk-modules/sample/src/main/java/org/dromara/sample/configuration/LingxiuMqttMessageProcessorImpl.java

@ -1,26 +1,32 @@
package org.dromara.sample.configuration;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.redis.config.RedisConst;
import org.dromara.common.redis.utils.RedisOpsUtils;
import org.dromara.common.websocket.dto.BizCodeEnum;
import org.dromara.sample.manage.domain.ManageDevicePayloadCustom;
import org.dromara.sample.manage.mapper.IDevicePayloadMapper;
import org.dromara.sample.manage.model.dto.DevicePayloadDTO;
import org.dromara.sample.manage.mapper.ManageDevicePayloadCustomMapper;
import org.dromara.sample.manage.model.dto.TelemetryDTO;
import org.dromara.sample.manage.model.entity.DevicePayloadEntity;
import org.dromara.sample.manage.model.enums.UserTypeEnum;
import org.dromara.sample.manage.service.IDevicePayloadService;
import org.dromara.sample.wayline.domain.WaylineJobAtmosphere;
import org.dromara.sample.manage.service.IDeviceRedisService;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import org.dromara.sample.wayline.model.dto.WaylineJobDTO;
import org.dromara.sample.wayline.model.entity.WaylineJobEntity;
import org.dromara.sample.wayline.service.IWaylineJobAtmosphereService;
import org.dromara.sample.wayline.service.IWaylineJobService;
import org.dromara.sample.websocket.config.WebSocketSubscriptionManager;
import org.dromara.sample.websocket.service.IWebSocketMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Slf4j
@ -31,49 +37,91 @@ public class LingxiuMqttMessageProcessorImpl implements LingxiuMqttMessageProces
private final IWaylineJobService waylineJobService;
private final IDevicePayloadService devicePayloadService;
@Autowired
private IDeviceRedisService deviceRedisService;
@Autowired
private final IDevicePayloadMapper devicePayloadMapper;
@Autowired
private final ManageDevicePayloadCustomMapper manageDevicePayloadCustomMapper;
@Autowired
private IWebSocketMessageService webSocketMessageService;
@Autowired
private WebSocketSubscriptionManager webSocketSubscriptionManager;
@Override
public void process(String topic, String payload) {
log.info("处理灵嗅MQTT数据 topic={}, payload={}", topic, payload);
// {"serial":"37c12953","sequence":12,"sateNum":0,"hdop":0,"utcTime":"2025-06-17-01-37-22","latitude":34.270486,"longitude":117.149302,"altitude":0.03,"temperature":29.61,"humidity":21.37,"pressure":100071.42,"airData":{"SO2(ppm)":0,"NO2(ppm)":0,"Ox(ppm)":0,"PM1.0(ug/m3)":14,"PM2.5(ug/m3)":35,"PM10(ug/m3)":46}}
// if(1==1){ //关闭数据
// return;
// }
// System.out.println("灵嗅"+topic);
log.info("处理灵嗅MQTT数据 topic={}, payload={}", topic, payload);
// {"serial":"37c12953","sequence":12,"sateNum":0,"hdop":0,"utcTime":"2025-06-17-01-37-22","latitude":34.270486,"longitude":117.149302,"altitude":0.03,"temperature":29.61,"humidity":21.37,"pressure":100071.42,"airData":{"SO2(ppm)":0,"NO2(ppm)":0,"Ox(ppm)":0,"PM1.0(ug/m3)":14,"PM2.5(ug/m3)":35,"PM10(ug/m3)":46}}
try {
JSONObject json = JSONUtil.parseObj(payload);
System.out.println("json打印"+json);
JSONObject airData = json.getJSONObject("airData");
JSONObject airData = json.getJSONObject("airData"); //"airData":{"SO2(ppm)":0,"NO2(ppm)":0,"Ox(ppm)":0,"PM1.0(ug/m3)":16,"PM2.5(ug/m3)":39,"PM10(ug/m3)":51}
/** 数据处理-简化对象内容 {"SO2":0,"NO2":0,"Ox":0.082399,"PM1_0":20,"PM2_5":39,"PM10":48} */
JSONObject simplified = new JSONObject();
for (String key : airData.keySet()) {
Object value = airData.get(key);
String newKey = key.replaceAll("\\(.*?\\)", ""); // 去掉括号及其内容:"SO2(ppm)" -> "SO2"
newKey = newKey.replace(".", "_"); // 替换 . 为 _,例如 "PM2.5" -> "PM2_5"
newKey = newKey.replaceAll("(?i)point", "_"); //point去除:PM1point0 or PM2POINT5 → PM1_0, PM2_5
newKey = newKey.trim(); // 去除空格
simplified.put(newKey, value);
}
String utcTimeStr = json.getStr("utcTime");
Boolean missionOpenStatus = json.getBool("missionOpenStatus");
Float relativeAltitude = json.getFloat("relativeAltitude");
Boolean dataRecoveryFlag = json.getBool("dataRecoveryFlag");
//"airData":{"SO2(ppm)":0,"NO2(ppm)":0,"Ox(ppm)":0,"PM1.0(ug/m3)":16,"PM2.5(ug/m3)":39,"PM10(ug/m3)":51}
//找默认数据
WaylineJobAtmosphereBo jobAtmosphere = JSONUtil.toBean(json, WaylineJobAtmosphereBo.class);
jobAtmosphere.setAirData(JSONUtil.toJsonStr(airData));
jobAtmosphere.setAirData(JSONUtil.toJsonStr(simplified));
jobAtmosphere.setUtcTimeStr(utcTimeStr);
jobAtmosphere.setMissionOpenStatus(missionOpenStatus);
jobAtmosphere.setRelativeAltitude(relativeAltitude);
jobAtmosphere.setDataRecoveryFlag(dataRecoveryFlag);
LambdaQueryWrapper<DevicePayloadEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DevicePayloadEntity::getPayloadSn,jobAtmosphere.getSerial());
DevicePayloadEntity devicePayloadEntity = devicePayloadMapper.selectOne(queryWrapper);
String deviceSn = devicePayloadEntity.getDeviceSn();
System.out.println(deviceSn);
// List<DevicePayloadDTO> devicePayloadEntitiesByDeviceSn = devicePayloadService.getDevicePayloadEntitiesByDeviceSn("");
//找第三方挂载
LambdaQueryWrapper<ManageDevicePayloadCustom> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ManageDevicePayloadCustom::getPayloadSn, jobAtmosphere.getSerial());
ManageDevicePayloadCustom paloadCustom = manageDevicePayloadCustomMapper.selectOne(queryWrapper);
if (paloadCustom == null) {
return;
}
String deviceSn = paloadCustom.getDockSn();//8UUXN2B00A00SK
String payloadType = paloadCustom.getPayloadType();
jobAtmosphere.setPayloadType(payloadType);
// List<DevicePayloadDTO> devicePayloadEntitiesByDeviceSn = devicePayloadService.getDevicePayloadEntitiesByDeviceSn("");
// String sn = json.getStr("8UUXN3100A01WS");//8UUXN3100A01WS 8UUXN2T00A01R8
/** 更新设备灵嗅状态上线 **/
// 查找无人机/wayline信息
WaylineJobDTO waylineJobDTO = waylineJobService.getJobByDockSn(deviceSn);
// waylineJobService.getid
// Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobInternalId(sn);
// if (jobOpt.isPresent()) {
// waylineJobAtmosphereService.handleAdd(jobOpt.get(), json);
// } else {
// log.warn("找不到对应无人机任务:sn={}", sn);
WaylineJobDTO waylineJobDTO = waylineJobService.getJobByDockSn(deviceSn); //飞行中+倒序》找第一个
// if(ObjectUtil.isEmpty(waylineJobDTO.getId())){
// //测试-找一个临时备用的
// Optional<WaylineJobDTO> jobByJobId = waylineJobService.getJobByJobId("e3dea0f5-37f2-4d79-ae58-490af3228070","8fc5e69e-2f8d-4a8f-9bdd-0c59eab39d74");
// if (jobByJobId.isPresent()) {
// waylineJobDTO = jobByJobId.get();
// }
// }
// "{\"SO2(ppm)\":0,\"NO2(ppm)\":0,\"Ox(ppm)\":0,\"PM1.0(ug/m3)\":16,\"PM2.5(ug/m3)\":38,\"PM10(ug/m3)\":48}"
if(ObjectUtil.isEmpty(waylineJobDTO.getId())){
return;
}
if(ObjectUtil.isNotEmpty(waylineJobDTO)){
jobAtmosphere.setWaylineJobId((long)waylineJobDTO.getId());
@ -83,8 +131,42 @@ public class LingxiuMqttMessageProcessorImpl implements LingxiuMqttMessageProces
jobAtmosphere.setFileId(waylineJobDTO.getFileId());
jobAtmosphere.setWorkspaceId(waylineJobDTO.getWorkspaceId());
jobAtmosphere.setDockSn(waylineJobDTO.getDockSn());
// BeanUtil.copyProperties();
waylineJobAtmosphereService.insertByBo(jobAtmosphere);
waylineJobAtmosphereService.insertByBo(jobAtmosphere);//存到库里
//拼接数据到redis ,负责push 对象到灵嗅记录列表-正常
// deviceRedisService.pushDeviceSpiritSmellList(deviceSn, jobAtmosphere);
// RedisOpsUtils.setWithExpire(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn, data, 30);
//动态赋值给dockOsd的redis缓存。
jobAtmosphere.setAirDataJson(JSONUtil.parseObj(jobAtmosphere.getAirData()));
// 存到缓存-单独缓存
deviceRedisService.setDeviceSpiritSmellOnline(deviceSn,jobAtmosphere);
// 存到缓存-扩展OSD数据
// Object osdRedis = RedisOpsUtils.get(RedisConst.OSD_PREFIX + deviceSn);
// if(Optional.ofNullable(osdRedis).isPresent()){
//// OsdDockDrone
// JSONObject entries = JSONUtil.parseObj(osdRedis);
// entries.set("spiritsmell", jobAtmosphere);
// RedisOpsUtils.setWithExpire(RedisConst.OSD_PREFIX + deviceSn, entries, RedisConst.ONLINE_REDIS_USER_ALIVE_SECOND);
//
// }
// deviceRedisService.getDeviceSpiritSmellOnline(deviceSn);
//socket发送-正常
Optional<WaylineJobAtmosphereBo> deviceOpt = deviceRedisService.getDeviceSpiritSmellOnline(deviceSn);
if (deviceOpt.isEmpty()) {
return;
}
String bizCode = BizCodeEnum.SPIRIT_SMELL.getCode();
String dockSn = deviceSn; // 设备码
String workspaceId = deviceOpt.get().getWorkspaceId();
// Collection<MyConcurrentWebSocketSession> subs = webSocketSubscriptionManager.getSessionsForDock(dockSn);
webSocketMessageService.sendBatch(workspaceId, UserTypeEnum.WEB.getVal(),bizCode, TelemetryDTO.builder().sn(deviceSn).host(jobAtmosphere).build());
// Collection<MyConcurrentWebSocketSession> subs = webSocketSubscriptionManager.getSessionsForDock(dockSn);
// webSocketMessageService.sendBatchBySessions(subs, bizCode, TelemetryDTO.builder().sn(deviceSn).host(jobAtmosphere).build());
}
} catch (Exception e) {
log.error("处理灵嗅MQTT消息异常", e);

14
dk-modules/sample/src/main/java/org/dromara/sample/configuration/LingxiuMqttStartupRunner.java

@ -1,6 +1,8 @@
package org.dromara.sample.configuration;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
@ -11,10 +13,20 @@ import org.springframework.stereotype.Component;
public class LingxiuMqttStartupRunner implements ApplicationRunner {
private final MqttLingxiuSubscriber subscriber;
@Value("${lingxiu.mqtt.enable}")
private String enable;
@Value("${lingxiu.mqtt.topic}")
private String topic;
@Override
public void run(ApplicationArguments args) {
if(!StrUtil.equals(enable, "true")) {
return;
}
try {
subscriber.subscribe();
subscriber.subscribe(topic);
} catch (Exception e) {
System.err.println("启动灵嗅 MQTT 订阅失败"+ e);
}

5
dk-modules/sample/src/main/java/org/dromara/sample/configuration/MqttLingxiuSubscriber.java

@ -17,8 +17,9 @@ public class MqttLingxiuSubscriber {
this.processor = processor;
}
public void subscribe() throws MqttException {
String[] topics = new String[]{"/topic/#"};
public void subscribe(String topicParam) throws MqttException {
// String[] topics = new String[]{"/topic/#"};
String[] topics = new String[]{topicParam};
client.setCallback(new MqttCallback() {
@Override

63
dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceController.java

@ -1,24 +1,15 @@
package org.dromara.sample.manage.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.dromara.common.core.domain.R;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.config.RedisConst;
import org.dromara.common.redis.utils.RedisOpsUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sdk.cloudapi.device.OsdDock;
import org.dromara.common.web.core.BaseController;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
import org.dromara.sample.manage.model.entity.DeviceEntity;
import org.dromara.sample.manage.service.IDeviceProService;
import org.dromara.sample.manage.model.param.DeviceSubscribeParam;
import org.dromara.sample.manage.service.IDeviceRedisService;
import org.dromara.sample.manage.service.IDeviceService;
import com.fasterxml.jackson.databind.JsonNode;
@ -27,12 +18,10 @@ import org.dromara.common.sdk.common.HttpResultResponse;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.common.sdk.exception.CloudSDKErrorEnum;
import org.dromara.common.sdk.mqtt.property.PropertySetReplyResultEnum;
import org.dromara.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author sean.zhou
@ -65,6 +54,52 @@ public class DeviceController {
return HttpResultResponse.success(devicesList);
}
@GetMapping("/{workspace_id}/devices/osd")
@Operation(summary = "获取一个工作区中所有在线设备+OSD的列表", description = "获取一个工作区中所有在线设备+OSD的列表")
public HttpResultResponse<List<DeviceDTO>> getDevicesOsd(@PathVariable("workspace_id") String workspaceId,
@RequestParam(name = "nickname",required = false) String nickname,@RequestParam(name = "proIds",required = false) List<Integer> proIds) {
List<DeviceDTO> devicesList = deviceService.getDevicesTopoForWeb(workspaceId,nickname,proIds);
//拼接osd信息
JSONObject devicesOsdJson = deviceRedisService.getDevicesOsdList("");
for (DeviceDTO deviceDTO : devicesList) {
if(devicesOsdJson.containsKey(deviceDTO.getDeviceSn())){
deviceDTO.setOsdData(devicesOsdJson.getJSONObject(deviceDTO.getDeviceSn()));
}
}
return HttpResultResponse.success(devicesList);
}
//设备订阅-关闭保存到redis
@PostMapping("/{workspace_id}/subscribe")
@Operation(summary = "设备消息订阅", description = "设备消息订阅")
public HttpResultResponse subscribe(@PathVariable("workspace_id") String workspaceId,
@RequestBody DeviceSubscribeParam deviceSubscribeParam
// @RequestParam("action") String action,
// @RequestParam("token") String token,
// @RequestParam("dockSn") String dockSn
) {
String action = deviceSubscribeParam.getAction();
String token = deviceSubscribeParam.getToken();
String dockSn = deviceSubscribeParam.getDockSn();
if(action.equals("subscribe")) {
System.out.println("sub"+token);
String key = RedisConst.DEVICE_ONLINE_USER_PREFIX + token;
//基于机场sn找无人机sn
String value = "";
Optional<DeviceDTO> deviceBySn = deviceService.getDeviceBySn(dockSn);
if(deviceBySn.isPresent()) {
value = dockSn + "," + deviceBySn.get().getChildDeviceSn();
}
RedisOpsUtils.setWithExpire(key, value ,RedisConst.ONLINE_REDIS_USER_ALIVE_SECOND);
}else if(action.equals("unsubscribe")) {
RedisOpsUtils.del(RedisConst.DEVICE_ONLINE_USER_PREFIX+token);
}
return HttpResultResponse.success();
}
@GetMapping("/{workspace_id}/devices/redis")
@Operation(summary = "获取一个工作区中所有在线设备的列表。", description = "获取一个工作区中所有在线设备的列表")
public HttpResultResponse<JSONObject> getDeviceRedis(@PathVariable("workspace_id") String workspaceId,

13
dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceRedisController.java

@ -1,6 +1,7 @@
package org.dromara.sample.manage.controller;
import cn.hutool.json.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@ -9,10 +10,7 @@ import org.dromara.common.sdk.common.HttpResultResponse;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.service.IDeviceRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@ -54,4 +52,11 @@ public class DeviceRedisController {
return HttpResultResponse.success(deviceRedisService.getDevicesOsdInfo(snList));
}
@GetMapping("/device/osd/list")
@Operation(summary = "获取设备osd实时列表", description = "获取设备osd实时列表")
public HttpResultResponse<JSONObject> getDevicesOsdList(@RequestParam(value = "type", required = false) String type) {
return HttpResultResponse.success(deviceRedisService.getDevicesOsdList(type));
}
}

7
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/DeviceDTO.java

@ -1,11 +1,13 @@
package org.dromara.sample.manage.model.dto;
import cn.hutool.json.JSONObject;
import io.swagger.v3.oas.annotations.media.Schema;
import org.dromara.common.sdk.cloudapi.device.ControlSourceEnum;
import org.dromara.common.sdk.cloudapi.device.DeviceDomainEnum;
import org.dromara.common.sdk.cloudapi.device.DeviceSubTypeEnum;
import org.dromara.common.sdk.cloudapi.device.DeviceTypeEnum;
import org.dromara.common.sdk.cloudapi.tsa.DeviceIconUrl;
import org.dromara.sample.manage.domain.ManageDevicePayloadCustom;
import org.dromara.sample.manage.model.entity.DeviceProEntity;
import org.dromara.sample.manage.model.enums.DeviceFirmwareStatusEnum;
@ -117,4 +119,9 @@ public class DeviceDTO {
private Long pushDeptId;
private boolean spiritsmellFlag;//灵嗅设备状态:
private ManageDevicePayloadCustom payloadSpiritsmell;//灵嗅设备挂载:
private JSONObject osdData;//redis中的osd数据
}

12
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/IDeviceRedisService.java

@ -1,10 +1,12 @@
package org.dromara.sample.manage.service;
import cn.hutool.json.JSONObject;
import org.dromara.common.sdk.cloudapi.device.OsdDockDrone;
import org.dromara.common.sdk.cloudapi.device.VideoId;
import org.dromara.common.sdk.cloudapi.firmware.OtaProgress;
import org.dromara.sample.component.mqtt.model.EventsReceiver;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import java.util.List;
import java.util.Optional;
@ -135,4 +137,14 @@ public interface IDeviceRedisService {
Optional<VideoId> getDeviceVideo(String sn, Object data);
Boolean delDeviceVideo(String sn);
void setDeviceSpiritSmellOnline(String deviceSn, WaylineJobAtmosphereBo data);
boolean checkDeviceSpiritSmell(String deviceSn);
Optional<WaylineJobAtmosphereBo> getDeviceSpiritSmellOnline(String deviceSn);
void pushDeviceSpiritSmellList(String deviceSn, WaylineJobAtmosphereBo jobAtmosphere);
JSONObject getDevicesOsdList(String type);
}

12
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceProServiceImpl.java

@ -1,6 +1,8 @@
package org.dromara.sample.manage.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.impl.BeanConverter;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -9,6 +11,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.redis.utils.RedisOpsUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sdk.common.*;
@ -251,6 +254,15 @@ public class DeviceProServiceImpl implements IDeviceProService {
*/
@Override
public List<Integer> listDeviceGroup(Long userId) {
try {
List<Integer> userIds = RedisUtils.getCacheList("device_pro:" + userId);
// List<Object> collect = RedisOpsUtils.listGetAll("device_pro:" + userId);
// List<Integer> userIds = collect.stream().map(item -> (Integer) item).collect(Collectors.toList());
if (CollUtil.isNotEmpty(userIds)) {return userIds;}//从缓存直接读取
} catch (Exception e) {
System.out.println("找不到pro用户" + "device_pro" + userId);
}
LambdaQueryWrapper<DeviceProUserEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DeviceProUserEntity::getUserId,userId);
List<DeviceProUserEntity> proUserEntityList = deviceProUserMapper.selectList(wrapper);

140
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceRedisServiceImpl.java

@ -1,8 +1,14 @@
package org.dromara.sample.manage.service.impl;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import org.dromara.common.redis.config.RedisConst;
import org.dromara.common.redis.utils.RedisOpsUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.sdk.cloudapi.device.DockDronePayload;
import org.dromara.common.sdk.cloudapi.device.OsdDock;
import org.dromara.common.sdk.cloudapi.device.OsdDockDrone;
import org.dromara.common.sdk.cloudapi.device.VideoId;
import org.dromara.common.sdk.cloudapi.firmware.OtaProgress;
@ -10,6 +16,7 @@ import org.dromara.sample.component.mqtt.model.EventsReceiver;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.service.ICapacityCameraService;
import org.dromara.sample.manage.service.IDeviceRedisService;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -41,6 +48,125 @@ public class DeviceRedisServiceImpl implements IDeviceRedisService {
return Optional.ofNullable((DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn));
}
@Override
public JSONObject getDevicesOsdList(String type) {
JSONObject resJsonObject = new JSONObject(); //机场 droneInDock
Set<String> osdKeys = RedisOpsUtils.getAllKeys(RedisConst.OSD_PREFIX+"*");
for (String osdKey : osdKeys) {
Object osdObj = RedisOpsUtils.get(osdKey);
if(StrUtil.equals(type,"dock") && osdObj instanceof OsdDockDrone ){continue;}
if(StrUtil.equals(type,"drone") && osdObj instanceof OsdDock){continue;}
//处理机场数据
/**
* {
* "droneChargeState": {
* "state": false,
* "capacityPercent": 90
* },
* "latitude": 34.64393,
* "longitude": 116.82873,
* "height": 37.8021,
* "modeCode": 0
* }
*/
if(osdObj instanceof OsdDock ){ //org.plough.sdk.cloudapi.device.OsdDock
OsdDock dock = (OsdDock) osdObj;
// OsdDockVo osdDockVo = new OsdDockVo();
// BeanUtil.copyProperties(dock, osdDockVo);
JSONObject item = new JSONObject();
item.put("droneChargeState", dock.getDroneChargeState());
item.put("latitude", dock.getLatitude());
item.put("longitude", dock.getLongitude());
item.put("height", dock.getHeight());
item.put("modeCode", dock.getModeCode());
resJsonObject.put(osdKey.replaceFirst(RedisConst.OSD_PREFIX,""), item);
//处理无人机数据
/**
* {
* "attitudeHead": -177,
* "attitudePitch": 0.9,
* "attitudeRoll": -0.8,
* "battery": {
* "capacityPercent": 89,
* "landingPower": 8,
* "remainFlightTime": 0,
* "returnHomePower": 25
* },
* "height": 63.853302,
* "latitude": 34.19659,
* "longitude": 117.0838,
* "modeCode": 5,
* "trackId": "48f03cfa-4c61-4cff-8388-faac69605426",
* "payload": [
* {
* "payloadIndex": "81-0-0",
* "gimbalPitch": 0,
* "gimbalRoll": 0,
* "gimbalYaw": -177.15698
* }
* ]
* }
*/
// }else if(osdObj.getClass().getName().equals("org.plough.sdk.cloudapi.device.OsdDockDrone") ){ //org.plough.sdk.cloudapi.device.OsdDockDrone
}else if(osdObj instanceof OsdDockDrone){ //org.plough.sdk.cloudapi.device.OsdDockDrone
OsdDockDrone dockDrone = (OsdDockDrone) osdObj;
// OsdDockDroneVo dockDroneVo = new OsdDockDroneVo();
// BeanUtil.copyProperties(dockDrone, dockDroneVo);
JSONObject item = new JSONObject();
item.put("attitudeHead", dockDrone.getAttitudeHead());
item.put("attitudePitch", dockDrone.getAttitudePitch());
item.put("attitudeRoll", dockDrone.getAttitudeRoll());
if (dockDrone.getBattery() != null) {
JSONObject battery = new JSONObject();
battery.put("capacityPercent", dockDrone.getBattery().getCapacityPercent());
battery.put("landingPower", dockDrone.getBattery().getLandingPower());
battery.put("remainFlightTime", dockDrone.getBattery().getRemainFlightTime());
battery.put("returnHomePower", dockDrone.getBattery().getReturnHomePower());
item.put("battery", battery);
}
item.put("height", dockDrone.getHeight());
item.put("latitude", dockDrone.getLatitude());
item.put("longitude", dockDrone.getLongitude());
item.put("modeCode", dockDrone.getModeCode());
item.put("trackId", dockDrone.getTrackId());
// payload(只保留4个字段)
if (dockDrone.getPayloads() != null && !dockDrone.getPayloads().isEmpty()) {
JSONArray payloadArray = new JSONArray();
for (DockDronePayload payload : dockDrone.getPayloads()) {
JSONObject payloadObj = new JSONObject();
payloadObj.put("payloadIndex", payload.getPayloadIndex());
payloadObj.put("gimbalPitch", payload.getGimbalPitch());
payloadObj.put("gimbalRoll", payload.getGimbalRoll());
payloadObj.put("gimbalYaw", payload.getGimbalYaw());
payloadArray.add(payloadObj);
}
item.put("payload", payloadArray);
}
resJsonObject.put(osdKey.replaceFirst(RedisConst.OSD_PREFIX, ""), item);
}
}
return resJsonObject;
}
@Override
public Optional<WaylineJobAtmosphereBo> getDeviceSpiritSmellOnline(String deviceSn) {
return Optional.ofNullable((WaylineJobAtmosphereBo) RedisOpsUtils.get(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn));
}
@Override
public void pushDeviceSpiritSmellList(String deviceSn, WaylineJobAtmosphereBo jobAtmosphere) {
RedisOpsUtils.listRPush(RedisConst.DEVICE_SPIRITSMELL_LIST_PREFIX + deviceSn,jobAtmosphere);
// RedisOpsUtils.setWithExpire(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn, data, 30);
}
@Override
public void setDeviceOnline(DeviceDTO device) {
RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), device, RedisConst.DEVICE_ALIVE_SECOND);
@ -51,6 +177,20 @@ public class DeviceRedisServiceImpl implements IDeviceRedisService {
return RedisOpsUtils.del(RedisConst.DEVICE_ONLINE_PREFIX + sn);
}
@Override
public void setDeviceSpiritSmellOnline(String deviceSn, WaylineJobAtmosphereBo data) {
RedisOpsUtils.setWithExpire(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn, data, 30);
}
@Override
public boolean checkDeviceSpiritSmell(String deviceSn) {
return RedisOpsUtils.checkExist(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn);
// &&
// RedisOpsUtils.getExpire(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn) > 0 ;
// BooleanUtil.toBoolean(RedisOpsUtils.get(RedisConst.DEVICE_SPIRITSMELL_PREFIX + deviceSn).toString());
}
@Override
public void setDeviceOsd(String sn, Object data) {
RedisOpsUtils.setWithExpire(RedisConst.OSD_PREFIX + sn, data, RedisConst.DEVICE_ALIVE_SECOND);

24
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceServiceImpl.java

@ -1,5 +1,7 @@
package org.dromara.sample.manage.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -32,8 +34,10 @@ import org.dromara.common.websocket.dto.BizCodeEnum;
import org.dromara.sample.common.error.CommonErrorEnum;
import org.dromara.sample.component.mqtt.model.EventsReceiver;
import org.dromara.sample.control.model.enums.DroneAuthorityEnum;
import org.dromara.sample.manage.domain.ManageDevicePayloadCustom;
import org.dromara.sample.manage.mapper.IDeviceMapper;
import org.dromara.sample.manage.mapper.IDeviceProMapper;
import org.dromara.sample.manage.mapper.ManageDevicePayloadCustomMapper;
import org.dromara.sample.manage.model.dto.*;
import org.dromara.sample.manage.model.entity.DeviceEntity;
import org.dromara.sample.manage.model.entity.DeviceProEntity;
@ -61,6 +65,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.dromara.common.core.utils.StringUtils.DASH;
import static org.dromara.common.core.utils.StringUtils.SLASH;
@ -139,6 +144,9 @@ public class DeviceServiceImpl implements IDeviceService {
@Autowired
private IDeviceProMapper deviceProMapper;
@Autowired
private ManageDevicePayloadCustomMapper manageDevicePayloadCustomMapper;
@Autowired
private IDeviceProService deviceProService;
@ -264,6 +272,21 @@ public class DeviceServiceImpl implements IDeviceService {
deviceRedisService.checkDeviceOnline(gateway.getDeviceSn()))
.forEach(this::spliceDeviceTopo);
//验证第三方挂载状态(灵嗅)
List<String> deviceSnList = devicesList.stream().map(DeviceDTO::getDeviceSn).collect(Collectors.toList());
LambdaQueryWrapper<ManageDevicePayloadCustom> payloadCustomMapper = new LambdaQueryWrapper<>();
payloadCustomMapper.in(CollUtil.isNotEmpty(deviceSnList),ManageDevicePayloadCustom::getDockSn, deviceSnList);
List<ManageDevicePayloadCustom> payloadCustomList = manageDevicePayloadCustomMapper.selectList(payloadCustomMapper);
for (DeviceDTO deviceDTO : devicesList) {
ManageDevicePayloadCustom spiritsmellV2 = payloadCustomList.stream().filter(item ->
ObjectUtil.equals(item.getDockSn(), deviceDTO.getDeviceSn()) &&
ObjectUtil.equals(item.getPayloadName(), "spiritsmellV2")).findFirst().orElse(null);
// long spiritsmellV2Count = payloadCustomList.stream().filter(item ->
// ObjectUtil.equals(item.getDockSn(), deviceDTO.getDeviceSn()) &&
// ObjectUtil.equals(item.getPayloadName(), "spiritsmellV2")).count();
deviceDTO.setPayloadSpiritsmell(spiritsmellV2);
}
return devicesList;
}
@ -271,6 +294,7 @@ public class DeviceServiceImpl implements IDeviceService {
public void spliceDeviceTopo(DeviceDTO gateway) {
gateway.setStatus(deviceRedisService.checkDeviceOnline(gateway.getDeviceSn()));
gateway.setSpiritsmellFlag(deviceRedisService.checkDeviceSpiritSmell(gateway.getDeviceSn()));//验证是否有灵嗅在线
// sub device
if (!StringUtils.hasText(gateway.getChildDeviceSn())) {

42
dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/WaylineJobAtmosphereController.java

@ -1,8 +1,13 @@
package org.dromara.sample.wayline.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
@ -51,6 +56,43 @@ public class WaylineJobAtmosphereController extends BaseController {
return waylineJobAtmosphereService.queryPageList(bo, pageQuery);
}
/**
* 查询灵嗅记录 waylineJobId / dockSn
* @param bo
* @return
*/
@SaCheckPermission("sample:WaylineJobAtmosphere:list")
@GetMapping("/listQuery")
public List<WaylineJobAtmosphereVo> listQuery(WaylineJobAtmosphereBo bo) {
return waylineJobAtmosphereService.queryList(bo);
}
/**
* 根据时间查询记录 day / dayTime
* @param bo
* @return
*/
@SaCheckPermission("sample:WaylineJobAtmosphere:list")
@GetMapping("/listTime")
public List<WaylineJobAtmosphereVo> listTime(WaylineJobAtmosphereBo bo) {
List<WaylineJobAtmosphereVo> dayList = new ArrayList<>();
//没传开始-结束时间 默认查今天数据
if (ObjectUtil.isEmpty(bo.getDayStart()) || ObjectUtil.isEmpty(bo.getDayEnd())) {
Date currentDate = new Date();
DateTime dayStart = DateUtil.beginOfDay(currentDate);
DateTime dayEnd = DateUtil.endOfDay(currentDate);
dayList = waylineJobAtmosphereService.selectByDateRange(bo.getDockSn(), dayStart, dayEnd);
} else if (ObjectUtil.isNotEmpty(bo.getDayStart()) || ObjectUtil.isNotEmpty(bo.getDayEnd())) {
DateTime dayStart = DateUtil.date(bo.getDayStart());
DateTime dayEnd = DateUtil.date(bo.getDayEnd());
dayList = waylineJobAtmosphereService.selectByDateRange(bo.getDockSn(), dayStart, dayEnd);
}
return dayList;
}
/** 演示数据返回前台 **/
@GetMapping("/list/demo")
public List<WaylineJobAtmosphereVo> demoList(WaylineJobAtmosphereBo bo, PageQuery pageQuery) {
List<WaylineJobAtmosphereVo> demoList= waylineJobAtmosphereService.demoList(bo);

37
dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/WaylineJobController.java

@ -19,10 +19,7 @@ import org.dromara.sample.wayline.model.enums.WaylineTaskStatusEnum;
import org.dromara.sample.wayline.model.entity.WaylineFileEntity;
import org.dromara.sample.wayline.model.param.CreateJobParam;
import org.dromara.sample.wayline.model.param.UpdateJobParam;
import org.dromara.sample.wayline.service.IFlightTaskService;
import org.dromara.sample.wayline.service.IWaylineFileService;
import org.dromara.sample.wayline.service.IWaylineJobService;
import org.dromara.sample.wayline.service.IWaylineRedisService;
import org.dromara.sample.wayline.service.*;
import org.dromara.sample.wayline.service.IWaylineRedisService;
import org.dromara.sample.wayline.service.impl.WaylineFileServiceImpl;
import org.dromara.system.api.model.LoginUser;
@ -95,6 +92,38 @@ public class WaylineJobController {
return HttpResultResponse.success(data);
}
/** 灵嗅航线-分页查询 **/
@GetMapping("/{workspace_id}/jobsSpiritSmellPage")
@Operation(summary = "查询工作区中的所有作业。。", description = "查询工作区中的所有作业。")
public HttpResultResponse<PaginationData<WaylineJobDTO>> getJobsSpiritSmellPage(
@RequestParam(name = "pageNum", defaultValue = "1") Long page,
@RequestParam(name = "pageSize", defaultValue = "10") Long pageSize,
@PathVariable(name = "workspace_id") String workspaceId,
@RequestParam(name = "proIds" , required = false) List<Integer> proIds,
@RequestParam(name = "fileId" , required = false) String fileId,
@RequestParam(name = "name" , required = false) String name,
@RequestParam(name = "dockSn" , required = false) String dockSn
) {
PaginationData<WaylineJobDTO> data = waylineJobService.getJobsSpiritSmellPageByWorkspaceId(workspaceId,page, pageSize,fileId,proIds,name,dockSn);
return HttpResultResponse.success(data);
}
/** 灵嗅航线-全量查询-单个设备下查询 **/
@GetMapping("/{workspace_id}/jobsSpiritSmell")
@Operation(summary = "查询工作区中的所有作业。。", description = "查询工作区中的所有作业。")
public HttpResultResponse<List<WaylineJobDTO>> getJobsSpiritSmell(
@PathVariable(name = "workspace_id") String workspaceId,
@RequestParam(name = "proIds" , required = false) List<Integer> proIds,
@RequestParam(name = "fileId" , required = false) String fileId,
@RequestParam(name = "name" , required = false) String name,
@RequestParam(name = "dockSn" , required = false) String dockSn
) {
List<WaylineJobDTO> data = waylineJobService.getJobsSpiritSmellByWorkspaceId(workspaceId,fileId,proIds,name,dockSn);
return HttpResultResponse.success(data);
}
/**
* Send the command to cancel the jobs.
* @param jobIds

18
dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/WaylineJobAtmosphere.java

@ -122,6 +122,24 @@ public class WaylineJobAtmosphere implements Serializable {
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
//灵嗅扩展数据------
@TableField("mission_open_status")
private Boolean missionOpenStatus;//任务打开状态
@TableField("timestamp")
private Long timestamp;//时间戳
@TableField("relative_altitude")
private Float relativeAltitude;//相对高度
@TableField("data_recovery_flag")
private Boolean dataRecoveryFlag;//数据恢复标识
}

16
dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/bo/WaylineJobAtmosphereBo.java

@ -1,6 +1,7 @@
package org.dromara.sample.wayline.domain.bo;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.sample.wayline.domain.WaylineJobAtmosphere;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
@ -10,6 +11,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
/**
* 无人机任务-空气质量业务对象 wayline_job_atmosphere
@ -32,6 +34,7 @@ public class WaylineJobAtmosphereBo extends BaseEntity {
*/
// @NotNull(message = "无人机任务ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long waylineJobId;
private List<Integer> waylineJobIds;
/**
* 任务UUID
@ -93,6 +96,7 @@ public class WaylineJobAtmosphereBo extends BaseEntity {
*/
// @NotBlank(message = "负载设备序列号不能为空", groups = { AddGroup.class, EditGroup.class })
private String serial;
private String payloadType;//负载类型 spiritsmellV2 / spiritsmellV2_1
/**
* 数据序列号
@ -148,6 +152,7 @@ public class WaylineJobAtmosphereBo extends BaseEntity {
* 空气质量数据
*/
private String airData;
private JSONObject airDataJson;
/**
* 错误码
@ -174,5 +179,16 @@ public class WaylineJobAtmosphereBo extends BaseEntity {
*/
private Long proId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date dayStart;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date dayEnd;
//灵嗅扩展数据------
private Boolean missionOpenStatus;
private Long timestamp;
private Float relativeAltitude;
private Boolean dataRecoveryFlag;
}

14
dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/vo/WaylineJobAtmosphereVo.java

@ -206,5 +206,19 @@ public class WaylineJobAtmosphereVo implements Serializable {
@ExcelProperty(value = "proID")
private Long proId;
//灵嗅扩展数据------
@ExcelProperty(value = "任务打开状态")
private Boolean missionOpenStatus;
@ExcelProperty(value = "时间戳")
private Long timestamp;
@ExcelProperty(value = "相对高度")
private Float relativeAltitude;
@ExcelProperty(value = "数据恢复标识")
private Boolean dataRecoveryFlag;
}

6
dk-modules/sample/src/main/java/org/dromara/sample/wayline/mapper/WaylineJobAtmosphereMapper.java

@ -1,9 +1,12 @@
package org.dromara.sample.wayline.mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.sample.wayline.domain.WaylineJobAtmosphere;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
/**
* 无人机任务-空气质量Mapper接口
*
@ -12,4 +15,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
*/
public interface WaylineJobAtmosphereMapper extends BaseMapperPlus<WaylineJobAtmosphere, WaylineJobAtmosphereVo> {
List<Long> selectSpiritSmellWayLineJobIds(@Param("dockSn") String dockSn);
List<String> selectDistinctWaylineFileId();
}

3
dk-modules/sample/src/main/java/org/dromara/sample/wayline/model/dto/WaylineJobDTO.java

@ -8,6 +8,7 @@ import org.dromara.common.sdk.cloudapi.wayline.OutOfControlActionEnum;
import org.dromara.common.sdk.cloudapi.wayline.ProgressExtBreakPoint;
import org.dromara.common.sdk.cloudapi.wayline.TaskTypeEnum;
import org.dromara.common.sdk.cloudapi.wayline.WaylineTypeEnum;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import java.time.LocalDateTime;
import java.util.List;
@ -79,4 +80,6 @@ public class WaylineJobDTO {
private ProgressExtBreakPoint breakPoint;
private List<Integer> proIds;
private List<WaylineJobAtmosphereVo> waylineJobAtmosphereVoList;
}

7
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IWaylineJobAtmosphereService.java

@ -1,5 +1,6 @@
package org.dromara.sample.wayline.service;
import cn.hutool.core.date.DateTime;
import cn.hutool.json.JSONObject;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
@ -71,4 +72,10 @@ public interface IWaylineJobAtmosphereService {
void handleAdd(WaylineJobDTO waylineJobDTO, JSONObject json);
List<WaylineJobAtmosphereVo> demoList(WaylineJobAtmosphereBo bo);
List<Long> selectSpiritSmellWayLineJobIdsByDockSn(String dockSn);
List<WaylineJobAtmosphereVo> selectByDateRange(String dockSn, DateTime dayStart, DateTime dayEnd);
List<Long> selectSpiritSmellWayLineJobIds(String dockSn);
}

5
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IWaylineJobService.java

@ -91,4 +91,9 @@ public interface IWaylineJobService {
List<MediaFileDTO> getMediaFileDTO(String jobId, String fileType);
WaylineJobDTO getJobByDockSn(String dockSn);
/**获取灵嗅数据**/
List<WaylineJobDTO> getJobsSpiritSmellByWorkspaceId(String workspaceId, String fileId, List<Integer> proIds, String name, String dockSn);
/**获取灵嗅数据-分页**/
PaginationData<WaylineJobDTO> getJobsSpiritSmellPageByWorkspaceId(String workspaceId, Long page, Long pageSize, String fileId, List<Integer> proIds, String name, String dockSn);
}

12
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineFileServiceImpl.java

@ -1,5 +1,6 @@
package org.dromara.sample.wayline.service.impl;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -33,6 +34,7 @@ import org.dromara.sample.manage.service.IDeviceProService;
import org.dromara.sample.manage.service.IDeviceRedisService;
import org.dromara.sample.wayline.mapper.IAudioFileMapper;
import org.dromara.sample.wayline.mapper.IWaylineFileMapper;
import org.dromara.sample.wayline.mapper.WaylineJobAtmosphereMapper;
import org.dromara.sample.wayline.model.dto.KmzFileProperties;
import org.dromara.sample.wayline.model.dto.WaylineFileDTO;
import org.dromara.sample.wayline.model.entity.AudioFileEntity;
@ -87,6 +89,9 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
@Autowired
private IDeviceRedisService deviceRedisService;
@Autowired
private WaylineJobAtmosphereMapper waylineJobAtmosphereMapper;
@Override
public PaginationData<GetWaylineListResponse> getWaylinesByParam(String workspaceId, GetWaylineListRequest param,List<Integer> proIds) {
@ -94,6 +99,10 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
if(proIds == null){
proIds = deviceProService.listDeviceGroup(loginUser.getUserId());
}
if(BooleanUtil.isTrue(param.getSpiritSmellFlag())){ //灵嗅航线过滤wayLineFileIds
param.setSpiritSmellWaylineFileIds(waylineJobAtmosphereMapper.selectDistinctWaylineFileId());
}
// Paging Query
List<Integer> finalProIds = proIds;
Page<WaylineFileEntity> page = mapper.selectPage(
@ -122,6 +131,9 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
.and(ObjectUtil.isAllNotEmpty(proIds), wrapper -> {
wrapper.in(WaylineFileEntity::getProId, finalProIds);
})
.and(BooleanUtil.isTrue(param.getSpiritSmellFlag()), wrapper -> {
wrapper.in(WaylineFileEntity::getWaylineId,param.getSpiritSmellWaylineFileIds());
})
.like(Objects.nonNull(param.getKey()), WaylineFileEntity::getName, param.getKey())
// There is a risk of SQL injection
.last(Objects.nonNull(param.getOrderBy()), " order by " + param.getOrderBy().toString()));

29
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineJobAtmosphereServiceImpl.java

@ -1,9 +1,12 @@
package org.dromara.sample.wayline.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@ -22,6 +25,7 @@ import org.dromara.sample.wayline.service.IWaylineJobAtmosphereService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* 无人机任务-空气质量Service业务层处理
@ -83,6 +87,7 @@ public class WaylineJobAtmosphereServiceImpl implements IWaylineJobAtmosphereSer
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<WaylineJobAtmosphere> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getWaylineJobId() != null, WaylineJobAtmosphere::getWaylineJobId, bo.getWaylineJobId());
lqw.in(CollUtil.isNotEmpty(bo.getWaylineJobIds()), WaylineJobAtmosphere::getWaylineJobId, bo.getWaylineJobIds());
lqw.eq(StringUtils.isNotBlank(bo.getJobId()), WaylineJobAtmosphere::getJobId, bo.getJobId());
lqw.like(StringUtils.isNotBlank(bo.getName()), WaylineJobAtmosphere::getName, bo.getName());
lqw.eq(StringUtils.isNotBlank(bo.getFileId()), WaylineJobAtmosphere::getFileId, bo.getFileId());
@ -185,4 +190,28 @@ public class WaylineJobAtmosphereServiceImpl implements IWaylineJobAtmosphereSer
}
return waylineJobAtmosphereVos;
}
@Override
public List<Long> selectSpiritSmellWayLineJobIdsByDockSn(String dockSn) {
LambdaQueryWrapper<WaylineJobAtmosphere> wrapper = new LambdaQueryWrapper<>();
wrapper.select(WaylineJobAtmosphere::getWaylineJobId);
wrapper.eq(WaylineJobAtmosphere::getDockSn,dockSn);
List<WaylineJobAtmosphereVo> waylineJobAtmosphereVoList = baseMapper.selectVoList(wrapper);
List<Long> waylineJobIds = waylineJobAtmosphereVoList.stream().map(WaylineJobAtmosphereVo::getWaylineJobId).distinct().collect(Collectors.toList());
return waylineJobIds;
}
@Override
public List<WaylineJobAtmosphereVo> selectByDateRange(String dockSn, DateTime dayStart, DateTime dayEnd) {
LambdaQueryWrapper<WaylineJobAtmosphere> wrapper = new LambdaQueryWrapper<>();
wrapper.between(WaylineJobAtmosphere::getCreateTime, dayStart, dayEnd);
wrapper.eq(ObjectUtil.isNotEmpty(dockSn),WaylineJobAtmosphere::getDockSn,dockSn); //传值了再查询
List<WaylineJobAtmosphereVo> waylineJobAtmosphereVoList = baseMapper.selectVoList(wrapper);
return waylineJobAtmosphereVoList;
}
@Override
public List<Long> selectSpiritSmellWayLineJobIds(String dockSn) {
return baseMapper.selectSpiritSmellWayLineJobIds(dockSn);
}
}

107
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineJobServiceImpl.java

@ -1,6 +1,10 @@
package org.dromara.sample.wayline.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -25,12 +29,15 @@ import org.dromara.sample.manage.service.IDeviceService;
import org.dromara.sample.media.model.MediaFileCountDTO;
import org.dromara.sample.media.model.MediaFileDTO;
import org.dromara.sample.media.service.IFileService;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import org.dromara.sample.wayline.mapper.IWaylineJobMapper;
import org.dromara.sample.wayline.model.dto.WaylineJobDTO;
import org.dromara.sample.wayline.model.entity.WaylineJobEntity;
import org.dromara.sample.wayline.model.enums.WaylineJobStatusEnum;
import org.dromara.sample.wayline.model.param.CreateJobParam;
import org.dromara.sample.wayline.service.IWaylineFileService;
import org.dromara.sample.wayline.service.IWaylineJobAtmosphereService;
import org.dromara.sample.wayline.service.IWaylineJobService;
import org.dromara.sample.wayline.service.IWaylineRedisService;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -83,6 +90,9 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
@Autowired
private IDeviceProService deviceProService;
@Autowired
private IWaylineJobAtmosphereService waylineJobAtmosphereService;
private Optional<WaylineJobDTO> insertWaylineJob(WaylineJobEntity jobEntity) {
int id = mapper.insert(jobEntity);
if (id <= 0) {
@ -206,6 +216,103 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
return new PaginationData<WaylineJobDTO>(records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
}
@Override
public List<WaylineJobDTO> getJobsSpiritSmellByWorkspaceId(String workspaceId, String fileId, List<Integer> proIds, String name, String dockSn) {
LoginUser loginUser = LoginHelper.getLoginUser();
if(ObjectUtil.isAllEmpty(proIds)){
proIds = deviceProService.listDeviceGroup(loginUser.getUserId());
}
LambdaQueryWrapper<WaylineJobEntity> waylineJobEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
waylineJobEntityLambdaQueryWrapper.eq(WaylineJobEntity::getWorkspaceId, workspaceId);
if(ObjectUtil.isNotEmpty(fileId)) {
waylineJobEntityLambdaQueryWrapper.eq(WaylineJobEntity::getFileId, fileId);
}
waylineJobEntityLambdaQueryWrapper.in(ObjectUtil.isAllNotEmpty(proIds),WaylineJobEntity::getProId,proIds);
// if(ObjectUtil.isNotEmpty(name)){
// waylineJobEntityLambdaQueryWrapper.and(wrapper ->{
// wrapper.like(WaylineJobEntity::getName, name).or().like(WaylineJobEntity::getWaylineName, name);
// });
// };
if(ObjectUtil.isNotEmpty(dockSn)){//找指定设备的航线 //灵嗅数据过滤
List<Long> waylineJobIds = waylineJobAtmosphereService.selectSpiritSmellWayLineJobIds(dockSn);
waylineJobEntityLambdaQueryWrapper.in(WaylineJobEntity::getId,waylineJobIds);
}else{//找全部有灵嗅数据的航线
List<Long> waylineJobIds = waylineJobAtmosphereService.selectSpiritSmellWayLineJobIds(null);
waylineJobEntityLambdaQueryWrapper.in(WaylineJobEntity::getId,waylineJobIds);
}
waylineJobEntityLambdaQueryWrapper.orderByDesc(WaylineJobEntity::getId);
List<WaylineJobEntity> waylineJobEntitieList = mapper.selectList(waylineJobEntityLambdaQueryWrapper);
List<WaylineJobDTO> records = waylineJobEntitieList
.stream()
.map(this::entity2Dto)
.collect(Collectors.toList());
//拼接灵嗅数据
/* List<Integer> waylineJobIds = records.stream().map(WaylineJobDTO::getId).collect(Collectors.toList());
if(CollUtil.isNotEmpty(waylineJobIds)){
WaylineJobAtmosphereBo bo = new WaylineJobAtmosphereBo();
bo.setWaylineJobIds(waylineJobIds);
List<WaylineJobAtmosphereVo> waylineJobAtmosphereVoList = waylineJobAtmosphereService.queryList(bo);
for (WaylineJobDTO record : records) {
List<WaylineJobAtmosphereVo> collect = waylineJobAtmosphereVoList.stream().filter(item -> item.getWaylineJobId().equals(record.getId().longValue())).collect(Collectors.toList());
// collect.stream().forEach(item -> {item.setAirDataJson( JSONUtil.parseObj(item.getAirData())); });
record.setWaylineJobAtmosphereVoList(collect);
}
}*/
return records;
}
@Override
public PaginationData<WaylineJobDTO> getJobsSpiritSmellPageByWorkspaceId(String workspaceId, Long page, Long pageSize, String fileId, List<Integer> proIds, String name, String dockSn) {
LoginUser loginUser = LoginHelper.getLoginUser();
if(ObjectUtil.isAllEmpty(proIds)){
proIds = deviceProService.listDeviceGroup(loginUser.getUserId());
}
LambdaQueryWrapper<WaylineJobEntity> waylineJobEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
waylineJobEntityLambdaQueryWrapper.eq(WaylineJobEntity::getWorkspaceId, workspaceId);
if(ObjectUtil.isNotEmpty(fileId)) {
waylineJobEntityLambdaQueryWrapper.eq(WaylineJobEntity::getFileId, fileId);
}
waylineJobEntityLambdaQueryWrapper.in(ObjectUtil.isAllNotEmpty(proIds),WaylineJobEntity::getProId,proIds);
// if(ObjectUtil.isNotEmpty(name)){
// waylineJobEntityLambdaQueryWrapper.and(wrapper ->{
// wrapper.like(WaylineJobEntity::getName, name).or().like(WaylineJobEntity::getWaylineName, name);
// });
// };
if(ObjectUtil.isNotEmpty(dockSn)){//找指定设备的航线 //灵嗅数据过滤
List<Long> waylineJobIds = waylineJobAtmosphereService.selectSpiritSmellWayLineJobIds(dockSn);
waylineJobEntityLambdaQueryWrapper.in(WaylineJobEntity::getId,waylineJobIds);
}else{//找全部有灵嗅数据的航线
List<Long> waylineJobIds = waylineJobAtmosphereService.selectSpiritSmellWayLineJobIds(null);
waylineJobEntityLambdaQueryWrapper.in(WaylineJobEntity::getId,waylineJobIds);
}
waylineJobEntityLambdaQueryWrapper.orderByDesc(WaylineJobEntity::getId);
Page<WaylineJobEntity> pageData = mapper.selectPage(new Page<WaylineJobEntity>(page, pageSize),waylineJobEntityLambdaQueryWrapper);
List<WaylineJobDTO> records = pageData.getRecords()
.stream().map(this::entity2Dto)
.collect(Collectors.toList());
//拼接灵嗅数据
/* List<Integer> waylineJobIds = records.stream().map(WaylineJobDTO::getId).collect(Collectors.toList());
if(CollUtil.isNotEmpty(waylineJobIds)){
WaylineJobAtmosphereBo bo = new WaylineJobAtmosphereBo();
bo.setWaylineJobIds(waylineJobIds);
List<WaylineJobAtmosphereVo> waylineJobAtmosphereVoList = waylineJobAtmosphereService.queryList(bo);
for (WaylineJobDTO record : records) {
List<WaylineJobAtmosphereVo> collect = waylineJobAtmosphereVoList.stream().filter(item -> item.getWaylineJobId().equals(record.getId().longValue())).collect(Collectors.toList());
// collect.stream().forEach(item -> {item.setAirDataJson( JSONUtil.parseObj(item.getAirData())); });
record.setWaylineJobAtmosphereVoList(collect);
}
}*/
return new PaginationData<WaylineJobDTO>(records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
}
private WaylineJobEntity dto2Entity(WaylineJobDTO dto) {
WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder();
if (dto == null) {

5
dk-modules/sample/src/main/java/org/dromara/sample/websocket/config/MyWebSocketFactory.java

@ -20,8 +20,11 @@ public class MyWebSocketFactory extends WebSocketDefaultFactory {
@Autowired
private IWebSocketManageService webSocketManageService;
@Autowired
private WebSocketSubscriptionManager websocketSubscriptionManager;
@Override
public WebSocketHandler decorate(WebSocketHandler handler) {
return new MyWebSocketHandler(handler, webSocketManageService);
return new MyWebSocketHandler(handler, webSocketManageService, websocketSubscriptionManager);
}
}

81
dk-modules/sample/src/main/java/org/dromara/sample/websocket/config/MyWebSocketHandler.java

@ -1,6 +1,8 @@
package org.dromara.sample.websocket.config;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.redis.utils.RedisUtils;
@ -28,16 +30,26 @@ import static org.dromara.common.satoken.utils.LoginHelper.LOGIN_USER_KEY;
@Slf4j
public class MyWebSocketHandler extends WebSocketDefaultHandler {
private IWebSocketManageService webSocketManageService;
private final IWebSocketManageService webSocketManageService;
MyWebSocketHandler(WebSocketHandler delegate, IWebSocketManageService webSocketManageService) {
private final WebSocketSubscriptionManager websocketSubscriptionManager;
public MyWebSocketHandler(WebSocketHandler delegate,
IWebSocketManageService webSocketManageService,
WebSocketSubscriptionManager websocketSubscriptionManager) {
super(delegate);
this.webSocketManageService = webSocketManageService;
this.websocketSubscriptionManager = websocketSubscriptionManager;
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 从 URL Query 中解析 token
String query = session.getUri().getQuery(); // e.g. "Authorization=Bearer abc...&clientid=xxx"
String token = parseTokenFromQuery(query);
session.getAttributes().put("token", token);
Object cacheObject = RedisUtils.getCacheObject("000000:online_tokens:" + session.getUri().toString().substring(session.getUri().toString().indexOf("%20") + 3, session.getUri().toString().indexOf("&")));
SysUserOnline sysUserOnline = BeanUtil.copyProperties(cacheObject, SysUserOnline.class);
if (StringUtils.hasText(sysUserOnline.getUserName())) {
@ -61,9 +73,70 @@ public class MyWebSocketHandler extends WebSocketDefaultHandler {
}
/**
* 前台触发连接
* // 建好 ws 连接之后
* ws.send(JSON.stringify({
* action: "subscribe",
* bizCode: "SPIRIT_SMELL"
* }));
*
* // 页面关闭或切换时
* ws.send(JSON.stringify({
* action: "unsubscribe",
* bizCode: "SPIRIT_SMELL"
* }));
*
* @throws Exception
*/
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
log.debug("received message: {}", message.getPayload());
public void handleMessage(WebSocketSession rawSession, WebSocketMessage<?> wsMsg) throws Exception {
// log.debug("received message: {}", message.getPayload());
String payload = wsMsg.getPayload().toString();
if(!JSONUtil.isTypeJSON(payload)){
System.out.println(payload);
return;
}
System.out.println(payload);
JSONObject json = JSONUtil.parseObj(payload);
String action = json.getStr("action"); // subscribe/unsubscribe
// String bizCode = json.getStr("bizCode");
String dockSn = json.getStr("dockSn");
String token = (String) rawSession.getAttributes().get("token");
// 还可以带 workspaceId/userType 等,按需扩展
/* MyConcurrentWebSocketSession session = new MyConcurrentWebSocketSession(rawSession);
if ("subscribe".equals(action)) {
// websocketSubscriptionManager.subscribe(bizCode, dockSn, token, session);
// websocketSubscriptionManager.subscribe( dockSn, token, session);
websocketSubscriptionManager.subscribe(token,dockSn,session);
}
else if ("unsubscribe".equals(action)) {
// websocketSubscriptionManager.unsubscribe(bizCode, dockSn, token, session.getId());
// websocketSubscriptionManager.unsubscribe( dockSn, token, session.getId());
websocketSubscriptionManager.unsubscribe( token,dockSn,session.getId());
}
else {
log.warn("Unknown WS action: {}", action);
}*/
}
private String parseTokenFromQuery(String query) {
// 简单解析:找到 Authorization=Bearer XXX
for (String part : query.split("&")) {
if (part.startsWith("Authorization=")) {
String v = part.substring("Authorization=".length());
if (v.startsWith("Bearer ")) {
return v.substring("Bearer ".length());
}
return v;
}
}
return null;
}
}

2
dk-modules/sample/src/main/java/org/dromara/sample/websocket/service/IWebSocketMessageService.java

@ -27,6 +27,8 @@ public interface IWebSocketMessageService {
*/
void sendBatch(Collection<MyConcurrentWebSocketSession> sessions, WebSocketMessageResponse message);
void sendBatchBySessions(Collection<MyConcurrentWebSocketSession> sessions, String bizCode, Object data);
void sendBatch(String workspaceId, Integer userType, String bizCode, Object data);
void sendBatch(String workspaceId, String bizCode, Object data);

148
dk-modules/sample/src/main/java/org/dromara/sample/websocket/service/impl/WebSocketMessageServiceImpl.java

@ -1,19 +1,33 @@
package org.dromara.sample.websocket.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.redis.config.RedisConst;
import org.dromara.common.redis.utils.RedisOpsUtils;
import org.dromara.common.websocket.dto.WebSocketMessageResponse;
import org.dromara.sample.websocket.config.MyConcurrentWebSocketSession;
import org.dromara.sample.websocket.config.WebSocketSubscriptionManager;
import org.dromara.sample.websocket.service.IWebSocketManageService;
import org.dromara.sample.websocket.service.IWebSocketMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.handler.WebSocketSessionDecorator;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.hutool.jwt.JWTUtil.parseToken;
/**
* @author sean.zhou
@ -29,12 +43,12 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
@Autowired
private IWebSocketManageService webSocketManageService;
@Autowired
private WebSocketSubscriptionManager webSocketSubscriptionManager;
@Override
public void sendMessage(MyConcurrentWebSocketSession session, WebSocketMessageResponse message) {
if (session == null) {
return;
}
if (session == null) {return;}
try {
if (!session.isOpen()) {
@ -43,7 +57,6 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
return;
}
session.sendMessage(new TextMessage(mapper.writeValueAsBytes(message)));
} catch (IOException e) {
log.info("Failed to publish the message. {}", message.toString());
@ -51,23 +64,68 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
}
}
/** 核心调用 **/
@Override
public void sendBatch(Collection<MyConcurrentWebSocketSession> sessions, WebSocketMessageResponse message) {
if (sessions.isEmpty()) {
return;
}
if (sessions.isEmpty()) {return;}
try {
TextMessage data = new TextMessage(mapper.writeValueAsBytes(message));
// System.out.println(data.getPayload());
String targetDockSn = extractDockSn(message.getData());//获取sn码
// if (StrUtil.isBlank(targetDockSn)) { //消息体中不包含 dockSn 可拦截
// return;
// }
//查redis在线用户
String onlineUserPrefix = RedisConst.DEVICE_ONLINE_USER_PREFIX ;
// Set<String> allKeys = RedisOpsUtils.getAllKeys(keyPrefix);
// allKeys = allKeys.stream()
// .map(key -> StrUtil.subAfter(key, keyPrefix, false))
// .filter(StrUtil::isNotBlank) // 排除 null 或 空字符串
// .collect(Collectors.toSet());
for (MyConcurrentWebSocketSession session : sessions) {
if (!session.isOpen()) {
session.close();
log.debug("This session is closed.");
return;
// log.debug("This session is closed.");
// return;
continue;
}
// session.sendMessage(data);//数据发送 //关闭数据
String token = parseTokenFromQuery(session.getUri().getQuery());
// System.out.println("tok"+token);
// String newToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJzeXNfdXNlcjoxIiwicm5TdHIiOiJMVUoxUGUzczNMVGNndHkzTVI1SFRRV3NUcjdWamlsSSIsImNsaWVudGlkIjoiZTVjZDdlNDg5MWJmOTVkMWQxOTIwNmNlMjRhN2IzMmUiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsImRlcHRJZCI6MiwiZGVwdE5hbWUiOiLmsZ_oi4_nnIEiLCJkZXB0Q2F0ZWdvcnkiOiIifQ.5SX070r1cmmQGbEmd9MMD88XnLDJ8wPJ5LvddJ3cn_Y";
// System.out.println(ObjectUtil.equals(token,newToken));
if (StrUtil.isBlank(token)) { continue;}
String key = onlineUserPrefix + token;
String snStr = (String)RedisOpsUtils.get(key);
if (StrUtil.isEmpty(snStr)) {continue;}
String[] snSplit = snStr.split(",");
// CollUtil.contains(Arrays.asList(snSplit),targetDockSn);
// if(ObjectUtil.isNotEmpty(snStr) && ObjectUtil.equals(sn, targetDockSn)){
if(ObjectUtil.isNotEmpty(snStr) && Arrays.asList(snSplit).contains(targetDockSn) ){
// if(ObjectUtil.isNotEmpty(sn)){
session.sendMessage(data);//数据发送
}
session.sendMessage(data);
// boolean keyExist = RedisOpsUtils.checkExist(keyPrefix + ":" + token);
// if (keyExist) {
// Object o = RedisOpsUtils.get(keyPrefix + ":" + token);
// }
//在线用户token里有当前用户,发送消息
// String query = session.getUri().getQuery(); // Authorization=Bearer abgffgx....
// String token = parseTokenFromQuery(query);
// if(CollUtil.contains(allKeys, token)){
// session.sendMessage(data);//数据发送
// }
}
} catch (IOException e) {
@ -77,11 +135,65 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
}
}
@Override
public void sendBatchBySessions(Collection<MyConcurrentWebSocketSession> sessions, String bizCode, Object data) {
WebSocketMessageResponse<Object> response = new WebSocketMessageResponse<>()
.setBizCode(bizCode)
.setTimestamp(System.currentTimeMillis())
.setData(Objects.requireNonNullElse(data, ""));
sendBatch(sessions, response);
}
private String extractDockSn(Object data) {
try {
// 假设DTO 都有 getSn() 方法
Method method = data.getClass().getMethod("getSn");
Object sn = method.invoke(data);
return sn == null ? null : sn.toString();
} catch (Exception e) {
return null;
}
}
@Override
public void sendBatch(String workspaceId, Integer userType, String bizCode, Object data) {
if (!StringUtils.hasText(workspaceId)) {
throw new RuntimeException("工作区ID不存在。");
}
// // 1. 优先尝试基于订阅投递
// String dockSn = extractDockSn(data);
// if (dockSn != null) {
// Collection<MyConcurrentWebSocketSession> subs =
// webSocketSubscriptionManager.getSubscribers(bizCode, dockSn);
// if (!subs.isEmpty()) {
// // 精准投递给订阅者
// this.sendBatchBySessions(subs, bizCode, data);
// return;
// }
// }
//2. 全部发送
/* String dockSn = extractDockSn(data); //获取设备sn码
System.out.println("dockSn: "+ dockSn);
if (dockSn != null) {
Collection<MyConcurrentWebSocketSession> subs = webSocketSubscriptionManager.getSessionsForDock(dockSn);
// subs.stream().map()
String collect = subs.stream().map(WebSocketSessionDecorator::getId).collect(Collectors.joining(","));
if (!subs.isEmpty()) {
System.out.println("订阅数:" + subs.size() + " collect: "+ collect);
this.sendBatch(subs, new WebSocketMessageResponse<>()
.setData(Objects.requireNonNullElse(data, ""))
.setTimestamp(System.currentTimeMillis())
.setBizCode(bizCode));
return;
}
}*/
//旧群发
Collection<MyConcurrentWebSocketSession> sessions = Objects.isNull(userType) ?
webSocketManageService.getValueWithWorkspace(workspaceId) :
webSocketManageService.getValueWithWorkspaceAndUserType(workspaceId, userType);
@ -96,4 +208,18 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
public void sendBatch(String workspaceId, String bizCode, Object data) {
this.sendBatch(workspaceId, null, bizCode, data);
}
private String parseTokenFromQuery(String query) {
// 简单解析:找到 Authorization=Bearer XXX
for (String part : query.split("&")) {
if (part.startsWith("Authorization=")) {
String v = part.substring("Authorization=".length());
if (v.startsWith("Bearer ")) {
return v.substring("Bearer ".length());
}
return v;
}
}
return null;
}
}

15
dk-modules/sample/src/main/resources/mapper/WaylineJobAtmosphereMapper.xml

@ -4,4 +4,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.sample.wayline.mapper.WaylineJobAtmosphereMapper">
<select id="selectSpiritSmellWayLineJobIds" resultType="java.lang.Long" parameterType="java.lang.String">
select distinct wayline_job_id from wayline_job_atmosphere
<where>
<if test="dockSn != null and dockSn != ''">
dock_sn = #{dockSn};
</if>
</where>
</select>
<select id="selectDistinctWaylineFileId" resultType="java.lang.String" >
select distinct file_id from wayline_job_atmosphere
</select>
</mapper>

15
pom.xml

@ -114,7 +114,7 @@
</properties>
</profile>
<profile>
<id>w_nantong</id>
<id>w_nantong</id> <!-- 南通生产环境 -->
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>w_nantong</profiles.active>
@ -126,6 +126,19 @@
<logstash.address>192.168.0.8:4560</logstash.address>
</properties>
</profile>
<profile>
<id>nantong-dev</id> <!-- 南通测试环境 -->
<properties>
<profiles.active>nantong-dev</profiles.active>
<nacos.server>127.0.0.1:8848</nacos.server>
<nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
<nacos.config.group>DEFAULT_GROUP</nacos.config.group>
<nacos.username>nacos</nacos.username>
<nacos.password>nacos</nacos.password>
<logstash.address>127.0.0.1:4560</logstash.address>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>

Loading…
Cancel
Save