Compare commits

...

2 Commits

  1. 44
      Jenkinsfile
  2. 2
      dk-api/api-business/src/main/java/org/dromara/business/api/domain/vo/RemoteBusinessAlertVo.java
  3. 32
      dk-modules/business/pom.xml
  4. 4
      dk-modules/business/src/main/java/org/dromara/business/controller/BusinessAlertConstructInfoOssController.java
  5. 13
      dk-modules/business/src/main/java/org/dromara/business/controller/BusinessAlertController.java
  6. 12
      dk-modules/business/src/main/java/org/dromara/business/domain/BusinessAlert.java
  7. 5
      dk-modules/business/src/main/java/org/dromara/business/domain/bo/BusinessAlertBo.java
  8. 1
      dk-modules/business/src/main/java/org/dromara/business/service/IBusinessAlertService.java
  9. 2
      dk-modules/business/src/main/java/org/dromara/business/service/impl/BusinessAlertConstructInfoOssServiceImpl.java
  10. 101
      dk-modules/business/src/main/java/org/dromara/business/service/impl/BusinessAlertServiceImpl.java
  11. 18
      dk-modules/business/src/main/java/org/dromara/business/service/impl/BusinessTaskServiceImpl.java
  12. 49
      dk-modules/business/src/main/java/org/dromara/business/utils/CoordinateModel.java
  13. 74
      dk-modules/business/src/main/java/org/dromara/business/utils/HtmlConvertPdfHelper.java
  14. 30
      dk-modules/business/src/main/java/org/dromara/business/utils/ImageRotator.java
  15. 224
      dk-modules/business/src/main/java/org/dromara/business/utils/KMZGenerator.java
  16. 2
      dk-modules/business/src/main/java/org/dromara/business/utils/constants/MinIOConstants.java
  17. BIN
      dk-modules/business/src/main/resources/icon/green.png
  18. BIN
      dk-modules/business/src/main/resources/icon/map.png
  19. 16
      dk-modules/business/src/main/resources/mapper/business/BusinessAlertMapper.xml
  20. 1
      dk-modules/business/src/main/resources/templates/pdfAlert.ftl
  21. 23
      dk-modules/sample/pom.xml
  22. 13
      dk-modules/sample/src/main/java/org/dromara/sample/control/service/impl/ControlServiceImpl.java
  23. 14
      dk-modules/sample/src/main/java/org/dromara/sample/feign/RemoteMsgConfigUserFeign.java
  24. 9
      dk-modules/sample/src/main/java/org/dromara/sample/feign/RemoteSystemFeign.java
  25. 5
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceController.java
  26. 4
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceProController.java
  27. 13
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceQrtzController.java
  28. 3
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/DeviceDTO.java
  29. 5
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/IManageDevicePayloadCustomService.java
  30. 1
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceProServiceImpl.java
  31. 44
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceQrtzServiceImpl.java
  32. 4
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceRedisServiceImpl.java
  33. 44
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceServiceImpl.java
  34. 18
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/ManageDevicePayloadCustomServiceImpl.java
  35. 6
      dk-modules/sample/src/main/java/org/dromara/sample/media/service/IFileService.java
  36. 13
      dk-modules/sample/src/main/java/org/dromara/sample/media/service/impl/FileServiceImpl.java
  37. 10
      dk-modules/sample/src/main/java/org/dromara/sample/media/service/impl/MediaServiceImpl.java
  38. 229
      dk-modules/sample/src/main/java/org/dromara/sample/utils/AtmosphereCalculateUtil.java
  39. 101
      dk-modules/sample/src/main/java/org/dromara/sample/utils/HtmlConvertPdfHelper.java
  40. 357
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/WaylineJobAtmosphereController.java
  41. 62
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/bo/WaylineJobAtmosphereData.java
  42. 3
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/mapper/WaylineJobAtmosphereMapper.java
  43. 8
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IWaylineJobAtmosphereService.java
  44. 3
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IWaylineJobService.java
  45. 10
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/FlightTaskServiceImpl.java
  46. 69
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineJobAtmosphereServiceImpl.java
  47. 8
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/WaylineJobServiceImpl.java
  48. 81
      dk-modules/sample/src/main/java/org/dromara/sample/websocket/service/impl/WebSocketMessageServiceImpl.java
  49. 8
      dk-modules/sample/src/main/resources/hms.json
  50. 2
      dk-modules/sample/src/main/resources/mapper/DeviceQrtzMapper.xml
  51. 4
      dk-modules/sample/src/main/resources/mapper/WaylineJobAtmosphereMapper.xml
  52. BIN
      dk-modules/sample/src/main/resources/templates/empty.png
  53. 101
      dk-modules/sample/src/main/resources/templates/pdfSpiritSmell-batch.ftl
  54. 81
      dk-modules/sample/src/main/resources/templates/pdfSpiritSmell.ftl
  55. BIN
      dk-modules/sample/src/main/resources/templates/灵嗅-pm2.5.png
  56. 141
      dk-modules/system/src/main/java/org/dromara/system/controller/SysMsgConfigUserController.java
  57. 4
      dk-modules/system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java
  58. 8
      dk-modules/system/src/main/java/org/dromara/system/controller/system/AiLabelController.java
  59. 4
      dk-modules/system/src/main/java/org/dromara/system/controller/system/SysDepartBoundaryController.java
  60. 4
      dk-modules/system/src/main/java/org/dromara/system/controller/system/SysUserController.java
  61. 331
      dk-modules/system/src/main/java/org/dromara/system/controller/system/YSYDeviceController.java
  62. 71
      dk-modules/system/src/main/java/org/dromara/system/domain/SysMsgConfigUser.java
  63. 74
      dk-modules/system/src/main/java/org/dromara/system/domain/bo/SysMsgConfigUserBo.java
  64. 87
      dk-modules/system/src/main/java/org/dromara/system/domain/vo/SysMsgConfigUserVo.java
  65. 15
      dk-modules/system/src/main/java/org/dromara/system/mapper/SysMsgConfigUserMapper.java
  66. 2
      dk-modules/system/src/main/java/org/dromara/system/service/ISysDepartBoundaryService.java
  67. 69
      dk-modules/system/src/main/java/org/dromara/system/service/ISysMsgConfigUserService.java
  68. 8
      dk-modules/system/src/main/java/org/dromara/system/service/impl/SysDepartBoundaryServiceImpl.java
  69. 135
      dk-modules/system/src/main/java/org/dromara/system/service/impl/SysMsgConfigUserServiceImpl.java
  70. 14
      dk-modules/system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
  71. 7
      dk-modules/system/src/main/resources/mapper/system/SysMsgConfigUserMapper.xml
  72. 12
      dk-modules/workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java
  73. 16
      dk-modules/workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java
  74. 1
      dk-modules/workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java
  75. 38
      dk-modules/workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java

44
Jenkinsfile

