Browse Source

自动播放直播流

dev
吴远 3 weeks ago
parent
commit
0adbdaa281
  1. 44
      Jenkinsfile
  2. 49
      dk-modules/business/src/main/java/org/dromara/business/utils/CoordinateModel.java
  3. 224
      dk-modules/business/src/main/java/org/dromara/business/utils/KMZGenerator.java
  4. BIN
      dk-modules/business/src/main/resources/icon/green.png
  5. BIN
      dk-modules/business/src/main/resources/icon/map.png
  6. 14
      dk-modules/sample/src/main/java/org/dromara/sample/feign/RemoteMsgConfigUserFeign.java
  7. 229
      dk-modules/sample/src/main/java/org/dromara/sample/utils/AtmosphereCalculateUtil.java
  8. 101
      dk-modules/sample/src/main/java/org/dromara/sample/utils/HtmlConvertPdfHelper.java
  9. 62
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/domain/bo/WaylineJobAtmosphereData.java
  10. BIN
      dk-modules/sample/src/main/resources/templates/empty.png
  11. 101
      dk-modules/sample/src/main/resources/templates/pdfSpiritSmell-batch.ftl
  12. 81
      dk-modules/sample/src/main/resources/templates/pdfSpiritSmell.ftl
  13. BIN
      dk-modules/sample/src/main/resources/templates/灵嗅-pm2.5.png
  14. 141
      dk-modules/system/src/main/java/org/dromara/system/controller/SysMsgConfigUserController.java
  15. 331
      dk-modules/system/src/main/java/org/dromara/system/controller/system/YSYDeviceController.java
  16. 71
      dk-modules/system/src/main/java/org/dromara/system/domain/SysMsgConfigUser.java
  17. 74
      dk-modules/system/src/main/java/org/dromara/system/domain/bo/SysMsgConfigUserBo.java
  18. 87
      dk-modules/system/src/main/java/org/dromara/system/domain/vo/SysMsgConfigUserVo.java
  19. 15
      dk-modules/system/src/main/java/org/dromara/system/mapper/SysMsgConfigUserMapper.java
  20. 69
      dk-modules/system/src/main/java/org/dromara/system/service/ISysMsgConfigUserService.java
  21. 135
      dk-modules/system/src/main/java/org/dromara/system/service/impl/SysMsgConfigUserServiceImpl.java
  22. 7
      dk-modules/system/src/main/resources/mapper/system/SysMsgConfigUserMapper.xml

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
"""
}
}
}
}
}
}

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;
}
}

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;
}
}

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

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);
}

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);
}
}

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;
}

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));
}
}

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> {
}

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);
}

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;
}
}

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>
Loading…
Cancel
Save