@ -0,0 +1,44 @@
pipeline {
agent any
stages {
stage('Compile Java') {
steps {
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
stage('Archive') {
steps {
archiveArtifacts artifacts: 'dk-auth/target/dk-auth.jar', fingerprint: true
archiveArtifacts artifacts: 'dk-gateway/target/dk-gateway.jar', fingerprint: true
archiveArtifacts artifacts: 'dk-modules/resource/resource.jar', fingerprint: true
archiveArtifacts artifacts: 'dk-modules/business/target/business.jar', fingerprint: true
archiveArtifacts artifacts: 'dk-modules/sample/sample.jar', fingerprint: true
archiveArtifacts artifacts: 'dk-modules/system/target/system.jar', fingerprint: true
archiveArtifacts artifacts: 'dk-modules/workflow/target/workflow.jar', fingerprint: true
}
}
stage('Upload') {
steps {
script {
def jars = [
['src': 'dk-auth/target/dk-auth.jar', 'name': 'dk-auth'],
['src': 'dk-gateway/target/dk-gateway.jar', 'name': 'dk-gateway'],
['src': 'dk-modules/resource/resource.jar', 'name': 'resource'],
['src': 'dk-modules/business/target/business.jar', 'name': 'business'],
['src': 'dk-modules/sample/sample.jar', 'name': 'sample'],
['src': 'dk-modules/system/target/system.jar', 'name': 'system'],
['src': 'dk-modules/workflow/target/workflow.jar', 'name': 'workflow']
]
for (jar in jars) {
sh """
cp ${jar.src} ${jar.name}-${BRANCH_NAME}-${BUILD_NUMBER}.jar
"""
}
}
}
}
}
}

2
dk-api/api-business/src/main/java/org/dromara/business/api/domain/vo/RemoteBusinessAlertVo.java

@ -193,5 +193,5 @@ public class RemoteBusinessAlertVo implements Serializable {
private RemoteBusinessAlertConstructInfo remoteAlertConstructInfo;
// private Map<String,Object> infoMap;
private String gimbalYaw;
}

32
dk-modules/business/pom.xml

@ -149,6 +149,38 @@
<version>5.2.0</version>
</dependency>
<!-- html——> word 导出 开始-->
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-core</artifactId>
<version>8.3.10</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-ImportXHTML</artifactId>
<version>8.3.10</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>8.3.10</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
<!-- html——> word 导出 结束-->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>

4
dk-modules/business/src/main/java/org/dromara/business/controller/BusinessAlertConstructInfoOssController.java

@ -78,12 +78,12 @@ public class BusinessAlertConstructInfoOssController extends BaseController {
//文件上传
// OssClient storage = OssFactory.instance(MinIOConstants.BUCKET_DKCY);
OssClient storage = OssFactory.instance(MinIOConstants.BUCKET_NANTONG);
OssClient storage = OssFactory.instance(MinIOConstants.BUCKET_ALERT);//BUCKET_NANTONG
String originalfileName = file.getOriginalFilename();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
UploadResult uploadResult;
try {
uploadResult = storage.uploadPrefix(file.getBytes(),"construct", suffix, originalfileName, file.getContentType());
uploadResult = storage.uploadPrefix(file.getBytes(),"nantong/construct", suffix, originalfileName, file.getContentType());
} catch (IOException e) {
throw new ServiceException(e.getMessage());
}

13
dk-modules/business/src/main/java/org/dromara/business/controller/BusinessAlertController.java

@ -205,4 +205,17 @@ public class BusinessAlertController extends BaseController {
// remoteSubmailConfigService.remoteCmdSend("smsMultixsend", JSON.toJSONString(vo));
// return R.ok();
// }
/**
* 预警导出kmz格式
*/
@Operation(summary ="预警导出kmz格式",description = "预警导出kmz格式")
@GetMapping("/alert/kmz/export")
public void exportBusinessAlertKmz(BusinessAlertBo bo, HttpServletResponse response) {
businessAlertService.exportBusinessAlertKmz(bo,response);
}
}

12
dk-modules/business/src/main/java/org/dromara/business/domain/BusinessAlert.java

@ -289,4 +289,16 @@ public class BusinessAlert {
*/
private String gimbalPitch;
@TableField(exist = false)
private Integer noticeType=1;
/**
* 忽略人id
*/
private String cancelId;
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "cancelId")
@TableField(exist = false)
private String cancelName;
}

5
dk-modules/business/src/main/java/org/dromara/business/domain/bo/BusinessAlertBo.java

@ -274,4 +274,9 @@ public class BusinessAlertBo {
private String postCategory; //类别编码,用来过滤预警查询返回值(南通的查询南通类,丰县查询丰县)
/**
* 处理状态集合
*/
private List<String> handleTypes;
}

1
dk-modules/business/src/main/java/org/dromara/business/service/IBusinessAlertService.java

@ -110,4 +110,5 @@ public interface IBusinessAlertService {
void pushAlert(Long alertId, String deptId, String deptName);
void pushAlertSimple(Long alertId, String deptId, String deptName);
void exportBusinessAlertKmz(BusinessAlertBo bo, HttpServletResponse response);
}

2
dk-modules/business/src/main/java/org/dromara/business/service/impl/BusinessAlertConstructInfoOssServiceImpl.java

@ -59,7 +59,7 @@ public class BusinessAlertConstructInfoOssServiceImpl implements IBusinessAlertC
QueryWrapper<BusinessAlertConstructInfoOss> wrapper = buildQueryWrapperQuery(bo);
Page<BusinessAlertConstructInfoOss> result = baseMapper.selectVoPageData(pageQuery.build(), wrapper);
result.getRecords().forEach(item -> {
item.setPreviewUrl(MinioUntil.getObjectUrlOne(MinIOConstants.BUCKET_NANTONG, item.getUrl(), 3600).toString());
item.setPreviewUrl(MinioUntil.getObjectUrlOne(MinIOConstants.BUCKET_ALERT, item.getUrl(), 3600).toString());//BUCKET_NANTONG
});
return TableDataInfo.build(result);
}

101
dk-modules/business/src/main/java/org/dromara/business/service/impl/BusinessAlertServiceImpl.java

@ -4,7 +4,6 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
@ -14,8 +13,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itextpdf.text.DocumentException;
import freemarker.template.TemplateException;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -72,7 +69,6 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@ -263,6 +259,7 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
JSONObject jsonObject = new JSONObject();
jsonObject.put("deptId", alert.getDeptId());
jsonObject.put("deptName", alert.getDeptName());
jsonObject.put("noticeType", 1);
jsonObject.put("images", imagePreviewUrl);
jsonObject.put("labelCn", alert.getLabelCn());
jsonObject.put("lat", alert.getLat());
@ -412,6 +409,10 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
wrapper.set(BusinessAlert::getCompleteDate,new Date());
}
if (BusinessStatusEnum.CANCEL.getStatus().equalsIgnoreCase(flowStatus)){
wrapper.set(BusinessAlert::getCancelId,LoginHelper.getUserId());
}
wrapper.eq(BusinessAlert::getId, businessId);
this.baseMapper.update(wrapper);
@ -548,7 +549,10 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
}
wrapper.eq("t.node_type", 1);
wrapper.in("t.approver", remoteWorkflowService.getPermissions());
List<String> permissions = remoteWorkflowService.getPermissions().stream().filter(p-> !p.equalsIgnoreCase("1")).toList();
wrapper.in("t.approver", permissions);
wrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus());
Page<BusinessAlert> page = this.baseMapper.pageAlertTodo(pageQuery.build(), wrapper, ptPrefix);
@ -688,6 +692,17 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
wrapper.eq("t.handle_type", bo.getHandleType());
}
if (ObjectUtil.isNotEmpty(bo.getHandleTypes())){
wrapper.in("t.handle_type", bo.getHandleTypes());
}else {
List<String> handleTypeList = new ArrayList<>();
handleTypeList.add("finish");
handleTypeList.add("waiting");
handleTypeList.add("cancel");
wrapper.in("t.handle_type",handleTypeList);
}
if (ObjectUtil.isNotEmpty(bo.getDeptId())){
//根据传过来的部门id查找是否存在下级部门
List<RemoteDeptVo> remoteDeptVos = remoteDeptService.selectListByParentId(bo.getDeptId());
@ -706,8 +721,8 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
}
if (ObjectUtil.isNotEmpty(bo.getStartTime()) && ObjectUtil.isNotEmpty(bo.getEndTime())){
wrapper.ge("DATE_FORMAT(t.create_time, '%Y-%m-%d')", bo.getStartTime());
wrapper.le("DATE_FORMAT(t.create_time, '%Y-%m-%d')", bo.getEndTime());
wrapper.ge("DATE_FORMAT(t.create_time, '%Y-%m-%d %H:%i')", bo.getStartTime());
wrapper.le("DATE_FORMAT(t.create_time, '%Y-%m-%d %H:%i')", bo.getEndTime());
}
wrapper.orderByDesc("t.create_time","t.complete_date");
@ -1066,19 +1081,23 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
}
//获取指南针
// Double gimbalYawDegree = feignDeviceGroup.getGimbalYawDegree(businessAlert.getTaskImageId());
// if (ObjectUtil.isNotEmpty(gimbalYawDegree) && gimbalYawDegree != -1.0){
// if (gimbalYawDegree < 0){
// gimbalYawDegree = gimbalYawDegree - 180;
// }else {
// gimbalYawDegree = 180 - gimbalYawDegree;
// }
//
// log.info("旋转角度${}",gimbalYawDegree);
//
// String rotatedAndOverlayImage = ImageRotator.rotateAndOverlayImage(null, null, gimbalYawDegree);
// businessAlert.setIcon(rotatedAndOverlayImage);
// }
try {
Double gimbalYawDegree = Double.valueOf(businessAlert.getGimbalYaw());
if (ObjectUtil.isNotEmpty(gimbalYawDegree) && gimbalYawDegree != -1.0){
if (gimbalYawDegree < 0){
gimbalYawDegree = gimbalYawDegree - 180;
}else {
gimbalYawDegree = 180 - gimbalYawDegree;
}
log.info("旋转角度${}",gimbalYawDegree);
String rotatedAndOverlayImage = ImageRotator.rotateAndOverlayImage(gimbalYawDegree);
businessAlert.setIcon(rotatedAndOverlayImage);
}
} catch (Exception e) {
businessAlert.setIcon(null);
}
} catch (Exception e) {
log.error(e.getMessage(),e);
@ -1106,6 +1125,8 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
//生成word
FreemarkerUtil.renderTplFileToWord("预警导出.docx","alert.ftl",resultMap,response);
// HtmlConvertPdfHelper.htmlConvertWord(response,"pdfAlert.ftl",resultMap);
} else if (bo.getExportType().equalsIgnoreCase("pdf")) {
try {
HtmlConvertPdfHelper.renderTplFileToPDF("预警导出.pdf","pdfAlert.ftl",resultMap,response);
@ -1177,6 +1198,46 @@ public class BusinessAlertServiceImpl implements IBusinessAlertService {
remoteWorkflowService.startWorkFlowBatch(List.of(startProcess));
}
@Override
public void exportBusinessAlertKmz(BusinessAlertBo bo, HttpServletResponse response) {
// if (ObjectUtil.hasEmpty(bo.getStartTime(),bo.getEndTime())) {
// bo.setStartTime(DateUtils.getDate());
// bo.setEndTime(DateUtils.getDate());
// }
QueryWrapper<BusinessAlert> wrapper = buildQueryWrapper(bo);
if (ObjectUtil.isEmpty(wrapper)){
throw new RuntimeException("没有可查看的预警数据!");
}
List<CoordinateModel> coordinateList = new ArrayList<>();
//根据前端查询条件查询
List<BusinessAlert> alertList = this.baseMapper.exportAlert(wrapper,ptPrefix);
List<Map<String, Object>> namePathList = remoteDeptService.getNamePathList(null);
Map<Long, List<Map<String, Object>>> namePathMap = namePathList.stream().collect(Collectors.groupingBy(p -> Long.valueOf(p.get("deptId") + "")));
alertList.forEach(businessAlert -> {
String dateStr = DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", businessAlert.getCreateTime());
String deptId = businessAlert.getDeptId();
String name = namePathMap.get(Long.valueOf(deptId)).getFirst().get("namePath") + "-" + dateStr;
CoordinateModel coordinate = new CoordinateModel(Double.parseDouble(businessAlert.getLat()),Double.parseDouble(businessAlert.getLng()),name);
coordinateList.add(coordinate);
});
try {
KMZGenerator.generateKmzFromCoordinates(coordinateList,response, "kmzInfo");
} catch (IOException e) {
e.printStackTrace();
}
}
// 压缩图片方法
private static BufferedImage compressImage(BufferedImage originalImage, int targetWidth, int targetHeight) {
Image resizedImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH);

18
dk-modules/business/src/main/java/org/dromara/business/service/impl/BusinessTaskServiceImpl.java

@ -1,11 +1,13 @@
package org.dromara.business.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.business.domain.BusinessTask;
import org.dromara.business.domain.BusinessTaskLabel;
import org.dromara.business.domain.bo.BusinessTaskBo;
@ -19,6 +21,8 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.resource.api.RemoteMessageService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.system.api.model.LoginUser;
import org.springframework.stereotype.Service;
@ -39,6 +43,10 @@ public class BusinessTaskServiceImpl implements IBusinessTaskService {
private final BusinessTaskMapper baseMapper;
private final BusinessTaskLabelMapper taskLabelMapper;
@DubboReference
private final RemoteMessageService remoteMessageService;
@DubboReference
private RemoteUserService remoteUserService;
/**
* 查询工单预约
@ -117,6 +125,16 @@ public class BusinessTaskServiceImpl implements IBusinessTaskService {
});
taskLabelMapper.insert(bo.getTaskLabelList());
}
//运营人员推送预警
JSONObject jsonObject = new JSONObject();
jsonObject.put("deptName", add.getDeptName());
jsonObject.put("noticeType", 2);
jsonObject.put("demand", add.getDemand());
jsonObject.put("createTime", ObjectUtil.isNotEmpty(add.getCreateTime()) ? add.getCreateTime() : new Date());
jsonObject.put("nickName", currentUser.getNickname());
jsonObject.put("taskLabelList", bo.getTaskLabelList());
List<Long> longList = remoteUserService.selectUserIdsByRoleIds(List.of(1898962197844905986L));
remoteMessageService.publishMessage(longList, jsonObject.toString());
return flag;
}

49
dk-modules/business/src/main/java/org/dromara/business/utils/CoordinateModel.java

@ -0,0 +1,49 @@
package org.dromara.business.utils;
import lombok.Data;
/**
* 坐标点数据模型
*
* @author Your Name
* @version 1.0
*/
@Data
public class CoordinateModel {
private double latitude;
private double longitude;
private String name;
/**
* 默认构造函数
*/
public CoordinateModel() {
}
/**
* 构造函数
*
* @param latitude 纬度
* @param longitude 经度
*/
public CoordinateModel(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
/**
* 构造函数
*
* @param latitude 纬度
* @param longitude 经度
* @param name 名称
*/
public CoordinateModel(double latitude, double longitude, String name) {
this.latitude = latitude;
this.longitude = longitude;
this.name = name;
}
}

74
dk-modules/business/src/main/java/org/dromara/business/utils/HtmlConvertPdfHelper.java

@ -9,13 +9,16 @@ import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.TemplateException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
import org.docx4j.wml.Styles;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
@ -63,6 +66,71 @@ public class HtmlConvertPdfHelper {
return htmlConvertPDF(htmlText);
}
/**
* freemarker转word
*
* @param templateName ftl文件名称需要在resources/templates目录下
* @return
* @throws IOException
*/
public static void htmlConvertWord(HttpServletResponse response,String templateName,Object map){
try {
// 4. 输出到 response
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setHeader("Content-Disposition", "attachment; filename=\"export.docx\"");
String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(freeMarkerConfigurer.getConfiguration().getTemplate(templateName), map);
// 1. 创建空白文档
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
// 2. 检查并添加样式定义
if (wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart() == null) {
StyleDefinitionsPart styleDefinitionsPart = new StyleDefinitionsPart();
styleDefinitionsPart.setJaxbElement(new Styles());
wordMLPackage.getMainDocumentPart().addTargetPart(styleDefinitionsPart);
}
// 3. 替换不是实体引用的 & 为 &amp;
htmlText = htmlText.replaceAll("&(?!(amp;|lt;|gt;|quot;|apos;|#))", "&amp;");
// 4. 导入 HTML
XHTMLImporterImpl xhtmlImporter = new XHTMLImporterImpl(wordMLPackage);
wordMLPackage.getMainDocumentPart().getContent().addAll(
xhtmlImporter.convert(htmlText, null)
);
// wordMLPackage.save(response.getOutputStream());
// 1. 保存到临时文件
File tempFile = File.createTempFile("wordExport", ".docx");
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
wordMLPackage.save(fos);
}
// 2. 分片读取并写入 response
try (FileInputStream fis = new FileInputStream(tempFile);
ServletOutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[4096]; // 4KB
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
os.flush(); // 可选,及时推送
}
} finally {
// 3. 删除临时文件
tempFile.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据HTML内容转Image
*

30
dk-modules/business/src/main/java/org/dromara/business/utils/ImageRotator.java

@ -15,12 +15,10 @@ public class ImageRotator {
/**
* 旋转图片并叠加到另一个图片上
* @param sourceImagePath 源图片路径
* @param targetImagePath 目标图片路径
* @param angle 旋转角度顺时针
* @return 处理后的图片的Base64字符串
*/
public static String rotateAndOverlayImage(String sourceImagePath, String targetImagePath, double angle) {
public static String rotateAndOverlayImage(double angle) {
try {
URL resourceUrl = ImageRotator.class.getClassLoader().getResource("templates/1.png");
@ -33,11 +31,21 @@ public class ImageRotator {
// 创建旋转后的图片
BufferedImage rotatedImage = rotateImage(sourceImage, angle);
// 等比例缩小为原来的0.5倍
double scale = 0.65;
int scaledWidth = (int) (rotatedImage.getWidth() * scale);
int scaledHeight = (int) (rotatedImage.getHeight() * scale);
Image scaledRotatedImage = rotatedImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
// 计算居中位置
int x = (targetImage.getWidth() - scaledWidth) / 2;
int y = (targetImage.getHeight() - scaledHeight) / 2;
// 创建新的画布,使用目标图片的尺寸
BufferedImage resultImage = new BufferedImage(
targetImage.getWidth(),
targetImage.getHeight(),
BufferedImage.TYPE_INT_ARGB
targetImage.getWidth(),
targetImage.getHeight(),
BufferedImage.TYPE_INT_ARGB
);
// 创建图形上下文
@ -46,12 +54,8 @@ public class ImageRotator {
// 绘制目标图片
g2d.drawImage(targetImage, 0, 0, null);
// 计算旋转图片的位置(居中)
int x = (targetImage.getWidth() - rotatedImage.getWidth()) / 2;
int y = (targetImage.getHeight() - rotatedImage.getHeight()) / 2;
// 绘制旋转后的图片
g2d.drawImage(rotatedImage, x, y, null);
// 居中绘制缩小后的旋转图片
g2d.drawImage(scaledRotatedImage, x, y, null);
// 释放图形上下文
g2d.dispose();
@ -116,7 +120,7 @@ public class ImageRotator {
String targetImagePath = "C:\\Users\\Administrator\\Desktop\\2.png"; // 目标图片路径
double angle = 45.0; // 旋转角度
String base64Result = rotateAndOverlayImage(sourceImagePath, targetImagePath, angle);
String base64Result = rotateAndOverlayImage(angle);
if (base64Result != null) {
System.out.println("处理成功,Base64结果:" + base64Result);
} else {

224
dk-modules/business/src/main/java/org/dromara/business/utils/KMZGenerator.java

@ -0,0 +1,224 @@
package org.dromara.business.utils;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.rmi.ServerException;
import java.util.List;
/**
* KMZ文件生成工具类
* 支持根据经纬度集合生成KMZ文件可在Google Earth中打开
*
* @author Your Name
* @version 1.0
*/
public class KMZGenerator {
/**
* 生成包含多个坐标点的KMZ文件并输出到HttpServletResponse
*
* @param coordinates 坐标点集合
* @param response HttpServletResponse对象
* @param fileName 文件名不含扩展名
* @throws IOException 文件操作异常
*/
public static void generateKmzFromCoordinates(List<CoordinateModel> coordinates, HttpServletResponse response, String fileName)
throws IOException {
//验证坐标点是否有效
if (!isValidCoordinateList(coordinates)) {
throw new ServerException("坐标点集合无效!");
}
// 生成KML内容
String kmlContent = generateKmlContent(coordinates, fileName);
// 创建KMZ文件并输出到response
createKmzFile(kmlContent, response, fileName);
System.out.println("KMZ文件已生成并输出到response: " + fileName + ".kmz");
}
/**
* 生成KML内容
*/
private static String generateKmlContent(List<CoordinateModel> coordinates, String fileName) {
StringBuilder kml = new StringBuilder();
kml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
kml.append("<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n");
kml.append(" <Document>\n");
// 添加绿色点的Style,引用包内图标
kml.append(" <Style id=\"green-dot\">\n");
kml.append(" <IconStyle>\n");
kml.append(" <scale>1.2</scale>\n");
kml.append(" <Icon>\n");
kml.append(" <href>green-dot.png</href>\n");
kml.append(" </Icon>\n");
kml.append(" </IconStyle>\n");
kml.append(" </Style>\n");
// 添加坐标点
coordinates.forEach(coordinate -> {
// 生成包含表格的描述
String description = generateDescriptionWithTable(coordinate);
kml.append(" <Placemark>\n");
kml.append(" <styleUrl>#green-dot</styleUrl>\n");
kml.append(" <description>").append(escapeXml(description)).append("</description>\n");
kml.append(" <Point>\n");
kml.append(" <coordinates>")
.append(coordinate.getLongitude()).append(",")
.append(coordinate.getLatitude()).append(",0")
.append("</coordinates>\n");
kml.append(" </Point>\n");
kml.append(" </Placemark>\n");
});
kml.append(" </Document>\n");
kml.append("</kml>");
return kml.toString();
}
/**
* 创建KMZ文件并输出到HttpServletResponse
*/
private static void createKmzFile(String kmlContent, HttpServletResponse response, String fileName) throws IOException {
response.setContentType("application/vnd.google-earth.kmz");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".kmz\"");
try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(response.getOutputStream())) {
zipOut.setEncoding(StandardCharsets.UTF_8.name());
// 添加KML文件到ZIP
ZipArchiveEntry kmlEntry = new ZipArchiveEntry("doc.kml");
zipOut.putArchiveEntry(kmlEntry);
try (InputStream kmlInputStream = new ByteArrayInputStream(kmlContent.getBytes(StandardCharsets.UTF_8))) {
IOUtils.copy(kmlInputStream, zipOut);
}
zipOut.closeArchiveEntry();
// 添加图标图片到ZIP
ZipArchiveEntry iconEntry = new ZipArchiveEntry("green-dot.png");
zipOut.putArchiveEntry(iconEntry);
try (InputStream iconInputStream = KMZGenerator.class.getClassLoader().getResourceAsStream("icon/green.png")) {
IOUtils.copy(iconInputStream, zipOut);
}
zipOut.closeArchiveEntry();
zipOut.finish();
}
}
/**
* 生成包含表格的描述
*/
private static String generateDescriptionWithTable(CoordinateModel coordinate) {
StringBuilder description = new StringBuilder();
description.append("<![CDATA[");
description.append("<table border='1' style='border-collapse: collapse; width: 100%;'>");
description.append("<tr style='background-color: #f2f2f2;'>");
description.append("<th style='padding: 8px; text-align: left;'>名称</th>");
description.append("<th style='padding: 8px; text-align: left;'>经度</th>");
description.append("<th style='padding: 8px; text-align: left;'>纬度</th>");
description.append("</tr>");
description.append("<tr>");
description.append("<td style='padding: 8px;'>").append(escapeHtml(coordinate.getName())).append("</td>");
description.append("<td style='padding: 8px;'>").append(String.format("%.13f", coordinate.getLongitude())).append("</td>");
description.append("<td style='padding: 8px;'>").append(String.format("%.13f", coordinate.getLatitude())).append("</td>");
description.append("</tr>");
description.append("</table>");
description.append("]]>");
return description.toString();
}
/**
* 转义HTML特殊字符
*/
private static String escapeHtml(String text) {
if (text == null) {
return "";
}
return text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&#39;");
}
/**
* 转义XML特殊字符
*/
private static String escapeXml(String text) {
if (text == null) {
return "";
}
return text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&apos;");
}
/**
* 验证坐标点是否有效
*
* @param coordinate 坐标点
* @return 是否有效
*/
public static boolean isValidCoordinate(CoordinateModel coordinate) {
if (coordinate == null) {
return false;
}
// 检查纬度范围 (-90 到 90)
if (coordinate.getLatitude() < -90 || coordinate.getLatitude() > 90) {
return false;
}
// 检查经度范围 (-180 到 180)
if (coordinate.getLongitude() < -180 || coordinate.getLongitude() > 180) {
return false;
}
return true;
}
/**
* 验证坐标点集合是否有效
*
* @param coordinates 坐标点集合
* @return 是否有效
*/
public static boolean isValidCoordinateList(List<CoordinateModel> coordinates) {
if (coordinates == null || coordinates.isEmpty()) {
return false;
}
for (CoordinateModel coordinate : coordinates) {
if (!isValidCoordinate(coordinate)) {
return false;
}
}
return true;
}
}

2
dk-modules/business/src/main/java/org/dromara/business/utils/constants/MinIOConstants.java

@ -19,5 +19,5 @@ public interface MinIOConstants {
String BUCKET_PATTERN = "pattern";
//南通-丰县Buskcet
String BUCKET_NANTONG = "nantongsitebucket";
// String BUCKET_NANTONG = "nantongsitebucket";
}

BIN
dk-modules/business/src/main/resources/icon/green.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
dk-modules/business/src/main/resources/icon/map.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

16
dk-modules/business/src/main/resources/mapper/business/BusinessAlertMapper.xml

@ -49,11 +49,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<!-- w_dk_business-->
<!-- w_dk_workflow-->
<select id="pageBusinessAlertCancel" resultType="org.dromara.business.domain.BusinessAlert">
select t.* from (
select
ba.*
from ${tbPrefix.tableBusiness}.business_alert ba
) t
SELECT
t.*
FROM
(
SELECT
ba.*
FROM
${tbPrefix.tableBusiness}.business_alert ba
) t
${ew.getCustomSqlSegment}
</select>
@ -932,7 +936,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
GROUP BY
temp.label_en,temp.label_cn, ba.lat, ba.lng
HAVING
<![CDATA[ ST_Distance_Sphere(POINT(lng, lat), POINT(ba.lng, ba.lat)) <= 30 ]]>
<![CDATA[ ST_Distance_Sphere(POINT(lng, lat), POINT(ba.lng, ba.lat)) <= 10 ]]>
</select>
<select id="selectCurrentAlertCount" resultType="java.lang.Integer">

1
dk-modules/business/src/main/resources/templates/pdfAlert.ftl

@ -77,6 +77,7 @@
<#list alertList as item>
<div style="page-break-before: always;"></div>
<h2>(${item_index + 1})预警信息:${item.jobName?if_exists}</h2>
<table>
<colgroup>
<col style="width: 50%;"/>

23
dk-modules/sample/pom.xml

@ -152,6 +152,29 @@
<groupId>org.dromara</groupId>
<artifactId>common-job</artifactId>
</dependency>
<!-- html转pdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>

13
dk-modules/sample/src/main/java/org/dromara/sample/control/service/impl/ControlServiceImpl.java

@ -2,6 +2,7 @@ package org.dromara.sample.control.service.impl;
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sdk.cloudapi.control.*;
import org.dromara.common.sdk.cloudapi.wayline.OutOfControlActionEnum;
@ -9,6 +10,7 @@ import org.dromara.common.sdk.cloudapi.wayline.TaskTypeEnum;
import org.dromara.common.sdk.cloudapi.wayline.WaylineJobTypeEnum;
import org.dromara.common.sdk.cloudapi.wayline.WaylineTypeEnum;
import org.dromara.sample.control.model.enums.PayloadCommandsEnum;
import org.dromara.sample.feign.RemoteMsgConfigUserFeign;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.service.IDevicePayloadService;
import org.dromara.sample.manage.service.IDeviceRedisService;
@ -86,6 +88,10 @@ public class ControlServiceImpl implements IControlService {
@Autowired
private IMediaRedisService mediaRedisService;
@Resource
private RemoteMsgConfigUserFeign remoteMsgConfigUserFeign;
private RemoteDebugHandler checkDebugCondition(String sn, RemoteDebugParam param, RemoteDebugMethodEnum controlMethodEnum) {
RemoteDebugHandler handler = Objects.nonNull(controlMethodEnum.getClazz()) ?
mapper.convertValue(Objects.nonNull(param) ? param : new Object(), controlMethodEnum.getClazz())
@ -214,6 +220,13 @@ public class ControlServiceImpl implements IControlService {
waylineJobEntity.setCreateTime(new Date());
waylineJobMapper.insert(waylineJobEntity);
}
//短信提示-客户在手动飞行
if(reply.getResult().isSuccess()){
remoteMsgConfigUserFeign.sendMsg(sn,1);
return null;
}
return reply.getResult().isSuccess() ?
HttpResultResponse.success()
: HttpResultResponse.error("无人机未能起飞。" + reply.getResult());

14
dk-modules/sample/src/main/java/org/dromara/sample/feign/RemoteMsgConfigUserFeign.java

@ -0,0 +1,14 @@
package org.dromara.sample.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "gateway",path = "system")
public interface RemoteMsgConfigUserFeign {
@GetMapping("/msgConfigUser/sendMsg/{deviceSn}/{taskType}")
String sendMsg(@PathVariable String deviceSn, @PathVariable int taskType);
}

9
dk-modules/sample/src/main/java/org/dromara/sample/feign/RemoteSystemFeign.java

@ -2,10 +2,7 @@ package org.dromara.sample.feign;
import org.dromara.common.core.domain.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@ -20,7 +17,7 @@ public interface RemoteSystemFeign {
public String getConfigKeyFeign(@PathVariable String configKey);
//根据目标值类型查询用户信息
@GetMapping("/user/list/target")
public List<Map<String,Object>> listTargetTypeByUser(List<Map<String,Object>> params);
@PostMapping("/user/list/target")
public List<Map<String,Object>> listTargetTypeByUser(@RequestBody List<Map<String,Object>> params);
}

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

@ -65,6 +65,9 @@ public class DeviceController {
if(devicesOsdJson.containsKey(deviceDTO.getDeviceSn())){
deviceDTO.setOsdData(devicesOsdJson.getJSONObject(deviceDTO.getDeviceSn()));
}
if(devicesOsdJson.containsKey(deviceDTO.getChildDeviceSn())){
deviceDTO.setOsdDroneData(devicesOsdJson.getJSONObject(deviceDTO.getChildDeviceSn()));
}
}
return HttpResultResponse.success(devicesList);
}
@ -83,7 +86,7 @@ public class DeviceController {
String token = deviceSubscribeParam.getToken();
String dockSn = deviceSubscribeParam.getDockSn();
if(action.equals("subscribe")) {
System.out.println("sub"+token);
// System.out.println("sub"+token);
String key = RedisConst.DEVICE_ONLINE_USER_PREFIX + token;
//基于机场sn找无人机sn
String value = "";

4
dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceProController.java

@ -84,9 +84,9 @@ public class DeviceProController {
*/
@Operation(summary = "新增/更新项目人员。", description = "新增/更新项目人员。")
@PostMapping("/{proId}/update")
public HttpResultResponse<Boolean> updateDeviceProUser(@RequestBody List<DeviceProUserEntity> userEntity, @PathVariable Integer proId) {
public HttpResultResponse<Boolean> updateDeviceProUser(@RequestBody List<Map<String, Object>> userEntity, @PathVariable Integer proId) {
return HttpResultResponse.success(deviceProService.updateDeviceProUser(userEntity,proId));
return HttpResultResponse.success(deviceProService.updateDeviceProUserNew(userEntity,proId));
}

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

@ -1,6 +1,7 @@
package org.dromara.sample.manage.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -13,15 +14,18 @@ import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.sample.manage.model.dto.DeviceQrtzDTO;
import org.dromara.sample.manage.model.entity.DeviceQrtzEntity;
import org.dromara.sample.manage.model.entity.DeviceQrtzFileEntity;
import org.dromara.sample.manage.service.IDeviceQrtzService;
import org.dromara.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -49,6 +53,15 @@ public class DeviceQrtzController extends BaseController {
@GetMapping(value = "/page")
public TableDataInfo<DeviceQrtzEntity> queryPageDept(@RequestParam(name="pageNo", defaultValue="1") Integer pageNum,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize, DeviceQrtzDTO deviceQrtzDTO) {
//查询自动飞行任务时加部门数据隔离
if (ObjectUtil.isEmpty(deviceQrtzDTO.getDeptIds())) {
LoginUser loginUser = LoginHelper.getLoginUser();
if(!NumberUtil.equals(loginUser.getUserId().longValue(), 1l)){ //非超管,就得加deptId过滤
ArrayList<Long> deptIds = new ArrayList<>();
deptIds.add(loginUser.getDeptId());
deviceQrtzDTO.setDeptIds(deptIds);
}
}
Page<DeviceQrtzEntity> page = new Page(pageNum,pageSize);
return deviceQrtzService.listManageDeviceQrtz(page, deviceQrtzDTO);
}

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

@ -120,8 +120,9 @@ public class DeviceDTO {
private Long pushDeptId;
private boolean spiritsmellFlag;//灵嗅设备状态:
private Boolean spiritsmellFlag;//灵嗅设备状态:
private ManageDevicePayloadCustom payloadSpiritsmell;//灵嗅设备挂载:
private JSONObject osdData;//redis中的osd数据
private JSONObject osdDroneData;//redis中的osd数据
}

5
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/IManageDevicePayloadCustomService.java

@ -1,5 +1,6 @@
package org.dromara.sample.manage.service;
import org.dromara.sample.manage.domain.ManageDevicePayloadCustom;
import org.dromara.sample.manage.domain.vo.ManageDevicePayloadCustomVo;
import org.dromara.sample.manage.domain.bo.ManageDevicePayloadCustomBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -65,4 +66,8 @@ public interface IManageDevicePayloadCustomService {
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
ManageDevicePayloadCustom selectDevicePayloadBySerial(String serial);
ManageDevicePayloadCustom selectDevicePayloadByDocnSn(String deviceSn);
}

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

@ -132,6 +132,7 @@ public class DeviceProServiceImpl implements IDeviceProService {
deviceProUserEntity.setUserId(Long.valueOf(resultMap.get("userId") + ""));
deviceProUserEntity.setUserName(resultMap.get("userName") + "");
deviceProUserEntity.setNickName(resultMap.get("nickName") + "");
deviceProUserEntity.setDeptName(resultMap.get("deptName") + "");
userEntityList.add(deviceProUserEntity);
});

44
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/DeviceQrtzServiceImpl.java

@ -1,6 +1,7 @@
package org.dromara.sample.manage.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -133,13 +134,23 @@ public class DeviceQrtzServiceImpl implements IDeviceQrtzService {
@Override
public Boolean updateByBo(DeviceQrtzEntity entity) {
List<DeviceQrtzDateEntity> deviceQrtzDates = new ArrayList<>();
deviceQrtzDateMapper.delete(new QueryWrapper<DeviceQrtzDateEntity>().eq("qrtz_id", entity.getId()));
if(!CollectionUtils.isEmpty(entity.getDeviceQrtzDates())){
entity.getDeviceQrtzDates().forEach(e->{
e.setQrtzId(entity.getId());
deviceQrtzDateMapper.insert(e);
});
if(CollectionUtils.isEmpty(entity.getDeviceQrtzDates())){
throw new RuntimeException("时间范围不能为空");
}
for (DeviceQrtzDateEntity qrtzDate : entity.getDeviceQrtzDates()) {
if(StrUtil.isNotEmpty(qrtzDate.getStartDate())&&StrUtil.isNotEmpty(qrtzDate.getEndDate())){
deviceQrtzDates.add(qrtzDate);
}
}
if(CollectionUtils.isEmpty(deviceQrtzDates)){
throw new RuntimeException("时间范围不能为空");
}
deviceQrtzDates.forEach(e->{
e.setQrtzId(entity.getId());
deviceQrtzDateMapper.insert(e);
});
return deviceQrtzMapper.updateById(entity)>0;
}
@ -153,6 +164,18 @@ public class DeviceQrtzServiceImpl implements IDeviceQrtzService {
@Override
public Boolean add( DeviceQrtzDTO deviceQrtzDTO) {
List<DeviceQrtzDateEntity> deviceQrtzDates = new ArrayList<>();
if(CollectionUtils.isEmpty(deviceQrtzDTO.getDeviceQrtzDates())){
throw new RuntimeException("时间范围不能为空");
}
for (DeviceQrtzDateEntity qrtzDate : deviceQrtzDTO.getDeviceQrtzDates()) {
if(StrUtil.isNotEmpty(qrtzDate.getStartDate())&&StrUtil.isNotEmpty(qrtzDate.getEndDate())){
deviceQrtzDates.add(qrtzDate);
}
}
if(CollectionUtils.isEmpty(deviceQrtzDates)){
throw new RuntimeException("时间范围不能为空");
}
DeviceQrtzEntity entity = new DeviceQrtzEntity();
@ -164,13 +187,12 @@ public class DeviceQrtzServiceImpl implements IDeviceQrtzService {
entity.setNickName(loginUser.getNickname());
entity.setCreateDept(loginUser.getDeptId());
entity.setDeptName(loginUser.getDeptName());
entity.setId(IdUtil.getSnowflakeNextId());
int insert = deviceQrtzMapper.insert(entity);
if(!CollectionUtils.isEmpty(deviceQrtzDTO.getDeviceQrtzDates())){
deviceQrtzDTO.getDeviceQrtzDates().forEach(e->{
e.setQrtzId(entity.getId());
deviceQrtzDateMapper.insert(e);
});
}
deviceQrtzDates.forEach(e->{
e.setQrtzId(entity.getId());
deviceQrtzDateMapper.insert(e);
});
return insert>0;
}

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

@ -82,7 +82,7 @@ public class DeviceRedisServiceImpl implements IDeviceRedisService {
item.put("latitude", dock.getLatitude());
item.put("longitude", dock.getLongitude());
item.put("height", dock.getHeight());
item.put("modeCode", dock.getModeCode());
item.put("modeCode", dock.getModeCode().getCode());
resJsonObject.put(osdKey.replaceFirst(RedisConst.OSD_PREFIX,""), item);
//处理无人机数据
@ -134,7 +134,7 @@ public class DeviceRedisServiceImpl implements IDeviceRedisService {
item.put("height", dockDrone.getHeight());
item.put("latitude", dockDrone.getLatitude());
item.put("longitude", dockDrone.getLongitude());
item.put("modeCode", dockDrone.getModeCode());
item.put("modeCode", dockDrone.getModeCode().getCode());
item.put("trackId", dockDrone.getTrackId());
// payload(只保留4个字段)
if (dockDrone.getPayloads() != null && !dockDrone.getPayloads().isEmpty()) {

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

@ -1,10 +1,8 @@
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;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.satoken.utils.LoginHelper;
@ -50,6 +48,7 @@ import org.dromara.sample.manage.service.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sample.wayline.service.IWaylineJobAtmosphereService;
import org.dromara.sample.websocket.service.IWebSocketMessageService;
import org.dromara.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
@ -58,14 +57,11 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
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;
@ -150,6 +146,10 @@ public class DeviceServiceImpl implements IDeviceService {
@Autowired
private IDeviceProService deviceProService;
@Autowired
private IWaylineJobAtmosphereService waylineJobAtmosphereService;
@Override
public void subDeviceOffline(String deviceSn) {
// If no information about this device exists in the cache, the drone is considered to be offline.
@ -263,6 +263,7 @@ public class DeviceServiceImpl implements IDeviceService {
List<DeviceDTO> devicesList = this.getDevicesByParams(
DeviceQueryParam.builder()
.workspaceId(workspaceId)
.nickname(nickname)
.domains(List.of(DeviceDomainEnum.REMOTER_CONTROL.getDomain(), DeviceDomainEnum.DOCK.getDomain()))
.proIds(proIds)
.build());
@ -272,21 +273,32 @@ public class DeviceServiceImpl implements IDeviceService {
deviceRedisService.checkDeviceOnline(gateway.getDeviceSn()))
.forEach(this::spliceDeviceTopo);
//基于记录查询灵嗅设备-通过记录来设置符号
List<String> dockSnList = waylineJobAtmosphereService.selectDistinctDockSnList();
for (DeviceDTO device : devicesList) {
if(device.getSpiritsmellFlag()){ continue;}//跳过缓存有设备
if(CollUtil.contains(dockSnList, device.getDeviceSn())){
device.setSpiritsmellFlag(true);
}
}
//验证第三方挂载状态(灵嗅)
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);
if(CollUtil.isNotEmpty(deviceSnList)){
LambdaQueryWrapper<ManageDevicePayloadCustom> payloadCustomMapper = new LambdaQueryWrapper<>();
payloadCustomMapper.in(CollUtil.isNotEmpty(deviceSnList),ManageDevicePayloadCustom::getDockSn, deviceSnList);
List<ManageDevicePayloadCustom> payloadCustomList = manageDevicePayloadCustomMapper.selectList(payloadCustomMapper);
if(CollUtil.isNotEmpty(payloadCustomList)){
for (DeviceDTO deviceDTO : devicesList) {
ManageDevicePayloadCustom spiritsmellV2 = payloadCustomList.stream().filter(item ->
ObjectUtil.equals(item.getDockSn(), deviceDTO.getDeviceSn()) &&
ObjectUtil.equals(item.getPayloadName(), "spiritsmellV2")).findFirst().orElse(null);
deviceDTO.setPayloadSpiritsmell(spiritsmellV2);
}
}
}
return devicesList;
}

18
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/ManageDevicePayloadCustomServiceImpl.java

@ -136,4 +136,22 @@ public class ManageDevicePayloadCustomServiceImpl implements IManageDevicePayloa
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public ManageDevicePayloadCustom selectDevicePayloadBySerial(String serial) {
LambdaQueryWrapper<ManageDevicePayloadCustom> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ManageDevicePayloadCustom::getPayloadSn,serial);
queryWrapper.last("limit 1");
ManageDevicePayloadCustom payloadCustom = baseMapper.selectOne(queryWrapper);
return payloadCustom;
}
@Override
public ManageDevicePayloadCustom selectDevicePayloadByDocnSn(String deviceSn) {
LambdaQueryWrapper<ManageDevicePayloadCustom> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ManageDevicePayloadCustom::getDockSn,deviceSn);
queryWrapper.last("limit 1");
ManageDevicePayloadCustom payloadCustom = baseMapper.selectOne(queryWrapper);
return payloadCustom;
}
}

6
dk-modules/sample/src/main/java/org/dromara/sample/media/service/IFileService.java

@ -5,6 +5,8 @@ import org.dromara.common.sdk.cloudapi.media.FlightTask;
import org.dromara.common.sdk.cloudapi.media.MediaUploadCallbackRequest;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.sample.job.WaylineDeviceJobBo;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.media.model.MediaFileCountDTO;
import org.dromara.sample.media.model.MediaFileDTO;
import org.dromara.sample.media.model.MediaFileEntity;
@ -33,7 +35,7 @@ public interface IFileService {
* @param file
* @return
*/
Integer saveFile(String workspaceId, MediaUploadCallbackRequest file, FlightTask flightTask,Integer proId);
Integer saveFile(String workspaceId, MediaUploadCallbackRequest file, FlightTask flightTask, Integer proId, MediaFileCountDTO mediaFileCount, DeviceDTO device);
/**
* Query information about all files in this workspace based on the workspace id.
@ -72,8 +74,6 @@ public interface IFileService {
List<MediaFileDTO> getFilesByWorkspaceAndJobId(String workspaceId, String jobId);
Integer getFilesByJobIdCount(String jobId);
/**
* Paginate through all media files in this workspace.
* @param workspaceId

13
dk-modules/sample/src/main/java/org/dromara/sample/media/service/impl/FileServiceImpl.java

@ -18,7 +18,6 @@ import org.dromara.common.sdk.cloudapi.media.MediaSubFileTypeEnum;
import org.dromara.common.sdk.cloudapi.media.MediaUploadCallbackRequest;
import org.dromara.common.sdk.common.Pagination;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.sample.job.WaylineDeviceJobBo;
import org.dromara.sample.manage.model.dto.DeviceDictionaryDTO;
import org.dromara.sample.manage.service.IDeviceDictionaryService;
import org.dromara.sample.manage.service.IDeviceProService;
@ -27,6 +26,7 @@ import org.dromara.sample.media.mapper.IFileMapper;
import org.dromara.sample.media.model.MediaFileDTO;
import org.dromara.sample.media.model.MediaFileEntity;
import org.dromara.sample.media.service.IFileService;
import org.dromara.sample.media.service.IMediaRedisService;
import org.dromara.sample.utils.MinioUtil;
import org.dromara.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
@ -59,6 +59,8 @@ public class FileServiceImpl implements IFileService {
@Autowired
private IDeviceProService deviceProService;
@Autowired
private IMediaRedisService mediaRedisService;
private Optional<MediaFileEntity> getMediaByFingerprint(String workspaceId, String fingerprint) {
MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
@ -80,11 +82,18 @@ public class FileServiceImpl implements IFileService {
}
@Override
public Integer saveFile(String workspaceId, MediaUploadCallbackRequest file, FlightTask flightTask,Integer proId) {
public Integer saveFile(String workspaceId, MediaUploadCallbackRequest file, FlightTask flightTask,Integer proId, MediaFileCountDTO mediaFileCount, DeviceDTO device) {
MediaFileEntity fileEntity = this.fileUploadConvertToEntity(file,flightTask);
if(RedisOpsUtils.checkExist("jobId-media:"+fileEntity.getJobId()+fileEntity.getFileName())) {
return 1;
}
if (!Objects.isNull(mediaFileCount)) {
mediaFileCount.setBid(mediaFileCount.getBid());
mediaFileCount.setTid(mediaFileCount.getTid());
mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1);
mediaRedisService.setMediaCount(device.getDeviceSn(), fileEntity.getJobId(), mediaFileCount);
}
RedisOpsUtils.setWithExpire("jobId-media:"+fileEntity.getJobId()+fileEntity.getFileName(),fileEntity.getObjectKey(),10800);
fileEntity.setWorkspaceId(workspaceId);
fileEntity.setFileId(UUID.randomUUID().toString());

10
dk-modules/sample/src/main/java/org/dromara/sample/media/service/impl/MediaServiceImpl.java

@ -115,7 +115,7 @@ public class MediaServiceImpl extends AbstractMediaService implements IMediaServ
@Override
public Integer saveMediaFile(String workspaceId, MediaUploadCallbackRequest file,Integer proId) {
return fileService.saveFile(workspaceId, file,null,proId);
return fileService.saveFile(workspaceId, file,null,proId,null,null);
}
@Override
@ -150,7 +150,7 @@ public class MediaServiceImpl extends AbstractMediaService implements IMediaServ
Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway());
if(deviceRedisService.getDeviceOsd(request.getGateway(), OsdDock.class).get().getModeCode().getCode() == 4){
DeviceDTO device = deviceOpt.get();
boolean isSave = parseMediaFile(callback, device);
boolean isSave = parseMediaFile(callback, device,null);
if (!isSave) {
log.error("将文件保存到数据库失败,请手动检查数据。");
return null;
@ -177,7 +177,7 @@ public class MediaServiceImpl extends AbstractMediaService implements IMediaServ
}
DeviceDTO device = deviceOpt.get();
boolean isSave = parseMediaFile(callback, device);
boolean isSave = parseMediaFile(callback, device,mediaFileCount);
if (!isSave) {
log.error("将文件保存到数据库失败,请手动检查数据。");
return null;
@ -218,7 +218,7 @@ public class MediaServiceImpl extends AbstractMediaService implements IMediaServ
return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
}
private Boolean parseMediaFile(FileUploadCallback callback, DeviceDTO device) {
private Boolean parseMediaFile(FileUploadCallback callback, DeviceDTO device,MediaFileCountDTO mediaFileCount) {
MediaUploadCallbackRequest file = convert2callbackRequest(callback.getFile());
// Set the drone sn that shoots the media
file.getExt().setSn(device.getChildDeviceSn());
@ -228,7 +228,7 @@ public class MediaServiceImpl extends AbstractMediaService implements IMediaServ
file.setPath(objectKey.substring(Optional.of(objectKey.indexOf("mediafile"))
.filter(index -> index > 0).map(index -> index++).orElse(0),
objectKey.lastIndexOf("/")));
Integer fileFlag = fileService.saveFile(device.getWorkspaceId(), file, callback.getFlightTask(),device.getProId());
Integer fileFlag = fileService.saveFile(device.getWorkspaceId(), file, callback.getFlightTask(),device.getProId(),mediaFileCount,device);
return fileFlag> 0;
}

229
dk-modules/sample/src/main/java/org/dromara/sample/utils/AtmosphereCalculateUtil.java

@ -0,0 +1,229 @@
package org.dromara.sample.utils;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import java.util.*;
public class AtmosphereCalculateUtil {
@Data
public static class GridGasStatResult {
private String gasKey;
private Float concentrationAvag;
private Float concentrationPointMax;
private Float concentrationPointMin;
private Float concentrationMax; // 网格平均最大
private Float concentrationMin; // 网格平均最小
private Double areaTotalSize; // 单位:平方米
private Double areaLat; // 区域中心纬度
private Double areaLon; // 区域中心经度
}
public static GridGasStatResult computeStats(List<WaylineJobAtmosphereVo> atmosphereList,
double latGridSize, double lonGridSize,
String gasKey) {
if (atmosphereList == null || atmosphereList.isEmpty()) return null;
// 计算动态起点
double baseLat = atmosphereList.stream()
.filter(vo -> vo.getLatitude() != null)
.mapToDouble(vo -> vo.getLatitude())
.min().orElse(0);
double baseLon = atmosphereList.stream()
.filter(vo -> vo.getLongitude() != null)
.mapToDouble(vo -> vo.getLongitude())
.min().orElse(0);
float globalSum = 0;
int globalCount = 0;
float pointMax = Float.MIN_VALUE;
float pointMin = Float.MAX_VALUE;
double minLat = Double.MAX_VALUE;
double maxLat = -Double.MAX_VALUE;
double minLon = Double.MAX_VALUE;
double maxLon = -Double.MAX_VALUE;
// 按网格聚合气体值
Map<String, List<Float>> gridMap = new HashMap<>();
for (WaylineJobAtmosphereVo vo : atmosphereList) {
Float lat = vo.getLatitude();
Float lon = vo.getLongitude();
String airData = vo.getAirData();
if (lat == null || lon == null || airData == null) continue;
// 区域边界统计
minLat = Math.min(minLat, lat);
maxLat = Math.max(maxLat, lat);
minLon = Math.min(minLon, lon);
maxLon = Math.max(maxLon, lon);
JSONObject json = JSONUtil.parseObj(airData);
if (!json.containsKey(gasKey)) continue;
float value = json.getFloat(gasKey, 0f);
// 全局更新点统计
globalSum += value;
globalCount++;
pointMax = Math.max(pointMax, value);
pointMin = Math.min(pointMin, value);
// 网格编号
int gridX = (int) ((lon - baseLon) / lonGridSize);
int gridY = (int) ((lat - baseLat) / latGridSize);
String gridKey = gridX + "_" + gridY;
gridMap.computeIfAbsent(gridKey, k -> new ArrayList<>()).add(value);
}
float gridMax = Float.MIN_VALUE;
float gridMin = Float.MAX_VALUE;
for (List<Float> values : gridMap.values()) {
float sum = 0;
for (float v : values) sum += v;
float avg = sum / values.size();
gridMax = Math.max(gridMax, avg);
gridMin = Math.min(gridMin, avg);
}
GridGasStatResult result = new GridGasStatResult();
double areaLat = (minLat + maxLat) / 2.0;
double areaLon = (minLon + maxLon) / 2.0;
// 粗略估算:纬度 ≈ 111000 米/度,经度 ≈ 85000 * cos(纬度) 米/度
double latLen = (maxLat - minLat) * 111000.0;
double lonLen = (maxLon - minLon) * 85000.0 * Math.cos(Math.toRadians(areaLat));
double areaSize = latLen * lonLen;
result.setAreaLat(areaLat);
result.setAreaLon(areaLon);
result.setAreaTotalSize(areaSize);
result.setConcentrationAvag(globalCount == 0 ? null : globalSum / globalCount);
result.setConcentrationPointMax(pointMax == Float.MIN_VALUE ? null : pointMax);
result.setConcentrationPointMin(pointMin == Float.MAX_VALUE ? null : pointMin);
result.setConcentrationMax(gridMax == Float.MIN_VALUE ? null : gridMax);
result.setConcentrationMin(gridMin == Float.MAX_VALUE ? null : gridMin);
return result;
}
public static List<GridGasStatResult> computeStats(List<WaylineJobAtmosphereVo> atmosphereList,
double latGridSize, double lonGridSize,
List<String> gasKeys) {
if (atmosphereList == null || atmosphereList.isEmpty() || gasKeys == null || gasKeys.isEmpty()) {
return Collections.emptyList();
}
// 动态起点
double baseLat = atmosphereList.stream()
.filter(vo -> vo.getLatitude() != null)
.mapToDouble(WaylineJobAtmosphereVo::getLatitude)
.min().orElse(0);
double baseLon = atmosphereList.stream()
.filter(vo -> vo.getLongitude() != null)
.mapToDouble(WaylineJobAtmosphereVo::getLongitude)
.min().orElse(0);
// 区域边界初始化
double minLat = Double.MAX_VALUE;
double maxLat = -Double.MAX_VALUE;
double minLon = Double.MAX_VALUE;
double maxLon = -Double.MAX_VALUE;
// 每个 gasKey 的统计结构
class GasStats {
float globalSum = 0;
int globalCount = 0;
float pointMax = Float.MIN_VALUE;
float pointMin = Float.MAX_VALUE;
Map<String, List<Float>> gridMap = new HashMap<>();
}
Map<String, GasStats> statsMap = new HashMap<>();
for (String gasKey : gasKeys) {
statsMap.put(gasKey, new GasStats());
}
for (WaylineJobAtmosphereVo vo : atmosphereList) {
Float lat = vo.getLatitude();
Float lon = vo.getLongitude();
String airData = vo.getAirData();
if (lat == null || lon == null || airData == null) continue;
minLat = Math.min(minLat, lat);
maxLat = Math.max(maxLat, lat);
minLon = Math.min(minLon, lon);
maxLon = Math.max(maxLon, lon);
JSONObject json = JSONUtil.parseObj(airData);
for (String gasKey : gasKeys) {
if (!json.containsKey(gasKey)) continue;
float value = json.getFloat(gasKey, 0f);
GasStats stats = statsMap.get(gasKey);
// 全局统计
stats.globalSum += value;
stats.globalCount++;
stats.pointMax = Math.max(stats.pointMax, value);
stats.pointMin = Math.min(stats.pointMin, value);
// 网格编号
int gridX = (int) ((lon - baseLon) / lonGridSize);
int gridY = (int) ((lat - baseLat) / latGridSize);
String gridKey = gridX + "_" + gridY;
stats.gridMap.computeIfAbsent(gridKey, k -> new ArrayList<>()).add(value);
}
}
// 区域中心、面积计算
double areaLat = (minLat + maxLat) / 2.0;
double areaLon = (minLon + maxLon) / 2.0;
double latLen = (maxLat - minLat) * 111000.0;
double lonLen = (maxLon - minLon) * 85000.0 * Math.cos(Math.toRadians(areaLat));
double areaSize = latLen * lonLen;
// 输出结果列表
List<GridGasStatResult> resultList = new ArrayList<>();
for (String gasKey : gasKeys) {
GasStats stats = statsMap.get(gasKey);
float gridMax = Float.MIN_VALUE;
float gridMin = Float.MAX_VALUE;
for (List<Float> values : stats.gridMap.values()) {
float sum = 0;
for (float v : values) sum += v;
float avg = sum / values.size();
gridMax = Math.max(gridMax, avg);
gridMin = Math.min(gridMin, avg);
}
GridGasStatResult result = new GridGasStatResult();
result.setGasKey(gasKey);
result.setAreaLat(areaLat);
result.setAreaLon(areaLon);
result.setAreaTotalSize(areaSize);
result.setConcentrationAvag(stats.globalCount == 0 ? null : stats.globalSum / stats.globalCount);
result.setConcentrationPointMax(stats.pointMax == Float.MIN_VALUE ? null : stats.pointMax);
result.setConcentrationPointMin(stats.pointMin == Float.MAX_VALUE ? null : stats.pointMin);
result.setConcentrationMax(gridMax == Float.MIN_VALUE ? null : gridMax);
result.setConcentrationMin(gridMin == Float.MAX_VALUE ? null : gridMin);
resultList.add(result);
}
return resultList;
}
}

101
dk-modules/sample/src/main/java/org/dromara/sample/utils/HtmlConvertPdfHelper.java

@ -0,0 +1,101 @@
package org.dromara.sample.utils;
import cn.hutool.extra.spring.SpringUtil;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.TemplateException;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* Html转换Pdf
*
* @author Mall
* @date Created in 2022-04-25
*/
public class HtmlConvertPdfHelper {
/**
* Freemarker的配置类模板目录默认 resources下的 templates
*/
private static FreeMarkerConfigurer freeMarkerConfigurer = SpringUtil.getBean(FreeMarkerConfigurer.class);
public static void renderTplFileToPDF(String fileName , String templateName, Map<String, Object> datas, HttpServletResponse response) {
try {
response.setCharacterEncoding(StandardCharsets.UTF_8.name()); // 字符集编码
response.setContentType("application/pdf;");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.addHeader("Access-Control-Allow-Origin", "*"); // 实现跨域
byte[] bytes = htmlConvertPDF(datas, templateName);
// 将生成的文档写入到响应的输出流中
response.reset(); // 重置响应
response.getOutputStream().write(bytes);
response.getOutputStream().flush();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* freemarker转PDF
*
* @param templateName ftl文件名称需要在resources/templates目录下
* @return
* @throws IOException
*/
public static byte[] htmlConvertPDF(Object map,String templateName) throws IOException, TemplateException, DocumentException {
String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(freeMarkerConfigurer.getConfiguration().getTemplate(templateName), map);
return htmlConvertPDF(htmlText);
}
/**
* 根据HTML内容转Image
*
* @param htmText HTML文本字符串
*/
public static byte[] htmlConvertPDF(String htmText) throws DocumentException, IOException {
//最终返回的byte流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//第一步,创建一个 iTextSharp.text.Document对象的实例:
Document document = new Document();
//第二步,为该Document创建一个Writer实例:
PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStream);
//第三步,打开当前Document
document.open();
XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(htmText.getBytes()), null, Charset.defaultCharset(), new AsianFontProvider());
document.close();
return byteArrayOutputStream.toByteArray();
}
}
/**
* 用于中文显示的Provider
*/
class AsianFontProvider extends XMLWorkerFontProvider {
@Override
public Font getFont(final String fontname, String encoding, float size, int style) {
try {
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
return new Font(bfChinese, size, style);
} catch (Exception e) {
}
return super.getFont(fontname, encoding, size, style);
}
}

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

@ -1,20 +1,38 @@
package org.dromara.sample.wayline.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.sdk.common.HttpResultResponse;
import org.dromara.sample.manage.domain.ManageDevicePayloadCustom;
import org.dromara.sample.manage.service.IManageDevicePayloadCustomService;
import org.dromara.sample.utils.AtmosphereCalculateUtil;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereData;
import org.dromara.sample.wayline.model.dto.WaylineJobDTO;
import org.dromara.sample.wayline.model.entity.WaylineJobEntity;
import org.dromara.sample.wayline.service.IWaylineJobService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -30,6 +48,7 @@ import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import org.dromara.sample.wayline.service.IWaylineJobAtmosphereService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 无人机任务-空气质量
@ -46,6 +65,7 @@ public class WaylineJobAtmosphereController extends BaseController {
private final IWaylineJobAtmosphereService waylineJobAtmosphereService;
private final IWaylineJobService waylineJobService;
private final IManageDevicePayloadCustomService manageDevicePayloadCustomService;
/**
* 查询无人机任务-空气质量列表
@ -110,6 +130,335 @@ public class WaylineJobAtmosphereController extends BaseController {
ExcelUtil.exportExcel(list, "无人机任务-空气质量", WaylineJobAtmosphereVo.class, response);
}
//NO2
/** 导出大气检测报告-单类型 **/
@Log(title = "无人机任务-空气质量-导出", businessType = BusinessType.EXPORT)
@PostMapping("/exportPdf")
// public void exportPdf( WaylineJobAtmosphereData data, @RequestPart("img") MultipartFile img, HttpServletResponse response) {
public void exportPdf(@RequestBody WaylineJobAtmosphereData data, HttpServletResponse response) {
float areaWidth = 150.0f;//网格默认宽
float areaLength = 150.0f;//网格默认高
// AtmosphereCalculateUtil.GridGasStatResult result = waylineJobAtmosphereService.dealBasicData(data, areaWidth, areaLength);
//处理任务信息
WaylineJobEntity waylineJob = waylineJobService.queryById(data.getWaylineJobId());
data.setStartTime(DateUtil.format(waylineJob.getExecuteTime(), "yyyy-MM-dd HH:mm:ss"));
data.setEndTime(DateUtil.format(waylineJob.getCompletedTime(), "yyyy-MM-dd HH:mm:ss"));
//处理设备信息
ManageDevicePayloadCustom payloadCustom = manageDevicePayloadCustomService.selectDevicePayloadBySerial(data.getSerial());
data.setDeviceName(waylineJob.getName());
data.setDeviceSerial(data.getSerial());
data.setDeviceId(payloadCustom.getId()+"");
data.setDeptDetectName(waylineJob.getWaylineName()+"大气监测");
if(JSONUtil.isTypeJSON(payloadCustom.getPayloadDesc())){
JSONObject entries = JSONUtil.parseObj(payloadCustom.getPayloadDesc());
String companyName = entries.getStr("companyName","");
data.setCompanyName(companyName);
areaWidth = entries.getFloat("areaWidth",150.0f);
areaLength = entries.getFloat("areaLength",150.0f);
}
//处理监测点+网格信息
WaylineJobAtmosphereBo bo = new WaylineJobAtmosphereBo();
bo.setWaylineJobId(data.getWaylineJobId());
List<WaylineJobAtmosphereVo> atmospherList = waylineJobAtmosphereService.queryList(bo);
data.setAnalysisCount(atmospherList.size());
data.setAreaWidth(areaWidth);
data.setAreaLength(areaLength);
data.setAreaSize(NumberUtil.round(NumberUtil.mul(areaWidth,areaLength),3).floatValue());
//计算关键参数
double latGridSize = areaWidth / 111000.0; // latGridSize(转经纬度计算)
double lonGridSize = areaLength / (85000.0 * Math.cos(Math.toRadians(34.27))); // lonGridSize(转经纬度计算)
AtmosphereCalculateUtil.GridGasStatResult result = AtmosphereCalculateUtil.computeStats(
atmospherList,
latGridSize,
lonGridSize,
data.getAtmosphereType()
);
data.setAreaTotalSize(result.getAreaTotalSize().floatValue());
data.setAreaLat(result.getAreaLat().floatValue());
data.setAreaLon(result.getAreaLon().floatValue());
data.setConcentrationAvag(result.getConcentrationAvag());
data.setConcentrationMax(result.getConcentrationMax());
data.setConcentrationMin(result.getConcentrationMin());
data.setConcentrationPointMax(result.getConcentrationPointMax());
data.setConcentrationPointMin(result.getConcentrationPointMin());
//模拟数据-PM2_5
// getAtmosphereData(data);
waylineJobAtmosphereService.exportPdf(data,response);
// List<WaylineJobAtmosphereVo> list = waylineJobAtmosphereService.queryList(bo);
// ExcelUtil.exportExcel(list, "无人机任务-空气质量", WaylineJobAtmosphereVo.class, response);
}
/** 导出大气检测报告-多类型 **/
@Log(title = "无人机任务-空气质量-导出", businessType = BusinessType.EXPORT)
@PostMapping("/exportBatchPdf")
// public void exportPdf( WaylineJobAtmosphereData data, @RequestPart("img") MultipartFile img, HttpServletResponse response) {
// public void exportBatchPdf(JSONArray json,WaylineJobAtmosphereData atmosphereType, HttpServletResponse response) {
// public void exportBatchPdf(@RequestBody List<WaylineJobAtmosphereData> list, HttpServletResponse response) {
public void exportBatchPdf(@RequestParam(name = "data_list") String dataList,
@RequestParam(name = "data_cal_list") String dataCalList,
@RequestPart(name = "file_list") List<MultipartFile> fileList,
HttpServletResponse response) {
// public void exportBatchPdf(@PathVariable(name = "data_ist") String dataList,
// @RequestPart("fileList") List<MultipartFile> fileList,
// HttpServletResponse response) {
// List<WaylineJobAtmosphereData> list = JSONUtil.toList(json, WaylineJobAtmosphereData.class);
//模拟数据-PM2_5
// List<WaylineJobAtmosphereData> list = JSONUtil.toList(dataList, WaylineJobAtmosphereData.class);
// System.out.println("dataCalList" + dataCalList);
WaylineJobAtmosphereData calData = JSONUtil.toBean(dataCalList, WaylineJobAtmosphereData.class);//数据从前台接收
WaylineJobAtmosphereData dataObj = JSONUtil.toBean(dataList, WaylineJobAtmosphereData.class);
List<WaylineJobAtmosphereData> list = new ArrayList<>();
String[] split = dataObj.getAtmosphereType().split(",");
for (int i = 0; i < split.length; i++) {
WaylineJobAtmosphereData newData = new WaylineJobAtmosphereData();
BeanUtil.copyProperties(dataObj,newData);
newData.setAtmosphereType(split[i].toUpperCase());
newData.setAtmosphereTypeStr(split[i].toUpperCase());
list.add(newData);
}
if(ObjectUtil.isEmpty(list)) {return;}
WaylineJobAtmosphereData data = list.get(0);
//找设备序列号
ManageDevicePayloadCustom manageDevicePayloadCustom = manageDevicePayloadCustomService.selectDevicePayloadByDocnSn(data.getDeviceSn());
if (ObjectUtil.isNotEmpty(manageDevicePayloadCustom)) {
for (WaylineJobAtmosphereData atmosphereData : list) {
atmosphereData.setSerial(manageDevicePayloadCustom.getPayloadSn());
}
}else{
return;
}
//处理任务信息
WaylineJobEntity waylineJob = waylineJobService.queryById(data.getWaylineJobId());
data.setStartTime(DateUtil.format(waylineJob.getExecuteTime(), "yyyy-MM-dd HH:mm:ss"));
data.setEndTime(DateUtil.format(waylineJob.getCompletedTime(), "yyyy-MM-dd HH:mm:ss"));
//处理设备信息
ManageDevicePayloadCustom payloadCustom = manageDevicePayloadCustomService.selectDevicePayloadBySerial(data.getSerial());
data.setDeviceName(waylineJob.getName());
data.setDeviceSerial(data.getSerial());
data.setDeviceId(payloadCustom.getId()+"");
data.setDeptDetectName(waylineJob.getWaylineName()+"大气监测");
float areaWidth = 150.0f;//网格默认宽
float areaLength = 150.0f;//网格默认高
if(JSONUtil.isTypeJSON(payloadCustom.getPayloadDesc())){
JSONObject entries = JSONUtil.parseObj(payloadCustom.getPayloadDesc());
String companyName = entries.getStr("companyName","");
data.setCompanyName(companyName);
areaWidth = entries.getFloat("areaWidth",150.0f);
areaLength = entries.getFloat("areaLength",150.0f);
}
//处理监测点+网格信息
WaylineJobAtmosphereBo bo = new WaylineJobAtmosphereBo();
bo.setWaylineJobId(data.getWaylineJobId());
List<WaylineJobAtmosphereVo> atmospherList = waylineJobAtmosphereService.queryList(bo);
data.setAnalysisCount(atmospherList.size());
data.setAreaWidth(areaWidth);
data.setAreaLength(areaLength);
data.setAreaSize(NumberUtil.round(NumberUtil.mul(areaWidth,areaLength),3).floatValue());
//计算关键参数
List<String> atmosphereTypeList = list.stream().map(WaylineJobAtmosphereData::getAtmosphereType).distinct().collect(Collectors.toList());
double latGridSize = areaWidth / 111000.0; // latGridSize(转经纬度计算)
double lonGridSize = areaLength / (85000.0 * Math.cos(Math.toRadians(34.27))); // lonGridSize(转经纬度计算)
List<AtmosphereCalculateUtil.GridGasStatResult> resultList = AtmosphereCalculateUtil.computeStats(
atmospherList,
latGridSize,
lonGridSize,
atmosphereTypeList
);
// List<WaylineJobAtmosphereData> dataList = new ArrayList<WaylineJobAtmosphereData>();
for (WaylineJobAtmosphereData atmosphereData : list) {
AtmosphereCalculateUtil.GridGasStatResult result = resultList.stream().filter(item -> item.getGasKey().equals(atmosphereData.getAtmosphereType())).findFirst().orElse(null);
atmosphereData.setAtmosphereTypeStr(atmosphereData.getAtmosphereType());
atmosphereData.setAreaTotalSize(result.getAreaTotalSize().floatValue());
atmosphereData.setAreaLat(result.getAreaLat().floatValue());
atmosphereData.setAreaLon(result.getAreaLon().floatValue());
atmosphereData.setConcentrationAvag(result.getConcentrationAvag());
atmosphereData.setConcentrationMax(result.getConcentrationMax());
atmosphereData.setConcentrationMin(result.getConcentrationMin());
atmosphereData.setConcentrationPointMax(result.getConcentrationPointMax());
atmosphereData.setConcentrationPointMin(result.getConcentrationPointMin());
// fileList.get()
// atmosphereData.setImgUrl();
}
if(list.size() > 0){//数据从前端覆盖
list.getFirst().setConcentrationMax(calData.getConcentrationMax());
list.getFirst().setConcentrationMin(calData.getConcentrationMin());
}
//统一排序,渲染图片-临时文件
List<File> tempFiles = new ArrayList<>();
List<String> imgPaths = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
WaylineJobAtmosphereData item = list.get(i);
MultipartFile multipartFile = fileList.get(i);
try {
String suffix = FileUtil.extName(multipartFile.getOriginalFilename());
File tempFile = File.createTempFile("pdfimg-", suffix);
multipartFile.transferTo(tempFile);
tempFiles.add(tempFile);
imgPaths.add(tempFile.toURI().toString());
item.setImgUrl(imgPaths.get(i));
} catch (IOException e) {
System.err.println("pdfimg处理异常");
}
item.setImgName(multipartFile.getName());
}
try {
waylineJobAtmosphereService.exportBatchPdf(list,response);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//清除临时文件
for (File tempFile : tempFiles) {
if(tempFile.exists()){
tempFile.delete();
}
}
}
// List<WaylineJobAtmosphereVo> list = waylineJobAtmosphereService.queryList(bo);
// ExcelUtil.exportExcel(list, "无人机任务-空气质量", WaylineJobAtmosphereVo.class, response);
}
private static void getAtmosphereData(WaylineJobAtmosphereData data) {
if(ObjectUtil.isEmpty(data.getImgUrl())) {
// File file = FileUtil.file("");
// byte[] bytes = FileUtil.readBytes(file);
// String base64 = Base64.getEncoder().encodeToString(bytes);
// data.setAtmosphereType(base64);
try {
ClassPathResource resource = new ClassPathResource("templates/empty.png");
byte[] bytes = StreamUtils.copyToByteArray(resource.getInputStream());
data.setImgUrl( "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
float v = 901.230543f;
double v1 = RandomUtil.randomDouble();
double v2 = RandomUtil.randomDouble();
double v3 = RandomUtil.randomDouble();
double v4 = RandomUtil.randomDouble();
switch (data.getAtmosphereType()){
case "PM2_5":
data.setAtmosphereTypeStr("PM2.5");
data.setDeptDetectName("和平街道大气监测");
data.setCompanyName("徐州市低空产业公司");
data.setStartTime("2020-01-01 00:00:00");
data.setEndTime("2020-12-31 23:59:59");
data.setDeviceName("低空-徐州鼓楼琵琶街道");
data.setDeviceSerial("37c12953");
data.setDeviceId("100");
data.setAnalysisCount(901);
data.setAreaWidth(NumberUtil.round(v1, 3, RoundingMode.DOWN).floatValue());//剪切数字长度
data.setAreaLength(NumberUtil.round(v2, 3, RoundingMode.DOWN).floatValue());
data.setAreaSize(NumberUtil.round(v3, 3, RoundingMode.DOWN).floatValue());
data.setAreaTotalSize(NumberUtil.round(v4, 3, RoundingMode.DOWN).floatValue());
data.setAreaLat(NumberUtil.round(v1, 3, RoundingMode.DOWN).floatValue());
data.setAreaLon(NumberUtil.round(v2, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationAvag(NumberUtil.round(v1, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationMax(NumberUtil.round(v2, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationMin(NumberUtil.round(v3, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationPointMax(NumberUtil.round(v3, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationPointMin(NumberUtil.round(v4, 3, RoundingMode.DOWN).floatValue());
// String imgBase64 = convertToBase64(img);
// data.setImgUrl(imgBase64);
// data.setImgUrl("templates/灵嗅-pm2.5.png");
break;
case "PM1_0":
break;
case "NO2":
data.setAtmosphereTypeStr("NO2");
data.setDeptDetectName("和平街道大气监测");
data.setCompanyName("徐州市低空产业公司");
data.setStartTime("2020-01-01 00:00:00");
data.setEndTime("2020-12-31 23:59:59");
data.setDeviceName("低空-徐州鼓楼琵琶街道");
data.setDeviceSerial("37c12953");
data.setDeviceId("100");
data.setAnalysisCount(901);
data.setAreaWidth(NumberUtil.round(v1, 3, RoundingMode.DOWN).floatValue());//剪切数字长度
data.setAreaLength(NumberUtil.round(v2, 3, RoundingMode.DOWN).floatValue());
data.setAreaSize(NumberUtil.round(v3, 3, RoundingMode.DOWN).floatValue());
data.setAreaTotalSize(NumberUtil.round(v4, 3, RoundingMode.DOWN).floatValue());
data.setAreaLat(NumberUtil.round(v1, 3, RoundingMode.DOWN).floatValue());
data.setAreaLon(NumberUtil.round(v2, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationAvag(NumberUtil.round(v1, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationMax(NumberUtil.round(v2, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationMin(NumberUtil.round(v3, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationPointMax(NumberUtil.round(v3, 3, RoundingMode.DOWN).floatValue());
data.setConcentrationPointMin(NumberUtil.round(v4, 3, RoundingMode.DOWN).floatValue());
break;
}
}
public static String toBase64(MultipartFile file) {
try {
byte[] bytes = file.getBytes();
String base64 = Base64.getEncoder().encodeToString(bytes);
String contentType = file.getContentType(); // eg: "image/png"
return "data:" + contentType + ";base64," + base64;
} catch (IOException e) {
throw new RuntimeException("图片转换失败", e);
}
}
// 将上传的 MultipartFile 转为 Base64 字符串
public String convertToBase64(MultipartFile file) {
String base64 = null;
try {
byte[] bytes = file.getBytes();
base64 = Base64.getEncoder().encodeToString(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
return "data:image/png;base64," + base64; // 注意类型可能是 image/jpeg 或其他
}
/**
* 获取无人机任务-空气质量详细信息
*

62
dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/bo/WaylineJobAtmosphereData.java

@ -0,0 +1,62 @@
package org.dromara.sample.wayline.domain.bo;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.sample.wayline.domain.WaylineJobAtmosphere;
import java.util.Date;
import java.util.List;
/**
* 无人机任务-空气质量业务对象 wayline_job_atmosphere
*
* @author szs
* @date 2025-06-16
*/
@Data
public class WaylineJobAtmosphereData {
private Long waylineJobId; //无人机任务ID
private String serial; //序列号
private String deviceSn;//机场SN
//{"SO2":0,"NO2":0,"Ox":0.058595,"PM1_0":39,"PM2_5":85,"PM10":108}
private String atmosphereType;//NO2,NO2,0x,PM1_0,PM2_5
private String atmosphereTypeStr;//NO2,NO2,0x,PM1_0,PM2_5
private String deptDetectName;//和平街道大气监测
private String companyName;//徐州市低空产业公司
private String startTime;
private String endTime;
private String deviceName;//低空-徐州鼓楼琵琶街道
private String deviceSerial;//37c12953
private String deviceId;//100
private String analysisMethod;//分析方法:激光散射
private Integer analysisCount;//923
private Float areaWidth; //检测区域宽
private Float areaLength; //检测区域长
private Float areaSize; //检测区域平均面积
private Float areaTotalSize; //检测区域总面积
private Float areaLat; //检测区域维度
private Float areaLon; //检测区域经度
private Float concentrationAvag; // 平均浓度
private Float concentrationMax; // 最高值
private Float concentrationMin; //最小值
private Float concentrationPointMax; //单点最高值
private Float concentrationPointMin; //单点最低值
private String imgUrl;
private String imgName;
}

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

@ -18,4 +18,7 @@ public interface WaylineJobAtmosphereMapper extends BaseMapperPlus<WaylineJobAtm
List<Long> selectSpiritSmellWayLineJobIds(@Param("dockSn") String dockSn);
List<String> selectDistinctWaylineFileId();
List<String> selectDistinctDockSnList();
}

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

@ -2,6 +2,8 @@ package org.dromara.sample.wayline.service;
import cn.hutool.core.date.DateTime;
import cn.hutool.json.JSONObject;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereData;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -78,4 +80,10 @@ public interface IWaylineJobAtmosphereService {
List<WaylineJobAtmosphereVo> selectByDateRange(String dockSn, DateTime dayStart, DateTime dayEnd);
List<Long> selectSpiritSmellWayLineJobIds(String dockSn);
void exportPdf(WaylineJobAtmosphereData data, HttpServletResponse response);
void exportBatchPdf(List<WaylineJobAtmosphereData> dataList, HttpServletResponse response);
List<String> selectDistinctDockSnList();
}

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

@ -3,6 +3,7 @@ package org.dromara.sample.wayline.service;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.sample.media.model.MediaFileDTO;
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;
@ -96,4 +97,6 @@ public interface IWaylineJobService {
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);
WaylineJobEntity queryById(Long waylineJobId);
}

10
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/FlightTaskServiceImpl.java

@ -1,5 +1,6 @@
package org.dromara.sample.wayline.service.impl;
import jakarta.annotation.Resource;
import org.dromara.common.redis.config.RedisConst;
import org.dromara.common.redis.utils.RedisOpsUtils;
import org.dromara.common.satoken.utils.LoginHelper;
@ -19,6 +20,7 @@ import org.dromara.common.sdk.mqtt.services.TopicServicesResponse;
import org.dromara.sample.common.error.CommonErrorEnum;
import org.dromara.sample.component.mqtt.model.EventsReceiver;
import org.dromara.sample.feign.RemoteMsgConfigUserFeign;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.service.IDeviceRedisService;
import org.dromara.sample.media.model.MediaFileCountDTO;
@ -86,6 +88,10 @@ public class FlightTaskServiceImpl extends AbstractWaylineService implements IFl
@Qualifier("mediaServiceImpl")
private AbstractMediaService abstractMediaService;
@Resource
private RemoteMsgConfigUserFeign remoteMsgConfigUserFeign;
@Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void checkScheduledJob() {
Object jobIdValue = RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB_TIMED_EXECUTE);
@ -262,6 +268,10 @@ public class FlightTaskServiceImpl extends AbstractWaylineService implements IFl
}
}
}
//短信提示-客户在航线飞行
remoteMsgConfigUserFeign.sendMsg(param.getDockSn(),2);
return HttpResultResponse.success();
}

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

@ -1,10 +1,13 @@
package org.dromara.sample.wayline.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
@ -14,7 +17,14 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.sample.manage.domain.ManageDevicePayloadCustom;
import org.dromara.sample.manage.service.IManageDevicePayloadCustomService;
import org.dromara.sample.utils.AtmosphereCalculateUtil;
import org.dromara.sample.utils.HtmlConvertPdfHelper;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereData;
import org.dromara.sample.wayline.model.dto.WaylineJobDTO;
import org.dromara.sample.wayline.model.entity.WaylineJobEntity;
import org.dromara.sample.wayline.service.IWaylineJobService;
import org.springframework.stereotype.Service;
import org.dromara.sample.wayline.domain.bo.WaylineJobAtmosphereBo;
import org.dromara.sample.wayline.domain.vo.WaylineJobAtmosphereVo;
@ -22,9 +32,8 @@ import org.dromara.sample.wayline.domain.WaylineJobAtmosphere;
import org.dromara.sample.wayline.mapper.WaylineJobAtmosphereMapper;
import org.dromara.sample.wayline.service.IWaylineJobAtmosphereService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -214,4 +223,58 @@ public class WaylineJobAtmosphereServiceImpl implements IWaylineJobAtmosphereSer
public List<Long> selectSpiritSmellWayLineJobIds(String dockSn) {
return baseMapper.selectSpiritSmellWayLineJobIds(dockSn);
}
@Override
public void exportPdf(WaylineJobAtmosphereData data, HttpServletResponse response) {
try {
Map<String, Object> map = BeanUtil.beanToMap(data);
String fileName = "大气监测.pdf";
fileName = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm") +data.getDeptDetectName() +"大气监测";
HtmlConvertPdfHelper.renderTplFileToPDF(fileName,"pdfSpiritSmell.ftl",map,response);
} catch (Exception e) {
e.printStackTrace();
// log.error(e.getMessage(),e);
}
}
@Override
public void exportBatchPdf(List<WaylineJobAtmosphereData> dataList, HttpServletResponse response) {
try {
Map<String, Object> map = new HashMap<>();
map.put("dataList", dataList);
/** 拿第一个数据的参数作为基础数据区展示 **/
// data.setDeptDetectName(waylineJob.getWaylineName()+"大气监测");
String deptDetectNameStr = dataList.stream().map(WaylineJobAtmosphereData::getAtmosphereTypeStr).collect(Collectors.joining(","));
// map.put("deptDetectNameStr",dataList.get(0).getDeptDetectName());
map.put("deptDetectNameStr",deptDetectNameStr);
map.put("deptDetectName",dataList.get(0).getDeptDetectName());
map.put("companyName",dataList.get(0).getCompanyName());
map.put("startTime",dataList.get(0).getStartTime());
map.put("endTime",dataList.get(0).getEndTime());
map.put("deviceName",dataList.get(0).getDeviceName());
map.put("deviceSerial",dataList.get(0).getDeviceSerial());
map.put("deviceId",dataList.get(0).getDeviceId());
map.put("analysisCount",dataList.get(0).getAnalysisCount());
map.put("areaWidth",dataList.get(0).getAreaWidth());
map.put("areaLength",dataList.get(0).getAreaLength());
map.put("areaSize",dataList.get(0).getAreaSize());
map.put("areaTotalSize",dataList.get(0).getAreaTotalSize());
map.put("areaLat",dataList.get(0).getAreaLat());
map.put("areaLon",dataList.get(0).getAreaLon());
// Map<String, Object> map = BeanUtil.beanToMap();
HtmlConvertPdfHelper.renderTplFileToPDF("大气监测.pdf","pdfSpiritSmell-batch.ftl",map,response);
} catch (Exception e) {
e.printStackTrace();
// log.error(e.getMessage(),e);
}
}
@Override
public List<String> selectDistinctDockSnList() {
return baseMapper.selectDistinctDockSnList();
}
}

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

@ -312,6 +312,14 @@ public class WaylineJobServiceImpl implements IWaylineJobService {
return new PaginationData<WaylineJobDTO>(records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
}
@Override
public WaylineJobEntity queryById(Long waylineJobId) {
WaylineJobEntity jobEntity = mapper.selectOne(
new LambdaQueryWrapper<WaylineJobEntity>()
.eq(WaylineJobEntity::getId, waylineJobId));
return jobEntity;
}
private WaylineJobEntity dto2Entity(WaylineJobDTO dto) {
WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder();

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

@ -1,14 +1,20 @@
package org.dromara.sample.websocket.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
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.sdk.cloudapi.device.OsdDockDrone;
import org.dromara.common.websocket.dto.WebSocketMessageResponse;
import org.dromara.sample.manage.model.dto.TelemetryDTO;
import org.dromara.sample.websocket.config.MyConcurrentWebSocketSession;
import org.dromara.sample.websocket.config.WebSocketSubscriptionManager;
import org.dromara.sample.websocket.service.IWebSocketManageService;
@ -21,10 +27,8 @@ 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.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import static cn.hutool.jwt.JWTUtil.parseToken;
@ -46,6 +50,34 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
@Autowired
private WebSocketSubscriptionManager webSocketSubscriptionManager;
// 数据优化配置文件路径和缓存
static String configPath = System.getProperty("os.name").toLowerCase().contains("win") ? "C:/deviceOpt.json" : "/usr/local/etc/deviceOpt.json";
static JSONObject cachedConfig = null;
static long lastLoadTime = 0L;
static final long CACHE_INTERVAL_MS = 30_000;
static final long RETRY_INTERVAL_MS = 15_000; // 失败后10秒再试
// 获取缓存数据
private static JSONObject getConfigFromCache() {
long now = System.currentTimeMillis();
boolean needReload = cachedConfig == null || (now - lastLoadTime > CACHE_INTERVAL_MS);
if (needReload) {
boolean success = false;
try {
String str = FileUtil.readUtf8String(configPath);
if (JSONUtil.isTypeJSON(str)) {
cachedConfig = JSONUtil.parseObj(str);
success = true;
}
} catch (Exception e) {
// System.err.println("读取配置失败: " + e.getMessage());
} finally {
lastLoadTime = success ? now : (now - CACHE_INTERVAL_MS + RETRY_INTERVAL_MS);
}
}
return cachedConfig;
}
@Override
public void sendMessage(MyConcurrentWebSocketSession session, WebSocketMessageResponse message) {
if (session == null) {return;}
@ -73,9 +105,23 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
TextMessage data = new TextMessage(mapper.writeValueAsBytes(message));
// System.out.println(data.getPayload());
String targetDockSn = extractDockSn(message.getData());//获取sn码
// if (StrUtil.isBlank(targetDockSn)) { //消息体中不包含 dockSn 可拦截
// return;
// }
//优化部分参数展示
JSONObject entries = getConfigFromCache();
if (entries != null && entries.containsKey(targetDockSn)) {
JSONArray arr = entries.getJSONArray(targetDockSn);
Integer addCount = arr.getInt(0);//数据类
Integer addPercent = arr.getInt(1);//比例类
OsdDockDrone drone = (OsdDockDrone) ((TelemetryDTO) message.getData()).getHost();
Integer loopTimes = drone.getBattery().getBatteries().get(0).getLoopTimes();
if (addCount.equals(0) && !addPercent.equals(0)) {
loopTimes += loopTimes * addPercent / 100;
}else{
loopTimes += addCount;
}
drone.getBattery().getBatteries().get(0).setLoopTimes(loopTimes);
data = new TextMessage(mapper.writeValueAsBytes(message));
}
//查redis在线用户
String onlineUserPrefix = RedisConst.DEVICE_ONLINE_USER_PREFIX ;
@ -92,9 +138,8 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
// return;
continue;
}
session.sendMessage(data);//数据发送 //关闭数据
/* String token = parseTokenFromQuery(session.getUri().getQuery());
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));
@ -106,26 +151,10 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
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);//数据发送
}*/
// 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) {

8
dk-modules/sample/src/main/resources/hms.json

@ -676,6 +676,10 @@
"en": "Wind speed too high (≥ 9 m/s). Unable to perform task",
"zh": "风速过大(≥9 m/s),无法执行飞行任务"
},
"dock_tip_0x19114001": {
"en": "The wind speed is too high to execute the task",
"zh": "风速过大,无法执行任务"
},
"dock_tip_0x19114800": {
"en": "Heavy rainfall. Unable to perform task",
"zh": "雨量过大,无法执行飞行任务"
@ -767,6 +771,10 @@
"en": "Rainfall gauge damaged",
"zh": "雨量计检测部件损坏"
},
"dock_tip_0x19114816": {
"en": "The wind speed is too high to execute the task",
"zh": "雨量较大,请谨慎飞行。"
},
"dock_tip_0x1911481A": {
"en": "Dock cover failed to deice",
"zh": "机场舱盖除冰功能异常"

2
dk-modules/sample/src/main/resources/mapper/DeviceQrtzMapper.xml

@ -38,7 +38,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND wd.dept_name like concat(concat('%',#{condition.deptName}),'%')
</if>
<if test="condition.deptIds.size > 0">
AND wd.dept_id in
AND wdq.create_dept in
<foreach item="id" collection="condition.deptIds" open="(" separator="," close=")">
#{id}
</foreach>

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

@ -17,6 +17,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select distinct file_id from wayline_job_atmosphere
</select>
<select id="selectDistinctDockSnList" resultType="java.lang.String" >
select distinct dock_sn from wayline_job_atmosphere
</select>
</mapper>

BIN
dk-modules/sample/src/main/resources/templates/empty.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

101
dk-modules/sample/src/main/resources/templates/pdfSpiritSmell-batch.ftl

@ -0,0 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<title>SpiritSmell Record</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
text-align: center;
}
h2 {
text-align: left;
margin-top: 30px;
margin-left: 60px;
}
p{
margin-top: 3px;
margin-left: 60px;
}
table {
margin: 40px auto;
width: 100%;
}
/*h2 {*/
/* color: #1b5aa1;*/
/*}*/
td {
word-wrap: break-word;
word-break: break-all;
}
body{
font-size: 14px;
}
</style>
</head>
<body style="margin-top:30px;">
<!--<div style="display: flex;justify-content: space-between;">-->
<#--<div >-->
<#-- <div style="display:inline-block;width: 45%;">${deptDetectName?if_exists}</div>-->
<#-- <div style="display:inline-block;width: 45%;text-align: right">${companyName?if_exists}</div>-->
<#--</div>-->
<#--
<table style="width: 100%; border-collapse: collapse; border: none">
<tr>
<td style="width: 50%; text-align: left; border-collapse: collapse; border: none ">
${deptDetectName?if_exists}
</td>
<td style="width: 50%; text-align: right;border-collapse: collapse; border: none ">
${companyName?if_exists}
</td>
</tr>
</table>
-->
<span style="display:inline-block;">${deptDetectName?if_exists} [${companyName?if_exists}]</span>
<span>分布情况:</span>
<#if (dataList?size gt 0) && dataList??>
<span>${deptDetectNameStr?if_exists}</span>
<#--<#list dataList as item>-->
<#-- <span>${item.atmosphereTypeStr?if_exists}</span>-->
<#--</#list>-->
</#if>
<hr/>
<p>检测时间: ${startTime?if_exists} 至 ${endTime?if_exists}</p>
<#--<p>监测设备: [${deviceName?if_exists}](${deviceSerial?if_exists}) 模块 ID: ${deviceId?if_exists}</p>-->
<p>监测设备: [${deviceName?if_exists}](${deviceSerial?if_exists}) </p>
<#--<p>${analysisMethod?if_exists}</p>-->
<p>采样点数量: ${analysisCount?if_exists}</p>
<p>检测区域网格平均尺寸: ${areaWidth?if_exists} 米 X ${areaLength?if_exists} 米 (${areaSize?if_exists} 平方米)</p>
<p>总检测区域网格面积: ${areaTotalSize?if_exists} (平方米)</p>
<p>检测区域中心点经纬度: ${areaLat?if_exists}, ${areaLon?if_exists}</p>
<hr/>
<#if (dataList?size gt 0) && dataList??>
<#list dataList as item>
<h3>${item.atmosphereTypeStr?if_exists}浓度分布情况</h3>
<p>${item.atmosphereTypeStr?if_exists}检测区域平均浓度:${item.concentrationAvag?if_exists} μg/m³</p>
<p>${item.atmosphereTypeStr?if_exists}网格浓度最高值: ${item.concentrationMax?if_exists} μg/m³</p>
<p>${item.atmosphereTypeStr?if_exists}网格浓度最小值: ${item.concentrationMin?if_exists} μg/m³</p>
<p>${item.atmosphereTypeStr?if_exists}单点浓度最高值: ${item.concentrationPointMax?if_exists} μg/m³ ${item.concentrationPointMaxTime?if_exists}</p>
<p>${item.atmosphereTypeStr?if_exists}单点浓度最低值: ${item.concentrationPointMin?if_exists} μg/m³ ${item.concentrationPointMinTime?if_exists}</p>
<div style="text-align: center">
<img src="${item.imgUrl?if_exists}" alt="${item.imgName?if_exists}" />
</div>
<div style="margin-top: 30px;">
<hr/>
</div>
</#list>
</#if>
</body>
</html>

81
dk-modules/sample/src/main/resources/templates/pdfSpiritSmell.ftl

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<title>SpiritSmell Record</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
text-align: center;
}
h2 {
text-align: left;
margin-top: 40px;
margin-left: 60px;
}
p{
margin-top: 10px;
margin-left: 60px;
}
table {
margin: 40px auto;
width: 100%;
}
/*h2 {*/
/* color: #1b5aa1;*/
/*}*/
td {
word-wrap: break-word;
word-break: break-all;
}
body{
font-size: 18px;
}
</style>
</head>
<body style="margin-top:30px;">
<!--<div style="display: flex;justify-content: space-between;">-->
<#--<div >-->
<#-- <div style="display:inline-block;width: 45%;">${deptDetectName?if_exists}</div>-->
<#-- <div style="display:inline-block;width: 45%;text-align: right">${companyName?if_exists}</div>-->
<#--</div>-->
<#--
<table style="width: 100%; border-collapse: collapse; border: none">
<tr>
<td style="width: 50%; text-align: left; border-collapse: collapse; border: none ">
${deptDetectName?if_exists}
</td>
<td style="width: 50%; text-align: right;border-collapse: collapse; border: none ">
${companyName?if_exists}
</td>
</tr>
</table>
-->
<h3 style="display:inline-block;">${deptDetectName?if_exists} [${companyName?if_exists}]</h3>
<hr/>
<h3 >${atmosphereTypeStr?if_exists}浓度分布情况</h3>
<p>检测时间: ${startTime?if_exists} 至 ${endTime?if_exists}</p>
<p>监测设备: [${deviceName?if_exists}](${deviceSerial?if_exists}) 模块 ID: ${deviceId?if_exists}</p>
<#--<p>${analysisMethod?if_exists}</p>-->
<p>采样点数量: ${analysisCount?if_exists}</p>
<p>检测区域网格平均尺寸: ${areaWidth?if_exists} 米 X ${areaLength?if_exists} 米 (${areaSize?if_exists} 平方米)</p>
<p>总检测区域网格面积: ${areaTotalSize?if_exists} (平方米)</p>
<p>检测区域中心点经纬度: ${areaLat?if_exists}, ${areaLon?if_exists}</p>
<p>${atmosphereTypeStr?if_exists}检测区域平均浓度:${concentrationAvag?if_exists} μg/m³</p>
<p>${atmosphereTypeStr?if_exists}网格浓度最高值: ${concentrationMax?if_exists} μg/m³</p>
<p>${atmosphereTypeStr?if_exists}网格浓度最小值: ${concentrationMin?if_exists} μg/m³</p>
<p>${atmosphereTypeStr?if_exists}单点浓度最高值: ${concentrationPointMax?if_exists} μg/m³ ${concentrationPointMaxTime?if_exists}</p>
<p>${atmosphereTypeStr?if_exists}单点浓度最低值: ${concentrationPointMin?if_exists} μg/m³ ${concentrationPointMinTime?if_exists}</p>
<div style="text-align: center">
<img src="${imgUrl?if_exists}" alt="${imgName?if_exists}" style=""/>
</div>
</body>
</html>

BIN
dk-modules/sample/src/main/resources/templates/灵嗅-pm2.5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

141
dk-modules/system/src/main/java/org/dromara/system/controller/SysMsgConfigUserController.java

@ -0,0 +1,141 @@
package org.dromara.system.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.system.service.ISysSubmailConfigService;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.system.domain.vo.SysMsgConfigUserVo;
import org.dromara.system.domain.bo.SysMsgConfigUserBo;
import org.dromara.system.service.ISysMsgConfigUserService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 系统短信发送配置用户
* 前端访问路由地址为:/system/msgConfigUser
*
* @author yq
* @date 2025-07-15
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/msgConfigUser")
public class SysMsgConfigUserController extends BaseController {
private final ISysMsgConfigUserService sysMsgConfigUserService;
private final ISysSubmailConfigService sysSubmailConfigService;
/**
* 查询系统短信发送配置用户列表
*/
@SaCheckPermission("system:msgConfigUser:list")
@GetMapping("/list")
public TableDataInfo<SysMsgConfigUserVo> list(SysMsgConfigUserBo bo, PageQuery pageQuery) {
return sysMsgConfigUserService.queryPageList(bo, pageQuery);
}
/**
* 导出系统短信发送配置用户列表
*/
@SaCheckPermission("system:msgConfigUser:export")
@Log(title = "系统短信发送配置用户", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(SysMsgConfigUserBo bo, HttpServletResponse response) {
List<SysMsgConfigUserVo> list = sysMsgConfigUserService.queryList(bo);
ExcelUtil.exportExcel(list, "系统短信发送配置用户", SysMsgConfigUserVo.class, response);
}
/**
* 短信消息发送
* @param deviceSn
* @param taskType 1:手动飞行 2:航线飞行
* @return
*/
@GetMapping("/sendMsg/{deviceSn}/{taskType}")
public R<String> sendMsg( @PathVariable String deviceSn, @PathVariable int taskType) {
// sysMsgConfigUserService.queryById(id)
SysMsgConfigUserBo bo = new SysMsgConfigUserBo();
bo.setDeviceSn(deviceSn);
bo.setMsgSendType("drone_send_opt_user");//机场操作-提示运行人员
List<SysMsgConfigUserVo> sysMsgConfigUserVoList = sysMsgConfigUserService.queryList(bo);
if(sysMsgConfigUserVoList.size() > 0) {
// remoteSubmailConfigService.remoteSendUser("smsMultixsend",List.of(Long.valueOf(completeTaskBo.getHandler())),noticeMap,"kKv7s3");
//构建推送信息
Map<String, Object> noticeMap = new HashMap<>();
noticeMap.put("dockName", sysMsgConfigUserVoList.get(0).getDeviceName());
noticeMap.put("taskType", taskType == 1 ? "手动飞行" : "航线飞行");
Set<String> phoneList = sysMsgConfigUserVoList.stream().map(SysMsgConfigUserVo::getPhonenumber).distinct().collect(Collectors.toSet());
sysSubmailConfigService.sendPhone("smsMultixsend",phoneList, JSON.toJSONString(noticeMap),"JXtQI");
}
return R.ok();
}
/**
* 获取系统短信发送配置用户详细信息
*
* @param id 主键
*/
@SaCheckPermission("system:msgConfigUser:query")
@GetMapping("/{id}")
public R<SysMsgConfigUserVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(sysMsgConfigUserService.queryById(id));
}
/**
* 新增系统短信发送配置用户
*/
@SaCheckPermission("system:msgConfigUser:add")
@Log(title = "系统短信发送配置用户", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysMsgConfigUserBo bo) {
return toAjax(sysMsgConfigUserService.insertByBo(bo));
}
/**
* 修改系统短信发送配置用户
*/
@SaCheckPermission("system:msgConfigUser:edit")
@Log(title = "系统短信发送配置用户", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysMsgConfigUserBo bo) {
return toAjax(sysMsgConfigUserService.updateByBo(bo));
}
/**
* 删除系统短信发送配置用户
*
* @param ids 主键串
*/
@SaCheckPermission("system:msgConfigUser:remove")
@Log(title = "系统短信发送配置用户", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(sysMsgConfigUserService.deleteWithValidByIds(List.of(ids), true));
}
}

4
dk-modules/system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java

@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@ -73,7 +74,8 @@ public class SysUserOnlineController extends BaseController {
Collections.reverse(userOnlineDTOList);
userOnlineDTOList.removeAll(Collections.singleton(null));
List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
return TableDataInfo.build(userOnlineList);
List<SysUserOnline> sysUserOnlineList = userOnlineList.stream().sorted(Comparator.comparing(SysUserOnline::getLoginTime).reversed()).collect(Collectors.toList());
return TableDataInfo.build(sysUserOnlineList);
}
/**

8
dk-modules/system/src/main/java/org/dromara/system/controller/system/AiLabelController.java

@ -74,6 +74,14 @@ public class AiLabelController extends BaseController {
}
@GetMapping("/{postCode}/dropDown/list")
public List<AiLabelVo> listDropDown(@PathVariable("postCode") String postCode) {
if (postCode.equalsIgnoreCase("all")){
return aiLabelService.allList();
}else {
return aiLabelService.selectLabelByList(postCode);
}
}
/**

4
dk-modules/system/src/main/java/org/dromara/system/controller/system/SysDepartBoundaryController.java

@ -52,8 +52,8 @@ public class SysDepartBoundaryController extends BaseController {
*/
@Operation(summary ="查询当前人所属部门的的地理位置",description = "查询当前人所属部门的的地理位置")
@GetMapping(value = "/listUserJson")
public R<String> listUserJson() {
String geom = departBoundaryService.listUserJson();
public R<String> listUserJson(@RequestParam("deptId") Long deptId) {
String geom = departBoundaryService.listUserJson(deptId);
return R.ok("查询成功!",geom);
}

4
dk-modules/system/src/main/java/org/dromara/system/controller/system/SysUserController.java

@ -340,8 +340,8 @@ public class SysUserController extends BaseController {
* @param params
* @return
*/
@GetMapping("/list/target")
public List<Map<String,Object>> listTargetTypeByUser(List<Map<String,Object>> params){
@PostMapping("/list/target")
public List<Map<String,Object>> listTargetTypeByUser(@RequestBody List<Map<String,Object>> params){
return userService.listTargetTypeByUser(params);
}

331
dk-modules/system/src/main/java/org/dromara/system/controller/system/YSYDeviceController.java

@ -0,0 +1,331 @@
package org.dromara.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysConfigBo;
import org.dromara.system.domain.vo.SysConfigVo;
import org.dromara.system.service.ISysConfigService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 参数配置 信息操作处理
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/ysyApi")
public class YSYDeviceController extends BaseController {
private final ISysConfigService configService;
private static final String BASE_URL = "https://open.ys7.com";
//opt操作
private static final String APP_TOKEN_GET = "/api/lapp/token/get";
//设备信息
private static final String DEVICE_LIST_API = "/api/lapp/device/list";
private static final String DEVICE_INFO = "/api/lapp/device/info";
private static final String DEVICE_CAMERA_LIST = "/api/lapp/device/camera/list";
private static final String DEVICE_NAME_UPDATE = "/api/lapp/device/name/update";
private static final String CAMERA_NAME_UPDATE = "/api/lapp/camera/name/update";
//云台控制
private static final String DEVICE_PTZ_START = "/api/lapp/device/ptz/start";
private static final String DEVICE_PTZ_STOP = "/api/lapp/device/ptz/stop";
//直播
private static final String LIVE_ADDRESS_GET = "/api/lapp/v2/live/address/get";
private static final String VIDEO_UNIFY_QUERY = "/api/v3/device/local/video/unify/query";
// @SaCheckPermission("system:config:list")
// @GetMapping("/device/list")
// public TableDataInfo<SysConfigVo> list(SysConfigBo config, PageQuery pageQuery) {
// return configService.selectPageConfigList(config, pageQuery);
// }
//--------------------------------------OPT操作---------------------------------------------
/** 获取accessToken https://open.ys7.com/help/81
* https://open.ys7.com/api/lapp/token/get
*/
@PostMapping("/token/get")
public JSONObject tokenGet(@RequestBody JSONObject params) {
if(!params.containsKey("appKey")){ return null; };
if(!params.containsKey("appSecret")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("appKey", params.getStr("appKey"));
body.put("appSecret", params.getStr("appSecret"));
String response = HttpRequest.post(BASE_URL + APP_TOKEN_GET)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
/** 修改云端设备名称 https://open.ys7.com/help/667
* https://open.ys7.com/api/lapp/device/name/update
*/
@PostMapping("/device/name/update")
public JSONObject deviceNameUpdate(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
if(!params.containsKey("deviceName")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getInt("deviceSerial"));
body.put("deviceName", params.getStr("deviceName"));
String response = HttpRequest.post(BASE_URL + DEVICE_NAME_UPDATE)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
/**
* 修改云端通道名称 https://open.ys7.com/help/668
* https://open.ys7.com/api/lapp/camera/name/update
*/
@PostMapping("/camera/name/update")
public JSONObject cameraNameUpdate(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
if(!params.containsKey("name")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getInt("deviceSerial"));
body.put("name", params.getStr("name"));
body.put("channelNo", params.getInt("channelNo",1));
String response = HttpRequest.post(BASE_URL + CAMERA_NAME_UPDATE)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
//--------------------------------------设备信息---------------------------------------------
/** 设备分页查询 https://open.ys7.com/help/673
* https://open.ys7.com/api/lapp/device/list
* **/
@PostMapping("/device/list")
public JSONObject deviceList(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("pageStart", params.getInt("pageStart",0));
body.put("pageSize", params.getInt("pageSize",10));
String response = HttpRequest.post(BASE_URL + DEVICE_LIST_API)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
/**获取单个设备信息 https://open.ys7.com/help/672
* https://open.ys7.com/api/lapp/device/info
*/
@PostMapping("/device/info")
public JSONObject deviceInfo(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getStr("deviceSerial"));
String response = HttpRequest.post(BASE_URL + DEVICE_INFO)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
/**获取指定设备的通道信息 https://open.ys7.com/help/1478
* https://open.ys7.com/api/lapp/device/camera/list
*/
@PostMapping("/device/camera/list")
public JSONObject deviceCameraList(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getStr("deviceSerial"));
String response = HttpRequest.post(BASE_URL + DEVICE_CAMERA_LIST)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
//--------------------------------------云台控制---------------------------------------------
/**开始云台控制 https://open.ys7.com/help/679
* https://open.ys7.com/api/lapp/device/ptz/start
*/
@PostMapping("/device/ptz/start")
public JSONObject devicePTZStart(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
if(!params.containsKey("channelNo")){ return null; };
if(!params.containsKey("direction")){ return null; };
if(!params.containsKey("speed")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getStr("deviceSerial"));
body.put("channelNo", params.getInt("channelNo",1));
body.put("direction", params.getInt("direction",16));
body.put("speed", params.getInt("speed",1));
//先停止一遍
String response_stop = HttpRequest.post(BASE_URL + DEVICE_PTZ_STOP)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
String response = HttpRequest.post(BASE_URL + DEVICE_PTZ_START)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
/**停止云台控制 https://open.ys7.com/help/680
* https://open.ys7.com/api/lapp/device/ptz/stop
*/
@PostMapping("/device/ptz/stop")
public JSONObject devicePTZStop(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
if(!params.containsKey("channelNo")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getStr("deviceSerial"));
body.put("channelNo", params.getInt("channelNo",1));
body.put("direction", params.getInt("direction",16));
String response = HttpRequest.post(BASE_URL + DEVICE_PTZ_STOP)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
//--------------------------------------播放---------------------------------------------
/**获取播放地址() https://open.ys7.com/help/1414
* https://open.ys7.com/api/lapp/v2/live/address/get
*
* protocol Integer 流播放协议1-ezopen2-hls3-rtmp4-flv默认为1
* type String 地址的类型1-预览2-本地录像回放3-云存储录像回放非必选默认为1回放仅支持rtmpezopenflv协议
*/
@PostMapping("/live/address/get")
public JSONObject liveAddressGet(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
if(!params.containsKey("type")){ return null; };
// if(!params.containsKey("channelNo")){ return null; };
Map<String, Object> body = new HashMap<>();
body.put("accessToken", params.getStr("accessToken"));
body.put("deviceSerial", params.getStr("deviceSerial"));
body.put("channelNo", params.getInt("channelNo",1));
body.put("protocol", params.getInt("protocol",1)); //流播放协议,1-ezopen(默认)、2-hls、3-rtmp、4-flv,
body.put("type", params.getInt("type",1)); //地址的类型,1-预览(默认),2-本地录像回放,3-云存储录像回放,回放仅支持rtmp、ezopen、flv协议
body.put("expireTime", params.getInt("expireTime",300));
body.put("quality", params.getInt("quality",2));
body.put("startTime", params.getStr("startTime"));
body.put("stopTime", params.getStr("stopTime"));
String response = HttpRequest.post(BASE_URL + LIVE_ADDRESS_GET)
.header("Content-Type", "application/x-www-form-urlencoded")
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
/**查询设备本地录像 https://open.ys7.com/help/3922
* https://open.ys7.com/api/v3/device/local/video/unify/query
*/
@PostMapping("/video/unify/query")
public JSONObject videoUnifyQuery(@RequestBody JSONObject params) {
if(!params.containsKey("accessToken")){ return null; };
if(!params.containsKey("deviceSerial")){ return null; };
if(!params.containsKey("startTime")){ return null; };
if(!params.containsKey("endTime")){ return null; };
Map<String, Object> body = new HashMap<>();
// body.put("accessToken", params.getStr("accessToken"));
// body.put("deviceSerial", params.getStr("deviceSerial"));
// body.put("localIndex", params.getInt("localIndex",1));
// body.put("recordType", params.getInt("recordType",1));
body.put("startTime", params.getStr("startTime"));
body.put("endTime", params.getStr("endTime"));
body.put("pageSize", params.getInt("pageSize",50));
String response = HttpRequest.get(BASE_URL + VIDEO_UNIFY_QUERY)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("accessToken", params.getStr("accessToken"))
.header("deviceSerial", params.getStr("deviceSerial"))
.header("localIndex", params.getStr("localIndex","1"))
.form(body)
.execute()
.body();
return JSONUtil.parseObj(response);
}
}

71
dk-modules/system/src/main/java/org/dromara/system/domain/SysMsgConfigUser.java

@ -0,0 +1,71 @@
package org.dromara.system.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 系统短信发送配置用户对象 sys_msg_config_user
*
* @author yq
* @date 2025-07-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_msg_config_user")
public class SysMsgConfigUser extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id")
private Long id;
/**
* 短信发送(字典)
*/
private String msgSendType;
/**
* 设备SN
*/
private String deviceSn;
/**
* 设备名称
*/
private String deviceName;
/**
* 赛邮配置ID
*/
private Long submailConfigId;
/**
* 用户Id
*/
private Long userId;
/**
* 用户名称
*/
private String name;
/**
* 手机号
*/
private String phonenumber;
/**
* 备注
*/
private String remark;
}

74
dk-modules/system/src/main/java/org/dromara/system/domain/bo/SysMsgConfigUserBo.java

@ -0,0 +1,74 @@
package org.dromara.system.domain.bo;
import org.dromara.system.domain.SysMsgConfigUser;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 系统短信发送配置用户业务对象 sys_msg_config_user
*
* @author yq
* @date 2025-07-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = SysMsgConfigUser.class, reverseConvertGenerate = false)
public class SysMsgConfigUserBo extends BaseEntity {
/**
* ID
*/
@NotNull(message = "ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 短信发送(字典)
*/
@NotBlank(message = "短信发送(字典)不能为空", groups = { AddGroup.class, EditGroup.class })
private String msgSendType;
/**
* 设备SN
*/
private String deviceSn;
/**
* 设备名称
*/
private String deviceName;
/**
* 赛邮配置ID
*/
private Long submailConfigId;
/**
* 用户Id
*/
@NotNull(message = "用户Id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long userId;
/**
* 用户名称
*/
@NotBlank(message = "用户名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String name;
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空", groups = { AddGroup.class, EditGroup.class })
private String phonenumber;
/**
* 备注
*/
private String remark;
}

87
dk-modules/system/src/main/java/org/dromara/system/domain/vo/SysMsgConfigUserVo.java

@ -0,0 +1,87 @@
package org.dromara.system.domain.vo;
import org.dromara.system.domain.SysMsgConfigUser;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 系统短信发送配置用户视图对象 sys_msg_config_user
*
* @author yq
* @date 2025-07-15
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = SysMsgConfigUser.class)
public class SysMsgConfigUserVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ExcelProperty(value = "ID")
private Long id;
/**
* 短信发送(字典)
*/
@ExcelProperty(value = "短信发送(字典)", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "msg_send_type")
private String msgSendType;
/**
* 设备SN
*/
@ExcelProperty(value = "设备SN")
private String deviceSn;
/**
* 设备名称
*/
@ExcelProperty(value = "设备名称")
private String deviceName;
/**
* 赛邮配置ID
*/
@ExcelProperty(value = "赛邮配置ID")
private Long submailConfigId;
/**
* 用户Id
*/
@ExcelProperty(value = "用户Id")
private Long userId;
/**
* 用户名称
*/
@ExcelProperty(value = "用户名称")
private String name;
/**
* 手机号
*/
@ExcelProperty(value = "手机号")
private String phonenumber;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

15
dk-modules/system/src/main/java/org/dromara/system/mapper/SysMsgConfigUserMapper.java

@ -0,0 +1,15 @@
package org.dromara.system.mapper;
import org.dromara.system.domain.SysMsgConfigUser;
import org.dromara.system.domain.vo.SysMsgConfigUserVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 系统短信发送配置用户Mapper接口
*
* @author yq
* @date 2025-07-15
*/
public interface SysMsgConfigUserMapper extends BaseMapperPlus<SysMsgConfigUser, SysMsgConfigUserVo> {
}

2
dk-modules/system/src/main/java/org/dromara/system/service/ISysDepartBoundaryService.java

@ -32,6 +32,6 @@ public interface ISysDepartBoundaryService extends IService<SysDepartBoundary>{
List<SysDepartBoundary> listDepartBoundaryByLngAndLat(Double lng, Double lat);
String listUserJson();
String listUserJson(Long deptId);
}

69
dk-modules/system/src/main/java/org/dromara/system/service/ISysMsgConfigUserService.java

@ -0,0 +1,69 @@
package org.dromara.system.service;
import org.dromara.system.domain.SysMsgConfigUser;
import org.dromara.system.domain.vo.SysMsgConfigUserVo;
import org.dromara.system.domain.bo.SysMsgConfigUserBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 系统短信发送配置用户Service接口
*
* @author yq
* @date 2025-07-15
*/
public interface ISysMsgConfigUserService {
/**
* 查询系统短信发送配置用户
*
* @param id 主键
* @return 系统短信发送配置用户
*/
SysMsgConfigUserVo queryById(Long id);
/**
* 分页查询系统短信发送配置用户列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 系统短信发送配置用户分页列表
*/
TableDataInfo<SysMsgConfigUserVo> queryPageList(SysMsgConfigUserBo bo, PageQuery pageQuery);
/**
* 查询符合条件的系统短信发送配置用户列表
*
* @param bo 查询条件
* @return 系统短信发送配置用户列表
*/
List<SysMsgConfigUserVo> queryList(SysMsgConfigUserBo bo);
/**
* 新增系统短信发送配置用户
*
* @param bo 系统短信发送配置用户
* @return 是否新增成功
*/
Boolean insertByBo(SysMsgConfigUserBo bo);
/**
* 修改系统短信发送配置用户
*
* @param bo 系统短信发送配置用户
* @return 是否修改成功
*/
Boolean updateByBo(SysMsgConfigUserBo bo);
/**
* 校验并批量删除系统短信发送配置用户信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

8
dk-modules/system/src/main/java/org/dromara/system/service/impl/SysDepartBoundaryServiceImpl.java

@ -247,8 +247,12 @@ public class SysDepartBoundaryServiceImpl extends ServiceImpl<SysDepartBoundaryM
}
@Override
public String listUserJson() {
Long deptId = LoginHelper.getDeptId();
public String listUserJson(Long deptId) {
if (ObjectUtil.isEmpty(deptId)){
deptId = LoginHelper.getDeptId();
}
return this.baseMapper.listJson(List.of(deptId));
}

135
dk-modules/system/src/main/java/org/dromara/system/service/impl/SysMsgConfigUserServiceImpl.java

@ -0,0 +1,135 @@
package org.dromara.system.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.dromara.system.domain.bo.SysMsgConfigUserBo;
import org.dromara.system.domain.vo.SysMsgConfigUserVo;
import org.dromara.system.domain.SysMsgConfigUser;
import org.dromara.system.mapper.SysMsgConfigUserMapper;
import org.dromara.system.service.ISysMsgConfigUserService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 系统短信发送配置用户Service业务层处理
*
* @author yq
* @date 2025-07-15
*/
@RequiredArgsConstructor
@Service
public class SysMsgConfigUserServiceImpl implements ISysMsgConfigUserService {
private final SysMsgConfigUserMapper baseMapper;
/**
* 查询系统短信发送配置用户
*
* @param id 主键
* @return 系统短信发送配置用户
*/
@Override
public SysMsgConfigUserVo queryById(Long id){
return baseMapper.selectVoById(id);
}
/**
* 分页查询系统短信发送配置用户列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 系统短信发送配置用户分页列表
*/
@Override
public TableDataInfo<SysMsgConfigUserVo> queryPageList(SysMsgConfigUserBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<SysMsgConfigUser> lqw = buildQueryWrapper(bo);
Page<SysMsgConfigUserVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的系统短信发送配置用户列表
*
* @param bo 查询条件
* @return 系统短信发送配置用户列表
*/
@Override
public List<SysMsgConfigUserVo> queryList(SysMsgConfigUserBo bo) {
LambdaQueryWrapper<SysMsgConfigUser> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<SysMsgConfigUser> buildQueryWrapper(SysMsgConfigUserBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<SysMsgConfigUser> lqw = Wrappers.lambdaQuery();
lqw.eq(StringUtils.isNotBlank(bo.getMsgSendType()), SysMsgConfigUser::getMsgSendType, bo.getMsgSendType());
lqw.eq(StringUtils.isNotBlank(bo.getDeviceSn()), SysMsgConfigUser::getDeviceSn, bo.getDeviceSn());
lqw.like(StringUtils.isNotBlank(bo.getDeviceName()), SysMsgConfigUser::getDeviceName, bo.getDeviceName());
lqw.eq(bo.getSubmailConfigId() != null, SysMsgConfigUser::getSubmailConfigId, bo.getSubmailConfigId());
lqw.eq(bo.getUserId() != null, SysMsgConfigUser::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getName()), SysMsgConfigUser::getName, bo.getName());
lqw.eq(StringUtils.isNotBlank(bo.getPhonenumber()), SysMsgConfigUser::getPhonenumber, bo.getPhonenumber());
return lqw;
}
/**
* 新增系统短信发送配置用户
*
* @param bo 系统短信发送配置用户
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(SysMsgConfigUserBo bo) {
SysMsgConfigUser add = MapstructUtils.convert(bo, SysMsgConfigUser.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改系统短信发送配置用户
*
* @param bo 系统短信发送配置用户
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(SysMsgConfigUserBo bo) {
SysMsgConfigUser update = MapstructUtils.convert(bo, SysMsgConfigUser.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(SysMsgConfigUser entity){
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除系统短信发送配置用户信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

14
dk-modules/system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

@ -659,7 +659,7 @@ public class SysUserServiceImpl implements ISysUserService {
.distinct()
.toList();
currentUserList = this.baseMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getDeptId, userRoleIds));
currentUserList = this.baseMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getUserId, userRoleIds));
break;
case 3:
@ -671,13 +671,25 @@ public class SysUserServiceImpl implements ISysUserService {
break;
}
//获取用户所属部门id
List<Long> deptIdList = currentUserList.stream().map(SysUser::getDeptId).distinct().toList();
List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>().in(SysDept::getDeptId, deptIdList));
Map<Long, String> deptMap = deptList.stream()
.collect(Collectors.toMap(
SysDept::getDeptId,
SysDept::getDeptName
));
//拿到目标值获取的信息进行构建返回格式
currentUserList.forEach(sysUser -> {
Map<String,Object> userMap = new HashMap<>();
//用户
userMap.put("userId",sysUser.getUserId());
userMap.put("userName",sysUser.getUserName());
userMap.put("nickName",sysUser.getNickName());
userMap.put("deptName",deptMap.get(sysUser.getDeptId()));
//防止重复存入
if (result.stream().noneMatch(p-> Long.valueOf(p.get("userId") + "").equals(sysUser.getUserId()))) {

7
dk-modules/system/src/main/resources/mapper/system/SysMsgConfigUserMapper.xml

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysMsgConfigUserMapper">
</mapper>

12
dk-modules/workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java

@ -1,5 +1,6 @@
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -223,4 +224,15 @@ public class FlwTaskController extends BaseController {
return R.ok(flwTaskService.currentTaskAllUser(taskId));
}
/**
* 取消忽略
*/
@SaCheckPermission("workflow:back:cancel")
@GetMapping("/back/cancel")
public R<RemoteStartProcessReturn> backCancel(@RequestParam("businessId") Long businessId,
@RequestParam(value = "labelEn") String labelEn) {
return R.ok(flwTaskService.backCancel(businessId,labelEn));
}
}

16
dk-modules/workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java

@ -40,14 +40,14 @@ public class WorkflowPermissionHandler implements PermissionHandler {
List<String> resultList = new ArrayList<>();
resultList.add("1");
resultList.add("dept:1");
resultList.add("${handler}");
//没登陆
if (!LoginHelper.isLogin()){
return resultList;
}
// resultList.add("dept:1");
// resultList.add("${handler}");
// //没登陆
// if (!LoginHelper.isLogin()){
//
// return resultList;
// }
LoginUser loginUser = LoginHelper.getLoginUser();

1
dk-modules/workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java

@ -226,4 +226,5 @@ public interface IFlwTaskService {
Boolean addSign(Long instanceId);
RemoteStartProcessReturn backCancel(Long businessId,String labelEn);
}

38
dk-modules/workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java

@ -18,15 +18,13 @@ import org.dromara.business.api.RemoteBusinessAlertService;
import org.dromara.business.api.domain.vo.RemoteBusinessAlertVo;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.utils.*;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.api.RemotePostService;
import org.dromara.system.api.RemoteSubmailConfigService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.system.api.domain.vo.RemoteUserVo;
@ -107,6 +105,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
@DubboReference
private RemoteBusinessAlertService remoteBusinessAlertService;
@DubboReference(timeout = 30000)
private RemotePostService remotePostService;
/**
* 启动任务
*
@ -239,6 +240,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
noticeMap.put("labelCn",remoteBusinessAlertVo.getLabelCn());
noticeMap.put("labelEn",remoteBusinessAlertVo.getLabelEn());
noticeMap.put("jobName",remoteBusinessAlertVo.getJobName());
noticeMap.put("createTime", DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss",remoteBusinessAlertVo.getCreateTime()));
//发送短信是否属于指派
if(completeTaskBo.getAssign()){
@ -997,4 +999,32 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
return true;
}
/**
* 取消忽略
* @param businessId
* @return
*/
@Override
public RemoteStartProcessReturn backCancel(Long businessId,String labelEn) {
//查询m是否存在未删除的业务对应的实例id
FlowInstance flowInstance = flowInstanceMapper.selectOne(new LambdaQueryWrapper<FlowInstance>().eq(FlowInstance::getBusinessId,String.valueOf(businessId)).eq(FlowInstance::getDelFlag,"0"));
//如果不是空的说明还有正在审批的流程
if (ObjectUtil.isNotEmpty(flowInstance)) {
throw new ServiceException("当前预警还存在正在审批的流程!");
}
//获取流程code
String flowCode = remotePostService.getFlowCode(labelEn);
//重新生成流程
StartProcessBo startProcessBo = new StartProcessBo();
startProcessBo.setFlowCode(flowCode);
startProcessBo.setBusinessId(String.valueOf(businessId));
IFlwTaskService service = applicationContext.getBean(IFlwTaskService.class);
return service.startWorkFlow(startProcessBo);
}
}

Loading…
Cancel
Save